import { httpx, log, utils } from "@/env";
import Qmsg from "qmsg";
import { GM_download } from "ViteGM";
import type { PopsFolderDataConfig } from "@whitesev/pops/dist/types/src/components/folder/types/index.js";
import { NetDiskView } from "@/main/view/NetDiskView";
import { NetDiskPops } from "@/main/pops/NetDiskPops";
import { NetDiskFilterScheme } from "@/main/scheme/NetDiskFilterScheme";
import { ParseFileCore } from "@/main/handler/parse/NetDiskParseAbstract";
import { NetDiskCommonUtils } from "@/utils/NetDiskCommonUtils";
export class NetDiskParse_UC extends ParseFileCore {
/**
* 入口
*/
async init(netDiskInfo: ParseFileInitConfig) {
super.init(netDiskInfo);
const that = this;
const { ruleIndex, shareCode, accessCode } = netDiskInfo;
const $loading = Qmsg.loading("正在检查是否已登录UC网盘...");
const loginStatus = await this.isLogin();
if (!loginStatus) {
$loading.close();
this.gotoLogin(
"检测到尚未登录UC网盘,是否前去登录?
(注意,需要当前浏览器的UA切换成PC才有登录选项)"
);
return;
}
$loading.setText("正在获取UC网盘的stoken...");
const stoken = await this.getStoken(this.shareCode, this.accessCode);
if (!stoken) {
$loading.close();
return;
}
$loading.setText("正在解析文件列表...");
const detail = await this.getDetail(this.shareCode, this.accessCode, stoken);
if (!detail) {
$loading.close();
return;
}
if (detail.length === 1 && detail[0].dir == false && detail[0].file_type === 1) {
// 单文件
$loading.setText("正在获取下载链接...");
const oneFileDetail = detail[0];
const oneFileDownloadDetail = await this.getDownload(
this.shareCode,
stoken,
oneFileDetail.fid,
oneFileDetail.share_fid_token
);
if (!oneFileDownloadDetail || !oneFileDownloadDetail[0].download_url) {
$loading.close();
Qmsg.error("获取单文件download_url失败");
return;
}
NetDiskView.$inst.linearChainDialogView.oneFile({
title: "UC网盘单文件直链",
fileName: oneFileDownloadDetail[0].file_name,
fileSize: utils.formatByteToSize(oneFileDownloadDetail[0].size),
downloadUrl: oneFileDownloadDetail[0].download_url,
fileUploadTime: utils.formatTime(oneFileDownloadDetail[0].created_at),
fileLatestTime: utils.formatTime(oneFileDownloadDetail[0].last_update_at),
clickCallBack: () => {
this.downloadFile(oneFileDownloadDetail[0].file_name, oneFileDownloadDetail[0].download_url);
return false;
},
});
} else {
// 多文件
log.success("该链接是 多文件");
$loading.setText("正在解析多文件...");
const folderInfoList = this.getFolderInfo(detail, stoken, 0);
log.info("递归完毕");
NetDiskView.$inst.linearChainDialogView.moreFile("UC网盘文件解析", folderInfoList);
}
$loading.close();
}
/**
* 判断是否已登录UC网盘
*/
async isLogin() {
const response = await httpx.get("https://drive.uc.cn/", {
headers: {
"User-Agent": utils.getRandomPCUA(),
},
});
log.success("判断是否已登录UC网盘", response);
if (!response.status) {
return false;
}
if (response.data.finalUrl === "https://drive.uc.cn/list") {
return true;
} else {
return false;
}
}
/**
* 下载文件
* @param fileName 文件名
* @param downloadUrl 下载链接
*/
downloadFile(fileName: string, downloadUrl: string) {
log.info(`调用【GM_download】下载:`, arguments);
if (window.location.hostname === "drive.uc.cn") {
window.open(downloadUrl, "_blank");
return;
}
if (!NetDiskCommonUtils.isSupport_GM_download()) {
Qmsg.error("当前脚本环境不支持API 【GM_download】");
return;
}
/** 取消下载函数 */
let abortDownload: null | Function = null;
/** 是否成功下载 */
let isSuccessDownload = false;
/** 是否下载完成 */
let isDownloadEnd = false;
let downloadingQmsg = Qmsg.loading("下载中...", {
showClose: true,
onClose() {
if (!isSuccessDownload && typeof abortDownload === "function") {
abortDownload();
}
},
});
let result = GM_download({
url: downloadUrl,
name: fileName,
headers: {
Referer: "https://drive.uc.cn/",
},
onload() {
isSuccessDownload = true;
downloadingQmsg.close();
Qmsg.success(`下载 ${fileName} 已完成`, { consoleLogContent: true });
},
onprogress(details) {
if (typeof details === "object" && "loaded" in details && "total" in details && !isDownloadEnd) {
let progressNum = details.loaded / details.total;
let formatProgressNum = (progressNum * 100).toFixed(2);
downloadingQmsg.setText(`下载中...${formatProgressNum}%`);
if (details.loaded === details.total) {
isDownloadEnd = true;
}
}
},
onerror(error) {
downloadingQmsg.close();
log.error("下载失败error👉", error);
if (typeof error === "object" && error["error"]) {
Qmsg.error(`下载 ${fileName} 失败或已取消 原因:${error["error"]}`, {
timeout: 6000,
consoleLogContent: true,
});
} else {
Qmsg.error(`下载 ${fileName} 失败或已取消`, {
consoleLogContent: true,
});
}
},
ontimeout() {
downloadingQmsg.close();
Qmsg.error(`下载 ${fileName} 请求超时`, { consoleLogContent: true });
},
});
if (typeof result === "object" && result != null && "abort" in result) {
abortDownload = result["abort"];
}
}
/**
* 前往登录
* @param text 弹窗的显示的内容
*/
gotoLogin(text = "") {
NetDiskPops.confirm(
{
title: {
position: "center",
text: "UC网盘",
},
content: {
text: text,
html: false,
},
btn: {
reverse: true,
position: "end",
ok: {
text: "前往",
enable: true,
callback() {
window.open("https://drive.uc.cn", "_blank");
},
},
},
},
NetDiskView.$config.viewSizeConfig.tianYiYunLoginTip
);
}
/**
* 获取stoken
* @param pwd_id 分享码
* @param passcode 访问码
*/
async getStoken(pwd_id: string, passcode: AccessCodeNonNullType) {
let response = await httpx.post(
"https://pc-api.uc.cn/1/clouddrive/share/sharepage/token?entry=ft&fr=pc&pr=UCBrowser",
{
data: JSON.stringify({
share_for_transfer: true,
passcode: passcode,
pwd_id: pwd_id,
}),
headers: {
Accept: "application/json, text/plain, */*",
"Content-Type": "application/json;charset=UTF-8",
"User-Agent": utils.getRandomPCUA(),
Origin: "https://drive.uc.cn",
Referer: "https://drive.uc.cn/",
},
allowInterceptConfig: false,
}
);
if (!response.status) {
let errorData = utils.toJSON(response.data.responseText);
log.error("获取stoken失败JSON信息", errorData);
if ("message" in errorData) {
Qmsg.error(errorData["message"]);
} else {
Qmsg.error("请求异常,获取stoken失败");
}
return;
}
let data = utils.toJSON(response.data.responseText);
log.info("获取stoken:", data);
if (data["code"] !== 0) {
log.error("获取stoken失败", data);
Qmsg.error("获取stoken失败");
return;
}
return data["data"]["stoken"];
}
/**
* 获取stoken
* @param pwd_id 分享码
* @param passcode 访问码
* @param stoken 获取的stoken
* @param pdir_fid 父fid,默认为0,如果为文件夹,那么它的fid就是这个值
* @param force
* @param _page
* @param _size=
* @param _fetch_banner
* @param _fetch_share
* @param _fetch_total
*/
async getDetail(
pwd_id: string,
passcode: AccessCodeNonNullType,
stoken: string,
pdir_fid = 0,
force = 0,
_page = 1,
_size = 50,
_fetch_banner = 0,
_fetch_share = 0,
_fetch_total = 1
): Promise {
const response = await httpx.get(
`https://pc-api.uc.cn/1/clouddrive/transfer_share/detail?pr=UCBrowser&fr=h5&pwd_id=${pwd_id}&__t=${new Date().getTime()}&passcode=${passcode}&stoken=${encodeURIComponent(
stoken
)}&pdir_fid=${pdir_fid}&force=${force}&_page=${_page}&_size=${_size}&_fetch_banner=${_fetch_banner}&_fetch_share=${_fetch_share}&_fetch_total=${_fetch_total}&_sort=${encodeURIComponent(
"file_type:asc,file_name:asc"
)}`,
{
headers: {
Accept: "application/json, text/plain, */*",
"User-Agent": utils.getRandomPCUA(),
Origin: "https://drive.uc.cn",
Referer: "https://drive.uc.cn/",
},
}
);
if (!response.status) {
return;
}
const data = utils.toJSON<{
status: number;
code: number;
message: string;
timestamp: number;
data: {
is_owner: number;
list: [];
};
metadata: {
_size: number;
_page: number;
_count: number;
_total: number;
check_fid_token: number;
};
}>(response.data.responseText);
log.info("获取detail:", data);
if (data.code !== 0) {
Qmsg.error("获取detail失败");
return;
}
const metadata = data.metadata;
if (metadata && metadata._total && metadata._total > metadata._size) {
// 文件的总数量超过默认的值
return await this.getDetail(
pwd_id,
passcode,
stoken,
pdir_fid,
force,
_page,
metadata._total,
_fetch_banner,
_fetch_share,
_fetch_total
);
}
if (data.data.list.length === 0) {
Qmsg.error("链接内容为空");
return;
}
return data.data.list;
}
/**
* 获取下载信息
* @param pwd_id 分享码
* @param stoken 获取的stoken
* @param fids 通过获取到的detail获取到的fid
* @param share_fid_token 通过获取到的detail获取到的share_fid_token
*/
async getDownload(pwd_id: string, stoken: string, fid: string, share_fid_token: string) {
let response = await httpx.post("https://pc-api.uc.cn/1/clouddrive/file/download?entry=ft&fr=pc&pr=UCBrowser", {
data: JSON.stringify({
fids: [fid],
pwd_id: pwd_id,
stoken: stoken,
fids_token: [share_fid_token],
}),
headers: {
Accept: "application/json, text/plain, */*",
"Content-Type": "application/json;charset=UTF-8",
"User-Agent": utils.getRandomPCUA(),
Origin: "https://drive.uc.cn",
Referer: "https://drive.uc.cn/",
},
});
if (!response.status) {
return;
}
let data = utils.toJSON(response.data.responseText);
log.info("获取download:", data);
if (data["code"] !== 0) {
log.error("获取download失败", data);
Qmsg.error("获取download失败");
return;
}
if (data["data"].length === 0) {
log.error("获取download detail失败", data);
Qmsg.error("获取download detail失败失败");
return;
}
return data["data"] as {
backup_sign: number;
backup_source: boolean;
ban: boolean;
big_thumbnail: string;
category: number;
created_at: number;
creator_ucid_or_default: string;
cur_version_or_default: number;
dir: boolean;
download_url: string;
duration: number;
event_extra: {
recent_created_at: number;
};
extra: string;
fid: string;
file: boolean;
file_name: string;
file_name_hl_end: number;
file_name_hl_start: number;
file_source: string;
file_type: number;
format_type: string;
l_created_at: number;
l_updated_at: number;
last_update_at: number;
like: number;
md5: string;
name_space: number;
obj_category: string;
offline_source: boolean;
operated_at: number;
owner_drive_type_or_default: number;
owner_ucid: string;
pdir_fid: string;
preview_url: string;
range_size: number;
raw_name_space: number;
risk_type: number;
save_as_source: boolean;
share_fid_token: string;
size: number;
status: number;
thumbnail: string;
updated_at: number;
video_height: number;
video_max_resolution: string;
video_width: number;
_extra: {};
}[];
}
/**
* 获取文件夹信息
* @param infoList
*/
getFolderInfo(infoList: any, stoken: string, index = 0) {
let folderInfoList: PopsFolderDataConfig[] = [];
infoList.forEach((item: any) => {
if (item.dir == false && item.file_type === 1) {
// 文件
folderInfoList.push({
fileName: item.file_name,
fileSize: item.size,
fileType: "",
createTime: item.created_at,
latestTime: item.updated_at,
isFolder: false,
index: index,
clickEvent: async () => {
const $loading = Qmsg.loading("正在获取下载链接...");
let fileDownloadUrl = "";
let fileDownloadUrlInfo = await this.getDownload(this.shareCode, stoken, item.fid, item.share_fid_token);
$loading.close();
if (fileDownloadUrlInfo) {
if (fileDownloadUrlInfo.length) {
fileDownloadUrl = fileDownloadUrlInfo[0].download_url;
} else {
fileDownloadUrl = "";
}
} else {
fileDownloadUrl = "";
}
if (item.ban) {
Qmsg.error("文件已被禁止下载");
} else {
let schemeDownloadUrl = fileDownloadUrl;
if (NetDiskFilterScheme.isForwardDownloadLink("uc")) {
schemeDownloadUrl = NetDiskFilterScheme.parseDataToSchemeUri("uc", schemeDownloadUrl);
}
// 如果已被scheme过滤,那么不进行GM_download下载
if (schemeDownloadUrl === fileDownloadUrl) {
this.downloadFile(item.file_name, fileDownloadUrl);
} else {
return {
mode: "aBlank",
url: fileDownloadUrl,
};
}
}
},
});
} else {
// 文件夹
folderInfoList.push({
fileName: item.file_name,
fileSize: item.size,
fileType: "",
createTime: item.created_at,
latestTime: item.updated_at,
isFolder: true,
index: index,
clickEvent: async () => {
if (item.include_items === 0) {
// 里面没有文件
log.success("里面没有文件");
return [];
}
const newDetail = await this.getDetail(this.shareCode, this.accessCode, stoken, item.fid);
if (newDetail) {
return this.getFolderInfo(newDetail, stoken, index + 1);
} else {
return [];
}
},
});
}
});
return folderInfoList;
}
}