您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
为alist中的音视频文件生成并上传或下载一个m3u播放列表文件,脚本编写过程由肉人辅助AI完成
当前为
// ==UserScript== // @name 为Alist生成m3u播放列表文件 // @namespace createM3UforAlist.whatGUI // @version 2024-08-23 // @description 为alist中的音视频文件生成并上传或下载一个m3u播放列表文件,脚本编写过程由肉人辅助AI完成 // @author whatGUI // @match http://*/* // @match https://*/* // @grant GM_setClipboard // @icon https://alist.nn.ci/favicon.ico // @license MIT // ==/UserScript== (function () { "use strict"; addButton(); })(); function addButton() { let buttonLock = false; const buttonDiv = document.createElement("div"); const style = document.createElement("style"); // 设置 CSS 规则 style.textContent = ` .toolbar-ex { position: fixed; right: 65px; bottom: 20px; } .toolbar-ex-icon { width: 2rem; height: 2rem; color: #ff8718; padding: 4px; border-radius: 0.5rem; cursor: pointer; margin-top: 0.25rem; } .toolbar-ex-icon:hover { color: #ffffff; background-color: #ff8718; } .m3u-method-menu { position: absolute; bottom: calc( 32px + 0.25rem); right: 0; transition: height 0.2s ease-out; height: 0; overflow: hidden; } `; // 将 <style> 元素插入到 <head> 中 document.head.appendChild(style); buttonDiv.className = "toolbar-ex"; buttonDiv.innerHTML = ` <div class="m3u-method-menu hidden"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="toolbar-ex-icon m3u-upload" viewBox="0 0 16 16"> <path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5"/> <path d="M7.646 1.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1-.708.708L8.5 2.707V11.5a.5.5 0 0 1-1 0V2.707L5.354 4.854a.5.5 0 1 1-.708-.708z"/> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="toolbar-ex-icon m3u-download" viewBox="0 0 16 16"> <path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5"/> <path d="M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708z"/> </svg> </div> <svg fill="none" stroke-width="0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="toolbar-ex-icon toolbar-ex-toggle" height="1em" width="1em" style="overflow: visible;"><path fill="currentColor" d="M7 14a2 2 0 100-4 2 2 0 000 4zM14 12a2 2 0 11-4 0 2 2 0 014 0zM17 14a2 2 0 100-4 2 2 0 000 4z"></path><path fill="currentColor" fill-rule="evenodd" d="M24 12c0 6.627-5.373 12-12 12S0 18.627 0 12 5.373 0 12 0s12 5.373 12 12zm-2 0c0 5.523-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2s10 4.477 10 10z" clip-rule="evenodd"></path></svg> `; document.body.appendChild(buttonDiv); document .querySelector(".toolbar-ex-toggle") .addEventListener("click", function () { var menu = document.querySelector(".m3u-method-menu"); if (menu.classList.contains("hidden")) { menu.classList.remove("hidden"); menu.style.height = "4.5rem"; } else { menu.style.height = "0"; menu.classList.add("hidden"); } }); document.querySelector(".m3u-upload").addEventListener("click", uploadM3U); document.querySelector(".m3u-download").addEventListener("click", downloadM3U); } async function uploadM3U() { let files = await getFileList(); let m3uBlob = generateM3U(files); if(!m3uBlob) return; await uploadBlob(m3uBlob.blob); let m3uURL = await getPlaylistURL(); if(typeof GM_setClipboard === "function" ){ //添加地址到剪贴板 GM_setClipboard(m3uURL); alert("m3u上传成功并已复制m3u文件链接到剪贴板,若需查看文件请刷新"); } else { alert("m3u上传成功,请刷新后查看"); } } async function downloadM3U() { let files = await getFileList(); let m3uBlob = generateM3U(files); if(!m3uBlob) return; // 创建一个隐藏的 <a> 标签 const link = document.createElement("a"); link.href = m3uBlob.href; link.download = "playlist.m3u"; link.style.display = "none"; document.body.appendChild(link); // 触发点击事件来下载文件 link.click(); // 清除元素 document.body.removeChild(link); } function isMediaFile(filename) { // 定义常见的影音文件扩展名 const mediaExtensions = [ ".mp4", ".mkv", ".mov", ".avi", ".flv", ".wmv", ".webm", ".wav", ".ogg", ".mp3", ".flac", ".aac", ".m4a", ".ape", ]; // 获取文件扩展名 const extension = filename.slice(((filename.lastIndexOf(".") - 1) >>> 0) + 2); // 检查扩展名是否在常见的影音类型列表中 return mediaExtensions.includes("." + extension.toLowerCase()); } function generateM3U(files) { if (!files) { alert("m3u生成失败:当前页面没有文件"); return; } let m3uContent = "#EXTM3U\n"; let videoCount = 0; files.forEach((video) => { if (isMediaFile(video.name)) { videoCount++; m3uContent += `#EXTINF:-1,${video.name}\n${video.url}\n`; } }); if (videoCount === 0) { alert("m3u生成失败:当前页面没有音视频文件"); return; } // 创建一个新的 Blob 对象,将 M3U 内容包装起来 const blob = new Blob([m3uContent], { type: "application/x-mpegURL" }); // 创建一个下载链接 const href = URL.createObjectURL(blob); return { blob, href }; } async function getFileList() { const alistListAPI = "/api/fs/list"; const folderPath = window.location.pathname; const decodedPath = decodeURIComponent(folderPath); const alistToken = localStorage.getItem("token"); const headers = new Headers({ Authorization: alistToken, "Content-Type": "application/json", }); const body = JSON.stringify({ path: decodedPath, password: "", page: 1, per_page: 0, refresh: false, }); const requestOptions = { method: "POST", headers, body, redirect: "follow", }; let result; try { const response = await fetch(alistListAPI, requestOptions); result = await response.json(); } catch (error) { console.log("error: ", error); } let fileList = []; result.data?.content.forEach((file) => { if (!file.is_dir) { fileList.push({ name: file.name, url: window.location.origin + "/d" + decodedPath + "/" + file.name + "?sign=" + file.sign, }); } }); console.log(fileList); return fileList; } async function uploadBlob(blob) { const alistUploadAPI = "/api/fs/put"; const alistToken = localStorage.getItem("token"); const currentURL = decodeURIComponent(window.location.pathname); const path = encodeURIComponent(currentURL + "/playlist.m3u"); // 设置请求头 const headers = new Headers({ Authorization: alistToken, "File-Path": path, // 注意路径需要 URL 编码 "Content-Type": "application/x-mpegURL", // M3U 文件的 Content-Type "Content-Length": blob.size.toString(), As_Task: "false", // 可选,是否作为任务 }); // 创建请求体 const body = blob; try { const response = await fetch(alistUploadAPI, { method: "PUT", headers, body, }); if (!response.ok) { throw new Error(`Failed to upload: ${response.statusText}`); } const result = await response.json(); console.log("Upload successful:", result); } catch (error) { console.error("Error uploading file:", error); } return window.location.origin + "/" + path; } async function getPlaylistURL() { const list = await getFileList(); for (let index = 0; index < list.length; index++) { const file = list[index]; if (file.name === "playlist.m3u") { return encodeURI(file.url); } } }