Skip to content

Instantly share code, notes, and snippets.

@Nerver4Ever
Last active February 13, 2024 08:41
Show Gist options
  • Save Nerver4Ever/751c0d68ea8b8d90385974b79fa015de to your computer and use it in GitHub Desktop.
Save Nerver4Ever/751c0d68ea8b8d90385974b79fa015de to your computer and use it in GitHub Desktop.
阿里云盘导出115sha1链接助手ui优化版,2022.12.24更新,修复无法获取目录的问题
// ==UserScript==
// @name 阿里云盘导出115sha1链接助手ui优化版
// @name:zh 阿里云盘导出115sha1链接助手ui优化版
// @description 2022.12.24更新,阿里云盘导出115sha1链接助手ui优化版
// @author Never4Ever
// @namespace aliyundriver4115helper@Never4Ever
// @version 0.10.1224.1
// @match https://passport.aliyundrive.com/*
// @match https://www.aliyundrive.com/drive/*
// @match https://www.aliyundrive.com/drive
// @match https://aliyundrive.com/drive/*
// @match https://aliyundrive.com/drive
// @match http://passport.aliyundrive.com/*
// @match http://www.aliyundrive.com/drive/*
// @match http://www.aliyundrive.com/drive
// @match http://aliyundrive.com/drive/*
// @match http://aliyundrive.com/drive
// @match https://www.aliyundrive.com/drive?spm=*
// @match https://aliyundrive.com/drive?spm=*
// @match http://www.aliyundrive.com/drive?spm=*
// @match http://aliyundrive.com/drive?spm=*
// @grant GM_xmlhttpRequest
// @grant GM_log
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_setClipboard
// @grant GM_addStyle
// @grant GM_download
// @connect aliyundrive.net
// @connect api.aliyundrive.com
// @connect aliyundrive.com
// @connect alicloudccp.com
// @connect websv.aliyundrive.com
// @run-at document-start
// @require https://cdn.jsdelivr.net/npm/sweetalert2@11
// @require https://cdn.bootcss.com/jsSHA/2.3.1/sha1.js
// @require https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js
// ==/UserScript==
/*
功能:从阿里云盘导出带目录的115sha1转存链接。注意:链接可能在115上转存无效!!!
地址:https://gist.github.com/Nerver4Ever/751c0d68ea8b8d90385974b79fa015de
*/
const versionInfo = {
lastVersion: "0.10.1224.1",
name: "阿里云盘导出115sha1链接助手ui优化版",
url: 'https://gist.github.com/Nerver4Ever/751c0d68ea8b8d90385974b79fa015de'
}
const jobCount = 2;//下载时任务数
const sleepTime = 3000;//ms,如果遭遇卡住,加大此处
const css = `
.sha1115Button{
color:gray;
height:28px;
display: none;
margin-left:auto;
margin-right:4px;
margin-top:2px;
margin-bottom:2px;
border-width: 1px;
border-style: solid;
border-color: transparent;
}
[data-index]:hover .sha1115Button{
color:white;
display: block;
border-color: white;
opacity:0.9;
background-color: gray;
}
.sha1115Button:hover{
opacity:1 !important;
background-color: #446dff !important;
border-color: #446dff !important;
}
`
GM_addStyle(css);
//api
const sharePattern = /https?:\/\/www.aliyundrive.com\/s\//
const folderPattern = /https?:\/\/www.aliyundrive.com\/drive\/folder\/(\w+)/;
const allFolderPattern = /https?:\/\/www.aliyundrive.com\/drive\/(\w+)/
const old = {
folderId: "",
lastUpdateTime: "",
directItems: []
};
const config = {
access_token: "",
refresh_token: "",
drive_id: "",
};
const httpHeaders = {
"accept": "application/json, text/plain, */*",
"content-type": "application/json;charset=UTF-8",
"origin": "https://www.aliyundrive.com",
"referer": "https://www.aliyundrive.com/",
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.128 Safari/537.36"
};
const httpHeaderWithAuthorization = {
"accept": "application/json, text/plain, */*",
"authorization": "",
"content-type": "application/json;charset=UTF-8",
"origin": "https://www.aliyundrive.com",
"referer": "https://www.aliyundrive.com/",
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.128 Safari/537.36"
};
function debugLog(o = "") {
GM_log(o);
}
function delay(ms) {
if (ms == 0) {
ms = 1000 * (Math.floor(Math.random() * (11 - 4)) + 4);
}
return new Promise(resolve => setTimeout(resolve, ms));
}
function initConfig() {
let token = JSON.parse(localStorage.getItem('token'));
config.access_token = token.access_token;
config.refresh_token = token.refresh_token;
config.drive_id = token.default_drive_id;
httpHeaderWithAuthorization.authorization = "Bearer " + config.access_token;
debugLog("initConfig");
debugLog(config);
}
function refreshToken() {
return new Promise((resolve, reject) => {
resolve(true);
});
}
/*
function refreshToken() {
let data = { refresh_token: config.refresh_token };
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
url: "https://websv.aliyundrive.com/token/refresh",
method: "POST",
data: JSON.stringify(data),
responseType: 'json',
headers: {
'Content-Type': "application/json;charset=UTF-8",
},
onload: function (xhr) {
debugLog("refreshToken")
console.log(xhr)
if (xhr.status === 200) {
config.access_token = xhr.response.access_token;
httpHeaderWithAuthorization.authorization = "Bearer " + config.access_token
resolve(true)
}
else resolve(false)
}
})
});
}
*/
async function getDirectChildItemsWithMarker(folderId = 'root', marker = '') {
debugLog("getDirectChildItemsWithMarker")
let payload = {
all: false,
fields: "*",
drive_id: config.drive_id,
image_thumbnail_process: "image/resize,w_400/format,jpeg",
image_url_process: "image/resize,w_1920/format,jpeg",
limit: 200,
order_by: "updated_at",
order_direction: "DESC",
parent_file_id: folderId,
url_expire_sec: 1600,
video_thumbnail_process: "video/snapshot,t_0,f_jpg,ar_auto,w_300"
}
if (marker) {
payload.marker = marker;
}
let data = JSON.stringify(payload);
let headers = {};
Object.assign(headers, httpHeaderWithAuthorization);
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
url: "https://api.aliyundrive.com/adrive/v3/file/list",
method: "POST",
data: data,
headers: headers,
responseType: 'json',
onload: function (xhr) {
debugLog("getDirectChildItemsWithMarker-onload");
console.log(xhr);
if (xhr.status === 401) {
resolve({ state: false, error: xhr.response.code })
} else if (xhr.status === 200) {
resolve({ state: true, error: "", data: xhr.response })
}
else {
resolve({ state: false, error: 'Blocked by Sentinel (flow limiting)' })
}
}
})
});
}
function converterDataToFileType(item) {
let thisFile = { type: item.type, name: item.name, size: item.size, sha1: item.content_hash, url: item.thumbnail, id: item.file_id, isIllegal: true, isVideo: item.category == "video" };
//是否被和谐
thisFile.isIllegal = item.thumbnail != "https://pds-system-file.oss-cn-beijing.aliyuncs.com/illegal_thumbnail.png";
return thisFile;
}
async function getDirectChildItems(folderId = "root", processCallback = function (index) { }) {
debugLog("getDirectChildItems");
let items = new Array();
let marker = "";
let index = 1;
while (true) {
let result = await getDirectChildItemsWithMarker(folderId, marker);
if (result.state === true) {
//console.log(result.data.items)
result.data.items.forEach(item => {
let thisFile = converterDataToFileType(item);
items.push(thisFile)
});
debugLog(`${index},count: ${result.data.items.length}`)
processCallback(index);
index = index + 1;
marker = result.data.next_marker;
if (marker === "") break;
}
else if (result.state === false && result.error === "AccessTokenInvalid") {
await refreshToken();
}
else {//too many requests
console.log("可能是 too many requests");
console.error(result);
await delay(sleepTime);
await refreshToken();
}
await delay(sleepTime);
}
return items;
}
async function getAllChildItems(root, folderId, processCallback = function (folderName, filesCount, folderSize = 0) { }, indexPrecess = function (folderName, index) { }) {
debugLog("getAllChildItems");
let directItems = await getDirectChildItems(folderId, index => {
indexPrecess(root.name, index);
});
debugLog(`directItems,count:${directItems.length}`);
//目录下的文件
root.files = new Array();
let files = directItems.filter(f => f.type === "file");
let childrenSize = 0;
if (files && files.length > 0) {
childrenSize = files.map(q => q.size).reduce((prev, next) => prev + next);
}
processCallback(root.name, files.length, childrenSize);
debugLog(`files>>>>>>: count:${files.length},childrenSize:${formatSizeString(childrenSize)}`);
for (let file of files) {
root.files.push(file)
}
root.dirs = new Array();
let folders = directItems.filter(d => d.type === "folder");
for (let dir of folders) {
let folder = { name: dir.name, id: dir.id };
await getAllChildItems(folder, dir.id, processCallback, indexPrecess);
root.dirs.push(folder);
await delay(sleepTime);
}
}
async function getItemInfoInternal(id) {
let payload = { drive_id: config.drive_id, file_id: id };
let data = JSON.stringify(payload);
let headers = {};
Object.assign(headers, httpHeaderWithAuthorization);
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
url: "https://api.aliyundrive.com/v2/file/get",
method: "POST",
data: data,
headers: headers,
responseType: 'json',
onload: function (xhr) {
if (xhr.status === 401) {
resolve({ state: false, error: xhr.response.code })
} else if (xhr.status === 200) {
resolve({ state: true, error: "", data: xhr.response })
}
else {
console.error("有未知错误,请打开F12,切换到console记录");
console.error(xhr.response);
resolve({ state: false, error: "有未知错误,请打开F12,切换到console记录" })
}
}
})
});
}
async function getItemInfo(id = "root") {
let r = await getItemInfoInternal(id);
if (!r.state && r.error === "AccessTokenInvalid") {
await refreshToken();
r = await getItemInfoInternal(id);
}
return r;
}
async function getDownloadUrl(file_id) {
let payload = { drive_id: config.drive_id, file_id: file_id };
let data = JSON.stringify(payload);
let headers = {};
Object.assign(headers, httpHeaderWithAuthorization);
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
url: "https://api.aliyundrive.com/v2/file/get_download_url",
method: "POST",
data: data,
headers: headers,
responseType: 'json',
onload: function (xhr) {
console.log("getDownloadUrl");
console.log(xhr);
if (xhr.status === 401) {
resolve({ state: false, error: xhr.response.code })
} else if (xhr.status === 200) {
resolve({ state: true, error: "", data: xhr.response })
}
else {
resolve({ state: false, error: 'Blocked by Sentinel (flow limiting)' })
}
}
})
});
}
async function getContentSha1(file_id) {
let r = await getDownloadUrl(file_id)
while (!r.state) {
await delay(sleepTime)
await refreshToken();
r = await getDownloadUrl(file_id)
}
console.log("getContentSha1")
debugLog(r);
if (r.data.url) {
let headers = {
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"accept-encoding": "gzip, deflate, br",
"accept-language": "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36",
"referer": "https://www.aliyundrive.com/",
"connection": "keep-alive",
"range": "bytes=0-131072"
};
await delay(1000)
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: "GET",
url: r.data.url,
timeout: 12000,
headers: headers,
responseType: 'arraybuffer',
onload: function (xhr) {
if (xhr.status === 206) {
let pre_buff = xhr.response;
let data = new Uint8Array(pre_buff);
let sha1 = new jsSHA('SHA-1', 'ARRAYBUFFER');
sha1.update(data.slice(0, 128 * 1024));
let contentSha1 = sha1.getHash('HEX', {
outputUpper: true
});
resolve({ state: true, sha1: contentSha1 });
}
else {
console.error(xhr);
resolve({ state: false, sha1: "", error: "服务器错误!" });
}
}
});
})
}
else {
return { state: false, sha1: "", error: "文件或许被和谐?" };
}
}
const MessageType =
{
Begin: 0,
End: 1,
Processing: 2,
EndWithOneItem: 3,
Error: 4,
Count: 5,
}
function reportBegin() {
display({
messageType: MessageType.Begin
});
}
function reportEnd(html) {
display({
messageType: MessageType.End,
msg: html
});
}
function reportEndWithOne(html) {
display({
messageType: MessageType.EndWithOneItem,
msg: html
});
}
function reportError(error) {
display({
messageType: MessageType.Error,
error: error
});
}
function reportCurrent(msg) {
display({
messageType: MessageType.Processing,
msg: msg
});
}
function reportIndex(total, index) {
display({
messageType: MessageType.Count,
currentIndex: index,
totalCount: total,
});
}
const htmlString = `
<div>
<div id="count" style="text-align:right;margin-bottom:10px"></div>
<hr id="hr115">
<div id="currentItem" style="text-align:left;font-weight: bold;font-size: large;"></div>
<hr>
<div style="height:128px;overflow-x: hidden;overflow-y: auto;">
<ul id="errorItems" style="list-style-type: disc;font-size: smaller;text-align: left;font-style: italic; ">
</ul>
</div>
</div>
`;
var $currentItem, $currentItemResult, $errorItems, $count, $hr115;
//{messageType: "",msg: currentItem:"": currentIndex:0,totalCount: "",error: ""}
function display(message) {
debugLog(message)
if (message.messageType == MessageType.Begin) {
Swal.fire({
title: '正在操作中...',
html: htmlString,
allowOutsideClick: false,
allowEscapeKey: false,
confirmButtonText: `完成`,
footer: `<p style="color:red"><b>${versionInfo.lastVersion}:重要提示:操作过程中,请置顶该页面防止脚本休眠!</b></p>`,
willOpen: function () {
Swal.showLoading(Swal.getConfirmButton());
let $container = $(Swal.getHtmlContainer());
$currentItem = $container.find("#currentItem");
$errorItems = $container.find("#errorItems");
$count = $container.find("#count")
$hr115 = $container.find("#hr115")
},
})
}
else if (message.messageType == MessageType.Count) {
if (message.currentIndex == -1 || message.totalCount == -1) return;
$count.html(`<span style="color:red">${message.currentIndex}</span>|<span style="color:red"><b>${message.totalCount}</b></span>`);
}
else if (message.messageType == MessageType.End) {
Swal.hideLoading();
$currentItem.html(message.msg);
Swal.getTitle().textContent = "操作完成!";
Swal.getCancelButton().style.display = "none";
Swal.getFooter().style.display = "none";
$hr115.css({ display: "none" });
$count.css({ display: "none" });
$currentItem.html(message.msg);
}
else if (message.messageType == MessageType.Processing) {
$currentItem.html(message.msg);
}
else if (message.messageType == MessageType.EndWithOneItem) {
Swal.fire({
title: '完成',
html: message.msg,
allowOutsideClick: false,
allowEscapeKey: false,
confirmButtonText: `完成`,
willOpen: function () {
let $container = $(Swal.getHtmlContainer());
$container.find("#copyFileSha1").click(function (e) {
let fileSha1 = $(this).attr("data");
console.log(fileSha1);
GM_setClipboard(fileSha1, "text");
alert(`已经复制到剪贴板: ${fileSha1}`);
});
}
})
}
else if (message.messageType == MessageType.Error) {
$errorItems.append(`
<li>${message.error}</li>
`);
}
}
async function createSha1LinksForRoot(root, jsonRoot, callback = function (state, file, error) { }) {
jsonRoot.dir_name = root.name;
jsonRoot.files = new Array();
jsonRoot.dirs = new Array();
debugLog(`root.files: ${root.files.length}`);
let index = 1;
let promisArray = new Array();
for (let file of root.files) {
let r = createSha1LinkForAFile(file).then(t => {
if (t.state === true) {
jsonRoot.files.push(converteFileTo115Sha1Link(file))
}
else {
console.error(t);
}
callback(t.state, t.file, t.error);
})
promisArray.push(r);
if (index % jobCount == 0) {
await Promise.all(promisArray);
await delay(sleepTime);
promisArray = new Array();
}
index++;
}
await Promise.all(promisArray);
debugLog(`root.dirs: ${root.dirs.length}`);
for (let dir of root.dirs) {
let childRoot = { dir_name: '' };
await createSha1LinksForRoot(dir, childRoot, callback);
jsonRoot.dirs.push(childRoot);
}
}
function internelFormat(folder, files, folderParents) {
let paths = folderParents.slice(0);
paths.push(folder.dir_name);
for (let file of folder.files) {
let link = file;
let fdomatrPaths = paths.slice(1).join('|');
if (fdomatrPaths != '') {
link = file + '|' + fdomatrPaths;
}
files.push(link);
}
for (let childFolder of folder.dirs) {
internelFormat(childFolder, files, paths)
}
}
//{state:true,error:"",text:""}
function formatJsonToCommon(jsonRoot) {
let files = new Array();
let paths = new Array();
internelFormat(jsonRoot, files, paths);
return files;
}
//单个文件使用
async function createSha1Link(file) {
let result = await createSha1LinkForAFile(file);
if (result.state === true) {
let sha1Link = converteFileTo115Sha1Link(file);
let msg = `<div style="display:grid"><p style="text-align:left;">${sha1Link}<a id="copyFileSha1" data="${sha1Link}">复制</a></p></div>`;
reportEndWithOne(msg);
} else {
let errorMsg = `<span style="color:black">${file.name}</span>,<span style="color:red;">115sha1生成失败!!!${result.error}</span>`;
reportError(errorMsg);
reportEnd("");
}
}
//目录使用
async function createSha1Links(root) {
let totalCount = 0;
let index = 0;
await getAllChildItems(root, root.id, (folderName, filesCount, _) => {
totalCount = totalCount + filesCount;
reportIndex(totalCount, index);
reportCurrent(`获取到当前目录【<b style="color:red">${folderName}</b>】下,文件数量:${filesCount}...`);
}, (folderName, pageIndex) => {
reportCurrent(`正在获取到当前目录【<b style="color:red">${folderName}</b>】下,第${pageIndex}页...`);
});
debugLog("root");
debugLog(root);
let jsonRoot = {};
reportCurrent(`开始生成115sha1链接...`);
await delay(300);
let succeedCount = 0;
await createSha1LinksForRoot(root, jsonRoot, (state, thisFile, error) => {
index++;
reportIndex(totalCount, index);
let msg = "";
let errorMsg = "";
if (state === true) {
succeedCount++;
msg = `115sha1生成成功!文件:<b>${thisFile.name}</b>`;
}
else {
msg = `<span style="color:red">115sha1生成失败!!!文件:<b>${thisFile.name}</b></span><hr>${error}`;
errorMsg = `<span style="color:black">${thisFile.name}</span>,<span style="color:red;">115sha1生成失败!!!${error}</span>`;
reportError(errorMsg);
}
reportCurrent(msg);
});
debugLog("jsonRoot");
//debugLog(jsonRoot);
//可生成json
let files = formatJsonToCommon(jsonRoot);
debugLog(`files.length: ${files.length}`);
let report = "";
if (files.length == 0) {
report = `<div style="display:flex">【<p style="color:red">${root.name}</p>】,空目录或者未获取到有效文件?</div>`
}
else if (files.length == 1) {
report = files[0];
}
else if (files.length > 1) {
report = `
<div><p>完成对【<span style="color:red">${root.name}</span>】的提取!</p><hr/>成功<b style="color:red">${succeedCount}</b>,
失败<b style="color:red">${(totalCount - succeedCount)}</b>
<hr/><b style="color:red">注意:导出的115转存链接,有效情况依据115服务器是否有对应的文件!!</b>
<hr/>获取最新版,或者遇到问题去此反馈,感谢 !
点击-> <a href="${versionInfo.url}" target="_blank">
${versionInfo.name}(${versionInfo.lastVersion})</a>
</div>
`;
let text = files.join("\r\n");
let fileName = root.name + "_从阿里云盘导出_sha1.txt";
download(fileName, text);
}
reportEnd(report);
}
function download(filename, content, contentType) {
if (!contentType) contentType = 'application/octet-stream';
var a = document.createElement('a');
var blob = new Blob([content], { 'type': contentType });
a.href = window.URL.createObjectURL(blob);
a.download = filename;
a.click();
}
//file: {id:_,name:_,size:,sha1:_,textSha1:_}
function converteFileTo115Sha1Link(file) {
return `115://${file.name}|${file.size}|${file.sha1}|${file.textSha1}`;
}
//file: {id:_,name:_,size:,sha1:_,textSha1:_}
async function createSha1LinkForAFile(file) {
let result = { state: true, file: file, error: '' }
console.log(result);
if (file.isIllegal) {
if (file.size > 128 * 1024) {
let t = await getContentSha1(file.id);
if (t.state) {
file.textSha1 = t.sha1;
}
else {
result.state = false;
result.error = t.error;
}
}
else {
file.textSha1 = file.sha1;
}
}
else {
result.state = false;
result.error = "该文件已经被和谐!";
}
return result;
}
async function getVideoPreviewInfo(file_id) {
await refreshToken();
let payload = { category: "live_transcoding", drive_id: config.drive_id, file_id: file_id, template_id: "" };
let data = JSON.stringify(payload);
let headers = {};
Object.assign(headers, httpHeaderWithAuthorization);
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
url: "https://api.aliyundrive.com/v2/file/get_video_preview_play_info",
method: "POST",
data: data,
headers: headers,
responseType: 'json',
onload: function (xhr) {
if (xhr.status === 200 && xhr.response.video_preview_play_info.live_transcoding_task_list) {
let previewData = [];
xhr.response.video_preview_play_info.live_transcoding_task_list.forEach(q => {
let t = { name: q.template_id, url: q.url };
previewData.push(t);
});
console.log(previewData)
resolve({ state: true, error: "", data: previewData })
}
else {
console.error(xhr);
resolve({ state: false, error: "getVideoPreviewInfo 中出错", data: [] })
}
}
})
});
}
function formatSizeString(size) {
if (size < 1024) {
return size + "B";
}
else if (size < 1024 * 1024) {
return (size / 1024).toFixed(2) + "KB";
}
else if (size < 1024 * 1024 * 1024) {
return (size / 1024 / 1024).toFixed(2) + "MB";
}
else if (size < 1024 * 1024 * 1024 * 1024) {
return (size / 1024 / 1024 / 1024).toFixed(2) + "GB";
}
else {
return (size / 1024 / 1024 / 1024 / 1024).toFixed(2) + "TB";
}
}
async function getFolderSize(root) {
console.log(`getFolderSize:${root.id}`)
reportBegin();
reportCurrent(`正在获取【<b style="color:red">${root.name}</b>】的信息...`);
let totalCount = 0;
let totalSize = 0;
await getAllChildItems(root, root.id, (folderName, filesCount, folderSize) => {
totalCount = totalCount + filesCount;
totalSize = totalSize + folderSize;
reportIndex(totalCount, filesCount);
reportCurrent(`获取到当前目录【<b style="color:red">${folderName}</b>】下,文件数量:${filesCount},大小:${formatSizeString(folderSize)}...`);
}, (folderName, pageIndex) => {
reportCurrent(`正在获取到当前目录【<b style="color:red">${folderName}</b>】下,第${pageIndex}页...`);
});
report = `
<div>
<div>完成大小统计,当前目录:【<b style="color:red">${root.name}</b>】</div>
<hr/>
包含文件<b style="color:red">${totalCount}</b>,共<b style="color:red">${formatSizeString(totalSize)}</b>
</div>
`;
reportEnd(report);
}
var lastParams;
function addXMLRequestCallback(callback) {
var oldSend, i;
if (XMLHttpRequest.prototype.callbacks) {
XMLHttpRequest.prototype.callbacks.push(callback);
} else {
XMLHttpRequest.prototype.callbacks = [callback];
oldSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function () {
//console.log('this',this)
console.log('arguments',arguments[0])
if('parent_file_id' in JSON.parse(arguments[0])){
lastParams=arguments[0]
console.log('set ',lastParams)
}
for (i = 0; i < XMLHttpRequest.prototype.callbacks.length; i++) {
XMLHttpRequest.prototype.callbacks[i](this);
}
oldSend.apply(this, arguments);
}
}
}
console.log("loading....")
//logic business
var itemsInThisFolder = new Array();
var thisFolderId = getFolderId();
initConfig();
waitForKeyElements("[data-index]", AddShareSHA1Btn);
debugLog("id: " + thisFolderId)
function getFolderId() {
let url = window.location.href;
let folderId = "root";
if (!sharePattern.test(url) && allFolderPattern.test(url)) {
let match = url.match(folderPattern);
if (match) folderId = match[1];
}
return folderId;
}
addXMLRequestCallback(function (xhr) {
//CreateBanList
xhr.addEventListener("load", function () {
if (xhr.readyState == 4 && xhr.status == 200) {
//console.log("**")
//console.log(xhr.responseURL)
if (xhr.responseURL.startsWith('https://api.aliyundrive.com/adrive/v3/file/list?jsonmask=next_marker')) {
console.log('get lastParams',lastParams)
let sendParams = JSON.parse(lastParams);
console.log(`event: ${sendParams.parent_file_id}:${thisFolderId}`)
console.log(sendParams)
let currentId = getFolderId();
if (thisFolderId != currentId) {
//目录已经切换
itemsInThisFolder = new Array();
thisFolderId = currentId;
}
if (sendParams.parent_file_id === thisFolderId) {
console.log(xhr.response)
let response = xhr.response;
if (response.items) {
response.items.forEach(item => {
let thisFile = converterDataToFileType(item);
if (itemsInThisFolder.findIndex(q => q.id === thisFile.id) === -1) {
itemsInThisFolder.push(thisFile);
}
});
}
}
}
}
});
});
//在文件的详细中显示文件的sha1
const fileSha1String = `<div>
<div>本文件的sha1<div>
<div id="fileSha1"></div>
</div>`;
function getItemByModalNode(node) {
let name = node.innerText.split('\n')[0];
let item = itemsInThisFolder.find(q => q.name == name);
return item;
}
function setFileSha1($node) {
let item = getItemByModalNode($node[0]);
if (!item) {
console.log($node[0]);
return;
}
let $fileSha1 = $node.find("#fileSha1");
if ($fileSha1.length == 0) {
let $div = $node.find('div[class^="group-wrapper"]').last();
$div.append(fileSha1String);
$fileSha1 = $node.find("#fileSha1");
}
console.log(item);
if (item.type === "file") {
$fileSha1.text(item.sha1);
}
else {
$fileSha1.text('(不对文件夹计算此项!)');
}
}
var observer = new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
if (mutation.type == "attributes" && mutation.attributeName == "style" && mutation.target.style.display != 'none') {
setFileSha1($(mutation.target));
}
});
});
waitForKeyElements(".ant-modal-wrap", $node => {
setFileSha1($node);
observer.observe($node[0], {
attributes: true //configure it to listen to attribute changes
});
});
function CopyText(text) {
GM_setClipboard(text, "text");
}
function downloadFile(url, name,resolve,reject,progress) {
return new Promise((resolve, reject) => {
GM_download({
url,
name,
headers: {
accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
referer: "https://www.aliyundrive.com/",
"user-agent": window.navigator.userAgent
},
saveAs: true,
onerror: reject,
onload: resolve,
onprogress:progress
});
});
}
function AddShareSHA1Btn(node) {
//只在folder下有效
let url = window.location.href;
if (sharePattern.test(url)) return;
if (allFolderPattern.test(url) && !folderPattern.test(url)) return;
//debugLog(`url: ${url}, 显示按钮!`);
var $div = node.find('[data-icon-type="PDSMore"]').parent('div');
var itemName = node.find('[data-col-key="name"] > p').text();
if (!itemName) {
itemName = $div.parent('div').find('p[class^="title-"]').text();
}
var $btn = $('<div class="sha1115Button" title="导出符合115的sha1转存链接"><span style="line-height:2;margin:0 4px;">*115sha1*</span></div>');
$div.after($btn);
$btn.on("click", async function (event) {
event.stopPropagation();
console.log(`点击了 ${itemName}`);
//获取当前页信息,folderId,数量,有变动,
reportBegin();
reportCurrent(`正在获取【<b style="color:red">${itemName}</b>】的信息...`);
let item;
debugLog("click item")
item = itemsInThisFolder.find(q => q.name === itemName);
// if (!item) {
// reportCurrent(`正在重新获取【<b style="color:red">${itemName}</b>】的信息,请稍后...`);
// itemsInThisFolder = await getDirectChildItems(currentFolderId);
// item = itemsInThisFolder.find(q => q.name === itemName);
// }
debugLog(item);
if (item) {
if (item.type === "folder") {
reportCurrent(`正在获取【<b style="color:red">${itemName}</b>】下文件以及目录...`);
let root = { name: item.name, id: item.id };
await createSha1Links(root);
}
else {
await createSha1Link(item);
}
}
else {
Swal.fire({
title: `出错!!`,
html: `无法获取到目录【<b style="color:red">${itemName}</b>】信息,请刷新网页后再尝试!`,
confirmButtonText: `刷新网页`,
}).then(r => {
location.reload();
});
}
})
let fItem = itemsInThisFolder.find(q => q.name === itemName && q.type === "file" && q.isVideo);
if (fItem) {
var $button = $('<div class="sha1115Button" title="获取网页播放的m3u8链接"><span style="line-height:2;margin:0 4px;">#m3u8#</span></div>');
$div.after($button);
$button.on("click", async function (e) {
e.stopPropagation();
console.log(fItem)
let result = await getVideoPreviewInfo(fItem.id);
if (result.state) {
let line = "";
result.data.forEach(q => {
line += `<tr>
<td style="width:80px">${q.name}</td>
<td style="width:120px"><span title="${q.url}">${q.url.substring(0, 20)}...</span></td>
<td style="width:80px"><a class="previewURL4A" href="javascript:;" data="${q.url}">复制</a></td>
</tr>
`
});
const htmlForPreview = `
<table width="100%">
<tr>
<th>类型</th>
<th>链接</th>
<th>复制</th>
</tr>
${line}
</table>
<hr>
<div>
特别注意:设置 referer: https://www.aliyundrive.com/
</div>
`;
Swal.fire({
title: `<span title="${fItem.name}">【${fItem.name.substring(0, 10)}...】</span>在线播放链接`,
html: htmlForPreview,
willOpen: () => {
let $container = $(Swal.getHtmlContainer());
$container.find(".previewURL4A").click(function (e) {
let previewUrl = $(this).attr("data");
console.log(previewUrl);
GM_setClipboard(previewUrl, "text");
alert(`已经复制到剪贴板: ${previewUrl}`);
});
}
});
}
else {
Swal.fire({
title: `出错!!`,
html: result.error,
})
}
});
}
let folderItem = itemsInThisFolder.find(q => q.name === itemName && q.type === "folder");
if (folderItem) {
var $button = $('<div class="sha1115Button" title="计算文件夹大小"><span style="line-height:2;margin:0 4px;">#size#</span></div>');
$div.after($button);
$button.on("click", async function (e) {
e.stopPropagation();
await getFolderSize(folderItem);
});
}
let dItem = itemsInThisFolder.find(q => q.name === itemName && q.type === "file");
if (dItem) {
var $button = $('<div class="sha1115Button" title="下载文件"><span style="line-height:2;margin:0 4px;">!download!</span></div>');
$div.after($button);
$button.on("click", async function (e) {
e.stopPropagation();
console.log(dItem)
let r = await getDownloadUrl(dItem.id);
console.log(r.data);
if(r.data&&r.data.url){
window.open(r.data.url, '_self ');
}
else{
Swal.fire({html:"文件已被和谐或者刷新网页后再尝试!"});
}
});
}
}
/*--- waitForKeyElements(): A utility function, for Greasemonkey scripts,
that detects and handles AJAXed content.
Usage example:
waitForKeyElements ("div.comments", commentCallbackFunction);
//--- Page-specific function to do what we want when the node is found.
function commentCallbackFunction (jNode) {
jNode.text ("This comment changed by waitForKeyElements().");
}
IMPORTANT: This function requires your script to have loaded jQuery.
*/
function waitForKeyElements(
selectorTxt, /* Required: The jQuery selector string that
specifies the desired element(s).
*/
actionFunction, /* Required: The code to run when elements are
found. It is passed a jNode to the matched
element.
*/
bWaitOnce, /* Optional: If false, will continue to scan for
new elements even after the first match is
found.
*/
iframeSelector /* Optional: If set, identifies the iframe to
search.
*/
) {
var targetNodes, btargetsFound;
if (typeof iframeSelector == "undefined")
targetNodes = $(selectorTxt);
else
targetNodes = $(iframeSelector).contents()
.find(selectorTxt);
if (targetNodes && targetNodes.length > 0) {
btargetsFound = true;
/*--- Found target node(s). Go through each and act if they
are new.
*/
targetNodes.each(function () {
var jThis = $(this);
var alreadyFound = jThis.data('alreadyFound') || false;
if (!alreadyFound) {
//--- Call the payload function.
var cancelFound = actionFunction(jThis);
if (cancelFound)
btargetsFound = false;
else
jThis.data('alreadyFound', true);
}
});
}
else {
btargetsFound = false;
}
//--- Get the timer-control variable for this selector.
var controlObj = waitForKeyElements.controlObj || {};
var controlKey = selectorTxt.replace(/[^\w]/g, "_");
var timeControl = controlObj[controlKey];
//--- Now set or clear the timer as appropriate.
if (btargetsFound && bWaitOnce && timeControl) {
//--- The only condition where we need to clear the timer.
clearInterval(timeControl);
delete controlObj[controlKey];
}
else {
//--- Set a timer, if needed.
if (!timeControl) {
timeControl = setInterval(function () {
waitForKeyElements(selectorTxt,
actionFunction,
bWaitOnce,
iframeSelector
);
},
300
);
controlObj[controlKey] = timeControl;
}
}
waitForKeyElements.controlObj = controlObj;
}

