imager

为轻小说文库++提供图床支持

目前为 2022-09-10 提交的版本。查看 最新版本

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.gf.qytechs.cn/scripts/451075/1091835/imager.js

  1. /* eslint-disable no-multi-spaces */
  2. /* eslint-disable no-implicit-globals */
  3. /* eslint-disable userscripts/no-invalid-headers */
  4. /* eslint-disable userscripts/no-invalid-grant */
  5.  
  6. // ==UserScript==
  7. // @name imager
  8. // @displayname 图床
  9. // @namespace Wenku8++
  10. // @version 0.1.7
  11. // @description 为轻小说文库++提供图床支持
  12. // @author PY-DNG
  13. // @license GPL-v3
  14. // @regurl https?://www\.wenku8\.net/.*
  15. // @require https://gf.qytechs.cn/scripts/449412-basic-functions/code/Basic%20Functions.js?version=1085783
  16. // @require https://gf.qytechs.cn/scripts/449583-configmanager/code/ConfigManager.js?version=1085836
  17. // @grant GM_getValue
  18. // @grant GM_setValue
  19. // @grant GM_listValues
  20. // @grant GM_deleteValue
  21. // @grant GM_xmlhttpRequest
  22. // ==/UserScript==
  23.  
  24. (function __MAIN__() {
  25. const DATA_IMAGERS = {
  26. default: 'SDAIDEV',
  27. /* Imager Model
  28. _IMAGER_KEY_: {
  29. available: true,
  30. name: '_IMAGER_DISPLAY_NAME_',
  31. tip: '_IMAGER_DISPLAY_TIP_',
  32. upload: {
  33. request: {
  34. url: '_UPLOAD_URL_',
  35. data: {
  36. '_FORM_NAME_FOR_FILE_': '$file$'
  37. }
  38. },
  39. response: {
  40. checksuccess: (json)=>{return json._SUCCESS_KEY_ === '_SUCCESS_VALUE_';},
  41. geturl: (json)=>{return json._PATH_._SUCCESS_URL_KEY_;},
  42. getname: (json)=>{return json._PATH_ ? json._PATH_._FILENAME_ : null;},
  43. getsize: (json)=>{return json._PATH_._SIZE_},
  44. getpage: (json)=>{return json._PATH_ ? json._PATH_._PAGE_ : null;},
  45. gethash: (json)=>{return json._PATH_ ? json._PATH_._HASH_ : null;},
  46. getdelete: (json)=>{return json._PATH_ ? json._PATH_._DELETE_ : null;}
  47. }
  48. },
  49. isImager: true
  50. },
  51. */
  52. PANDAIMG: {
  53. available: true,
  54. name: '熊猫图床',
  55. tip: '2022-01-16测试可用</br>单张图片最大5MB',
  56. upload: {
  57. request: {
  58. url: 'https://api.pandaimg.com/upload',
  59. data: {
  60. 'file': '$file$',
  61. 'classifications': '',
  62. 'day': '0'
  63. },
  64. headers: {
  65. 'usersOrigin': '5edd88d4dfe5d288518c0454d3ccdd2a'
  66. }
  67. },
  68. response: {
  69. checksuccess: (json)=>{return json.code === '200';},
  70. geturl: (json)=>{return json.data.url;},
  71. getname: (json)=>{return json.data.name;}
  72. }
  73. },
  74. isImager: true
  75. },
  76. SDAIDEV: {
  77. available: true,
  78. name: '流浪图床',
  79. tip: '2022-01-09测试可用</br>单张图片最大5MB',
  80. upload: {
  81. request: {
  82. url: 'https://p.sda1.dev/api/v1/upload_external_noform',
  83. urlargs: {
  84. 'filename': '$filename$',
  85. 'ts': '$time$',
  86. 'rand': '$random$'
  87. }
  88. },
  89. response: {
  90. checksuccess: (json)=>{return json.success;},
  91. geturl: (json)=>{return json.data.url;},
  92. getdelete: (json)=>{return json.data ? json.data.delete_url : null;},
  93. getsize: (json)=>{return json.data ? json.data.size : null;}
  94. }
  95. },
  96. isImager: true
  97. },
  98. JITUDISK: {
  99. available: true,
  100. name: '极兔兔床',
  101. tip: '2022-02-02测试可用',
  102. upload: {
  103. request: {
  104. url: 'https://pic.jitudisk.com/api/upload',
  105. data: {
  106. 'image': '$file$'
  107. }
  108. },
  109. response: {
  110. checksuccess: (json)=>{return json.code === 200;},
  111. geturl: (json)=>{return json.data.url;},
  112. getname: (json)=>{return json.data.name;}
  113. }
  114. },
  115. isImager: true
  116. },
  117. SMMS: {
  118. available: true,
  119. name: 'SM.MS',
  120. tip: '注意:此图床跨域访问较不稳定,且有用户反映其被国内部分服务商屏蔽,请谨慎使用此图床',
  121. warning: '注意:此图床跨域访问较不稳定,且有用户反映其被国内部分服务商屏蔽,请谨慎使用此图床</br>如出现上传错误/图片加载慢/无法加载图片等情况,请更换其他图床',
  122. upload: {
  123. request: {
  124. url: 'https://sm.ms/api/v2/upload?inajax=1',
  125. data: {
  126. 'smfile': '$file$'
  127. }
  128. },
  129. response: {
  130. checksuccess: (json)=>{return json.success === true || /^https?:\/\//.test(json.images);},
  131. geturl: (json)=>{return json.data ? json.data.url : json.images;},
  132. getname: (json)=>{return json.data ? json.data.filename : null;},
  133. getpage: (json)=>{return json.data ? json.data.page : null;},
  134. gethash: (json)=>{return json.data ? json.data.hash : null;},
  135. getdelete: (json)=>{return json.data ? json.data.delete : null;}
  136. }
  137. },
  138. isImager: true
  139. },
  140. CATBOX: {
  141. available: true,
  142. name: 'CatBox',
  143. tip: '注意:此图床访问较不稳定,请谨慎使用此图床',
  144. warning: '注意:此图床访问较不稳定,请谨慎使用此图床</br>如出现上传错误/图片加载慢/无法加载图片等情况,请更换其他图床',
  145. upload: {
  146. request: {
  147. url: 'https://catbox.moe/user/api.php',
  148. responseType: 'text',
  149. data: {
  150. 'fileToUpload': '$file$',
  151. 'reqtype': 'fileupload'
  152. }
  153. },
  154. response: {
  155. checksuccess: (text)=>{return true;},
  156. geturl: (text)=>{return text;}
  157. }
  158. },
  159. isImager: true
  160. }
  161. };
  162. const CONST = {
  163. Text: {
  164. InputImage: '选择/粘贴/拖拽 上传图片',
  165. UploadError: '上传错误!',
  166. NoNameFromSever: '空(服务器没有返回文件名)',
  167. },
  168. Config_Ruleset: {
  169. 'version-key': 'config-version',
  170. 'ignores': ["LOCAL-CDN"],
  171. 'defaultValues': {
  172. imager: 'SDAIDEV'
  173. }
  174. }
  175. };
  176.  
  177. const CM = new ConfigManager(CONST.Config_Ruleset);
  178. const CONFIG = CM.Config;
  179. const settings = require('settings');
  180. const SettingPanel = require('SettingPanel');
  181. SettingPanel.registerElement('image', {
  182. createElement: function() {
  183. const SO = this;
  184. const data = SO.hasOwnProperty('data') ? SO.data : {};
  185. const file = $CrE('input');
  186. file.type = 'file';
  187. file.addEventListener('change', fileGot);
  188. const div = $CrE('div');
  189. div.innerHTML = data.hasOwnProperty('innerText') ? data.innerHTML : '';
  190. div.innerHTML = data.hasOwnProperty('innerHTML') ? data.innerHTML : CONST.Text.InputImage;
  191. div.style.color = data.hasOwnProperty('textColor') ? data.textColor : 'grey';
  192. div.style.width = div.style.height = '100%';
  193. div.style.border = div.style.padding = div.style.margin = '0';
  194. data.hasOwnProperty('innerText') && (div.innerText = data.innerText);
  195. data.hasOwnProperty('innerHTML') && (div.innerHTML = data.innerHTML);
  196. div.addEventListener('click', file.click.bind(file));
  197. div.addEventListener('paste', fileGot);
  198. div.addEventListener('dragenter', destroyEvent);
  199. div.addEventListener('dragover', destroyEvent);
  200. div.addEventListener('drop', fileGot);
  201. return div;
  202.  
  203. function fileGot(e) {
  204. SO.file = fileEvent(e);
  205. if (!SO.file) {return false;}
  206. this.name = SO.file.name;
  207. uploadImage({
  208. file: SO.file,
  209. type: CONFIG.imager,
  210. onload: function(e) {
  211. copyProps(e, SO, Object.keys(e));
  212. div.dispatchEvent(new Event('change'));
  213. },
  214. });
  215. }
  216. },
  217. setValue: function(arg) {
  218. const SO = this;
  219. if (arg instanceof File) {
  220. this.file = arg;
  221. this.name = arg.name;
  222. delete this.url;
  223. uploadImage({
  224. file: SO.file,
  225. type: CONFIG.imager,
  226. onload: function(e) {
  227. copyProps(e, SO, Object.keys(e));
  228. },
  229. });
  230. }
  231. if (typeof arg === 'string') {
  232. this.url = arg;
  233. delete this.name;
  234. delete this.file;
  235. }
  236. },
  237. getValue: function() {return this.url},
  238. });
  239.  
  240. function fileEvent(e) {
  241. const input = e.dataTransfer || e.clipboardData || window.clipboardData || e.target;
  242. if (!input.files || input.files.length === 0) {return false;};
  243.  
  244. for (const file of input.files) {
  245. const splited = file.name.split('.');
  246. const ext = splited[splited.length-1].toLowerCase();
  247. const extOkay = ['jpg', 'jpeg', 'png', 'webp'].includes(ext);
  248. const mimeOkay = ['image/bmp', 'image/gif', 'image/vnd.microsoft.icon', 'image/jpeg', 'image/png', 'image/svg+xml', 'image/tiff', 'image/webp'].includes(file.type)
  249. if (extOkay || mimeOkay) {
  250. return file;
  251. }
  252. }
  253.  
  254. return null;
  255. }
  256.  
  257. // Upload image to KIENG images
  258. // details: {file: File, onload: Function({url, name, json}), onerror: Function, type: 'sm.ms/jd/sg/tt/...'}
  259. function uploadImage(details) {
  260. const file = details.file;
  261. const onload = details.onload ? details.onload : function() {};
  262. const onerror = details.onerror ? details.onerror : uploadError;
  263. const type = details.imager ? details.imager : CONFIG.imager;
  264. if (!DATA_IMAGERS.hasOwnProperty(type) || !DATA_IMAGERS[type].available) {
  265. onerror();
  266. return false;
  267. }
  268. const imager = DATA_IMAGERS[type];
  269. const upload = imager.upload;
  270. const request = upload.request;
  271. const response = upload.response;
  272.  
  273. // Construct request url
  274. let url = request.url;
  275. if (request.urlargs) {
  276. const args = request.urlargs;
  277. const makearg = (key, value) => ('{K}={V}'.replace('{K}', key).replace('{V}', value));
  278. const replacers = {
  279. '$filename$': () => (encodeURIComponent(file.name)),
  280. '$random$': () => (Math.random().toString()),
  281. '$time$': () => ((new Date()).getTime().toString())
  282. };
  283. for (let [key, value] of Object.entries(args)) {
  284. url += url.includes('?') ? '&' : '?';
  285. for (const [str, replacer] of Object.entries(replacers)) {
  286. while (value !== null && value.includes(str)) {
  287. const val = replacer(key);
  288. value = (val !== null) ? value.replace(str, val) : null;
  289. }
  290. }
  291. (value !== null) && (url += makearg(key, value));
  292. }
  293. }
  294.  
  295. // Construst request body
  296. let data;
  297. if (request.data) {
  298. data = new FormData();
  299. const replacers = {
  300. '$file$': (key) => ((data.append(key, file), null)),
  301. '$random$': () => (Math.random().toString()),
  302. '$time$': () => ((new Date()).getTime().toString())
  303. };
  304.  
  305. for (let [key, value] of Object.entries(request.data)) {
  306. for (const [str, replacer] of Object.entries(replacers)) {
  307. while (value !== null && value.includes(str)) {
  308. const val = replacer(key);
  309. value = (val !== null) ? value.replace(str, val) : null;
  310. }
  311. }
  312. (value !== null) && data.append(key, value);
  313. }
  314. } else {
  315. data = file;
  316. }
  317.  
  318. // headers
  319. const headers = request.headers || {};
  320.  
  321. GM_xmlhttpRequest({
  322. method: 'POST',
  323. url: url,
  324. timeout: 15 * 1000,
  325. data: data,
  326. headers: headers,
  327. responseType: request.responseType ? request.responseType : 'json',
  328. onerror: onerror,
  329. ontimeout: onerror,
  330. onabort: onerror,
  331. onload: (e) => {
  332. const json = e.response;
  333. const success = e.status === 200 && response.checksuccess(json);
  334. if (success) {
  335. const url = response.geturl(json);
  336. const name = response.getname ? (response.getname(json) ? response.getname(json) : CONST.Text.NoNameFromSever) : CONST.Text.NoNameFromSever
  337. onload({
  338. url: url,
  339. name: name,
  340. json: json
  341. });
  342. } else {
  343. onerror(json);
  344. return;
  345. }
  346. }
  347. });
  348.  
  349. function uploadError(json) {
  350. alertify.error(CONST.Text.UploadError);
  351. DoLog(LogLevel.Error, [CONST.Text.UploadError, json]);
  352. }
  353. }
  354. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址