您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
Henry Toys dav api
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.greasyfork.icu/scripts/539094/1606939/dav.js
// ==UserScript== // @name dav // @namespace dav // @version 1.0.0 // @description Henry Toys dav api // @author Henry // @grant unsafeWindow // @grant GM_download // @grant GM_addStyle // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @grant GM_xmlhttpRequest // @grant GM_registerMenuCommand // @license CC-BY-4.0 // ==/UserScript== const dav = { login: function () { // 添加样式,使用 !important 确保优先级 GM_addStyle(` .modal_bg { position: fixed !important; top: 0 !important; left: 0 !important; width: 100% !important; height: 100% !important; background-color: rgba(0, 0, 0, 0.5) !important; justify-content: center !important; align-items: center !important; z-index: 9999 !important; } .modal_content { background-color: white !important; padding: 20px !important; border-radius: 10px !important; width: 300px !important; max-width: 90% !important; max-height: 80vh !important; overflow-y: auto !important; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2) !important; box-sizing: border-box !important; } .modal_input { width: 100% !important; padding: 8px 10px !important; border: 1px solid #ddd !important; border-radius: 5px !important; display: block !important; box-sizing: border-box !important; font-family: inherit !important; font-size: inherit !important; line-height: 1.4 !important; height: 36px !important; -webkit-appearance: none !important; -moz-appearance: none !important; appearance: none !important; background-color: white !important; vertical-align: middle !important; outline: none !important; margin: 0 0 12px 0 !important; box-shadow: none !important; letter-spacing: normal !important; word-spacing: normal !important; text-transform: none !important; text-indent: 0px !important; text-shadow: none !important; text-align: start !important; -webkit-rtl-ordering: logical !important; cursor: text !important; } .modal_input:focus { border-color: #4CAF50 !important; box-shadow: 0 0 0 2px rgba(76, 175, 80, 0.2) !important; } .button_container { display: flex !important; gap: 10px !important; margin-top: 20px !important; } .modal_button { color: white !important; border: none !important; padding: 10px 15px !important; border-radius: 3px !important; cursor: pointer !important; flex: 1 !important; font-family: inherit !important; font-size: inherit !important; } .modal_button.login { color: #111 !important; background-color: #FFD814 !important; } .error_msg { color: red !important; margin-bottom: 15px !important; } `) // 创建模态框HTML结构 const create_login_modal = function () { // 检查登录模态框是否已存在 if (document.querySelector('.modal_bg')) { document.querySelector('.modal_bg').remove() } // 创建模态框容器 const modal_bg = document.createElement('div') modal_bg.className = 'modal_bg' modal_bg.style.display = 'none' // 创建模态框内容 const modal_content = document.createElement('div') modal_content.className = 'modal_content' const modal_title = document.createElement('h1') modal_title.textContent = '欢迎登录' const error_msg = document.createElement('div') error_msg.className = 'error_msg' error_msg.style.display = 'none' const loginurl_input = document.createElement('input') loginurl_input.type = 'text' loginurl_input.className = 'modal_input' loginurl_input.placeholder = '输入网址' // 添加用户名输入 const username_input = document.createElement('input') username_input.type = 'text' username_input.className = 'modal_input' username_input.placeholder = '输入用户名' // 添加密码输入 const password_input = document.createElement('input') password_input.type = 'password' password_input.className = 'modal_input' password_input.placeholder = '输入密码' // 添加按钮容器 const button_container = document.createElement('div') button_container.className = 'button_container' // 添加提交按钮 const submit_button = document.createElement('button') submit_button.type = 'submit' submit_button.textContent = '登录' submit_button.className = 'modal_button login' button_container.appendChild(submit_button) modal_content.appendChild(modal_title) modal_content.appendChild(error_msg) modal_content.appendChild(loginurl_input) modal_content.appendChild(username_input) modal_content.appendChild(password_input) modal_content.appendChild(button_container) modal_bg.appendChild(modal_content) document.body.appendChild(modal_bg) return { modal_bg, error_msg, loginurl_input, username_input, password_input, submit_button, } } // 创建模态框并添加事件处理 const { modal_bg, error_msg, loginurl_input, username_input, password_input, submit_button, } = create_login_modal() // 显示模态框 const show_login_modal = function () { modal_bg.style.display = 'flex' loginurl_input.focus() } // 隐藏模态框 const hide_login_modal = function () { modal_bg.style.display = 'none' loginurl_input.value = '' username_input.value = '' password_input.value = '' error_msg.style.display = 'none' } // 验证登录 const validate_login = function () { const loginurl = loginurl_input.value.trim() const username = username_input.value.trim() const password = password_input.value.trim() if (!loginurl || !username || !password) { error_msg.textContent = '请填写所有字段' error_msg.style.display = 'block' return false } // 验证URL格式 try { new URL(loginurl) } catch (e) { error_msg.textContent = '无效的URL格式' error_msg.style.display = 'block' return false } try { GM_xmlhttpRequest({ method: "PROPFIND", url: loginurl, headers: { 'Authorization': 'Basic ' + btoa(username + ':' + password), 'Depth': '0' }, // responseType: responseType, timeout: 10000, // 设置默认超时时间为10秒 onload: async (response) => { if (response.status == 207) { console.log('Request successful:', response) const dav_login_data = { "loginurl": loginurl, "username": username, "password": password, "expires": new Date().getTime() + 1000 * 60 * 60 * 24 * 7 // 7天后过期 } error_msg.style.display = 'none' GM_setValue("dav_login_data", JSON.stringify(dav_login_data)) hide_login_modal() return true } else { GM_deleteValue('dav_login_data') error_msg.textContent = '登录失败,请检查地址,用户名和密码' error_msg.style.display = 'block' return false } }, onerror: (error) => { GM_deleteValue('dav_login_data') error_msg.textContent = 'Request error:' + error error_msg.style.display = 'block' return false }, ontimeout: () => { // console.log('Request timed out') GM_deleteValue('dav_login_data') error_msg.textContent = 'Request timed out' error_msg.style.display = 'block' return false } }) } catch (requestError) { GM_deleteValue('dav_login_data') error_msg.textContent = 'GM_xmlhttpRequest error:' + requestError error_msg.style.display = 'block' return false } } // 添加事件监听 submit_button.addEventListener('click', validate_login) modal_bg.addEventListener('click', (e) => { if (e.target === modal_bg) { hide_login_modal() } }) // 添加键盘事件监听器,按ESC键关闭模态框 document.addEventListener('keydown', function (e) { if (e.key === 'Escape' && modal_bg.style.display === 'block') { hide_login_modal() } }) loginurl_input.addEventListener('keypress', (e) => { if (e.key === 'Enter') { validate_login() } }) username_input.addEventListener('keypress', (e) => { if (e.key === 'Enter') { validate_login() } }) password_input.addEventListener('keypress', (e) => { if (e.key === 'Enter') { validate_login() } }) GM_registerMenuCommand("登录", show_login_modal) }, logout: function () { const confirm = window.confirm('确定要退出登录吗?') if (!confirm) { return } // 如果用户确认退出登录,则删除登录数据 GM_deleteValue('dav_login_data') alert('已退出登录') }, get_login_data: function () { let dav_login_data = JSON.parse(GM_getValue("dav_login_data", null)) const self = this // 检查数据是否存在或已过期 if (!dav_login_data || !dav_login_data.loginurl || !dav_login_data.username || !dav_login_data.password || dav_login_data.expires < new Date().getTime()) { GM_deleteValue('dav_login_data') self.login() } else { GM_registerMenuCommand("退出登录", self.logout) } return JSON.parse(GM_getValue("dav_login_data", null)) }, request: async function (method, path, callback, responseType = 'text', data = null) { const login_data = JSON.parse(GM_getValue("dav_login_data", null)) if (!login_data || !login_data.loginurl || !login_data.username || !login_data.password || login_data.expires < new Date().getTime()) { GM_deleteValue('dav_login_data') this.get_login_data() throw new Error('Invalid or missing login credentials') } else { const updated_dav_login_data = { "loginurl": login_data.loginurl, "username": login_data.username, "password": login_data.password, "expires": new Date().getTime() + 1000 * 60 * 60 * 24 * 7 // 7天后过期 } GM_setValue("dav_login_data", JSON.stringify(updated_dav_login_data)) } return new Promise((resolve) => { try { GM_xmlhttpRequest({ method: method, data: data, url: login_data.loginurl + path, headers: { 'Authorization': 'Basic ' + btoa(login_data.username + ':' + login_data.password), }, responseType: responseType, timeout: 30000, // 设置默认超时时间为30秒 onload: async (response) => { await callback(response, resolve) }, onerror: (error) => { console.log('Request error:', error) GM_deleteValue('dav_login_data') this.get_login_data() }, ontimeout: () => { console.log('Request timed out') GM_deleteValue('dav_login_data') this.get_login_data() } }) } catch (requestError) { console.log('GM_xmlhttpRequest error:', requestError) GM_deleteValue('dav_login_data') this.get_login_data() } }) }, get_file_data: function (file_path) { let callback = function (response, resolve) { resolve(response.responseText) } return this.request('GET', file_path, callback, 'text') }, get_file_last_modified_time: function (file_path) { let callback = function (response, resolve) { resolve(response.responseXML.getElementsByTagName('d:getlastmodified')[0].textContent) } return this.request('PROPFIND', file_path, callback, 'document', '') }, upload_file_through_file_data: function (file_path, file_data) { let callback = function () { } this.request('PUT', file_path, callback, 'text', file_data) }, upload_file_through_url: async function (file_path, url) { function get_url_file_data(url) { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "GET", url: url, responseType: "arraybuffer", onload: (response) => { resolve(new Blob([response.response])) }, onerror: () => { reject('error'); } }) }) } let file_data = await get_url_file_data(url) this.upload_file_through_file_data(file_path, file_data) }, check_path_exists: function (path) { let callback = function (response, resolve) { if (response.status === 404) { resolve(false) } else { resolve(true) } } return this.request('PROPFIND', path, callback, 'text', '') }, create_dir_path: async function (dir_path) { let callback = function () { } try { let dir_exits = await this.check_path_exists(dir_path) if (dir_exits) { return } let currentPath = dir_path while (currentPath) { dir_exits = await this.check_path_exists(currentPath); if (dir_exits) { break } // 尝试创建目录 await this.request('MKCOL', currentPath, callback, 'text', ''); // 截取到上一级目录 const lastIndex = currentPath.lastIndexOf('/'); if (lastIndex === -1) { break } currentPath = currentPath.slice(0, lastIndex) } } catch (error) { console.error(`Error creating directory ${dir_path}:`, error); } }, get_latest_modified_file: async function (dir_path) { let callback = function (response, resolve) { if (response.status === 404) { resolve("error") } else { resolve(response.responseXML.firstChild.lastChild.firstElementChild.innerHTML) } } return this.request('PROPFIND', dir_path, callback, 'text', '') } } dav.get_login_data()