您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
为轻小说文库++提供图床支持
当前为
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.greasyfork.org/scripts/451075/1091921/imager.js
/* eslint-disable no-multi-spaces */ /* eslint-disable no-implicit-globals */ /* eslint-disable userscripts/no-invalid-headers */ /* eslint-disable userscripts/no-invalid-grant */ // ==UserScript== // @name imager // @displayname 图床 // @namespace Wenku8++ // @version 0.1.10 // @description 为轻小说文库++提供图床支持 // @author PY-DNG // @license GPL-v3 // @regurl https?://www\.wenku8\.net/.* // @require https://greasyfork.org/scripts/449412-basic-functions/code/Basic%20Functions.js?version=1085783 // @require https://greasyfork.org/scripts/449583-configmanager/code/ConfigManager.js?version=1085836 // @grant GM_getValue // @grant GM_setValue // @grant GM_listValues // @grant GM_deleteValue // @grant GM_xmlhttpRequest // ==/UserScript== (function __MAIN__() { const DATA_IMAGERS = { default: 'SDAIDEV', /* Imager Model _IMAGER_KEY_: { available: true, name: '_IMAGER_DISPLAY_NAME_', tip: '_IMAGER_DISPLAY_TIP_', upload: { request: { url: '_UPLOAD_URL_', data: { '_FORM_NAME_FOR_FILE_': '$file$' } }, response: { checksuccess: (json)=>{return json._SUCCESS_KEY_ === '_SUCCESS_VALUE_';}, geturl: (json)=>{return json._PATH_._SUCCESS_URL_KEY_;}, getname: (json)=>{return json._PATH_ ? json._PATH_._FILENAME_ : null;}, getsize: (json)=>{return json._PATH_._SIZE_}, getpage: (json)=>{return json._PATH_ ? json._PATH_._PAGE_ : null;}, gethash: (json)=>{return json._PATH_ ? json._PATH_._HASH_ : null;}, getdelete: (json)=>{return json._PATH_ ? json._PATH_._DELETE_ : null;} } }, isImager: true }, */ PANDAIMG: { available: true, name: '熊猫图床', tip: '2022-01-16测试可用</br>单张图片最大5MB', upload: { request: { url: 'https://api.pandaimg.com/upload', data: { 'file': '$file$', 'classifications': '', 'day': '0' }, headers: { 'usersOrigin': '5edd88d4dfe5d288518c0454d3ccdd2a' } }, response: { checksuccess: (json)=>{return json.code === '200';}, geturl: (json)=>{return json.data.url;}, getname: (json)=>{return json.data.name;} } }, isImager: true }, SDAIDEV: { available: true, name: '流浪图床', tip: '2022-01-09测试可用</br>单张图片最大5MB', upload: { request: { url: 'https://p.sda1.dev/api/v1/upload_external_noform', urlargs: { 'filename': '$filename$', 'ts': '$time$', 'rand': '$random$' } }, response: { checksuccess: (json)=>{return json.success;}, geturl: (json)=>{return json.data.url;}, getdelete: (json)=>{return json.data ? json.data.delete_url : null;}, getsize: (json)=>{return json.data ? json.data.size : null;} } }, isImager: true }, JITUDISK: { available: true, name: '极兔兔床', tip: '2022-02-02测试可用', upload: { request: { url: 'https://pic.jitudisk.com/api/upload', data: { 'image': '$file$' } }, response: { checksuccess: (json)=>{return json.code === 200;}, geturl: (json)=>{return json.data.url;}, getname: (json)=>{return json.data.name;} } }, isImager: true }, SMMS: { available: true, name: 'SM.MS', tip: '注意:此图床跨域访问较不稳定,且有用户反映其被国内部分服务商屏蔽,请谨慎使用此图床', warning: '注意:此图床跨域访问较不稳定,且有用户反映其被国内部分服务商屏蔽,请谨慎使用此图床</br>如出现上传错误/图片加载慢/无法加载图片等情况,请更换其他图床', upload: { request: { url: 'https://sm.ms/api/v2/upload?inajax=1', data: { 'smfile': '$file$' } }, response: { checksuccess: (json)=>{return json.success === true || /^https?:\/\//.test(json.images);}, geturl: (json)=>{return json.data ? json.data.url : json.images;}, getname: (json)=>{return json.data ? json.data.filename : null;}, getpage: (json)=>{return json.data ? json.data.page : null;}, gethash: (json)=>{return json.data ? json.data.hash : null;}, getdelete: (json)=>{return json.data ? json.data.delete : null;} } }, isImager: true }, CATBOX: { available: true, name: 'CatBox', tip: '注意:此图床访问较不稳定,请谨慎使用此图床', warning: '注意:此图床访问较不稳定,请谨慎使用此图床</br>如出现上传错误/图片加载慢/无法加载图片等情况,请更换其他图床', upload: { request: { url: 'https://catbox.moe/user/api.php', responseType: 'text', data: { 'fileToUpload': '$file$', 'reqtype': 'fileupload' } }, response: { checksuccess: (text)=>{return true;}, geturl: (text)=>{return text;} } }, isImager: true } }; const CONST = { Text: { CurImage: '当前图片:', InputImage: '选择/粘贴/拖拽 上传图片', UploadError: '上传错误!', NoNameFromSever: '空(服务器没有返回文件名)', }, Config_Ruleset: { 'version-key': 'config-version', 'ignores': ["LOCAL-CDN"], 'defaultValues': { imager: 'SDAIDEV' } } }; const CM = new ConfigManager(CONST.Config_Ruleset); const CONFIG = CM.Config; const settings = require('settings'); const SettingPanel = require('SettingPanel'); SettingPanel.registerElement('image', { createElement: function() { const SO = this; const data = SO.hasOwnProperty('data') ? SO.data : {}; // <input type="file"> const file = $CrE('input'); file.type = 'file'; file.addEventListener('change', fileGot); // Displayer div const div = $CrE('div'); div.innerText = CONST.Text.CurImage + SO.url + '\n' + CONST.Text.InputImage; div.style.color = data.hasOwnProperty('textColor') ? data.textColor : 'grey'; div.style.width = div.style.height = '100%'; div.style.border = div.style.padding = div.style.margin = '0'; data.hasOwnProperty('innerText') && (div.innerText = data.innerText); data.hasOwnProperty('innerHTML') && (div.innerHTML = data.innerHTML); div.addEventListener('click', file.click.bind(file)); div.addEventListener('paste', fileGot); div.addEventListener('dragenter', destroyEvent); div.addEventListener('dragover', destroyEvent); div.addEventListener('drop', fileGot); return div; function fileGot(e) { const file = fileEvent(e); if (!file) {return false;} uploadImage({ file: file, type: CONFIG.imager, onload: function(e) { copyProps(e, SO, Object.keys(e)); div.innerText = CONST.Text.CurImage + SO.url + '\n' + CONST.Text.InputImage; div.dispatchEvent(new Event('change')); }, }); } }, setValue: function(url) { this.url = url; this.element.innerText = CONST.Text.CurImage + url + '\n' + CONST.Text.InputImage; }, getValue: function() {return this.url;}, }); function fileEvent(e) { const input = e.dataTransfer || e.clipboardData || window.clipboardData || e.target; if (!input.files || input.files.length === 0) {return false;}; for (const file of input.files) { const splited = file.name.split('.'); const ext = splited[splited.length-1].toLowerCase(); const extOkay = ['jpg', 'jpeg', 'png', 'webp'].includes(ext); const mimeOkay = ['image/bmp', 'image/gif', 'image/vnd.microsoft.icon', 'image/jpeg', 'image/png', 'image/svg+xml', 'image/tiff', 'image/webp'].includes(file.type) if (extOkay || mimeOkay) { return file; } } return null; } // Upload image to KIENG images // details: {file: File, onload: Function({url, name, json}), onerror: Function, type: 'sm.ms/jd/sg/tt/...'} function uploadImage(details) { const file = details.file; const onload = details.onload ? details.onload : function() {}; const onerror = details.onerror ? details.onerror : uploadError; const type = details.imager ? details.imager : CONFIG.imager; if (!DATA_IMAGERS.hasOwnProperty(type) || !DATA_IMAGERS[type].available) { onerror(); return false; } const imager = DATA_IMAGERS[type]; const upload = imager.upload; const request = upload.request; const response = upload.response; // Construct request url let url = request.url; if (request.urlargs) { const args = request.urlargs; const makearg = (key, value) => ('{K}={V}'.replace('{K}', key).replace('{V}', value)); const replacers = { '$filename$': () => (encodeURIComponent(file.name)), '$random$': () => (Math.random().toString()), '$time$': () => ((new Date()).getTime().toString()) }; for (let [key, value] of Object.entries(args)) { url += url.includes('?') ? '&' : '?'; for (const [str, replacer] of Object.entries(replacers)) { while (value !== null && value.includes(str)) { const val = replacer(key); value = (val !== null) ? value.replace(str, val) : null; } } (value !== null) && (url += makearg(key, value)); } } // Construst request body let data; if (request.data) { data = new FormData(); const replacers = { '$file$': (key) => ((data.append(key, file), null)), '$random$': () => (Math.random().toString()), '$time$': () => ((new Date()).getTime().toString()) }; for (let [key, value] of Object.entries(request.data)) { for (const [str, replacer] of Object.entries(replacers)) { while (value !== null && value.includes(str)) { const val = replacer(key); value = (val !== null) ? value.replace(str, val) : null; } } (value !== null) && data.append(key, value); } } else { data = file; } // headers const headers = request.headers || {}; GM_xmlhttpRequest({ method: 'POST', url: url, timeout: 15 * 1000, data: data, headers: headers, responseType: request.responseType ? request.responseType : 'json', onerror: onerror, ontimeout: onerror, onabort: onerror, onload: (e) => { const json = e.response; const success = e.status === 200 && response.checksuccess(json); if (success) { const url = response.geturl(json); const name = response.getname ? (response.getname(json) ? response.getname(json) : CONST.Text.NoNameFromSever) : CONST.Text.NoNameFromSever onload({ url: url, name: name, json: json }); } else { onerror(json); return; } } }); function uploadError(json) { alertify.error(CONST.Text.UploadError); DoLog(LogLevel.Error, [CONST.Text.UploadError, json]); } } })();