您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
ダウンロードボタンをカスタムし、ファイル名に作成日時を、ExifのUserCommentに作成プロンプトを埋め込みます。
// ==UserScript== // @name BingImageCreator カスタムダウンロードボタン // @description ダウンロードボタンをカスタムし、ファイル名に作成日時を、ExifのUserCommentに作成プロンプトを埋め込みます。 // @namespace https://gf.qytechs.cn/ja/users/1189800-ggg-niya-to // @version 1.1 // @include /https://www\.bing\.com/images/create/.+\?(.+&)?view=detailv2(&|$)/ // @compatible edge on PC / manager : Tampermonkey // @author ggg.niya.to // @license MIT // ==/UserScript== //////////////////////// // loadイベントが何故か発行されない場合の対策 (()=>{ let executed; function startup(){ if(executed){ return; } executed=true; main(); } addEventListener("load",startup); setTimeout(startup,1000); })(); //////////////////////// if(!Blob.prototype.bytes){ Blob.prototype.bytes=function(){ return this.arrayBuffer().then(b => new Uint8Array(b)); }; } //////////////////////// function main(){ // 日時文字列 const TIME=(()=>{ function d2d(d){ return d<10 ? "0"+d : d; } let t=location.pathname.match("/create/[^/]+/\\d-([0-9a-f]{8})[0-9a-f]{24}"); return t ? ""+((t=new Date(parseInt(t[1],16)*1000)).getFullYear())+d2d(t.getMonth()+1)+d2d(t.getDate())+"-"+d2d(t.getHours())+d2d(t.getMinutes())+d2d(t.getSeconds()) : "unknown"; })(); // セレクター定数 const UL_SELECTOR=".actn_container UL",IMG_SELECTOR="#mainImageWindow .mainImage.current IMG"; // 監視とタイムアウト const OBS=new MutationObserver(setup); let timeout=true; setTimeout(()=>{ if(timeout){ OBS.disconnect(); alert("\""+UL_SELECTOR+"\" not found"); } },3000); OBS.observe(document.body,{ subtree:true, childList:true }); setup(); // 構築処理関数 function setup(){ // 追加場所を取得 const UL=document.querySelector(UL_SELECTOR); if(!UL){ return; } timeout=false; OBS.disconnect(); // 元のダウンロードボタンとカスタムボタン const M=UL.querySelector(".dldc"),O = M ? (M.style.display="none",M.children[0]) : null, B=UL.insertBefore(document.createElement("LI"),(()=>{ let e; return (e=M) || (e=UL.querySelector(".c")) || (e=UL.querySelector(".sharec")) ? e.nextSibling : UL.firstChild; })()).appendChild(document.createElement("DIV")); B.className="action dld nofocus"; B.role="button"; B.tabIndex=0; B.innerHTML="<SPAN><SPAN class=\"icon\"></SPAN><SPAN class=\"text\">ダウンロード</SPAN></SPAN>"; // ダウンロードリンク本体 const A=document.createElement("A"); document.body.appendChild(A).style.display="none"; // 本処理 let last; B.onclick=(event)=>{ // 前回のメモリがあれば解放 if(last){ URL.revokeObjectURL(last); last=null; } // Ctrlと一緒に押したら元のボタン(あれば)を起動 if(O && event.ctrlKey){ O.click(); return; } // ダウンロードする画像 const IMG=document.querySelector(IMG_SELECTOR); if(!IMG){ alert("\""+IMG_SELECTOR+"\" not found"); return; } // 基URLとそこから生成した識別コード const SRC=IMG.src.replace(/\?.+$/,""),CODE=(()=>{ var sum=0,a=1,s,i; for(s=SRC.replace(/^.+\/(.{4}\.)?/,""),i=0;i<s.length;i++,a*=2){ sum+=s.charCodeAt(i)*a; } return (new Uint8Array([sum/256,sum])).toHex(); })(); // 画像をロードしてBlobを取得 fetch(SRC+"?pid=ImgGn").then((RESPONSE)=>{ // そのままのパラメータだとwebpが返ってくる場合があるので if(!RESPONSE.ok){ alert("http-error : "+RESPONSE.status); return; } RESPONSE.blob().then((BLOB_I)=>{ // 生成プロンプトから APP1<EXIF<TIFF<コメントデータ のBlobを作成 (new Blob([IMG.alt],{ type:"text/plain" })).bytes().then((BA)=>{ // コメントバイトデータの整形(念のため) const com=Array.from(BA),S_A1D=52,S_CET=8,MAX_COM=256*256-1-S_A1D-S_CET-1-1; // USHRT_MAX-APP1_DATA-COM_ENC_TAG-末尾NUL-偶数整形マージン if(MAX_COM < com.length){ com.length=MAX_COM; } // 念のため1 com.push(0); // 念のため2 if(com.length%2){ com.push(0); } // 念のため3 while(com.length<8){ com.push(0); } // 念のため4 // データ構成(短いのでベタ書き) const CC=S_CET+com.length,SS=S_A1D+CC; return new Blob([ // APP1 new Uint8Array([0xFF,0xE1,SS/256,SS]), // EXIF "Exif",new Uint8Array([0x00,0x00]), // TIFF "MM",new Uint8Array([0x00,0x2A,0x00,0x00,0x00,0x08, // 0th-IFD 0x00,0x01,0x87,0x69,0x00,0x04,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x1A,0x00,0x00,0x00,0x00, // EXIF-IFD 0x00,0x01,0x92,0x86,0x00,0x07,0x00,0x00,CC/256,CC,0x00,0x00,0x00,0x2C,0x00,0x00,0x00,0x00, // コメントデータ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]),new Uint8Array(com)]); }).then((BLOB_C)=>{ // 画像BlobにコメントBlobを挿入したBlobからオブジェクトURLを生成し、それをリンク先に設定してダウンロード A.href=last=URL.createObjectURL(new Blob([BLOB_I.slice(0,2),BLOB_C,BLOB_I.slice(2)],{ type:BLOB_I.type })); A.setAttribute("download",TIME+"_"+CODE); A.click(); }); }); }); }; } } //////////////////////// /* 作成メモ 先頭2バイト(FF,D8)の後に挿入 -- APP1 構造 -- セグメントマーカー(FF,E1) セグメントサイズ(BE、2バイト、2+58+n) EXIFデータ本体(58+nバイト) -- EXIF 構造 -- 58+nバイト EXIF識別コード(6バイト、"Exif\0\0") TIFFデータ本体(52+nバイト) -- TIFF 構造 -- 52+nバイト TIFFヘッダー(8バイト) エンディアン指定(2バイト、BE="MM"=4D,4D) マジックナンバー(2バイト、00,2A) 0th-IFDへのポインタ(4バイト、8=00,00,00,08) 0th-IFD(18バイト) エントリ数(2バイト、EXIF-IFDのみなら1=00,01) エントリ(12バイト) タグ(2バイト、EXIF-IFDへのポインタ=34665=87,69) タイプ(2バイト、LONG=4=00,04) カウント(4バイト、1=00,00,00,01) ポインタ(4バイト、8+0th-IFDのサイズ(18)=26=00,00,00,1A) 1st-IFDへのポインタ(4バイト、存在しないので0=00,00,00,00) ↑ 一部サイトで2バイトと書いてあるが大嘘っぽい EXIF-IFD(26+nバイト) エントリ数(2バイト、1=00,01) エントリ(12バイト) タグ(2バイト、ユーザーコメント=37510=92,86) タイプ(2バイト、UNDEFINED=7=00,07) カウント(4バイト、コメントデータのバイト数=8+n) オフセット(4バイト、8+0th-IFDのサイズ(18)+2+12+4=44=00,00,00,2C) 不使用のポインタ(4バイト、00,00,00,00) コメントデータ(8+nバイト) エンコードタグ(8バイト、未定義=0=00,00,00,00,00,00,00,00) コメント本体(nバイト) */
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址