从阿里云盘导出带目录的115sha1转存链接。

注意:导出的链接可能在115上转存无效!!!

2022.12.24____0.10.1224.1

  • 修复无法获取目录的问题

2022.1009____0.9.1009.1

  • 缓解限流:减速,多sleep;遇到限流,sleep之后再尝试;如果一直卡住,你自己sleep一下吧...

2022.0702____0.8.0702.1

  • 修复因接口改变无法提取的问题
  • 增加大于100MB文件直接下载
  • 增加exe和谐文件的判断

2022.0524____0.7.0524.1

  • 修复因接口改变无法获取内容的bug
  • 新增统计文件夹大小的功能
  • 优化单文件提取sha1的界面:新增复制按钮
  • 优化获取内容的提醒,防止被误认为卡住
  • 优化提取结果弹窗的提示样式

2022.03.13____0.6.0313.1

  • 修复因接口改变无法获取内容的bug
  • 增加获取视频网页播放链接的功能

2021.10.04____0.5.1008.1

  • 修复部分正常文件提取时却提醒服务器错误的bug

2021.10.04____0.4.1004.1

  • 增加平铺模式下也可以获取转存链接
  • 按钮样式修改,使得在浏览器浅色背景下,更容易识别按钮

2021.09.26____0.3.0926.1

  • 修复“本文件的sha1”显示的问题,包括未获取到或者显示错位
  • 修改和谐文件的判定,之前仅仅判断了和谐视频,(由于手中没有其他类型的和谐文件,所以此项可能还有问题)
  • 减少了一个报错提醒

