// ==UserScript== // @name Bilibili File // @namespace npm/bilibili-file // @version 1.0.0 // @author https://github.com/WJZ-P // @description 一款基于哔哩哔哩弹幕网(B站)的文件托管插件( ̄▽ ̄) // @license Eclipse Public License 2.0 // @icon https://i0.hdslb.com/bfs/material_up/12d89bc3fa38ffd23e1e8bad1e26037ddcf2f152.png // @match https://www.bilibili.com/* // @require https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/vue/3.2.31/vue.cjs.min.js // @grant GM_addStyle // @downloadURL none // ==/UserScript== (e=>{if(typeof GM_addStyle=="function"){GM_addStyle(e);return}const t=document.createElement("style");t.textContent=e,document.head.append(t)})(" .cyber-title[data-v-733968ef]{position:relative;text-align:center;margin:2rem 0;perspective:1000px;width:fit-content;display:flex;justify-content:center}.gradient-text[data-v-733968ef]{font-size:3.5rem;font-weight:900;background:linear-gradient(90deg,#00aeec,#7be7ff,#00aeec,#7be7ff,#00aeec);background-size:200% auto;-webkit-background-clip:text;-webkit-text-fill-color:transparent;animation:gradient-733968ef 4s linear infinite;display:inline-block;transform:translateZ(0);transition:transform .3s;width:fit-content}.glow[data-v-733968ef]{position:absolute;top:0;left:50%;transform:translate(-50%);width:80%;height:100%;background:radial-gradient(circle at 50% 50%,rgba(0,174,236,.4) 0%,transparent 70%);filter:blur(30px);z-index:-1}@keyframes gradient-733968ef{0%{background-position:0% center}to{background-position:-200% center}}.cyber-title:hover .gradient-text[data-v-733968ef]{transform:scale(1.05) rotateX(10deg) rotateY(-5deg);text-shadow:0 10px 20px rgba(0,174,236,.4)}.main-menu[data-v-733968ef]{display:flex;flex-direction:column;justify-content:start;align-items:start;width:100%;height:100%}.file-manager[data-v-733968ef]{display:flex;flex-direction:column;width:95%;margin:20px auto;background:#fff;border-radius:12px;box-shadow:0 4px 12px #0000001a;padding:20px;height:100%;justify-content:center;align-items:center}.action-bar[data-v-733968ef]{width:100%;display:flex;gap:20px;margin-bottom:30px;justify-content:center;align-items:center}.upload-area[data-v-733968ef]{flex:1;border:2px dashed #00aeec;border-radius:8px;padding:20px;display:flex;align-items:center;justify-content:center;gap:10px;cursor:pointer;transition:all .3s}.upload-area[data-v-733968ef]:hover{background:#f5fbff;border-color:#09c}.search-box[data-v-733968ef]{width:320px;position:relative;transition:all .3s cubic-bezier(.4,0,.2,1);display:flex;justify-content:center;height:45px}.search-box input[data-v-733968ef]{width:100%;padding:14px 48px 14px 24px;border:2px solid #00aeec;border-radius:40px;background:#f5fbffcc;font-size:14px;color:#18191c;transition:all .3s}.search-box input[data-v-733968ef]::placeholder{color:#9499a0;font-weight:400}.search-box input[data-v-733968ef]:hover{border-color:#09c;box-shadow:0 2px 8px #00aeec1f}.search-box input[data-v-733968ef]:focus{outline:none;border-color:#0088b7;box-shadow:0 4px 16px #00aeec29;background:#fff}.search-box .iconfont[data-v-733968ef]{position:absolute;right:20px;top:50%;transform:translateY(-50%);color:#00aeec;font-size:20px;transition:all .3s}.search-box:hover .iconfont[data-v-733968ef]{color:#09c;transform:translateY(-50%) scale(1.1)}.search-box input:focus~.iconfont[data-v-733968ef]{color:#0088b7;animation:searchPulse-733968ef 1.5s infinite}@keyframes searchPulse-733968ef{0%,to{transform:translateY(-50%) scale(1)}50%{transform:translateY(-50%) scale(1.15)}}.file-list[data-v-733968ef]{width:100%}.list-header[data-v-733968ef]{width:100%;display:grid;grid-template-columns:3fr 1fr 1.5fr 1fr;padding:12px 0;border-bottom:1px solid #eee;color:#666;font-weight:500}.list-item[data-v-733968ef]{width:100%;display:grid;grid-template-columns:3fr 1fr 1.5fr 1fr;align-items:center;padding:15px 0;border-bottom:1px solid #f5f5f5;transition:background .2s}.list-item[data-v-733968ef]:hover{background:#f9f9f9}.list-item .col-name[data-v-733968ef]{display:flex;align-items:center;gap:10px;color:#333}.list-item .col-name .iconfont[data-v-733968ef]{font-size:20px;color:#00aeec}.col-actions[data-v-733968ef]{display:flex;gap:15px;justify-content:center}.col-actions button[data-v-733968ef]{background:#00c1ff;border:none;padding:6px;border-radius:6px;cursor:pointer;transition:.2s;width:70px;display:flex;justify-content:center}.col-actions button .iconfont[data-v-733968ef]{color:#fff;font-size:18px}.col-actions button[data-v-733968ef]:hover{background:#6cf;transform:translateY(-2px);box-shadow:0 3px 12px #00aeec4d}.col-actions button.btn-delete[data-v-733968ef]{background:#f54b4cdb}.col-actions button.btn-delete[data-v-733968ef]:hover{background:#ff6668;box-shadow:0 3px 12px #ff4d4f4d}.col-actions button.btn-preview[data-v-733968ef]{background:#00ff438f}.storage-progress[data-v-733968ef]{height:8px;background:#f0f0f0;border-radius:4px;overflow:hidden}.storage-progress .progress-inner[data-v-733968ef]{height:100%;background:#00aeec;transition:width .3s}@font-face{font-family:iconfont;src:url(//at.alicdn.com/t/c/font_123456_xxxxxx.css)}.modal-fade-enter-active[data-v-733968ef],.modal-fade-leave-active[data-v-733968ef]{transition:opacity .25s ease}.modal-fade-enter-from[data-v-733968ef],.modal-fade-leave-to[data-v-733968ef]{opacity:0}.preview-modal[data-v-733968ef]{position:fixed;top:0;right:0;bottom:0;left:0;background:#e5e8e833;z-index:9999;display:flex;justify-content:center;align-items:center;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px)}.modal-container[data-v-733968ef]{position:relative;background:#fff;border-radius:12px;box-shadow:0 12px 24px #66ccff59;max-width:90vw;max-height:90vh;overflow:hidden}.close-btn[data-v-733968ef]{position:absolute;top:16px;right:16px;background:#ffffff1a;border:none;border-radius:50%;width:40px;height:40px;display:flex;align-items:center;justify-content:center;cursor:pointer;transition:all .2s;z-index:1}.close-btn[data-v-733968ef]:hover{background:#fff3;transform:scale(1.1)}.icon-close[data-v-733968ef]{color:#fff;font-size:24px}.image-wrapper[data-v-733968ef]{position:relative;width:80vw;max-width:2000px;height:80vh;display:flex;align-items:center;justify-content:center;padding:40px}.image-wrapper img[data-v-733968ef]{max-width:100%;max-height:100%;object-fit:contain;border-radius:8px}.loading-indicator[data-v-733968ef]{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);display:flex;flex-direction:column;align-items:center;color:#fffc}.spinner[data-v-733968ef]{width:40px;height:40px;border:4px solid rgba(255,255,255,.1);border-top-color:#00aeec;border-radius:50%;animation:spin-733968ef 1s linear infinite;margin-bottom:12px}@keyframes spin-733968ef{to{transform:rotate(360deg)}}.error-message[data-v-733968ef]{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);color:#ff4d4f;font-size:1.2em}.file-info[data-v-733968ef]{position:absolute;bottom:0;left:0;right:0;background:linear-gradient(transparent,#66ccffc2);color:#1e1d1d;padding:15px;text-align:center;font-size:1em;-webkit-backdrop-filter:blur(1px);backdrop-filter:blur(1px)}.footer-info[data-v-733968ef]{width:100%;text-align:center;padding:10px;color:#777;font-size:.9em;border-top:1px solid #eee;margin-top:-20px}.footer-info a[data-v-733968ef]{color:#6cf}.main-container{width:100%;height:100%;display:flex;justify-content:center;align-items:center} "); (function (vue) { 'use strict'; var _a; const uploadUrl = "https://member.bilibili.com/x/material/up/upload"; const pixelPath = "https://i0.hdslb.com/bfs/material_up/49f20d9277765f51227fecb4db010350c3ed90ad.gif"; const headers = new Headers({ "accept": "*/*", "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,zh-TW;q=0.5", "origin": "https://member.bilibili.com", //"priority": "u=1, i", "referer": "https://member.bilibili.com/york/image-material-upload", "sec-ch-ua": '"Chromium";v="136", "Microsoft Edge";v="136", "Not.A/Brand";v="99"', "sec-ch-ua-mobile": "?0", "sec-ch-ua-platform": '"Windows"', "sec-fetch-dest": "empty", "sec-fetch-mode": "cors", "sec-fetch-site": "same-origin", "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36 Edg/135.0.0.0" //"Cookie": credentials.cookie }); const credentials = {}; async function uploadFile(file) { if (!file || !(credentials == null ? void 0 : credentials.bili_jct)) { throw new Error("缺少必要参数:file/csrf"); } if (!isImage(file)) { const pixelBuffer = await (await fetch(pixelPath)).arrayBuffer(); const mergedBlob = new Blob([pixelBuffer, new Blob([file])]); file = new File([mergedBlob], file.name); } const formData = new FormData(); const filename = file.name || `bili_upload_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`; formData.append("bucket", "material_up"); formData.append("dir", ""); formData.append("file", file, filename); formData.append("csrf", credentials.bili_jct); try { const response = await fetch(uploadUrl, { method: "POST", headers, body: formData, redirect: "follow", credentials: "include" }); const result = await response.json(); if (result.code !== 0) return new Error(`上传失败: ${result.message} (code: ${result.code})`); return result; } catch (error) { console.error("上传请求异常:", error); throw new Error(`网络请求失败: ${error.message}`); } } function isImage(file) { var _a2; const imageExtensions = ["jpg", "jpeg", "png", "gif", "webp", "bmp"]; const extension = ((_a2 = file.name.split(".").pop()) == null ? void 0 : _a2.toLowerCase()) || ""; return imageExtensions.includes(extension); } const SAVE_FILES_KEY = "bili_files"; function saveFiles(files) { try { localStorage.setItem(SAVE_FILES_KEY, JSON.stringify(files)); } catch (error) { console.error("[Bilibili-File] 本地存储失败", error); } } function loadFiles() { try { const data = localStorage.getItem(SAVE_FILES_KEY); return data ? JSON.parse(data) : []; } catch (error) { console.log("存储读取失败", error); return []; } } const _export_sfc = (sfc, props) => { const target = sfc.__vccOpts || sfc; for (const [key, val] of props) { target[key] = val; } return target; }; const _hoisted_1$1 = { class: "main-menu" }; const _hoisted_2 = { class: "file-manager" }; const _hoisted_3 = { class: "cyber-title" }; const _hoisted_4 = { class: "gradient-text" }; const _hoisted_5 = { class: "action-bar" }; const _hoisted_6 = { class: "search-box" }; const _hoisted_7 = { class: "file-list" }; const _hoisted_8 = { class: "col-name", style: { "text-align": "center" } }; const _hoisted_9 = { key: 0, class: "iconfont icon-file" }; const _hoisted_10 = { key: 1, class: "iconfont icon-file" }; const _hoisted_11 = { key: 2, class: "iconfont icon-file" }; const _hoisted_12 = { class: "col-size", style: { "text-align": "center" } }; const _hoisted_13 = { class: "col-date", style: { "text-align": "center" } }; const _hoisted_14 = { class: "col-actions" }; const _hoisted_15 = ["onClick"]; const _hoisted_16 = ["onClick"]; const _hoisted_17 = ["onClick"]; const _hoisted_18 = { class: "modal-container" }; const _hoisted_19 = { class: "image-wrapper" }; const _hoisted_20 = ["src", "alt"]; const _hoisted_21 = { key: 0, class: "loading-indicator" }; const _hoisted_22 = { class: "file-info" }; const sliceIndex = 35; const _sfc_main$1 = { __name: "MainMenu", setup(__props) { const fileInputEl = vue.ref(null); const isHovered = vue.ref(false); const files = vue.ref([]); const previewImg = vue.ref(null); const loading = vue.ref(false); const loadError = vue.ref(false); const showViewModel = vue.ref(false); const searchQuery = vue.ref(""); vue.onMounted(() => { files.value = loadFiles(); }); const filteredFiles = vue.computed(() => { if (!searchQuery.value.trim()) return files.value; const query = searchQuery.value.trim().toLowerCase(); return files.value.filter((file) => { return file.name.toLowerCase().includes(query); }); }); const handleUpload = () => fileInputEl.value.click(); const handleFileSelect = async (event) => { console.log("下面打印出传入的文件参数"); console.log(event.target.files); for (let file of event.target.files) { const result = await uploadFile(file); console.log(result); const resultFile = {}; resultFile.name = file.name; resultFile.size = file.size; resultFile.url = result.data.location.replace(/^http:\/\//i, "https://"); resultFile.lastModified = Date.now(); files.value.push(resultFile); } saveFiles(files.value); }; const formatDate = (timestamp) => { const date = new Date(timestamp); return date.toISOString().split("T")[0]; }; const formatSize = (bytes) => { if (bytes === 0) return "0 B"; const units = ["B", "KB", "MB", "GB"]; const i = Math.floor(Math.log(bytes) / Math.log(1024)); return `${(bytes / Math.pow(1024, i)).toFixed(2)} ${units[i]}`; }; const handleDownload = async (file) => { try { const response = await fetch(file.url); let blob = await response.blob(); if (!isImage2(file)) blob = blob.slice(sliceIndex); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = file.name; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } catch (error) { console.error("下载失败:", error); } }; const handleDelete = (file) => { files.value = files.value.filter( (f) => !(f.name === file.name && f.size === file.size) ); console.log("删除后的总文件列表"); console.log(files.value); saveFiles(files.value); }; const handlePreview = (file) => { showViewModel.value = true; previewImg.value = file; }; const closePreview = () => { showViewModel.value = false; previewImg.value = null; }; const handleImageLoad = () => { loading.value = false; }; const handleImageError = () => { loading.value = false; loadError.value = true; }; function isImage2(file) { var _a2; const imageExtensions = ["jpg", "jpeg", "png", "gif", "webp", "bmp"]; const extension = ((_a2 = file.name.split(".").pop()) == null ? void 0 : _a2.toLowerCase()) || ""; return imageExtensions.includes(extension); } function isCompressed(file) { var _a2; const compressedExtensions = [ "zip", "rar", "7z", "tar", "gz", "bz2", "xz", "dmg", "iso", "cab", "arj", "z", "lz", "lzma", "tgz" ]; const extension = ((_a2 = file.name.split(".").pop()) == null ? void 0 : _a2.toLowerCase()) || ""; return compressedExtensions.includes(extension); } const handleMouseEnter = () => isHovered.value = true; const handleMouseLeave = () => isHovered.value = false; return (_ctx, _cache) => { return vue.openBlock(), vue.createElementBlock(vue.Fragment, null, [ vue.createElementVNode("div", _hoisted_1$1, [ vue.createElementVNode("div", _hoisted_2, [ vue.createElementVNode("h1", _hoisted_3, [ vue.createElementVNode("a", { href: "https://github.com/WJZ-P/Bilibili-File", target: "_blank", rel: "noopener noreferrer", class: "title-link", onMouseenter: handleMouseEnter, onMouseleave: handleMouseLeave }, [ vue.createElementVNode("span", _hoisted_4, [ !isHovered.value ? (vue.openBlock(), vue.createElementBlock(vue.Fragment, { key: 0 }, [ vue.createTextVNode("Bilibili File ( 。・▽・。 )ノ") ], 64)) : (vue.openBlock(), vue.createElementBlock(vue.Fragment, { key: 1 }, [ vue.createTextVNode("Bilibili File (*ノωノ)") ], 64)) ]), _cache[2] || (_cache[2] = vue.createElementVNode("span", { class: "glow" }, null, -1)) ], 32) ]), vue.createElementVNode("div", _hoisted_5, [ vue.createElementVNode("div", { class: "upload-area", onClick: handleUpload }, [ _cache[3] || (_cache[3] = vue.createElementVNode("i", { class: "iconfont icon-upload" }, null, -1)), _cache[4] || (_cache[4] = vue.createElementVNode("span", null, "点击上传文件", -1)), vue.createElementVNode("input", { type: "file", ref_key: "fileInputEl", ref: fileInputEl, multiple: "", style: { "display": "none" }, onChange: handleFileSelect }, null, 544) ]), vue.createElementVNode("div", _hoisted_6, [ vue.withDirectives(vue.createElementVNode("input", { type: "text", "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => searchQuery.value = $event), placeholder: "搜索文件...", onInput: _cache[1] || (_cache[1] = (...args) => _ctx.handleSearch && _ctx.handleSearch(...args)) }, null, 544), [ [vue.vModelText, searchQuery.value] ]), _cache[5] || (_cache[5] = vue.createElementVNode("i", { class: "iconfont icon-search" }, [ vue.createElementVNode("svg", { xmlns: "http://www.w3.org/2000/svg", height: "24px", viewBox: "0 -960 960 960", width: "24px", fill: "#5f6368" }, [ vue.createElementVNode("path", { d: "M781.69-136.92 530.46-388.16q-30 24.77-69 38.77-39 14-80.69 14-102.55 0-173.58-71.01-71.03-71.01-71.03-173.54 0-102.52 71.01-173.6 71.01-71.07 173.54-71.07 102.52 0 173.6 71.03 71.07 71.03 71.07 173.58 0 42.85-14.38 81.85-14.39 39-38.39 67.84l251.23 251.23-42.15 42.16ZM380.77-395.38q77.31 0 130.96-53.66 53.66-53.65 53.66-130.96t-53.66-130.96q-53.65-53.66-130.96-53.66t-130.96 53.66Q196.15-657.31 196.15-580t53.66 130.96q53.65 53.66 130.96 53.66Z" }) ]) ], -1)) ]) ]), vue.createElementVNode("div", _hoisted_7, [ _cache[12] || (_cache[12] = vue.createStaticVNode('