generated at
PDFをGyazoにuploadするUserScript
PDFを画像に変換してGyazoにuploadするscript


使用例
these scripts can be executed on scrapbox.io without bundling.
開発コンソールに直接貼り付けても実行できる
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(); } })();
main.js
import { uploadPDF } from "./mod.js"; export const upload = async (init) => { const list = []; for await (const result of uploadPDF(init)) { console.log(result); if (!result.success) { console.error(result.reason); } else { console.log(`[${result.value.index + 1}/${result.value.count}] upload ${result.value.permalink_url}`); list.push(result.value.permalink_url); } } return list; };

実装

バグ
created で順番を調節するのを止める
未来の日時にuploadしたことになったりして、画像の閲覧で支障が生じる
uploadしたのに一覧にでなくなったり
ci7lusさんがやっている方法で順番を調整することにする

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分刻みでしか変えられないことがわかった
この結果をもとに、うまく順番通りにpdfをあげられるように直した

printResolution 300 を初期値にしてある
$ deno check --remote -r=https://scrapbox.io https://scrapbox.io/api/code/takker/講義資料PDF_uploader/mod.ts
mod.ts
/// <reference no-default-lib="true" /> /// <reference lib="esnext" /> /// <reference lib="dom" /> import { pdfConverter } from "../scrapbox-pdftoimage/mod.ts"; import { upload } from "../deno-gyazo/mod.ts"; import { upload as uploadFile } from "../scrapbox-file-uploader/mod.ts"; import { pool, sort, Result } from "../async-lib/mod.ts"; import { getGyazoToken } from "../scrapbox-userscript-std/rest.ts"; export interface Props { title?: string; refererURL?: string; printResolution?: number; }; export async function* uploadPDF(props: Props): AsyncGenerator<Result<unknown & { index: number; count: number; }>, void, unknown> { // 読み込み const file = await uploadFile({ accept: "application/pdf, *.pdf"}); if (!file) throw new Error("no file specified"); // access token取得 const result = await getGyazoToken(); if (!result.ok) throw new Error(JSON.stringify(result.value)); const accessToken = result.value; if (!accessToken) throw new Error("Could not get the access token"); // 画像に変換する const { convert, count } = await pdfConverter(new Uint8Array(await file.arrayBuffer())); let counter = 0; const reader = pool( 5, Array(count).keys(), async (index: number): Promise<unknown & { index: number; }> => { const image = await convert(index + 1, { mimeType: "image/jpeg", printResolution: props?.printResolution ?? 300, quality: 0.95, }); // 順番を保証する if (counter < index) { await new Promise<void>((resolve) => { const timer = setInterval( () => { if (counter < index) return; clearInterval(timer); resolve(); }, 1000, ); }); } const result = await upload(image, { created: new Date(), accessToken, description: `${file.name} ${index + 1}`, ...props, }); counter++; if (!result.ok) throw new Error(JSON.stringify(result.value)); return { index, count, ...result.value}; }, ); yield* sort([...reader]); };