2021.09.24____0.2.0924.1

  • 修改获取项目id的方式(hook了相应的api,减少发包)
  • 在文件的详细信息中,增加了显示文件的sha1

2021.09.20____0.1.0920.1

  • 第一次更新
@lee-li
Copy link

lee-li commented May 24, 2022

好的,我看是接口不需要转json对象了,直接返回item是数组了

@lee-li
Copy link

lee-li commented May 24, 2022

对了,引入的jquery的cdn地址不行了,我也换了地址

@longde300
Copy link

longde300 commented May 24, 2022

阿里云盘 无法导出 115sha1 大佬有空修复一下

@Nerver4Ever
Copy link
Author

@longde300 在你留言之前,已经更新,注意检查版本。。。

@longde300
Copy link

@Nerver4Ever 你好大佬我更新了5.24最新的 我点击115sha1 还是无法提取下载下来 我用的是谷歌浏览器+Tampermonkey+你最新的5.24插件 我点115sha1的时候没反应 一直无法下下来 不知道是不是我这里的问题

@liuhangbin
Copy link

@Nerver4Ever 你好大佬我更新了5.24最新的 我点击115sha1 还是无法提取下载下来 我用的是谷歌浏览器+Tampermonkey+你最新的5.24插件 我点115sha1的时候没反应 一直无法下下来 不知道是不是我这里的问题

