Instantly share code, notes, and snippets.
Last active
December 28, 2022 15:08
-
Star
(0)
0
You must be signed in to star a gist -
Fork
(0)
0
You must be signed in to fork a gist
-
Save whiteball/03c4953d7f547187d979267f5ef36c59 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name TrinArtで生成画像とパラメータをまとめて自動でダウンロード | |
// @namespace https://ai-novelist-share.geo.jp/ | |
// @version 0.4.1 | |
// @description TrinArtの生成ページで、画像を生成するごとに生成画像とパラメータをまとめて自動でダウンロードするオプションを追加します。 | |
// @author しらたま | |
// @match https://ai-novel.com/art/ | |
// @match https://ai-novel.com/art/index.php* | |
// @icon https://www.google.com/s2/favicons?sz=64&domain=ai-novel.com | |
// @updateURL https://gist.github.com/whiteball/03c4953d7f547187d979267f5ef36c59/raw/ai_novelist_trinart_download_button.user.js | |
// @downloadURL https://gist.github.com/whiteball/03c4953d7f547187d979267f5ef36c59/raw/ai_novelist_trinart_download_button.user.js | |
// @supportURL https://gist.github.com/whiteball/03c4953d7f547187d979267f5ef36c59 | |
// @grant none | |
// @require https://cdnjs.cloudflare.com/ajax/libs/crc-32/1.2.2/crc32.min.js | |
// ==/UserScript== | |
(function() { | |
'use strict'; | |
const isSmartPhone = /iPhone|Android.+Mobile/.test(navigator.userAgent) | |
let pref = localStorage.getItem('mod_trinart_pref') ? JSON.parse(localStorage.getItem('mod_trinart_pref')) : {} | |
let radio_list = ['mod_auto_save', 'mod_auto_save_emb', 'mod_auto_save_none'] | |
if (isSmartPhone) { | |
document.getElementById('current_req_lumina').previousElementSibling.insertAdjacentHTML('beforebegin', `<h3>自動保存(ユーザースクリプト)</h3> | |
<div><label><input type="radio" style="font-size: 18px; transform:scale(1.5);" value="2" id="mod_auto_save_emb" name="mod_auto_save" ${(pref.auto_save === true || pref.auto_save === '1' || pref.auto_save === '2') ? 'checked' : ''}> 生成画像の自動保存(テキスト埋め込み)</label> | |
<div class="explanations"style="margin-bottom: 10px">画像を生成した直後にパラメータを画像に埋め込み、PNGとしてダウンロードを開始します。</div> | |
<label><input type="radio" style="font-size: 18px; transform:scale(1.5);" value="0" id="mod_auto_save_none" name="mod_auto_save" ${(!(pref.auto_save === true || pref.auto_save === '1' || pref.auto_save === '2')) ? 'checked' : ''}> 自動保存しない</label> | |
<div class="explanations">自動ダウンロードをしません。</div> | |
</div><br><br>`) | |
radio_list = ['mod_auto_save_emb', 'mod_auto_save_none'] | |
} else { | |
document.getElementById('current_req_lumina').previousElementSibling.insertAdjacentHTML('beforebegin', `<h3>自動保存(ユーザースクリプト)</h3> | |
<div><label><input type="radio" style="font-size: 18px; transform:scale(1.5);" value="1" id="mod_auto_save" name="mod_auto_save" ${(pref.auto_save === true || pref.auto_save === '1') ? 'checked' : ''}> 生成画像の自動保存</label> | |
<div class="explanations" style="margin-bottom: 10px">画像を生成した直後に画像とパラメータのダウンロードを開始します。</div> | |
<label><input type="radio" style="font-size: 18px; transform:scale(1.5);" value="2" id="mod_auto_save_emb" name="mod_auto_save" ${(pref.auto_save === '2') ? 'checked' : ''}> 生成画像の自動保存(テキスト埋め込み)</label> | |
<div class="explanations"style="margin-bottom: 10px">画像を生成した直後にパラメータを画像に埋め込み、PNGとしてダウンロードを開始します。</div> | |
<label><input type="radio" style="font-size: 18px; transform:scale(1.5);" value="0" id="mod_auto_save_none" name="mod_auto_save" ${(!(pref.auto_save === true || pref.auto_save === '1' || pref.auto_save === '2')) ? 'checked' : ''}> 自動保存しない</label> | |
<div class="explanations">自動ダウンロードをしません。</div> | |
</div><br><br>`) | |
} | |
for (const name of radio_list) { | |
const mod_auto_save = document.getElementById(name) | |
if (mod_auto_save) { | |
mod_auto_save.addEventListener('change', function () { | |
pref.auto_save = this.value | |
localStorage.setItem('mod_trinart_pref', JSON.stringify(pref)) | |
}) | |
} | |
} | |
const originalShowpost = window.showpost | |
window.showpost = function () { | |
const mod_download_name = document.getElementById('mod_download_name') | |
if (mod_download_name) { | |
mod_download_name.style.display = '' | |
} | |
originalShowpost() | |
} | |
document.body.insertAdjacentHTML('afterbegin','<canvas id="mod_canvas" style="display: none;"></canvas>') | |
const mod_canvas = document.getElementById('mod_canvas') | |
const context = mod_canvas.getContext('2d') | |
const downloadImageWithEmbededParam = function (div){ | |
const temp_data = JSON.parse(div.innerText) | |
const local_image = div.getAttribute('mod-file') | |
const softener_strength_array = ['無し', "弱", "弱", "中", "中", "強", "強"] | |
const complexity_array = ['構図モード', "精度モード"] | |
const text = `プロンプト: ${temp_data.text} | |
画像のサイズ: ${temp_data.width}x${temp_data.height} | |
ステップ数: ${temp_data.steps} steps | |
プロンプトの強さ/色の濃さ: ${temp_data.guidance} | |
重ね描きの強さ: ${Math.floor(temp_data.strength*100)}% | |
スタイルソフテナ: ${softener_strength_array[temp_data.softener]} | |
固定シード: ${temp_data.seed} | |
サンプラー: ${temp_data.sampler_type} | |
参考にする画像のURL: ${temp_data.init_image_url} | |
参考にする画像ファイル: ${local_image} | |
モデル/プロンプトタイプ: ${temp_data.prompt_type} | |
作画モード: ${complexity_array[temp_data.complexity]} | |
拡張プリセット: ${temp_data.neg_enhancer}`; | |
const title = temp_data.seed + '_' + document.getElementById('mod_download_name').value | |
const img = new Image() | |
img.addEventListener('load', function () { | |
mod_canvas.width = img.naturalWidth | |
mod_canvas.height = img.naturalHeight | |
context.drawImage(img, 0, 0) | |
mod_canvas.toBlob(blob => { | |
const reader = new FileReader() | |
reader.addEventListener('load', function () { | |
// チャンクを挿入する位置で分割 | |
const head = new Uint8Array(reader.result.slice(0, 33)) | |
const tail = new Uint8Array(reader.result.slice(33)) | |
// プロンプトをバイト配列に変k何 | |
const text_encoder = new TextEncoder() | |
const chank_data = text_encoder.encode(text) | |
// テキストチャンクのヘッダ | |
// let size = 11 + chank_data.length | |
// const chunk_head = Uint8Array.from([(size&0xff000000)>>24, (size&0xff0000)>>16, (size&0xff00)>>8, size&0xff, 0x74, 0x45, 0x58, 0x74, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x65, 0x74, 0x65, 0x72, 0x73, 0x00]) | |
let size = 20 + chank_data.length | |
const chunk_head = Uint8Array.from([(size&0xff000000)>>24, (size&0xff0000)>>16, (size&0xff00)>>8, size&0xff, 0x69, 0x54, 0x58, 0x74, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x65, 0x74, 0x65, 0x72, 0x73, 0x00, 0x00, 0x00, 106, 97, 45, 74, 80, 0, 0]) | |
// 書き込み領域確保 | |
const png_data = new Uint8Array(head.length + chunk_head.length + chank_data.length + 4/*crc*/ + tail.length) | |
// 書き込み | |
png_data.set(head) | |
png_data.set(chunk_head, head.length) | |
png_data.set(chank_data, head.length + chunk_head.length) | |
// TypeとDataのCRC32 | |
const crc_target = png_data.slice(head.length + 4, head.length + chunk_head.length + chank_data.length) | |
const crc = CRC32.buf(crc_target) | |
png_data.set(Uint8Array.from([(crc&0xff000000)>>24, (crc&0xff0000)>>16, (crc&0xff00)>>8, crc&0xff]), head.length + chunk_head.length + chank_data.length) | |
png_data.set(tail, head.length + chunk_head.length + chank_data.length + 4) | |
// PNGとしてダウンロード | |
const img_blob = new Blob([png_data], { type: 'image/png' }) | |
const a = document.createElement("a") | |
document.body.appendChild(a) | |
a.href = URL.createObjectURL(img_blob) | |
a.download = title + '.png' | |
a.click() | |
URL.revokeObjectURL(a.href) | |
document.body.removeChild(a) | |
}) | |
reader.readAsArrayBuffer(blob) | |
}, 'image/png') | |
}) | |
img.src = 'data:image/jpeg;base64,' + div.getAttribute('img_val') | |
} | |
const originalUndoRedoLabel = window.UndoRedoLabel | |
window.UndoRedoLabel = function () { | |
originalUndoRedoLabel() | |
const div = document.querySelector('.undo_img:nth-child(' + (Number(document.getElementById('cur_undo_pos').value) + 1) + ')') | |
document.getElementById('mod_download_name').value = div.getAttribute('mod-title') | |
const mod_auto_save = document.getElementById('mod_auto_save_emb') | |
const submit_area = document.getElementById('submit_area') | |
if (mod_auto_save && mod_auto_save.checked && submit_area && submit_area.style.display === 'none') { | |
if (!div) { return; } | |
downloadImageWithEmbededParam(div) | |
} | |
} | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment