PDFをGyazoにuploadするUserScript
PDFを画像に変換してGyazoにuploadするscript
使用例
these scripts can be executed on scrapbox.io without bundling.
開発コンソールに直接貼り付けても実行できる
code:sample.js
await (async () => {
const { upload } = await import(
"/api/code/takker/PDFをGyazoにuploadするUserScript/main.js"
);
const list = await upload({
// title: "本の名前",
// refererURL: "https://example.com",
//printResolution: 600,
});
console.log(list);
// gyazo.jsonが不要ならfalseにする
if (true) {
const blob = new Blob(JSON.stringify(list), { type: "application/json" });
const a = document.createElement("a");
a.download = "gyazo.json";
a.src = URL.createObjectURL(blob);
document.body.append(a);
a.click();
a.remove();
}
})();
code:main.js
import { uploadPDF } from "./mod.js";
export const upload = async (init) => {
const list = [];
for await (const { index, count, permalink_url } of await uploadPDF(init)) {
console.log([${index}/${count}] upload ${permalink_url});
list.push(permalink_url);
}
return list;
};
実装
scrapbox-pdftoimageとdeno-gyazoを使う
バグ
✅createdで順番を調節するのを止める
未来の日時にuploadしたことになったりして、画像の閲覧で支障が生じる
uploadしたのに一覧にでなくなったり
ci7lusさんがやっている方法で順番を調整することにする
2025-06-16 07:55:39 pooledMapやめる
順番がばらばらになる
2025-02-22
10:08:28
PDFの途中からuploadできるようにした
jsrとnpm importに切り替えた
async-libをやめて、deno_std/asyncを使う
2023-02-11
07:10:36
bundle済みコードを貼り付けた
サンプルコードを書き換えた
06:34:13 タイトル変更
もともとは「講義資料PDF uploader」という名前のscriptだった
H1-2022F-1用に即席でこしらえたもの
その後、講義その他で入手したPDF形式の資料や講義スライド、講義ノートPDFをgyazoるのに使い続けた
名前が実体にあわなくなったので、「PDFをGyazoにuploadするUserScript」に解明した
2022-05-09
10:11:49 解像度を設定できるようにした
2022-04-20
11:42:14 moduleにして使いやすくした
11:30:09 H1-2022F-1から切り出した
2022-04-17
14:46:53 順番の調整で手こずった
createdで順番が決まり、さらに1分刻みでしか変えられないことがわかった
#Gyazo_API
この結果をもとに、うまく順番通りにpdfをあげられるように直した
printResolutionは300を初期値にしてある
$ deno check --remote -r=https://scrapbox.io https://scrapbox.io/api/code/takker/PDFをGyazoにuploadするUserScript/mod.ts
code:mod.ts
/// <reference no-default-lib="true" />
/// <reference lib="esnext" />
/// <reference lib="dom" />
import { pdfConverter } from "../scrapbox-pdftoimage/mod.ts";
import { upload as uploadFile } from "../scrapbox-file-uploader/mod.ts";
import { getGyazoToken } from "jsr:@cosense/std@0.29/rest";
import { upload } from "jsr:@takker/gyazo@0.4";
import { range } from "jsr:@core/iterutil@0.9/range";
import { map } from "jsr:@core/iterutil@0.9/async/map";
import { pooledMap } from "jsr:@std/async@1/pool";
import { getUnixTime } from "npm:date-fns@4/getUnixTime";
import { isErr, unwrapErr, unwrapOk } from "npm:option-t@51/plain_result";
export interface Props {
title?: string;
refererURL?: string;
/** @default {300} */
printResolution?: number;
/** @default {1} */
start?: number;
};
export async function* uploadPDF(props: Props): AsyncGenerator<{ index: number; count: number; }> {
// 読み込み
const file = await uploadFile({ accept: "application/pdf, *.pdf"});
if (!file) throw new Error("no file specified");
// access token取得
const result = await getGyazoToken();
if (isErr(result)) throw new Error(JSON.stringify(unwrapErr(result)));
const accessToken = unwrapOk(result);
if (!accessToken) throw new Error("Could not get the access token");
// 画像に変換する
const { convert, count } = await pdfConverter(new Uint8Array(await file.arrayBuffer()));
yield* map(
range(props?.start ?? 1, count),
async (index: number) => {
const image = await convert(index, {
mimeType: "image/jpeg",
printResolution: props?.printResolution ?? 300,
quality: 0.95,
});
const res = await upload(image, {
created: getUnixTime(new Date()),
accessToken,
description: ${file.name} ${index},
...props,
});
if (!res.ok) throw new Error("gyazo error", { cause: res });
return { index, count, ...(await res.json())};
});
}
built script
build
code:mod.js
var Q=()=>new Promise(e=>{window.pdfjsLib&&e(window.pdfjsLib);let r=setInterval(()=>{window.pdfjsLib&&(clearInterval(r),e(window.pdfjsLib))},1e3)}),X=async e=>{let r=//cdnjs.cloudflare.com/ajax/libs/pdf.js/${e}/pdf.min.js;if(!document.querySelector(script[src="${r}"])){let o=document.createElement("script");o.src=r,await new Promise((n,i)=>{o.addEventListener("load",()=>n()),o.addEventListener("error",i),document.body.append(o)});let t=await Q();return t.GlobalWorkerOptions.workerSrc=//cdnjs.cloudflare.com/ajax/libs/pdf.js/${e}/pdf.worker.min.js,t}return await Q()};var Z=async(e,r)=>{let o=Array.prototype.getIndexByTitleLc;delete Array.prototype.getIndexByTitleLc;try{let n=await(await X("2.13.216")).getDocument({data:e,cMapUrl:r?.cMapUrl??"https://storage.googleapis.com/chrono-lexica/ci7lus-assets/pdfjs/cmaps/",cMapPacked:!0}).promise,i=await n.getMetadata(),u=async(T,c)=>{let I=await n.getPage(T),S=I.getViewport({scale:window.devicePixelRatio??1.5}),E=document.createElement("canvas"),K=E.getContext("2d");if(!K)throw Error("2D rendering on <canvas> is not supported");let k=(c?.printResolution??150)/72,Ne={canvasContext:K,viewport:S,transform:k,0,0,k,0,0};return E.height=Math.floor(S.height*k),E.width=Math.floor(S.width*k),await I.render(Ne).promise,new Promise((we,Fe)=>E.toBlob(J=>J?we(J):Fe(new Error("Faild to create Blob")),c?.mimeType??"image/png",c?.quality))};return{metadata:{info:i.info,metadata:i.metadata},count:n.numPages,convert:(T,c)=>u(Math.min(Math.max(1,T),n.numPages),c),read:async function*(T){for(let c=1;c<=n.numPages;c++)yield await u(c,T)}}}finally{Array.prototype.getIndexByTitleLc=o}};function ee(e){return new Promise((r,o)=>{let t=document.createElement("input");t.type="file",t.accept=e.accept,t.multiple=e.multiple??!1,t.addEventListener("change",()=>{r(e.multiple===!0?t.files?t.files.length===0?void 0:t.files:void 0:t.files?.0??void 0)}),t.addEventListener("error",o),t.click()})}function l(e){return e.val}function R(e){return e.err}var re=" must not return ",Pe="transformer",Ae="recoverer",be="defaultValue",P=Pe+re,x="called with ",A=be+" must not be ",b=Ae+re;var Ue="Ok",te="Err",oe=x+te,ne=x+Ue,Ie="Carrying E in "+te+" instead of throwing it directly. See .cause",V="an instance of Error of the current realm.",sr="The thrown value is not "+V,ar="The contained E should be "+V,Se="This .cause is not "+V;function p(e){return e.ok}function a(e){return{ok:!0,val:e,err:null}}function s(e){return!e.ok}function f(e){return{ok:!1,val:null,err:e}}function m(e){return se(e,oe)}function D(e){return ae(e,ne)}function se(e,r){if(s(e))throw new TypeError(r);return e.val}function ae(e,r){if(p(e))throw new TypeError(r);return e.err}async function d(e,r){if(s(e))return e;let o=l(e),t=await r(o);return a(t)}async function y(e,r){if(p(e))return e;let o=R(e),t=await r(o);return f(t)}var L="null",gt=P+L,Me=x+L,ht=A+L,Ot=b+L;var M="undefined",wt=P+M,Ce=x+M,Ft=A+M,Pt=b+M;var g=e=>e.ok?a(e):f({name:"HTTPError",message:${e.status} ${e.statusText},response:e});var ue=async(e,r)=>{let o=new Request(e,r);try{return a(await globalThis.fetch(o))}catch(t){if(t instanceof DOMException&&t.name==="AbortError")return f({name:"AbortError",message:t.message,request:o});if(t instanceof TypeError)return f({name:"NetworkError",message:t.message,request:o});throw t}};var h=e=>{let{fetch:r=ue,hostName:o="scrapbox.io",...t}=e;return{fetch:r,hostName:o,...t}};var O=e=>connect.sid=${e};function C(e,r={}){if(e===null)return"null";if(Array.isArray(e))return qe(e,r);switch(typeof e){case"string":return JSON.stringify(e);case"bigint":return${e}n;case"object":return e.constructor?.name!=="Object"?e.constructor?.name:$e(e,r);case"function":return e.name||"(anonymous)"}return e?.toString()??"undefined"}function qe(e,r){let{threshold:o=20}=r,t=e.map(u=>C(u,r)),n=t.join(", ");if(n.length<=o)return[${n}];let i=t.join(`,
);return[
${ce(2,i)}
]}function $e(e,r){let{threshold:o=20}=r,t=[...Object.keys(e),...Object.getOwnPropertySymbols(e)].map(u=>${u.toString()}: ${C(eu,r)}),n=t.join(", ");if(n.length<=o)return{${n}};let i=t.join(,
);return{
${ce(2,i)}
}}function ce(e,r){let o=" ".repeat(e);return r.split(
).map(t=>${o}${t}).join(
)}function B(e,r,...o){let t;return Object.defineProperties(e,{name:{get:()=>t||(t=${r}(${o.map(n=>C(n)).join(", ")}),t)}})}function me(e){return Array.isArray(e)}function le(e){return B(r=>me(r)&&r.every(o=>e(o)),"isArrayOf",e)}function W(e){let r=new Set(e);return B(o=>r.has(o),"isLiteralOneOf",e)}function H(e){return e!=null&&!Array.isArray(e)&&typeof e=="object"}function j(e){return typeof e=="string"}var _=async(e,r)=>{let o=e.response.clone(),t=W(r);try{let n=await o.json();if(!H(n))return;if(o.status===422){if(!j(n.message))return;for(let i of["NoQueryError","InvalidURLError"])if(r.includes(i))return{name:i,message:n.message}}return!t(n.name)||!j(n.message)?void 0:n.name==="NotLoggedInError"?!H(n.detals)||!j(n.detals.project)||!le(ve)(n.detals.loginStrategies)?void 0:{name:n.name,message:n.message,details:{project:n.detals.project,loginStrategies:n.detals.loginStrategies}}:{name:n.name,message:n.message}}catch(n){if(n instanceof SyntaxError)return;throw n}},ve=W(["google","github","microsoft","gyazo","email","saml","easy-trial"]);var G="null or undefined",U=P+G,Ve=x+G,z=A+G,q=b+G;var de=async e=>{let{fetch:r,sid:o,hostName:t,gyazoTeamsName:n}=h(e??{}),i=new Request(https://${t}/api/login/gyazo/oauth-upload/token${n??gyazoTeamsName=${n}:""},o?{headers:{Cookie:O(o)}}:void 0),u=await r(i);return s(u)?u:d(await y(g(m(u)),async T=>await _(T,["NotLoggedInError"])??T),T=>T.json().then(c=>c.token))};var He=new TextEncoder().encode("0123456789abcdef"),Ee=new Uint8Array(128).fill(16);He.forEach((e,r)=>Ee[e]=r);new TextEncoder().encode("ABCDEF").forEach((e,r)=>Ee[e]=r+10);var ze=new TextEncoder().encode("0123456789abcdef"),Re=new Uint8Array(128).fill(16);ze.forEach((e,r)=>Re[e]=r);new TextEncoder().encode("ABCDEF").forEach((e,r)=>Re[e]=r+10);var v=e=>{let{fetch:r=globalThis.fetch,...o}=e;return{fetch:r,...o}};var ye=(e,r)=>{let{title:o,description:t,metadataIsPublic:n,collectionId:i,refererURL:u,accessToken:T,created:c,app:I,fetch:S}=v(r),E=new FormData;return E.append("imagedata",e),E.append("access_token",T),u&&E.append("referer_url",u.toString()),I!==void 0&&E.append("app",I),o!==void 0&&E.append("title",o),t!=null&&E.append("desc",t),i&&E.append("collection_id",i),n&&E.append("metadata_is_public","true"),c!==void 0&&E.append("created_at",${c}),S("https://upload.gyazo.com/api/upload",{method:"POST",mode:"cors",credentials:"omit",body:E})};function ge(e,r,o){if(o??=e>r?-1:1,!Number.isFinite(e))throw new RangeError(start must be finite, but got ${e}.);if(!Number.isFinite(o))throw new RangeError(step must be finite, but got ${o}.);if(o===0)throw new RangeError("step must not be 0.");if(!Number.isFinite(r))throw new RangeError(stop must be finite, but got ${r}.);if(o>0&&e>r)throw new RangeError("start must be less than stop for positive step.");if(o<0&&e<r)throw new RangeError("start must be greater than stop for negative step.");return function*(){if(o>=0)for(let t=e;t<=r;t+=o)yield t;else for(let t=e;t>=r;t+=o)yield t}()}async function*he(e,r){let o=0;for await(let t of e)yield await r(t,o++)}var Ye=Math.pow(10,8)*24*60*60*1e3,ac=-Ye;var Ke=3600;var Oe=Ke*24,ic=Oe*7,Je=Oe*365.2425,Qe=Je/12,pc=Qe*3,Y=Symbol.for("constructDateFrom");function Te(e,r){return typeof e=="function"?e(r):e&&typeof e=="object"&&Y in e?e[Y](r):e instanceof Date?new e.constructor(r):new Date(r)}function _e(e,r){return Te(r||e,e)}function xe(e){return Math.trunc(+_e(e)/1e3)}async function*bc(e){let r=await ee({accept:"application/pdf, *.pdf"});if(!r)throw new Error("no file specified");let o=await de();if(s(o))throw new Error(JSON.stringify(D(o)));let t=m(o);if(!t)throw new Error("Could not get the access token");let{convert:n,count:i}=await Z(new Uint8Array(await r.arrayBuffer()));yield*he(ge(e?.start??1,i),async u=>{let T=await n(u,{mimeType:"image/jpeg",printResolution:e?.printResolution??300,quality:.95}),c=await ye(T,{created:xe(new Date),accessToken:t,description:${r.name} ${u}`,...e});if(!c.ok)throw new Error("gyazo error",{cause:c});return{index:u,count:i,...await c.json()}})}export{bc as uploadPDF};
#2025-04-03 08:45:08
#2025-03-02 12:06:50
#2025-02-22 10:08:22
#2023-02-11 07:12:08
#2023-02-10 21:36:35
#2022-09-13 09:40:00
#2022-08-12 20:38:08
#2022-05-09 10:11:45
#2022-04-20 11:29:34
#2022-04-19 15:29:23
#2022-04-17 15:15:13