反馈: 我这里没问题。而且最新版生成的报告页面格式也比之前的好看了。之前生成的文件数量大的时候显示有偏移。新版解决了这个问题。

@longde300
Copy link

@Nerver4Ever 终于搞明白了 可以用了谢谢大佬 是我这里浏览器和DNS问题

@CheatAbuser
Copy link

請問可否逆向通過SHA1碼導入文件到阿里雲盤?

@liuhangbin
Copy link

請問可否逆向通過SHA1碼導入文件到阿里雲盤?

阿里的机制已经改了,没办法了。参考小白羊版的issue

@CheatAbuser
Copy link

請問可否逆向通過SHA1碼導入文件到阿里雲盤?

阿里的机制已经改了,没办法了。参考小白羊版的issue

明白了,謝謝!

@CheatAbuser
Copy link

請問可否逆向通過SHA1碼導入文件到阿里雲盤?

阿里的机制已经改了,没办法了。参考小白羊版的issue

我看了那篇文章,是因爲卡在沒有proof code這步導致用戶間無法通過sha1+size的方式分享文件,但如果用戶把自己的115的資源跨網盤轉存到自己的阿里雲賬號是否可行呢?proof code可以用工具讀取用戶自己的115網盤裡的文件並計算獲得。115免費賬號下載限速但是空間大,動輒上百TB,如果能轉存到下載不限速的阿里雲就爽了。

