Pornhub封面与切片下载
// ==UserScript==
// @name Pornhub Enhancer
// @namespace github/BlackVisor
// @version v1.0.2
// @description Pornhub封面与切片下载
// @license GPL-3.0
// @icon https://ei.phncdn.com/www-static/favicon.ico
// @match https://*.pornhub.com/model/*/videos*
// @match https://*.pornhub.com/pornstar/*/videos*
// @connect *
// @grant GM_xmlhttpRequest
// @run-at document-end
// ==/UserScript==
(function() {
'use strict';
var _GM_xmlhttpRequest = typeof GM_xmlhttpRequest != "undefined" ? GM_xmlhttpRequest : void 0;
var crawlButtonId = "crawl-button";
var uploadTimePattern = /\/videos\/(\d{6})\/(\d{2})\//;
var stripInvalidChar = (source) => {
let text = source;
text = text.replace(/\xa0/g, " ");
text = text.replace(/[<>:"/\\|?*]/g, "");
text = text.replace(/[\x00-\x1F]/g, "");
text = text.replace(/\s+/g, " ").trim();
while (text.endsWith(".")) text = text.slice(0, -1).trim();
return text;
};
var addCrawlButton = () => {
console.log("[Pornhub] add crawl button event triggered");
if (document.querySelector(crawlButtonId)) {
console.warn("[Pornhub] crawl button existed");
return;
}
const crawlButton = document.createElement("button");
crawlButton.textContent = "下载封面与切片";
crawlButton.id = crawlButtonId;
crawlButton.style.position = "fixed";
crawlButton.style.top = "30px";
crawlButton.style.right = "30px";
crawlButton.style.zIndex = "9999";
crawlButton.style.padding = "8px 16px";
crawlButton.style.backgroundColor = "#4CAF50";
crawlButton.style.color = "white";
crawlButton.style.border = "none";
crawlButton.style.borderRadius = "4px";
crawlButton.style.cursor = "pointer";
crawlButton.style.boxShadow = "0 2px 5px rgba(0,0,0,0.2)";
crawlButton.style.transition = "background-color 0.3s";
crawlButton.addEventListener("mouseover", () => {
crawlButton.style.backgroundColor = "#9ef5a0";
});
crawlButton.addEventListener("mouseout", () => {
crawlButton.style.backgroundColor = "#4CAF50";
});
crawlButton.addEventListener("click", handleClickCrawlButton);
document.body.appendChild(crawlButton);
};
var handleClickCrawlButton = () => {
const videoCards = document.querySelectorAll("div.profileVids li.pcVideoListItem");
const pendingDownloadList = [];
for (const videoCard of videoCards) {
const tagA = videoCard.querySelector("div.phimage a");
let viewkey = "";
let cover = "";
let preview = "";
let uploadTime = "";
let title = "";
if (tagA) {
const href = tagA.getAttribute("href");
if (href) {
viewkey = href.split("=").at(-1) || "";
title = stripInvalidChar(tagA.getAttribute("data-title") || "");
const tagImg = tagA.querySelector("img");
if (tagImg) {
cover = tagImg.getAttribute("src") || "";
preview = tagImg.getAttribute("data-mediabook") || "";
if (cover) {
const matched = cover.match(uploadTimePattern);
if (matched) {
uploadTime = `${matched[1]}${matched[2]}`;
pendingDownloadList.push({
viewkey,
cover,
preview,
uploadTime,
title
});
}
}
}
}
}
}
pendingDownloadList.forEach((video) => {
if (!video.viewkey || !video.uploadTime || !video.title) console.error(`[Pornhub] found empty in viewkey=${video.viewkey}, uploadTime=${video.uploadTime}, title=${video.title}`);
else {
if (video.cover) download(video.cover, `${video.uploadTime}-----${video.viewkey}-----${video.title}-----cover.jpg`);
else console.error(`[Pornhub] found empty cover for ${video.viewkey}`);
if (video.preview) download(video.preview, `${video.uploadTime}-----${video.viewkey}-----${video.title}-----preview.webm`);
else console.error(`[Pornhub] found empty preview for ${video.viewkey}`);
}
});
console.log(`[Pornhub] download ${pendingDownloadList.length} / ${Array.from(videoCards).length} videos`);
};
var download = (src, name) => {
if (!src || !name) {
console.error(`[Pornhub] check empty for src=${src}, name=${name}`);
return;
}
try {
_GM_xmlhttpRequest({
method: "GET",
url: src,
headers: {
"Referer": window.location.href,
"User-Agent": navigator.userAgent
},
responseType: "blob",
onload: function(response) {
if (response.status === 200) {
const blob = response.response;
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = name;
document.body.appendChild(link);
link.click();
setTimeout(() => {
URL.revokeObjectURL(url);
link.remove();
}, 100);
} else console.error(`[Pornhub] download ${src} failed with status: ${response.status}`);
},
onerror: function(err) {
console.error(`[Pornhub] GM_xmlhttpRequest ${src} error:`, err);
}
});
} catch (err) {
console.error(`[Pornhub] download ${src} failed by gm: `, err);
}
};
var init = () => {
addCrawlButton();
};
init();
})();