// ==UserScript== // @name GoFile 增强 // @name:en GoFile Enhanced // @namespace https://github.com/ewigl/gofile-enhanced // @version 0.6.2 // @description 在 GoFile 文件下载页面添加亿个按钮,导出文件下载链接。配合 IDM、aria2 等下载器使用。 // @description:en Export files' download link. Use along with IDM, aria2 and similar downloaders. // @author Licht // @license MIT // @homepage https://github.com/ewigl/gofile-enhanced // @match http*://gofile.io/* // @icon https://gofile.io/dist/img/favicon16.png // @connect localhost // @connect * // @grant GM_getValue // @grant GM_setValue // @grant GM_xmlhttpRequest // @downloadURL https://update.greasyfork.cloud/scripts/515250/GoFile%20Enhanced.user.js // @updateURL https://update.greasyfork.cloud/scripts/515250/GoFile%20Enhanced.meta.js // ==/UserScript== ;(function () { 'use strict' // Api // appdata: literally, app data // Funcs // function createNotification(title, message, type = 'success', duration = 3000) // function createPopup({ title, content, icon = null, backgroundOpacity = true, showCloseButton = true }) // function createAlert(type, content) // Formats // // IDM Exported Format (support CRLF(\r\n) only): // < // url // cookie: accountToken=ABCDEFG // > // // constants const DEFAULT_LANGUAGE = 'en-US' const CRLF = '\r\n' const ARIA2_RPC_TUTORIAL_URL = 'https://aria2.github.io/manual/en/html/aria2c.html#rpc-interface' const SUPPORTED_FORMATS = [ { name: 'Direct', value: 'direct' }, { name: 'IDM', value: 'ef2' }, { name: 'Aria2', value: 'rpc' }, ] const GE_CONTAINER_ID = 'GofileEnhanced_Container' // const FOLDER_TYPE = 'folder' const FILE_TYPE = 'file' const I18N = { 'zh-CN': { // Button downloadAll: '下载全部', downloadSelected: '下载选中', exportAll: '导出全部', exportSelected: '导出选中', sendAll: '发送全部', sendSelected: '发送选中', aria2RpcSettings: '配置 RPC', aria2RpcReset: '重置 RPC', // Notification noFileSelected: '未选中任何文件', noFileSelectedDescription: '请先选中文件', noFiles: '没有文件可以下载', noFilesDescription: '没有可以下载的文件 暂不支持文件夹下载', // RPC rpcSendSuccess: '已通过 RPC 发送至 Aria2 下载', rpcSendFailed: '通过 RPC 发送至 Aria2 失败', unknownError: '未知错误', // RPC Settings rpcAddress: 'RPC 地址', rpcSecret: 'RPC 密钥', rpcDir: 'RPC 下载目录', // Common ok: '确定', cancel: '取消', success: '成功', fail: '失败', reset: '重置', to: '为', }, 'en-US': { // Button downloadAll: 'Download All', downloadSelected: 'Download Selected', exportAll: 'Export All', exportSelected: 'Export Selected', sendAll: 'Send All', sendSelected: 'Send Selected', aria2RpcSettings: 'RPC Settings', aria2RpcReset: 'RPC Reset', // Notification noFileSelected: 'No file selected', noFileSelectedDescription: 'Please select files first', noFiles: 'No files can be downloaded', noFilesDescription: 'No files can be downloaded, folder download is not supported, yet', // RPC rpcSendSuccess: 'RPC send success', rpcSendFailed: 'RPC send failed', unknownError: 'Unknown error', // RPC Settings rpcAddress: 'RPC address', rpcSecret: 'RPC secret', rpcDir: 'RPC dir', // Common ok: 'OK', cancel: 'Cancel', success: 'Success', fail: 'Fail', reset: 'Reset', to: 'to', }, } const ARIA2_RPC_CONFIG_KEY = { rpcAddress: 'aria2_rpc_address', rpcSecret: 'aria2_rpc_secret', rpcDir: 'aria2_rpc_dir', } const ARIA2_RPC_CONFIG_ICONS = { rpcAddress: 'fa-link', rpcSecret: 'fa-key', rpcDir: 'fa-folder', } const DEFAULT_CONFIG = { rpcSettings: [ { name: ARIA2_RPC_CONFIG_KEY.rpcAddress, value: 'http://localhost:6800/jsonrpc', }, { name: ARIA2_RPC_CONFIG_KEY.rpcSecret, value: '', }, { name: ARIA2_RPC_CONFIG_KEY.rpcDir, value: '', }, ], } const ICON_CLASS = { gofileEnhanced: 'fa-brands fa-google-plus', exportAll: 'fas fa-circle-down', exportSelected: 'far fa-circle-down', aria2RpcSettings: 'fas fa-gear', aria2RpcReset: 'fas fa-rotate-left', } const utils = { getValue: (name) => GM_getValue(name), setValue(name, value) { GM_setValue(name, value) }, // init default configs if not exists initDefaultConfig() { DEFAULT_CONFIG.rpcSettings.forEach((item) => { utils.getValue(item.name) === undefined && utils.setValue(item.name, item.value) }) }, // get translation by key getTranslation(key) { const lang = I18N[navigator.language] ? navigator.language : DEFAULT_LANGUAGE return I18N[lang][key] || key // fallback to key }, // get token from cookie getToken: () => document.cookie, getAria2RpcConfig() { return { address: utils.getValue(ARIA2_RPC_CONFIG_KEY.rpcAddress), secret: utils.getValue(ARIA2_RPC_CONFIG_KEY.rpcSecret), dir: utils.getValue(ARIA2_RPC_CONFIG_KEY.rpcDir).trim() === '' ? undefined : utils.getValue(ARIA2_RPC_CONFIG_KEY.rpcDir), } }, resetRPCConfig() { DEFAULT_CONFIG.rpcSettings.forEach((item) => { utils.setValue(item.name, item.value) createNotification( utils.getTranslation('success'), `${utils.getTranslation('reset')} ${item.name} ${utils.getTranslation('to')} "${item.value}"` ) }) // for each DEFAULT_CONFIG.rpcSettings }, goDirectLink(links) { links.forEach((link) => { window.open(link, link) }) }, downloadFile(links, format) { const blob = new Blob([links], { type: 'text/plain;charset=utf-8' }) const url = URL.createObjectURL(blob) const link = document.createElement('a') link.href = url // generate file name by timestamp link.download = `${appdata.fileManager.mainContent.data.name} - ${new Date().getTime()}.${format.value}` link.click() URL.revokeObjectURL(url) }, sendToRPC: async (fileLinks, cookie) => { const { address, secret, dir } = utils.getAria2RpcConfig() const header = [`Cookie: ${cookie}`] const rpcData = fileLinks.map((link) => { return { id: new Date().getTime(), jsonrpc: '2.0', method: 'aria2.addUri', params: [ `token:${secret}`, [link], { header, dir, }, ], } }) GM_xmlhttpRequest({ method: 'POST', url: address, data: JSON.stringify(rpcData), onload: (httpRes) => { if (httpRes.status === 200) { try { const responseArray = JSON.parse(httpRes.response) responseArray.forEach((item) => { if (item.error) { createNotification( utils.getTranslation('fail'), `${utils.getTranslation('rpcSendFailed')} / ${item.error.code} - ${item.error.message}`, 'error' ) } else { createNotification( utils.getTranslation('success'), `${utils.getTranslation('rpcSendSuccess')} / ${item.result}` ) } }) } catch (error) { createAlert('error', error.toString()) } } else { createAlert( 'error', `${utils.getTranslation('rpcSendFailed')} / ${httpRes.status} - ${httpRes.statusText}` ) } }, onerror: (error) => { // createNotification(utils.getTranslation('fail'), JSON.stringify(error), 'error') createAlert('error', JSON.stringify(error)) }, onabort: () => { createAlert('error', utils.getTranslation('unknownError') + ' / (abort)') }, }) }, getHrLine() { const hrLine = document.createElement('li') hrLine.classList.add('border-b', 'border-gray-700') return hrLine }, getButtonTemplate(iconClass, buttonText) { return ` ${buttonText} ` }, getRegularButtons(format) { // Header Title const formatTitleElement = document.createElement('li') formatTitleElement.innerHTML = ` ${format.name} ` // buttonText let exportAllText, exportSelectedText switch (format.name) { case 'IDM': exportAllText = utils.getTranslation('exportAll') exportSelectedText = utils.getTranslation('exportSelected') break case 'Aria2': exportAllText = utils.getTranslation('sendAll') exportSelectedText = utils.getTranslation('sendSelected') break default: exportAllText = utils.getTranslation('downloadAll') exportSelectedText = utils.getTranslation('downloadSelected') break } // create export buttons const exportAllButton = document.createElement('li') const exportSelectedButton = document.createElement('li') // set innerHTML exportAllButton.innerHTML = this.getButtonTemplate(ICON_CLASS.exportAll, exportAllText) exportSelectedButton.innerHTML = this.getButtonTemplate(ICON_CLASS.exportSelected, exportSelectedText) // add click event for each button exportAllButton.addEventListener('click', operations.handleExport.bind(null, false, format)) exportSelectedButton.addEventListener('click', operations.handleExport.bind(null, true, format)) return [formatTitleElement, exportAllButton, exportSelectedButton] }, getAria2Buttons() { // create rpc settings button const rpcSettingsButton = document.createElement('div') rpcSettingsButton.innerHTML = utils.getRPCButtonDom('settings') // click rpc settings button to open modal rpcSettingsButton.addEventListener('click', () => { createPopup({ title: utils.getTranslation('aria2RpcSettings'), content: utils.getRPCSettingsDom(), icon: 'fas fa-gears', }) const form = document.forms['GofileEnhanced_Form'] if (form) { form.addEventListener('submit', (event) => { event.preventDefault() Object.keys(ARIA2_RPC_CONFIG_KEY).forEach((key) => { utils.setValue(ARIA2_RPC_CONFIG_KEY[key], form.elements[ARIA2_RPC_CONFIG_KEY[key]].value) }) closePopup() }) } }) const rpcResetButton = document.createElement('div') rpcResetButton.innerHTML = utils.getRPCButtonDom('reset') // click aria2 rpc reset button to reset rpc config rpcResetButton.addEventListener('click', () => { utils.resetRPCConfig() }) return [rpcSettingsButton, rpcResetButton] }, getRPCButtonDom(type) { const buttonText = utils.getTranslation(type === 'settings' ? 'aria2RpcSettings' : 'aria2RpcReset') const iconClass = type === 'settings' ? ICON_CLASS.aria2RpcSettings : ICON_CLASS.aria2RpcReset return this.getButtonTemplate(iconClass, buttonText) }, getFormInputItemTemplate(name, i18nKey) { return `