@liuhangbin
Copy link

liuhangbin commented May 30, 2022

我看了那篇文章,是因爲卡在沒有proof code這步導致用戶間無法通過sha1+size的方式分享文件,但如果用戶把自己的115的資源跨網盤轉存到自己的阿里雲賬號是否可行呢?proof code可以用工具讀取用戶自己的115網盤裡的文件並計算獲得。

不只是proof code 问题, 现在阿里秒传需要 文件sha1+大小+动态定位的8字节文件内容计算,文件不在本地的话,这个动态定位不好搞定。
参考这个issue

@wuyuan9527
Copy link

更新脚本后,点击115sha1无法生成是怎么回事啊

@i990049
Copy link

i990049 commented Jun 10, 2022

广东联通表示根本不能提取链接,那三个提取转存链接的按钮根本没有,隔壁115sha1优化助手使用正常
https://gist.github.com/Nerver4Ever/953447c9ecd330ffc0861d4cbb839369

@59140735
Copy link

59140735 commented Aug 3, 2022

大佬 现在使用显示 无法获取到目录信息 2022.08.03 10:44 am

@59140735
Copy link

59140735 commented Aug 6, 2022

大佬去哪儿了?

@liuhangbin
Copy link

大佬 现在使用显示 无法获取到目录信息 2022.08.03 10:44 am

