- /* 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.2
- // @description 为轻小说文库++提供图床支持
- // @author PY-DNG
- // @license GPL-v3
- // @regurl https?://www\.wenku8\.net/.*
- // @require https://gf.qytechs.cn/scripts/449412-basic-functions/code/Basic%20Functions.js?version=1085783
- // @require https://gf.qytechs.cn/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: '选择/粘贴/拖拽 上传图片',
- InvalidFile: '您选择的文件不是可识别的图片:(</br>请选择.jpg/.jpeg/.png格式的图片',
- 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 alertify = require('alertify');
- 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) {
- alertify.error(CONST.Text.InvalidFile);
- 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) {
- destroyEvent(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]);
- }
- }
- })();