built script
mod.js
var P=()=>new Promise(e=>{window.pdfjsLib&&e(window.pdfjsLib);let t=setInterval(()=>{!window.pdfjsLib||(clearInterval(t),e(window.pdfjsLib))},1e3)}),b=async e=>{let t=`//cdnjs.cloudflare.com/ajax/libs/pdf.js/${e}/pdf.min.js`;if(!document.querySelector(`script[src="${t}"]`)){let r=document.createElement("script");r.src=t,await new Promise((s,n)=>{r.addEventListener("load",()=>s()),r.addEventListener("error",n),document.body.append(r)});let o=await P();return o.GlobalWorkerOptions.workerSrc=`//cdnjs.cloudflare.com/ajax/libs/pdf.js/${e}/pdf.worker.min.js`,o}return await P()};var I=async(e,t)=>{let r=Array.prototype.getIndexByTitleLc;delete Array.prototype.getIndexByTitleLc;try{let s=await(await b("2.13.216")).getDocument({data:e,cMapUrl:t?.cMapUrl??"https://storage.googleapis.com/chrono-lexica/ci7lus-assets/pdfjs/cmaps/",cMapPacked:!0}).promise,n=await s.getMetadata(),c=async(i,a)=>{let p=await s.getPage(i),l=p.getViewport({scale:window.devicePixelRatio??1.5}),d=document.createElement("canvas"),h=d.getContext("2d");if(!h)throw Error("2D rendering on <canvas> is not supported");let g=(a?.printResolution??150)/72,W={canvasContext:h,viewport:l,transform:[g,0,0,g,0,0]};return d.height=Math.floor(l.height*g),d.width=Math.floor(l.width*g),await p.render(W).promise,new Promise((X,K)=>d.toBlob(R=>R?X(R):K(new Error("Faild to create Blob")),a?.mimeType??"image/png",a?.quality))};return{metadata:{info:n.info,metadata:n.metadata},count:s.numPages,convert:(i,a)=>c(Math.min(Math.max(1,i),s.numPages),a),read:async function*(i){for(let a=1;a<=s.numPages;a++)yield await c(a,i)}}}finally{Array.prototype.getIndexByTitleLc=r}};var k=e=>{let{fetch:t=globalThis.fetch,...r}=e;return{fetch:t,...r}},N=e=>typeof e=="object"&&e!==null;var w=class extends Error{constructor(t){super(`${t.status} ${t.statusText} when fetching ${t.path.toString()}`);this.name="UnexpectedResponseError";this.status=t.status,this.statusText=t.statusText,this.body=t.body,this.path=t.path,Error.captureStackTrace&&Error.captureStackTrace(this,w)}};var x=async e=>{let t=await e.text();if(e.ok)return{ok:!0,value:t};if(e.status===400)return{ok:!1,value:{name:"BadRequestError",message:t}};try{let r=JSON.parse(t);if(!N(r)||typeof r.message!="string")throw new w({status:e.status,statusText:e.statusText,body:t,path:new URL(e.url)});switch(e.status){case 401:return{ok:!1,value:{name:"UnauthorizedError",message:r.message}};case 403:return{ok:!1,value:{name:"NotPrivilegeError",message:r.message}};case 404:return{ok:!1,value:{name:"NotFoundError",message:r.message}};case 422:return{ok:!1,value:{name:"InvalidParameterError",message:r.message}};case 429:return{ok:!1,value:{name:"RateLimitError",message:r.message}};default:throw new w({status:e.status,statusText:e.statusText,body:t,path:new URL(e.url)})}}catch(r){throw r instanceof SyntaxError?new w({status:e.status,statusText:e.statusText,body:t,path:new URL(e.url)}):r}};var v=async(e,t)=>{let{title:r,description:o,metadataIsPublic:s,collectionId:n,refererURL:c,accessToken:i,created:a,app:p,fetch:l}=k(t),d=new FormData;d.append("imagedata",e),d.append("access_token",i),c&&d.append("referer_url",c.toString()),p!==void 0&&d.append("app",p),r!==void 0&&d.append("title",r),o!=null&&d.append("desc",o),n&&d.append("collection_id",n),s&&d.append("metadata_is_public","true"),a!==void 0&&d.append("created_at",`${a}`);let h=await l("https://upload.gyazo.com/api/upload",{method:"POST",mode:"cors",credentials:"omit",body:d}),g=await x(h);return g.ok?{ok:!0,value:JSON.parse(g.value)}:g};function L(e){return new Promise((t,r)=>{let o=document.createElement("input");o.type="file",o.accept=e.accept,o.multiple=e.multiple??!1,o.addEventListener("change",()=>{t(e.multiple===!0?o.files?o.files.length===0?void 0:o.files:void 0:o.files?.[0]??void 0)}),o.addEventListener("error",r),o.click()})}function T(e){let t=e?.maxQueued===void 0?void 0:Math.max(0,e.maxQueued),r=[],o=[],s,n;return[async()=>{t!==void 0&&r.splice(0,r.length-t);let p=o.shift()??r.shift();if(p){if(p.success)return p.value;throw p.reason}return await new Promise((l,d)=>{s=l,n=d})},(p,l)=>{if(!s){l?.forceQueued?o.push({success:!0,value:p}):r.push({success:!0,value:p});return}s(p),s=n=void 0},(p,l)=>{if(!n){l?.forceQueued?o.push({success:!1,reason:p}):r.push({success:!1,reason:p});return}n(p),s=n=void 0}]}function*O(e,t,r){let o=0,s=[],n=async()=>(o++,o<=e?o-1:await new Promise(c=>s.push(c)));for(let c of t)yield(async()=>{let i=await n();try{return{success:!0,value:await r(c,i)}}catch(a){return{success:!1,reason:a}}finally{o--,s.shift()?.(i)}})()}async function*j(e){let[t,r]=T(),o=0;for(let s of e)o++,s.then(n=>r(n)).catch(()=>o--);for(let s=0;s<o;s++)yield await t()}var u=e=>{let{fetch:t=globalThis.fetch,hostName:r="scrapbox.io",...o}=e;return{fetch:t,hostName:r,...o}};var V=e=>typeof e=="object"&&e!==null,H=e=>V(e)?(e.name===void 0||typeof e.name=="string")&&typeof e.message=="string":!1,S=e=>{try{let t=typeof e=="string"?JSON.parse(e):e;return H(t)?t:!1}catch(t){if(t instanceof SyntaxError)return!1;throw t}};var E=class extends Error{constructor(t){super(`${t.status} ${t.statusText} when fetching ${t.url}`);this.response=t;this.name="UnexpectedResponseError";Error.captureStackTrace&&Error.captureStackTrace(this,E)}},f=async e=>{let t=e.clone(),r=await t.text(),o=S(r);if(!o)throw new E(t);return{ok:!1,value:o}};var m=e=>`connect.sid=${e}`;var q=e=>[...e].map((t,r)=>t===" "?"_":!Y.includes(t)||r===e.length-1&&Z.includes(t)?encodeURIComponent(t):t).join(""),Y='@$&+=:;",',Z=':;",';var F=(e,t,r)=>{let{sid:o,hostName:s,followRename:n,projects:c}=u(r??{}),i=new URLSearchParams;i.append("followRename",`${n??!0}`);for(let p of c??[])i.append("projects",p);let a=`https://${s}/api/pages/${e}/${q(t)}?${i.toString()}`;return new Request(a,o?{headers:{Cookie:m(o)}}:void 0)},U=async e=>{if(!e.ok)return e.status===414?{ok:!1,value:{name:"TooLongURIError",message:"project ids may be too much."}}:f(e);let t=await e.json();return{ok:!0,value:t}},$=async(e,t,r)=>{let{fetch:o}=u(r??{}),s=F(e,t,r),n=await o(s);return await U(n)};$.toRequest=F;$.fromResponse=U;var B=(e,t)=>{let{sid:r,hostName:o,sort:s,limit:n,skip:c}=u(t??{}),i=new URLSearchParams;s!==void 0&&i.append("sort",s),n!==void 0&&i.append("limit",`${n}`),c!==void 0&&i.append("skip",`${c}`);let a=`https://${o}/api/pages/${e}?${i.toString()}`;return new Request(a,r?{headers:{Cookie:m(r)}}:void 0)},C=async e=>{if(!e.ok)return f(e);let t=await e.json();return{ok:!0,value:t}},M=async(e,t)=>{let{fetch:r}=u(t??{}),o=await r(B(e,t));return await C(o)};M.toRequest=B;M.fromResponse=C;var G=(e,t)=>{let{sid:r,hostName:o}=u(t??{});return new Request(`https://${o}/api/projects/${e}`,r?{headers:{Cookie:m(r)}}:void 0)},A=async e=>{if(!e.ok)return f(e);let t=await e.json();return{ok:!0,value:t}},_=async(e,t)=>{let{fetch:r}=u(t??{}),o=G(e,t),s=await r(o);return A(s)};_.toRequest=G;_.fromResponse=A;var D=(e,t)=>{let{sid:r,hostName:o}=u(t??{}),s=new URLSearchParams;for(let n of e)s.append("ids",n);return new Request(`https://${o}/api/projects?${s.toString()}`,r?{headers:{Cookie:m(r)}}:void 0)},z=async e=>{if(!e.ok)return f(e);let t=await e.json();return{ok:!0,value:t}},J=async(e,t)=>{let{fetch:r}=u(t??{}),o=await r(D(e,t));return z(o)};J.toRequest=D;J.fromResponse=z;var Q=async e=>{let{sid:t,hostName:r,gyazoTeamsName:o}=u(e??{}),s=new Request(`https://${r}/api/login/gyazo/oauth-upload/token${o?`?gyazoTeamsName=${o}`:""}`,t?{headers:{Cookie:m(t)}}:void 0),n=await fetch(s);if(!n.ok)return f(n);let{token:c}=await n.json();return{ok:!0,value:c}};async function*Dr(e){let t=await L({accept:"application/pdf, *.pdf"});if(!t)throw new Error("no file specified");let r=await Q();if(!r.ok)throw new Error(JSON.stringify(r.value));let o=r.value;if(!o)throw new Error("Could not get the access token");let{convert:s,count:n}=await I(new Uint8Array(await t.arrayBuffer())),c=0,i=O(5,Array(n).keys(),async a=>{let p=await s(a+1,{mimeType:"image/jpeg",printResolution:e?.printResolution??300,quality:.95});c<a&&await new Promise(d=>{let h=setInterval(()=>{c<a||(clearInterval(h),d())},1e3)});let l=await v(p,{created:new Date,accessToken:o,description:`${t.name} ${a+1}`,...e});if(c++,!l.ok)throw new Error(JSON.stringify(l.value));return{index:a,count:n,...l.value}});yield*j([...i])}export{Dr as uploadPDF};

#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