我这里用着没问题

@59140735
Copy link

59140735 commented Aug 8, 2022

啊 可能是跟其他插件冲突了 我换个浏览器试试

@CheatAbuser
Copy link

CheatAbuser commented Oct 11, 2022 via email

@xiongzhenglong
Copy link

image
大佬看下,无法生成 115sha1,出错

@saysaiplz
Copy link

大佬,我跟楼上一样的问题,一直转圈,无法生成115sha1链接,帮看看哪里的问题

@hlinkk
Copy link

hlinkk commented Nov 2, 2022

大佬,我跟楼上一样的问题,一直转圈,无法生成115sha1链接,帮看看哪里的问题

34行之后加上这句就可以:
// @connect aliyundrive.net

@ceeeeeeeeeeeeeeeeb
Copy link

大佬,你他娘的真是太牛逼了

@wanghaio
Copy link

是不是又不能用了,求更新

@mizhewei
Copy link

Object { readyState: 4, responseHeaders: "access-control-allow-credentials: true\r\naccess-control-allow-origin: www.aliyundrive.com\r\naccess-control-expose-headers: Content-MD5,X-Request-Id,X-Canary,X-Share-Token,X-Ca-Request-Id,X-Ca-Error-Code,X-Ca-Error-Message\r\ncontent-length: 63\r\ncontent-type: application/json; charset=utf-8\r\ndate: Mon, 13 Feb 2023 08:12:27 GMT\r\nvary: Origin\r\nx-canary: product=adrive,uid=9--2\r\nx-firefox-spdy: h2\r\nx-request-id: --\r\n", finalUrl: "api.aliyundrive.com status: 403, statusText: "Forbidden", responseType: "json", DONE: 4, HEADERS_RECEIVED: 2, LOADING: 3, OPENED: 1, … } 是阿里升级api的原因吗 首页有个公告

@i990049
Copy link

i990049 commented Feb 14, 2023

大佬,我跟楼上一样的问题,一直转圈,无法生成115sha1链接,帮看看哪里的问题

34行之后加上这句就可以: // @connect aliyundrive.net

现在阿里云又升级端口了,应该怎么修改?

@longde300
Copy link

已经不能使用了 大佬有空帮忙更新一下

@wanghaio
Copy link

凉凉。。。。。。。

@i990049
Copy link

i990049 commented Mar 7, 2023

不用改了,115已经修改成跟阿里一样的秒传端口,必须要有本地文件才能秒传,想通过sha1秒传到115已经成为历史,这个脚本可以成为历史文物留作纪念了

@liuhangbin
Copy link

不用改了,115已经修改成跟阿里一样的秒传端口,必须要有本地文件才能秒传,想通过sha1秒传到115已经成为历史,这个脚本可以成为历史文物留作纪念了

噩耗啊,存了3个多G的sha链接都浪费了,都怪自己懒...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment