// ==UserScript==
// @name QQ 群相册批量下载
// @namespace http://lvshuncai.com
// @homepage https://github.com/ShunCai/QQ-Group-Albums-Downloader
// @version 1.0
// @description 自动点击 QQ 群相册的下载功能,实现所有的群相册的批量下载
// @author 芷炫
// @match https://h5.qzone.qq.com/groupphoto/index?inqq=*&groupId=*
// @match https://h5.qzone.qq.com/groupphoto/album?inqq=*&groupId=*
// @icon https://qzonestyle.gtimg.cn/aoi/img/logo/favicon.ico
// @grant none
// @run-at document-end
// ==/UserScript==
(function () {
'use strict';
// 获取查询参数
var getQueryString = function (name) {
let reg = new RegExp ("(^|&)" + name + "=([^&]*)(&|$)", "i");
let r = window.location.search.substring (1).match (reg);
if (r != null) {
return decodeURI (r [2]);
};
return null;
}
// 延迟函数
var delay = milliseconds => new Promise (resolve => {
setTimeout (resolve, milliseconds);
});
// 上传相片按钮
let $uploadBtn = document.querySelector ('#js_pic_upload_btn,#js-header-upload')
if (!$uploadBtn) {
return;
}
// 替换文件名称的特殊字符
const replaceFileName = name => {
return name.replace (/'|#|~|&| |!|\\|\/|:|\?|"|<|>|\*|\|/g,"_");
}
// 下载文件
const download = async albums => {
for (const ablum of albums) {
// 创建 A 标签
const downLink = document.createElement ('a');
downLink.download = replaceFileName (ablum.title) + '.zip';
downLink.href = ablum.downloadUrl.replace ('http:', 'https:');
downLink.style.display = 'none';
document.body.append (downLink);
// 模拟点击
downLink.click ();
// Chrome requires the timeout
await delay (1000);
// 移除 A 标签
downLink.remove ();
}
}
// 迅雷下载
const invokeThunder = albums => {
// 迅雷下载任务
const thunderTask = [];
for (const album of albums) {
thunderTask.push ({
name: replaceFileName (album.title) + '.zip',
url: album.downloadUrl
})
}
// 迅雷下载信息
const thunderInfo = {
taskGroupName: 'QQ 群相册_' + getQueryString ('groupId'),
hideYunPan: '0',
referer: 'https://h5.qzone.qq.com/',
tasks: thunderTask
}
copyToClipboard ('thunderx://' + JSON.stringify (thunderInfo));
}
// 复制文本到剪切板
const copyToClipboard = async text => {
navigator.clipboard.writeText (text).catch ((error) => {
console.error (' 异步复制失败 ', error);
// 创建 text area
let textArea = document.createElement ("textarea");
textArea.value = text;
// 使 text area 不在 viewport,同时设置不可见
textArea.style.position = "absolute";
textArea.style.opacity = 0;
textArea.style.left = "-999999px";
textArea.style.top = "-999999px";
document.body.appendChild (textArea);
textArea.focus ();
textArea.select ();
// 执行复制命令并移除文本框
document.execCommand ('copy') ? res () : rej ();
textArea.remove ();
});
}
// 复制相册下载地址
const copyAlbumUrls = async albums => {
const urls = [];
for (const album of albums) {
urls.push (album.downloadUrl);
}
await copyToClipboard (urls.join ('\n'));
}
// 获取下载 URL
var getDownloadUrl = async album => {
// 请求地址参数
const urlParmas = new URLSearchParams ();
urlParmas.append ('g_tk', PSY.user.token ());
urlParmas.append ('qzonetoken', window.g_qzonetoken);
// 请求实体参数
const bodyParmas = new URLSearchParams ();
bodyParmas.append ('uin', PSY.user.getLoginUin ());
bodyParmas.append ('hostUin', -1);
bodyParmas.append ('inCharset', 'utf-8');
bodyParmas.append ('outCharset', 'utf-8');
bodyParmas.append ('refer', 'refer');
bodyParmas.append ('source', 'qzone');
bodyParmas.append ('platform', 'qzone');
bodyParmas.append ('format', 'json');
bodyParmas.append ('appid', 422);
bodyParmas.append ('selectMode', 1);
bodyParmas.append ('albumid', album.id);
bodyParmas.append ('hostid', getQueryString ('groupId'));
bodyParmas.append ('albumName', album.title);
const response = await fetch ('https://h5.qzone.qq.com/proxy/domain/app.photo.qq.com/cgi-bin/app/cgi_arch_photo_v2?' + urlParmas.toString (), {
method: 'POST',
body: bodyParmas
});
return await response.json ();
}
// 相册每页条目数
const ALBUMNS_PAGE_SIZE = 2;
// 获取相册信息
const getAlbumInfo = async (page) => {
const parmas = new URLSearchParams ();
parmas.append ('g_tk', PSY.user.token ());
parmas.append ('qzonetoken', window.g_qzonetoken);
parmas.append ('qunId', getQueryString ('groupId'));
parmas.append ('uin', PSY.user.getLoginUin ());
parmas.append ('start', page * ALBUMNS_PAGE_SIZE);
parmas.append ('num', ALBUMNS_PAGE_SIZE);
parmas.append ('format', 'json');
parmas.append ('inCharset', 'utf-8');
parmas.append ('outCharset', 'utf-8');
parmas.append ('platform', 'qzone');
parmas.append ('source', 'qzone');
parmas.append ('cmd', 'qunGetAlbumList');
const response = await fetch ('https://h5.qzone.qq.com/proxy/domain/u.photo.qzone.qq.com/cgi-bin/upp/qun_list_album_v2?' + parmas.toString ());
return await response.json ();
}
// 获取相册信息
const getAlbumList = async () => {
window.albums = [];
// 获取第一页相册
const albumInfo = await getAlbumInfo (0);
window.albums.push (...albumInfo.data.album || []);
// 相册个数
const total = albumInfo.data.total;
if (total > ALBUMNS_PAGE_SIZE) {
for (let page = 1; page * ALBUMNS_PAGE_SIZE < total; page++) {
const pageAlbumInfo = await getAlbumInfo (page);
window.albums.push (...pageAlbumInfo.data.album || []);
await delay (1500);
}
}
return window.albums;
}
// 获取相册下载链接
const getDownloadLinks = async albums => {
for (const album of albums) {
// 获取相册下载地址
const downloadInfo = await getDownloadUrl (album);
album.downloadUrl = downloadInfo.data.downloadUrl;
// 延迟
await delay (1000);
}
}
// 批量下载
const $downloadBtn = document.createElement ('a');
$downloadBtn.innerText = ' 批量下载 ';
$downloadBtn.setAttribute ('class', 'mod-btn-upload');
$downloadBtn.setAttribute ('title', ' 直接通过浏览器自身下载全部相册,相册过多时,下载容易出错,建议自行复制链接到第三方工具下载 ');
$downloadBtn.style.cssText = "background-color: #dd905b;border-color: #dd905b;margin-left: 10px;";
$downloadBtn.addEventListener ("click", async function () {
// 获取相册列表
if (!window.albums) {
this.innerText = ' 获取下载链接...';
window.albums = await getAlbumList ();
await getDownloadLinks (window.albums);
}
this.innerText = ' 正在下载 ';
// 浏览器下载
await download (albums);
this.innerText = ' 下载完成 ';
setTimeout (() => {
this.innerText = ' 批量下载 ';
}, 1500);
})
$uploadBtn.parentElement.appendChild ($downloadBtn);
// 迅雷下载
const $thunderBtn = document.createElement ('a');
$thunderBtn.innerText = ' 迅雷下载 ';
$thunderBtn.setAttribute ('title', ' 需先安装迅雷,并打开迅雷,以及打开剪切板监听,或直接复制链接到迅雷下载 ');
$thunderBtn.setAttribute ('class', 'mod-btn-upload');
$thunderBtn.style.cssText = "background-color: #5b63dd;border-color: #5b63dd;margin-left: 10px;";
$thunderBtn.addEventListener ("click", async function () {
// 获取相册列表
if (!window.albums) {
this.innerText = ' 获取下载链接...';
window.albums = await getAlbumList ();
await getDownloadLinks (window.albums);
}
this.innerText = ' 正在唤醒迅雷 ';
// 迅雷下载
invokeThunder (albums);
this.innerText = ' 已唤醒迅雷 ';
setTimeout (() => {
this.innerText = ' 迅雷下载 ';
}, 1500);
})
$uploadBtn.parentElement.appendChild ($thunderBtn);
// 复制链接
const $copyLinks = document.createElement ('a');
$copyLinks.innerText = ' 复制链接 ';
$copyLinks.setAttribute ('title', ' 复制下载链接到剪切板,可以自行到迅雷、IDM 等第三方工具下载,建议尽快下载,避免存在有效期 ');
$copyLinks.setAttribute ('class', 'mod-btn-upload');
$copyLinks.style.cssText = "background-color: #5bdd6b;border-color: #5bdd6b;margin-left: 10px;";
$copyLinks.addEventListener ("click", async function () {
// 获取相册列表
if (!window.albums) {
this.innerText = ' 获取下载链接...';
window.albums = await getAlbumList ();
await getDownloadLinks (window.albums);
}
this.innerText = ' 正在复制 ';
// 复制到剪切板
await copyAlbumUrls (albums);
this.innerText = ' 复制完成 ';
setTimeout (() => {
this.innerText = ' 复制链接 ';
}, 1500);
})
$uploadBtn.parentElement.appendChild ($copyLinks);
})();