Sleazy Fork is available in English.
Adds useful QoL features when browsing boundhub
// ==UserScript==
// @name better-bh
// @namespace ocalectu/better-bh
// @version 0.1.2
// @description Adds useful QoL features when browsing boundhub
// @license GNU GPLv3
// @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAnFBMVEUaGhoaHB0bGxsaGxwbFRMcDwoQEhWghlsUFRf/2o6tkGFMQzMNW3oZHiAMDxMMYYIIeKQFh7phUz2lj2HGpG3SrnOXf1cABA1qWkEyLSY8Niu7nGmzlWUbFxX1yoX804r/5pV2ZEcNbpMRSF4jIh//3ZBeTDQAjMsHg7QAfrvEo2zjvn0SWXQeAwAIdJ7qx4J8aUmHck8DkccBmdXThxiHAAAAt0lEQVQYlT2O2xaCIBREj3JAVAJNydTK7Iqplfn//xZWth/3mjUzsPQsSw5/Ej9Ntb9i7iyCW8azdZXz2QSpFXlVfAUhEOgiDMsNw601LiJ4/m5X7Wt5OKJDUCibOOV5fb5cFXUcaoztkACZDhpBFwsaRdOKlLL1mq6ntL8bSB7PYUj9IjZCKfGKYNC6bcuQxWNsGe/AOWNMMnyJHnHqcL9Y8Sv9MQk7+xcEx8+PyMyJrToiIdiJN5hDDp5C9Lj9AAAAAElFTkSuQmCC
// @match https://www.boundhub.com/*
// @require https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js
// @connect *
// @grant GM_addStyle
// @grant GM_download
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_xmlhttpRequest
// ==/UserScript==
(function (JSZip) {
'use strict';
var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
var _GM_addStyle = (() => typeof GM_addStyle != "undefined" ? GM_addStyle : void 0)();
var _GM_download = (() => typeof GM_download != "undefined" ? GM_download : void 0)();
var _GM_getValue = (() => typeof GM_getValue != "undefined" ? GM_getValue : void 0)();
var _GM_setValue = (() => typeof GM_setValue != "undefined" ? GM_setValue : void 0)();
var _GM_xmlhttpRequest = (() => typeof GM_xmlhttpRequest != "undefined" ? GM_xmlhttpRequest : void 0)();
var require_dataTypes = __commonJSMin(((exports) => {
Object.defineProperty(exports, "__esModule", { value: true });
exports.stringOrInt = stringOrInt;
exports.processRelativeDate = processRelativeDate;
function stringOrInt(input, force = false) {
try {
if (force) {
const result = parseInt(input);
if (isNaN(result)) return input;
return result;
} else {
if (input.length > 0 && input.endsWith("%")) {
const result = parseInt(input.substring(0, input.length - 1));
if (isNaN(result)) return input;
return result / 100;
}
const result = parseInt(input);
if (isNaN(result)) return input;
return result;
}
} catch {
return input;
}
}
function processRelativeDate(input, replaceExtra, now = new Date()) {
if (input === void 0) return { filtered: input };
const exclude = [
"\n",
" ",
...replaceExtra ?? []
];
let filteredInput = input;
for (const item of exclude) filteredInput = filteredInput.replaceAll(item, "");
const match = filteredInput.trim().toLowerCase().match(/^(\d+)\s+(minute|hour|day|week|month|year)s?\s*(?:ago)?$/);
if (!match) {
console.warn(`Invalid relative date: "${filteredInput}"`);
return { filtered: filteredInput };
}
const amount = Number(match[1]);
const unit = match[2];
const date = new Date(now);
switch (unit) {
case "minute":
date.setUTCMinutes(date.getUTCMinutes() - amount);
break;
case "hour":
date.setUTCHours(date.getUTCHours() - amount);
break;
case "day":
date.setUTCDate(date.getUTCDate() - amount);
break;
case "week":
date.setUTCDate(date.getUTCDate() - amount * 7);
break;
case "month":
date.setUTCMonth(date.getUTCMonth() - amount);
break;
case "year": date.setUTCFullYear(date.getUTCFullYear() - amount);
}
return {
filtered: filteredInput,
date,
accuracy: unit
};
}
}));
var require_functions = __commonJSMin(((exports) => {
Object.defineProperty(exports, "__esModule", { value: true });
exports.delay = void 0;
var delay = (ms) => new Promise((res) => setTimeout(res, ms));
exports.delay = delay;
}));
var require_utils = __commonJSMin(((exports) => {
Object.defineProperty(exports, "__esModule", { value: true });
exports.delay = exports.processRelativeDate = exports.stringOrInt = void 0;
var dataTypes_1 = require_dataTypes();
Object.defineProperty(exports, "stringOrInt", {
enumerable: true,
get: function() {
return dataTypes_1.stringOrInt;
}
});
Object.defineProperty(exports, "processRelativeDate", {
enumerable: true,
get: function() {
return dataTypes_1.processRelativeDate;
}
});
var functions_1 = require_functions();
Object.defineProperty(exports, "delay", {
enumerable: true,
get: function() {
return functions_1.delay;
}
});
}));
var require_logger = __commonJSMin(((exports) => {
Object.defineProperty(exports, "__esModule", { value: true });
exports.log = log;
exports.trace = trace;
exports.setDebug = setDebug;
var config = {
debug: {}.COMMON_DEBUG === "true",
trace: {}.COMMON_TRACE === "true"
};
function log(...args) {
if (config.debug) console.log("[debug]", ...args);
}
function trace(...args) {
if (config.trace) console.log("[trace]", ...args);
}
function setDebug(value) {
config.debug = value;
}
}));
var require_categories = __commonJSMin(((exports) => {
Object.defineProperty(exports, "__esModule", { value: true });
exports.extractCategories = extractCategories;
var utils_1 = require_utils();
var logger_1 = require_logger();
function extractCategories(rootNode) {
let result = [];
rootNode.find(".item").each((_, item) => {
const extracted = extractCategoryItem(item);
if (extracted !== void 0) result.push(extracted);
});
(0, logger_1.trace)("extractCategories", result);
return result;
}
function extractCategoryItem(item) {
const url = item.attr("href");
if (url === void 0) return;
const id = url.replace(/\/$/, "").split("/").pop();
if (id === void 0) return;
const name = item.attr("title");
if (name === void 0) return;
const thumbnailUrl = item.find(".thumb").attr("src");
if (thumbnailUrl === void 0) return;
const split = item.find(".videos").text().trim().split(" ");
if (split.length === 0) return;
const videos = (0, utils_1.stringOrInt)(split[0]);
const ratingPercentString = item.find(".rating").text().trim();
try {
return {
url,
id,
name,
thumbnailUrl,
videos,
ratingPercentString,
rating: parseInt(ratingPercentString.substring(0, ratingPercentString.length - 1)) / 100
};
} catch (e) {
console.error(e);
return;
}
}
}));
var require_bh = __commonJSMin(((exports) => {
Object.defineProperty(exports, "__esModule", { value: true });
exports.albumContainerIds = exports.conversationsContainerIds = exports.messagesContainerIds = exports.categoryContainerIds = exports.albumCommentId = exports.videoCommentId = exports.siteContentId = exports.singleAlbumId = exports.singleVideoId = exports.videoContainerIds = exports.BLANK_IMAGE = void 0;
exports.BLANK_IMAGE = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
exports.videoContainerIds = {
list: "list_videos_most_recent_videos",
popular: "list_videos_videos_watched_right_now_items",
latest: "list_videos_latest_videos_list",
common: "list_videos_common_videos_list",
favorite: "list_videos_favourite_videos",
uploaded: "list_videos_uploaded_videos",
related: "list_videos_related_videos",
private: "list_videos_private_videos"
};
exports.singleVideoId = "tab_video_info";
exports.singleAlbumId = "tab_album_info";
exports.siteContentId = "siteContent";
exports.videoCommentId = "video_comments_video_comments_items";
exports.albumCommentId = "album_comments_album_comments_items";
exports.categoryContainerIds = { list: "list_categories_categories_list_items" };
exports.messagesContainerIds = { messages: "list_messages_my_conversation_messages_items" };
exports.conversationsContainerIds = { conversations: "list_members_my_conversations" };
exports.albumContainerIds = {
common: "list_albums_common_albums_list",
related: "list_albums_related_albums",
private: "list_albums_private_albums",
favorite: "list_albums_my_favourite_albums",
uploaded: "list_albums_created_albums"
};
}));
var import_categories = require_categories();
var import_bh = require_bh();
var ContainerType = function(ContainerType) {
ContainerType["ALBUM"] = "album";
ContainerType["VIDEO"] = "video";
ContainerType["CATEGORY"] = "category";
ContainerType["MESSAGES"] = "messages";
ContainerType["CONVERSATIONS"] = "conversations";
return ContainerType;
}({});
var SettingsKey = function(SettingsKey) {
SettingsKey["HOSTNAME"] = "hostname";
SettingsKey["HIDE_ADS"] = "hide-ads";
SettingsKey["SCRAPE"] = "scrape";
SettingsKey["UNHIDE_PRIVATE"] = "unhide-private";
SettingsKey["DEBUG"] = "debug";
SettingsKey["HIDE_SHARE"] = "hide-share";
return SettingsKey;
}({});
var HOSTNAME = "http://localhost:3000";
var DELAY = 1e3;
var settingValues = [
{
key: SettingsKey.SCRAPE,
title: "Scrape on browse",
type: "boolean",
default: false
},
{
key: SettingsKey.HOSTNAME,
title: "Scrape Destination Hostname",
type: "string",
default: HOSTNAME
},
{
key: SettingsKey.HIDE_ADS,
title: "Hide Ads (doesn't affect video player)",
type: "boolean",
default: false
},
{
key: SettingsKey.HIDE_SHARE,
title: "Hide Share button on video and album pages",
type: "boolean",
default: true
},
{
key: SettingsKey.UNHIDE_PRIVATE,
title: "Make private videos easier to see",
type: "boolean",
default: true
},
{
key: SettingsKey.DEBUG,
title: "Enable debug logs in console",
type: "boolean",
default: false
}
];
var settingDefaults = settingValues.reduce((prev, item) => ({
...prev,
[item.key]: item.default
}), {});
var PREFIX = "better-bh";
var pageDefinitions = {
videos: {
segmentMatch: ["/videos", ""],
containerIds: [
{
id: import_bh.videoContainerIds.list,
type: ContainerType.VIDEO
},
{
id: import_bh.videoContainerIds.popular,
type: ContainerType.VIDEO
},
{
id: import_bh.videoContainerIds.related,
type: ContainerType.VIDEO
}
]
},
albums: {
segmentMatch: ["/albums"],
containerIds: [{
id: import_bh.albumContainerIds.common,
type: ContainerType.ALBUM
}, {
id: import_bh.albumContainerIds.related,
type: ContainerType.ALBUM
}]
},
latest: {
segmentMatch: ["/latest-updates"],
containerIds: [{
id: import_bh.videoContainerIds.latest,
type: ContainerType.VIDEO
}]
},
categories: {
segmentMatch: ["/categories"],
containerIds: [{
id: import_bh.categoryContainerIds.list,
type: ContainerType.CATEGORY
}, {
id: import_bh.videoContainerIds.common,
type: ContainerType.VIDEO
}]
},
members: {
segmentMatch: ["/members"],
containerIds: [
{
id: import_bh.videoContainerIds.private,
type: ContainerType.VIDEO
},
{
id: import_bh.videoContainerIds.favorite,
type: ContainerType.VIDEO
},
{
id: import_bh.videoContainerIds.uploaded,
type: ContainerType.VIDEO
},
{
id: import_bh.albumContainerIds.private,
type: ContainerType.ALBUM
},
{
id: import_bh.albumContainerIds.favorite,
type: ContainerType.ALBUM
},
{
id: import_bh.albumContainerIds.uploaded,
type: ContainerType.ALBUM
}
]
},
messages: {
segmentMatch: ["/messages"],
containerIds: [{
id: import_bh.conversationsContainerIds.conversations,
type: ContainerType.CONVERSATIONS
}, {
id: import_bh.messagesContainerIds.messages,
type: ContainerType.MESSAGES
}]
},
common: {
segmentMatch: [
"/top-rated",
"/most-popular",
"/sites",
"/models",
"/channels"
],
containerIds: [{
id: import_bh.videoContainerIds.common,
type: ContainerType.VIDEO
}]
}
};
async function GM_xmlhttpRequestPromise(details) {
return new Promise((resolve, reject) => {
_GM_xmlhttpRequest({
...details,
onload: (response) => resolve(response),
onerror: (error) => reject(error),
ontimeout: () => reject()
});
});
}
var import_logger = require_logger();
async function processCategories(rootNode) {
let result = (0, import_categories.extractCategories)(rootNode);
(0, import_logger.log)("Pushing categories...", result);
const webResult = await GM_xmlhttpRequestPromise({
url: `${await _GM_getValue("hostname", HOSTNAME)}/api/bh/categories`,
method: "POST",
data: JSON.stringify({ items: result })
});
(0, import_logger.log)("Categories result: ", webResult);
}
var require_common = __commonJSMin(((exports) => {
Object.defineProperty(exports, "__esModule", { value: true });
exports.getPathnameVariables = getPathnameVariables;
exports.getCommonDetails = getCommonDetails;
exports.getCommonItemDetails = getCommonItemDetails;
exports.getDurationOrImageCountText = getDurationOrImageCountText;
exports.getScreenshots = getScreenshots;
exports.getRelatedIds = getRelatedIds;
var utils_1 = require_utils();
var logger_1 = require_logger();
function getPathnameVariables(pathname) {
let slashSplit = pathname.replace(/\/$/, "").split("/");
return {
stringId: slashSplit.pop(),
id: slashSplit.pop()
};
}
function getCommonDetails(rootNode, commentId, relatedId, pathname) {
(0, logger_1.log)("getCommonDetails");
const pathnameVars = getPathnameVariables(pathname);
if (pathnameVars.id === void 0) return;
const title = rootNode.find(".headline h2").text().trim();
const detailsBlock = rootNode.find(".block-details").first();
const infoBlock = detailsBlock.find(".info").first();
const firstInfoItemBlock = infoBlock.find(".item").first();
const viewsText = firstInfoItemBlock.find("span").eq(1).find("em").first().text();
const submittedText = firstInfoItemBlock.find("span").eq(2).find("em").first().text();
const publishDateObj = (0, utils_1.processRelativeDate)(submittedText);
const description = infoBlock.find(".item").eq(1).find("em").text();
const categories = infoBlock.find(".item").eq(2).find("a").map(function(_, adapter) {
return adapter.text().replace(/\s+/g, " ").trim();
});
const tags = infoBlock.find(".item").eq(3).find("a").map(function(_, adapter) {
return adapter.text().replace(/\s+/g, " ").trim();
});
const userBlock = detailsBlock.find(".block-user").first();
const username = userBlock.find(".username").first().find("a").first().text().trim();
const userUrl = userBlock.find(".username").first().find("a").first().attr("href");
const userId = userUrl?.replace(/\/$/, "").split("/").pop();
const avatarUrl = userBlock.find(".avatar").first().find("img").first().attr("src");
const related = getRelatedIds(rootNode, relatedId);
const comments = rootNode.find(`#${commentId}`).find(".item").map(function(_, adapter) {
const userA = adapter.find(".image").first().find("a").first();
(0, logger_1.trace)(userA.length);
let userInfo = {
name: adapter.find(".username").first().text().trim(),
url: void 0,
id: adapter.attr("data-comment-id"),
avatarUrl: void 0
};
if (userA.length > 0) userInfo = {
name: userA.attr("title") || adapter.find(".username").first().text().trim(),
url: userA.attr("href"),
id: userA.attr("href")?.replace(/\/$/, "").split("/").pop(),
avatarUrl: userA.find("img").first().attr("src")
};
const commentInfo = adapter.find(".comment-info").first().text().trim();
const relativeDateText = commentInfo.match(/\d+\s+(?:minute|hour|day|week|month|year)s?\s+ago/i)?.[0] ?? commentInfo;
const relativeDate = (0, utils_1.processRelativeDate)(relativeDateText);
return {
user: userInfo,
relativeDateString: relativeDate.filtered,
dateExtracted: new Date(),
rating: (0, utils_1.stringOrInt)(adapter.find(".comment-rating").first().text()),
content: adapter.find(".original-text").first().text()
};
});
const isPrivate = rootNode.find(".no-player").length > 0 || rootNode.find(".block-album .images .item.private").length > 0;
const voteParts = rootNode.find(".rating-container").find(".voters").first().text().trim().split("(");
if (voteParts.length < 2) return;
const votePercentageText = voteParts[0].trim();
const voteAmountText = voteParts[1].split("votes")[0].trim();
let viewCount = viewsText ? parseInt(viewsText.split(" ").join("")) : void 0;
let ratingPercent = parseInt(votePercentageText.substring(0, votePercentageText.length - 1)) / 100;
let voteAmount = (0, utils_1.stringOrInt)(voteAmountText);
(0, logger_1.log)("Basic extracting done");
const fullItem = {
title,
id: pathnameVars.id,
stringId: pathnameVars.stringId,
viewCount,
description,
relativeDateString: submittedText,
publishDate: publishDateObj?.date,
publishDateAccuracy: publishDateObj?.accuracy,
tags,
categories,
related,
user: {
name: username,
url: userUrl,
id: userId,
avatarUrl
},
comments,
isPrivate: isPrivate ? isPrivate : void 0,
ratingPercent,
voteAmount,
ratingPercentString: votePercentageText
};
(0, logger_1.trace)("Full Item", fullItem);
return fullItem;
}
function getCommonItemDetails(itemNode) {
const aNode = itemNode.find("a").first();
const url = aNode.attr("href");
if (url === void 0) return;
const pathnameVars = getPathnameVariables(url);
if (pathnameVars.id === void 0) return;
const isPrivate = aNode.find(".line-private").length > 0;
const viewsText = aNode.find(".views").text().trim().split(" views")[0].split(" ").join("");
const ratingPercentString = aNode.find(".rating").text().trim();
const title = aNode.attr("title") ?? aNode.find(".title").text().trim() ?? "";
const imgNode = aNode.find("img").first();
let thumbnailUrl = imgNode.attr("src");
if (thumbnailUrl === void 0 || thumbnailUrl.startsWith("data:image")) thumbnailUrl = imgNode.attr("data-original");
const thumbnailCount = imgNode.attr("data-cnt");
try {
let viewCount = (0, utils_1.stringOrInt)(viewsText);
let ratingPercent = parseInt(ratingPercentString.substring(0, ratingPercentString.length - 1)) / 100;
return {
id: pathnameVars.id,
stringId: pathnameVars.stringId,
title,
url,
viewCount,
thumbnailUrl,
thumbnailCount: thumbnailCount === void 0 ? thumbnailCount : (0, utils_1.stringOrInt)(thumbnailCount),
ratingPercent,
ratingPercentString,
isPrivate
};
} catch (e) {
console.error(e);
return;
}
}
function getDurationOrImageCountText(rootNode, node) {
if (node === void 0) return rootNode.find(".block-details").first().find(".info").first().find(".item").first().find("span").first().find("em").first().text().trim();
else if (node.find(".photos").length > 0) return node.find(".photos").first().text().trim();
else return node.find(".duration").first().text().trim();
}
function getScreenshots(rootNode, selector) {
return rootNode.find(selector).find(".item").map(function(_, adapter) {
return {
url: adapter.attr("href"),
contentUrl: (() => {
const image = adapter.find("img").first();
const src = image.attr("src");
return src === void 0 || src.startsWith("data:image") ? image.attr("data-original") : src;
})()
};
});
}
function getRelatedIds(rootNode, containerId) {
return rootNode.find(`#${containerId}`).find(".item").map(function(_, adapter) {
const extracted = getCommonItemDetails(adapter);
if (extracted !== void 0) return extracted.id;
}).filter((id) => id !== void 0);
}
}));
var import_videos = ( __commonJSMin(((exports) => {
Object.defineProperty(exports, "__esModule", { value: true });
exports.extractVideoList = extractVideoList;
exports.extractSingleVideo = extractSingleVideo;
var bh_1 = require_bh();
var common_1 = require_common();
var logger_1 = require_logger();
function extractVideoList(rootNode) {
let result = [];
rootNode.find(".item").each((index, item) => {
(0, logger_1.trace)("extractVideoList item", index);
const extracted = extractVideoItem(item);
if (extracted !== void 0) result.push(extracted);
});
(0, logger_1.trace)("extractVideoList", result);
if (result.length === 0) return [];
return result;
}
function extractSingleVideo(rootNode, pathname) {
(0, logger_1.log)("extractSingleVideo");
const common = (0, common_1.getCommonDetails)(rootNode, bh_1.videoCommentId, bh_1.videoContainerIds.related, pathname);
if (common === void 0) return;
const durationSplit = (0, common_1.getDurationOrImageCountText)(rootNode).split(" ");
const screenshots = (0, common_1.getScreenshots)(rootNode, ".block-screenshots");
(0, logger_1.log)("Basic extracting done");
try {
let secondsString = durationSplit.pop();
let minutesString = durationSplit.pop();
if (secondsString === void 0 || minutesString === void 0) {
(0, logger_1.trace)("secondsString, minutesString undefined");
return;
}
secondsString = secondsString.substring(0, secondsString.length - 3);
minutesString = minutesString.substring(0, minutesString.length - 3);
let length = parseInt(minutesString) * 60 + parseInt(secondsString);
const fullItem = {
...common,
length,
screenshots
};
(0, logger_1.trace)("fullItem", fullItem);
return fullItem;
} catch (e) {
console.error(e);
return;
}
}
function extractVideoItem(itemNode) {
const common = (0, common_1.getCommonItemDetails)(itemNode);
(0, logger_1.trace)("common", common);
if (common === void 0) return;
const durationText = (0, common_1.getDurationOrImageCountText)(itemNode, itemNode);
const durationSplit = durationText.split(":");
try {
let secondsString = durationSplit.pop();
let minutesString = durationSplit.pop();
(0, logger_1.trace)("durationText", durationText, "durationSplit", durationSplit);
(0, logger_1.trace)("secondsString", secondsString, "minutesString", minutesString);
if (secondsString === void 0 || minutesString === void 0) return;
secondsString = secondsString.substring(0, secondsString.length - 1);
minutesString = minutesString.substring(0, minutesString.length - 1);
let length = parseInt(minutesString) * 60 + parseInt(secondsString);
return {
...common,
length
};
} catch (e) {
console.error(e);
return;
}
}
})))();
async function processVideoList(rootNode) {
let result = (0, import_videos.extractVideoList)(rootNode);
if (result.length === 0) return;
(0, import_logger.log)("Pushing video list...", result);
const webResult = await GM_xmlhttpRequestPromise({
url: `${await _GM_getValue("hostname", HOSTNAME)}/api/bh/video_items`,
method: "POST",
data: JSON.stringify({ items: result })
});
(0, import_logger.log)("Video list result: ", webResult);
}
async function processSingleVideo(rootNode) {
let result = (0, import_videos.extractSingleVideo)(rootNode, window.location.pathname);
(0, import_logger.log)("Pushing single video...", result);
const webResult = await GM_xmlhttpRequestPromise({
url: `${await _GM_getValue("hostname", HOSTNAME)}/api/bh/video_info`,
method: "POST",
data: JSON.stringify(result)
});
(0, import_logger.log)("Single video result: ", webResult);
}
function waitForVideo() {
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
reject( new Error("Video did not load within 10 seconds"));
}, 1e4);
const checkVideo = () => {
const video = document.querySelector("video");
if (video && video.src) {
clearTimeout(timeout);
console.log("Video Downloader: Video loaded successfully");
resolve();
} else setTimeout(checkVideo, 500);
};
checkVideo();
});
}
async function loadVideo() {
const fpUiElement = document.querySelector(".fp-ui");
if (fpUiElement) {
fpUiElement.click();
console.log("Video Downloader: Clicked fp-ui element");
await waitForVideo();
} else {
if (document.querySelector("video")) return;
throw new Error("Video player not found. Make sure you are on a video page.");
}
}
async function handleVideoDownload() {
try {
console.log("newnew");
await loadVideo();
} catch {
let slashSplit = window.location.pathname.replace(/\/$/, "").split("/");
slashSplit.pop();
const id = slashSplit.pop();
if (id === void 0) throw new Error("Unable to get ID from title, make sure the page is correct.");
let contentUrl = (await _GM_xmlhttpRequest({
url: `${await _GM_getValue("hostname", HOSTNAME)}/api/bh/get_download`,
responseType: "json",
method: "POST",
data: JSON.stringify({ id })
})).response;
if (contentUrl === void 0 || !contentUrl.success) throw new Error("Unable to get contentUrl, make sure server is correct.");
var vLink = document.createElement("a");
vLink.setAttribute("href", contentUrl.link);
vLink.setAttribute("download", `${id}.mp4`);
vLink.click();
return;
}
let video = document.querySelector("video");
if (!video || !video.src) {
video = document.querySelector("source");
if (!video || !video.src) throw new Error("Video source not found. Make sure the video is loaded first.");
}
const filename = getFilenameFromUrl(document.URL);
await downloadVideo(video.src, filename);
}
function getFilenameFromUrl(url) {
try {
let split = url.split("/");
console.log(split[split.length - 3]);
return `${split[split.length - 3]}.mp4`;
} catch {
try {
const filename = new URL(url).pathname.split("/").pop();
if (!filename || filename === "" || !filename.includes(".")) return `video-${( new Date()).toISOString().replace(/[:.]/g, "-")}.mp4`;
return filename;
} catch (error) {
console.error("Video Downloader: Error parsing URL:", error);
return `video-${( new Date()).toISOString().replace(/[:.]/g, "-")}.mp4`;
}
}
}
async function downloadVideo(url, filename) {
try {
console.log("Video Downloader: Opening video URL...");
console.log("Video Downloader: URL:", url);
console.log("Video Downloader: Filename:", filename);
if (!url || url === "") throw new Error("Video URL is empty or invalid");
if (!url.startsWith("http://") && !url.startsWith("https://") && !url.startsWith("blob:")) throw new Error("Video URL is not a valid HTTP/HTTPS/blob URL: " + url);
console.log("Video Downloader: Opening video URL in new tab...");
try {
console.log("Video Downloader: Auto-downloading MP4...");
console.log("Video Downloader: URL:", url);
console.log("Video Downloader: Filename:", filename);
_GM_download({
url,
name: filename,
onerror: (e) => console.error("download error", e),
ontimeout: () => console.error("download timeout"),
onload: () => console.error("download load")
});
console.log("Video Downloader: Auto-download started for", filename);
} catch (error) {
console.error("Video Downloader: Auto-download failed:", error);
}
console.log("Video Downloader: Video URL opened successfully");
} catch (error) {
console.error("Video Downloader: Error opening video URL:", {
error,
url,
filename
});
throw error;
}
}
async function handleAlbumDownload() {
const titleElement = document.querySelector(".headline");
const cleanTitle = (titleElement ? titleElement.innerText.trim() : "album").replace(/[<>:"/\\|?*]/g, "_").substring(0, 100);
const imagesContainer = document.querySelector(".images");
if (!imagesContainer) throw new Error("Images container not found");
const imageUrls = [...imagesContainer.children].map((child) => child.href).filter((href) => href);
if (imageUrls.length === 0) throw new Error("No images found in album");
console.log(`Video Downloader: Found ${imageUrls.length} images in album`);
await downloadAlbumAsZip(imageUrls, cleanTitle);
}
async function downloadAlbumAsZip(imageUrls, albumTitle) {
try {
console.log("Video Downloader: Starting album download...");
console.log("Video Downloader: Album title:", albumTitle);
console.log("Video Downloader: Image count:", imageUrls.length);
if (typeof JSZip === "undefined") throw new Error("JSZip library is not available. Please reload the extension.");
console.log("Video Downloader: JSZip library ready, creating ZIP...");
const zip = new JSZip();
let downloadedCount = 0;
const progressDiv = document.createElement("div");
progressDiv.id = "download-progress";
progressDiv.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: #333;
color: white;
padding: 15px;
border-radius: 5px;
z-index: 10000;
font-family: Arial, sans-serif;
`;
progressDiv.innerHTML = `Downloading album... 0/${imageUrls.length}`;
document.body.appendChild(progressDiv);
for (let i = 0; i < imageUrls.length; i++) try {
const imageUrl = imageUrls[i];
console.log(`Video Downloader: Downloading image ${i + 1}/${imageUrls.length}: ${imageUrl}`);
const response = await fetch(imageUrl, {
method: "GET",
mode: "cors",
credentials: "omit",
headers: { "Accept": "image/*" }
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${response.statusText}`);
const blob = await response.blob();
console.log(`Video Downloader: Image ${i + 1} blob size: ${blob.size} bytes, type: ${blob.type}`);
let extension = ".jpg";
if (imageUrl.includes(".png")) extension = ".png";
else if (imageUrl.includes(".gif")) extension = ".gif";
else if (imageUrl.includes(".webp")) extension = ".webp";
else if (blob.type.includes("png")) extension = ".png";
else if (blob.type.includes("gif")) extension = ".gif";
else if (blob.type.includes("webp")) extension = ".webp";
const filename = `image_${String(i + 1).padStart(3, "0")}${extension}`;
const arrayBuffer = await blob.arrayBuffer();
zip.file(filename, arrayBuffer);
downloadedCount++;
progressDiv.innerHTML = `Downloading album... ${downloadedCount}/${imageUrls.length}`;
console.log(`Video Downloader: Added ${filename} to ZIP`);
} catch (error) {
console.warn(`Failed to download image ${i + 1}:`, error);
}
progressDiv.innerHTML = "Creating ZIP file...";
const zipBlob = await zip.generateAsync({ type: "blob" });
const downloadUrl = URL.createObjectURL(zipBlob);
const a = document.createElement("a");
a.href = downloadUrl;
a.download = `${albumTitle}.zip`;
a.style.display = "none";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(downloadUrl);
document.body.removeChild(progressDiv);
console.log("Video Downloader: Album download completed");
} catch (error) {
console.error("Video Downloader: Album download failed:", error);
throw new Error("Failed to download album, see console for details");
}
}
var import_messages = ( __commonJSMin(((exports) => {
Object.defineProperty(exports, "__esModule", { value: true });
exports.extractConversations = extractConversations;
exports.extractMessages = extractMessages;
var bh_1 = require_bh();
var utils_1 = require_utils();
var logger_1 = require_logger();
function extractUsers(rootNode) {
const userId = window.location.pathname?.replace(/\/$/, "").split("/").pop();
if (userId === void 0) return;
const username = rootNode.find(".main-container-user").first().find(".headline").first().find("a").eq(1).text().trim();
const messages = rootNode.find(".item:not(.me):not(.grouped)");
let avatarUrl = void 0;
messages.each((_, item) => {
if (avatarUrl !== void 0) return;
let foundUrl = item.find(".image").first().find("img").first().attr("src");
if (foundUrl !== void 0) {
avatarUrl = foundUrl;
return;
}
});
const memberMenu = rootNode.find(".member-menu").first();
const meAvatarUrl = memberMenu.find("img").first().attr("src");
if (meAvatarUrl === void 0) return;
const meUsername = memberMenu.find("img").first().attr("alt");
if (meUsername === void 0) return;
const dotSplit = meAvatarUrl.split(".");
dotSplit.pop();
const meId = dotSplit.pop()?.replace(/\/$/, "").split("/").pop();
if (meId === void 0) return;
const result = {
user: {
name: username,
url: `https://www.boundhub.com/members/${userId}`,
id: userId,
avatarUrl: avatarUrl === bh_1.BLANK_IMAGE ? void 0 : avatarUrl
},
me: {
name: meUsername,
url: `https://www.boundhub.com/members/${meId}`,
id: meId,
avatarUrl: meAvatarUrl === bh_1.BLANK_IMAGE ? void 0 : meAvatarUrl
}
};
(0, logger_1.trace)("extractUsers", result);
return result;
}
async function extractConversations(rootNode) {
let result = [];
for (const item of rootNode.find(".item").toArray()) {
const extracted = await extractConversationItem(item);
if (extracted !== void 0) result.push(extracted);
}
(0, logger_1.trace)("extractConversations", result);
return result;
}
async function extractConversationItem(item) {
const a = item.find("a").first();
const messageUrl = a.attr("href");
const userId = messageUrl?.replace(/\/$/, "").split("/").pop();
const id = userId;
if (id === void 0 || messageUrl === void 0) return;
const userUrl = `https://www.boundhub.com/members/${userId}`;
const username = a.attr("title") || a.find(".title").first().text().trim();
const avatar = a.find("img").first();
let avatarUrl = void 0;
if (avatar !== void 0) {
if (avatar.complete) avatarUrl = $(avatar).attr("src");
else await new Promise((resolve, reject) => {
const timer = setTimeout(() => {
reject( new Error(`Failed to wait for pic loading`));
}, 3e4);
avatar.addEventListener("load", function() {
clearTimeout(timer);
return resolve();
});
if (avatar.complete) {
clearTimeout(timer);
return resolve();
}
}).catch(() => null);
}
const relativeDateString = (0, utils_1.processRelativeDate)(a.find(".added").first().text().trim()).filtered;
const messageCountString = a.find(".views").first().text().trim();
return {
id,
url: messageUrl,
user: {
name: username,
url: userUrl,
id: userId,
avatarUrl: avatarUrl === bh_1.BLANK_IMAGE ? void 0 : avatarUrl
},
messageCount: (0, utils_1.stringOrInt)(messageCountString),
relativeDateString,
dateExtracted: new Date()
};
}
function extractMessages(rootNode) {
const mainNode = rootNode.find(".main-content").first();
if (mainNode === void 0) {
console.warn("Failed to find main-content");
return [];
}
const users = extractUsers(mainNode);
if (users === void 0) {
console.warn("Failed to find users");
return [];
}
let result = [];
rootNode.find(".item").each((_, item) => {
const extracted = extractMessageItem(item, users.user, users.me);
if (extracted !== void 0) result.push(extracted);
});
(0, logger_1.trace)("extractMessages", result);
return result;
}
function extractMessageItem(item, user, me) {
let id = item.attr("data-message-id");
if (id === void 0) {
id = item.find(".added").first().attr("data-message-id");
if (id === void 0) return;
}
let originalText = item.find(".message-text");
let content = "";
if (originalText.find(".original-text").length > 0) {
originalText.find(".original-text").first().find("img").each((_, item) => {
item.replaceWith(`${item.attr("alt")}`);
});
content = originalText.find(".original-text").first().text().trim();
} else content = originalText.text().trim();
let relativeDateString = (0, utils_1.processRelativeDate)($(item).find(".added").first().text().trim(), ["(unread)"]).filtered;
let isMe = $(item).hasClass("me");
return {
id,
from: isMe ? me : user,
to: isMe ? user : me,
relativeDateString,
dateExtracted: new Date(),
content
};
}
})))();
async function processConversations(rootNode) {
let result = await (0, import_messages.extractConversations)(rootNode);
(0, import_logger.log)("Pushing conversations...", result);
const webResult = await GM_xmlhttpRequestPromise({
url: `${await _GM_getValue("hostname", HOSTNAME)}/api/bh/conversations`,
method: "POST",
data: JSON.stringify({ items: result })
});
(0, import_logger.log)("Conversation result: ", webResult);
}
async function processMessages(rootNode) {
let result = await (0, import_messages.extractMessages)(rootNode);
(0, import_logger.log)("Pushing messages...", result);
const webResult = await GM_xmlhttpRequestPromise({
url: `${await _GM_getValue("hostname", HOSTNAME)}/api/bh/messages`,
method: "POST",
data: JSON.stringify({ items: result })
});
(0, import_logger.log)("Conversation result: ", webResult);
}
var import_utils = require_utils();
function getSettingInput(value) {
const id = `${PREFIX}-${value.key}`;
return `
<div class="row">
<label for="${id}" class="field-label">${value.title}</label>
${value.type === "string" ? `<input type="text" name="${id}" id="${id}" class="textfield" value="${_GM_getValue(value.key, value.default)}" maxlength="253" placeholder="${value.title}">` : `<input type="checkbox" class="checkbox" id="${id}" name="${id}" ${_GM_getValue(value.key, value.default) ? "checked" : ""}>`}
</div>
`;
}
function initSettings() {
$("body").append(`
<dialog class="${PREFIX}-settings-dialog">
<div class="center-content">
<strong class="popup-title">Better BH Settings</strong>
<div class="dialog-content">
<form id="${PREFIX}-settings-form" method="dialog">
${settingValues.map((value) => getSettingInput(value))}
</form>
</div>
<a title="Close" class="${PREFIX}-close-settings fancybox-item fancybox-close" href="javascript:;"></a>
</div>
</dialog>
`);
setDialogOpen(false);
$(`.${PREFIX}-close-settings`).on("click", () => {
settingValues.forEach((settingValue) => {
const id = `${PREFIX}-${settingValue.key}`;
if (settingValue.type === "string") {
const value = $(`#${id}`).val();
console.log(settingValue.key, value);
_GM_setValue(settingValue.key, value);
} else {
const value = $(`#${id}`).is(":checked");
console.log(settingValue.key, value);
_GM_setValue(settingValue.key, value);
}
syncSettings();
});
setDialogOpen(false);
});
syncSettings();
}
function syncSettings() {
(0, import_logger.setDebug)(getBooleanSetting(SettingsKey.DEBUG));
}
function setDialogOpen(open) {
if (open) $(`.${PREFIX}-settings-dialog`).show();
else $(`.${PREFIX}-settings-dialog`).hide();
}
function getBooleanSetting(key) {
return Boolean(_GM_getValue(key, settingDefaults[key]));
}
var import_albums = ( __commonJSMin(((exports) => {
Object.defineProperty(exports, "__esModule", { value: true });
exports.extractAlbumList = extractAlbumList;
exports.extractSingleAlbum = extractSingleAlbum;
var bh_1 = require_bh();
var utils_1 = require_utils();
var common_1 = require_common();
var logger_1 = require_logger();
function extractAlbumList(rootNode) {
let result = [];
rootNode.find(".item").each((_, item) => {
const extracted = extractAlbumItem(item);
if (extracted !== void 0) result.push(extracted);
});
(0, logger_1.trace)("extractAlbumList", result);
if (result.length === 0) return [];
return result;
}
function extractSingleAlbum(rootNode, pathname) {
(0, logger_1.trace)("extractSingleAlbum");
const common = (0, common_1.getCommonDetails)(rootNode, bh_1.albumCommentId, bh_1.albumContainerIds.related, pathname);
if (common === void 0) return;
const imageCountText = (0, common_1.getDurationOrImageCountText)(rootNode);
const screenshots = (0, common_1.getScreenshots)(rootNode, ".images");
(0, logger_1.log)("Basic extracting done");
try {
let imageCount = (0, utils_1.stringOrInt)(imageCountText);
const fullItem = {
...common,
imageCount,
images: screenshots
};
(0, logger_1.trace)("fullItem", fullItem);
return fullItem;
} catch (e) {
console.error(e);
return;
}
}
function extractAlbumItem(itemNode) {
const common = (0, common_1.getCommonItemDetails)(itemNode);
if (common === void 0) return;
const imageCountSplit = (0, common_1.getDurationOrImageCountText)(itemNode, itemNode).split("photo");
try {
let imageCount = (0, utils_1.stringOrInt)(imageCountSplit[0]);
return {
...common,
imageCount
};
} catch (e) {
console.error(e);
return;
}
}
})))();
async function processAlbumList(rootNode) {
let result = (0, import_albums.extractAlbumList)(rootNode);
if (result.length === 0) return;
(0, import_logger.log)("Pushing album list...", result);
const webResult = await GM_xmlhttpRequestPromise({
url: `${await _GM_getValue("hostname", HOSTNAME)}/api/bh/album_items`,
method: "POST",
data: JSON.stringify({ items: result })
});
(0, import_logger.log)("Album list result: ", webResult);
}
async function processSingleAlbum(rootNode) {
let result = (0, import_albums.extractSingleAlbum)(rootNode, window.location.pathname);
(0, import_logger.log)("Pushing single album...", result);
const webResult = await GM_xmlhttpRequestPromise({
url: `${await _GM_getValue("hostname", HOSTNAME)}/api/bh/album_info`,
method: "POST",
data: JSON.stringify(result)
});
(0, import_logger.log)("Single album result: ", webResult);
}
var import_jquery = ( __commonJSMin(((exports) => {
Object.defineProperty(exports, "__esModule", { value: true });
exports.JQueryAdapter = void 0;
exports.JQueryAdapter = class JQueryAdapter {
$el;
constructor($el) {
this.$el = $el;
}
find(selector) {
return new JQueryAdapter(this.$el.find(selector));
}
first() {
return new JQueryAdapter(this.$el.first());
}
replaceWith(content) {
return new JQueryAdapter(this.$el.replaceWith(content));
}
text() {
return this.$el.text();
}
html() {
return this.$el.html() || "";
}
attr(name) {
return this.$el.attr(name);
}
toArray() {
return this.$el.map((_, el) => new JQueryAdapter($(el))).get();
}
map(callback) {
const results = [];
this.$el.each((i, el) => {
results.push(callback(i, new JQueryAdapter($(el))));
});
return results;
}
each(callback) {
this.$el.each((i, el) => callback(i, new JQueryAdapter($(el))));
return this;
}
get(index) {
if (index === void 0) return this.$el.get(0);
return this.$el.get(index);
}
eq(index) {
if (index === void 0) return new JQueryAdapter(this.$el.eq(0));
return new JQueryAdapter(this.$el.eq(index));
}
get complete() {
return this.$el.get(0)?.complete || false;
}
get length() {
return this.$el.length;
}
addEventListener(event, handler) {
this.$el.each((_i, el) => {
el.addEventListener(event, handler);
});
}
};
})))();
var import_common = require_common();
function segmentMatches(pathname) {
let keys = [];
for (const key of Object.keys(pageDefinitions)) {
const pageDefinition = pageDefinitions[key];
let matches = false;
for (const matcher of pageDefinition.segmentMatch) if (typeof matcher === "string") {
if (matcher.length <= 1) {
if (pathname === (matcher.length === 0 ? "/" : matcher)) {
matches = true;
break;
}
} else if (pathname.includes(matcher)) {
matches = true;
break;
}
} else if (pathname.match(matcher)) {
matches = true;
break;
}
if (matches) keys.push(key);
}
return keys;
}
function isSupportedPage(pathname) {
for (const pageDefinition of Object.values(pageDefinitions)) {
let matches = false;
for (const matcher of pageDefinition.segmentMatch) if (typeof matcher === "string") {
if (matcher.length <= 1) {
if (pathname === (matcher.length === 0 ? "/" : matcher)) {
matches = true;
break;
}
} else if (pathname.includes(matcher)) {
matches = true;
break;
}
} else if (pathname.match(matcher)) {
matches = true;
break;
}
if (matches) return matches;
}
return false;
}
function isCorrectDomain() {
const hostname = window.location.hostname;
return hostname.includes("boundhub.com") || hostname.includes("www.boundhub.com");
}
function isSingleVideo() {
const singleVideo = document.getElementById(import_bh.singleVideoId);
return singleVideo !== void 0 && singleVideo !== null;
}
function isSingleAlbum() {
const singleAlbum = document.getElementById(import_bh.singleAlbumId);
return singleAlbum !== void 0 && singleAlbum !== null;
}
async function processContainer(container) {
await (0, import_utils.delay)(DELAY);
const rawNode = document.getElementById(container.id);
if (rawNode === void 0 || rawNode === null) {
console.log("Container ID isnt on page:", container.id);
return;
}
const node = $(rawNode);
if (container.type === ContainerType.ALBUM) {
console.log("Processing ALBUM:", container.id);
processAlbumList(new import_jquery.JQueryAdapter(node));
const singleAlbum = document.getElementById(import_bh.singleAlbumId);
if (singleAlbum !== void 0 && singleAlbum !== null) {
const rawContentNode = document.getElementById(import_bh.siteContentId);
if (rawContentNode === void 0 || rawContentNode === null) {
console.log("Container ID isnt on page:", import_bh.siteContentId);
return;
}
processSingleAlbum(new import_jquery.JQueryAdapter($(rawContentNode)));
}
} else if (container.type === ContainerType.VIDEO) {
console.log("Processing VIDEO:", container.id);
processVideoList(new import_jquery.JQueryAdapter(node));
const singleVideo = document.getElementById(import_bh.singleVideoId);
if (singleVideo !== void 0 && singleVideo !== null) {
const rawContentNode = document.getElementById(import_bh.siteContentId);
if (rawContentNode === void 0 || rawContentNode === null) {
console.log("Container ID isnt on page:", import_bh.siteContentId);
return;
}
processSingleVideo(new import_jquery.JQueryAdapter($(rawContentNode)));
}
} else if (container.type === ContainerType.CATEGORY) {
console.log("Processing CATEGORY:", container.id);
processCategories(new import_jquery.JQueryAdapter(node));
} else if (container.type === ContainerType.MESSAGES) {
console.log("Processing MESSAGES:", container.id);
processMessages(new import_jquery.JQueryAdapter(node));
} else if (container.type === ContainerType.CONVERSATIONS) {
console.log("Processing CONVERSATIONS:", container.id);
processConversations(new import_jquery.JQueryAdapter(node));
} else console.log("Unknown container id type");
}
function createDownloadButton(buttonText = "Download") {
createSinglePageButton(buttonText, "video-downloader-btn", handleDownloadClick, ["download-button"]);
}
function createCopyIdButton() {
createSinglePageButton("Copy ID", "copy-id-btn", async (event) => {
const id = (0, import_common.getPathnameVariables)(window.location.pathname).id;
if (id === void 0) {
console.warn("Unable to determine id from pathname", window.location.pathname);
return;
}
await navigator.clipboard.writeText(id);
const element = event.target;
if (element !== void 0) {
$(element).text("Copied");
$(element).addClass("active");
setTimeout(() => {
$(element).text("Copy ID");
$(element).removeClass("active");
}, 1e3);
}
});
}
function createSinglePageButton(buttonText, buttonId, onClick, classes) {
if (document.getElementById(buttonId)) return;
const tabsMenu = document.querySelector(".tabs-menu ul");
if (!tabsMenu) {
console.log("Better BH: Tabs menu not found, retrying...", buttonId);
setTimeout(() => createSinglePageButton(buttonText, buttonId, onClick, classes), 1e3);
return;
}
const newBtn = document.createElement("li");
newBtn.id = buttonId;
newBtn.innerHTML = `<a href="#" class="toggle-button ${classes === void 0 ? "" : classes.join(" ")}">${buttonText}</a>`;
tabsMenu.insertBefore(newBtn, tabsMenu.firstChild);
const anchor = newBtn.querySelector("a");
if (anchor === null) {
console.warn("Failed to create download button");
return;
}
anchor.addEventListener("click", onClick);
console.log(`Better BH: ${buttonText} button added`);
}
async function handleDownloadClick(event) {
event.preventDefault();
$(".download-button").addClass("disable-click");
try {
if (isSingleVideo()) await handleVideoDownload();
else if (isSingleAlbum()) await handleAlbumDownload();
$(".download-button").removeClass("disable-click");
} catch (error) {
console.error("Video Downloader Error:", error);
alert("Download failed, see console for details");
}
}
function init() {
console.log("Scraper: init");
if (isCorrectDomain() && isSupportedPage(window.location.pathname)) {
const definitions = segmentMatches(window.location.pathname);
for (const key of definitions) {
const definition = pageDefinitions[key];
for (const container of definition.containerIds) processContainer(container);
}
if (isSingleVideo() || isSingleAlbum()) {
createDownloadButton("Download");
createCopyIdButton();
}
} else if (!isCorrectDomain()) console.log("Scraper: Not on correct domain, skipping");
else console.log("Scraper: Not a supported page type, skipping");
const network = $(".top-links").first().find(".network").first();
$(network).find("ul li").each((index, item) => {
if (index === 0) {
let a = $(item).find("a");
$(a).attr("target", "").attr("href", "javascript:void(0)").text("Better BH Settings").attr("id", "better-bh-settings-open").on("click", () => {
setDialogOpen(true);
});
} else $(item).html("");
});
initSettings();
}
(async () => {
if (document.readyState === "loading") {
console.log("Scraper: DOMContentLoaded");
document.addEventListener("DOMContentLoaded", () => {
init();
});
} else init();
new MutationObserver((mutations) => {
mutations.forEach(async (mutation) => {
if (mutation.type === "childList") for (const entry of mutation.addedNodes) {
await (0, import_utils.delay)(DELAY);
const id = $(entry).attr("id");
if (id === void 0) return;
const rawNode = document.getElementById(id);
if (rawNode === void 0 || rawNode === null) return;
const node = $(rawNode);
if (Object.values(import_bh.videoContainerIds).includes(id)) {
console.log("Scraper: video found", id);
processVideoList(new import_jquery.JQueryAdapter(node));
} else if (Object.values(import_bh.categoryContainerIds).includes(id)) {
console.log("Scraper: category found", id);
processCategories(new import_jquery.JQueryAdapter(node));
} else if (Object.values(import_bh.messagesContainerIds).includes(id)) {
console.log("Scraper: messages found", id);
processMessages(new import_jquery.JQueryAdapter(node));
} else if (Object.values(import_bh.conversationsContainerIds).includes(id)) {
console.log("Scraper: conversations found", id);
processConversations(new import_jquery.JQueryAdapter(node));
} else if (Object.values(import_bh.albumContainerIds).includes(id)) {
console.log("Scraper: album found", id);
processAlbumList(new import_jquery.JQueryAdapter(node));
}
}
});
}).observe(document.body, {
childList: true,
subtree: true
});
_GM_addStyle(`
${getBooleanSetting(SettingsKey.HIDE_ADS) ? `
.ta4ble, .spo8nsor, .to3op, .p8lace {
display: none;
}
` : ""}
${getBooleanSetting(SettingsKey.HIDE_SHARE) ? `
.tabs-menu ul li:nth-last-child(2) {
display: none;
}
` : ""}
${getBooleanSetting(SettingsKey.UNHIDE_PRIVATE) ? `
.item.private .thumb {
opacity: 1 !important;
}
` : ""}
.disable-click {
pointer-events: none;
}
.${PREFIX}-settings-dialog {
z-index: 100000;
position: absolute;
top: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
width: 100%;
background: none;
background-color: rgba(17, 13, 13, 0.65);
.center-content {
position: relative;
max-width: 600px;
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.5);
border-radius: 4px;
.popup-title {
width: 100%;
}
}
.dialog-content {
position: relative;
background-color: #2c2c2c;
padding: 20px;
width: 560px;
border-radius: 4px;
color: #444;
}
}
`);
})();
})(JSZip);