// ==UserScript==
// @name JAVLibrary Improvements
// @description Many improvements mainly in details view of a video for recherche: easier collect of Google Drive and Rapidgator links for JDownloader (press <), save/show favorite actresses, recherche links for actresses, auto reload on Cloudflare rate limit, save cover with actress names just by clicking, advertising photos in full size
// @version 20240825a
// @author resykano
// @icon https://icons.duckduckgo.com/ip2/javlibrary.com.ico
// @match *://*.javlibrary.com/*
// @match *://x75p.com/*
// @match *://*.y78k.com/*
// @match *://javx357.com/*
// @match *://arcjav.com/*
// @match *://javgg.me/*
// @match *://maxjav.com/*
// @match *://jav.guru/*
// @match *://supjav.com/*
// @match *://missav.com/*
// @match *://video-jav.net/*
// @match *://www.akiba-online.com/search/*
// @grant GM_xmlHttpRequest
// @grant GM_download
// @grant GM_setClipboard
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_addStyle
// @grant window.close
// @run-at document-start
// @compatible chrome
// @license GPL3
// @noframes
// @namespace https://greasyfork.org/users/1342111
// ==/UserScript==
"use strict";
// ---------------------------------------------------------------------------------------
// Config/Requirements
// ---------------------------------------------------------------------------------------
let copied = false;
const url = window.location.href;
const originalDocumentTitle = document.title;
let avid = null;
function getTitleElement() {
return document.querySelector("#video_id > table > tbody > tr > td.text");
}
function getAvid() {
if (!avid) {
avid = getTitleElement()?.textContent;
}
}
function castContainer() {
return document.querySelector("#video_cast");
}
function createBase64Svg(fillColor) {
const svgTemplate = `
<svg class="mr-1 md:mr-2 h-3 w-3 xs:h-4 xs:w-4" xmlns="http://www.w3.org/2000/svg" fill="${fillColor}" viewBox="0 0 24 24" stroke-width="1" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M21 8.25c0-2.485-2.099-4.5-4.688-4.5-1.935 0-3.597 1.126-4.312 2.733-.715-1.607-2.377-2.733-4.313-2.733C5.1 3.75 3 5.765 3 8.25c0 7.22 9 12 9 12s9-4.78 9-12z"></path>
</svg>`;
return `"data:image/svg+xml;base64,${btoa(svgTemplate)}"`;
}
let favoriteImage = createBase64Svg("red");
let nonFavoriteImage = createBase64Svg("lightgray");
/**
* Waits for an element until it exists
*
* @param {string} selector CSS selector of a NodeList/HTMLCollection
* @param {number} index
* @see source: {@link https://stackoverflow.com/a/61511955/13427318}
* @returns Element
*/
function waitForElement(selector, index = 0) {
return new Promise((resolve) => {
if (selector && document.querySelector(selector) && document.querySelectorAll(selector)[index]) {
return resolve(document.querySelectorAll(selector)[index]);
}
const observer = new MutationObserver(() => {
if (selector && document.querySelectorAll(selector) && document.querySelectorAll(selector)[index]) {
resolve(document.querySelectorAll(selector)[index]);
observer.disconnect();
}
});
observer.observe(document, {
childList: true,
subtree: true,
});
});
}
// ---------------------------------------------------------------------------------------
// Functions
// ---------------------------------------------------------------------------------------
function addCSS() {
GM_addStyle(`
/* Saving space on top and left */
#toplogo {
height: 26px;
}
#toplogo .sitelogo {
display: none;
}
div#topbanner11 {
height: unset;
}
#content {
padding-top: 0;
}
div.boxtitle {
top: 0em;
padding: unset;
}
/* improve space on smaller viewports */
@media screen and (max-width: 1300px) {
#leftmenu {
display: none;
}
#rightcolumn {
margin-left: 10px;
}
}
`);
switch (true) {
// JAV Details
case /[a-z]{2}\/\?v=jav.*/.test(url): {
GM_addStyle(`
#video_info {
min-width: 430px;
}
.added-links {
margin-left: 107px;
max-width: 370px;
}
.added-links-separator {
margin-top: 10px;
}
/* preview video separated from advertising photos */
a.btn_videoplayer {
display: block;
text-align: center;
}
/* prevent video metadata from becoming too narrow */
#video_jacket_info > tbody > tr > td:nth-child(2) {
min-width: 370px;
}
@media screen and (min-width: 1301px) {
/* reduce FOUC for cover image */
img#video_jacket_img {
width: 800px;
object-fit: contain;
/* not too high, especially portraits */
max-height: 800px;
}
}
@media screen and (max-width: 1300px) {
/* same size for cover and metadata area */
#video_jacket_info > tbody > tr > td {
width: 50%;
}
img#video_jacket_img {
width: 100% !important;
}
}
/* addImageSearchToCasts */
.customButton {
cursor: default;
background-color: buttonface;
padding-block: 2px;
padding-inline: 6px;
border-width: 1px;
border-style: solid;
border-color: #767676;
border-radius: 2px;
color: black;
transition: background-color 0.3s ease;
user-select: none;
}
.customButton:visited {
color: #aaa;
}
.customButton:hover {
background-color: #e0e0e0;
}
/* addSearchLinkAndOpenAllButton */
button.open-group {
margin-left: 8px;
padding: 3px;
width: 150px;
height: 22px;
user-select = none;
}
`);
break;
}
// no video found
case /\/vl_searchbyid.php/.test(url): {
GM_addStyle(`
.added-links {
margin-left: 107px;
max-width: 370px;
margin-left: auto;
margin-right: auto;
}
.added-links-separator {
margin-top: 10px;
}
`);
break;
}
}
}
function removeRedirects() {
let externalLinks = document.querySelectorAll("table[id^=comment] > tbody > tr:nth-child(1) > td.t > div a[href^='redirect.php']");
for (let externalLink of externalLinks) {
externalLink.href = decodeURIComponent(
externalLink.href?.replace(/https:\/\/www\.javlibrary\.com\/.*\/redirect\.php\?url=/, "").replace(/\&ver=.*/, "")
);
}
}
function addTitleCopyPerClick() {
let titleElement = getTitleElement();
titleElement.style.cursor = "pointer";
titleElement.addEventListener("click", function () {
copyTitleToClipboard();
});
}
async function initalCopyVideoTitleToClipboard(source) {
const authorsMode = await GM_getValue("authorsMode", false);
if (authorsMode) {
const textElement = getTitleElement();
if (textElement && !copied && document.hasFocus()) {
// only put once to clipboard
console.log(`${source}: ${avid}`);
copyTitleToClipboard(avid)
.then(() => {
copied = true;
// if tab was opened with link
if (history.length === 1) {
runLocalSearch();
}
})
.catch(function (err) {
console.error("Failed to copy text: ", err);
copied = false;
});
}
}
}
/**
* requires a local script such as AHK, which recognizes the window title
* as information for the execution of another local script
* Button and auto execute disabled if GM variable privateMode is not set
*/
async function addLocalSearch() {
const authorsMode = await GM_getValue("authorsMode", false);
if (authorsMode) {
let targetElement = getTitleElement();
let newButton = document.createElement("button");
newButton.textContent = "Local-Search";
newButton.className = "smallbutton localsearch";
newButton.style = "position:relative;top:-3px;margin-left:10px";
targetElement.insertAdjacentElement("afterend", newButton);
newButton.addEventListener(
"click",
function () {
copyTitleToClipboard().then(() => {
runLocalSearch();
});
},
false
);
}
}
function runLocalSearch() {
// not on image videos
if (!document.querySelector("#genre199")) {
document.title = "Browser Local-Search";
setTimeout(() => {
document.title = originalDocumentTitle;
}, 50);
}
}
function copyTitleToClipboard() {
return navigator.clipboard.writeText(avid);
}
function coverImageDownload() {
// big preview screen shots
const screenShots = document.querySelectorAll("#rightcolumn > div.previewthumbs > img");
for (let img of screenShots) {
srcBigPictures = img.src.replace(/(.*)(-[0-9].*)$/i, "$1jp$2");
img.src = srcBigPictures;
}
// rename cover image
let casts = document.querySelectorAll("[id^=cast] > span.star > a");
let newFilename = avid + " - ";
let iteration = casts.length;
for (let cast of casts) {
// also replace non-ASCII characters
newFilename += cast.textContent.replace(/[^\x00-\x7F]/g, "");
// do as long not last iteration
if (--iteration) newFilename += ", ";
}
const coverPicture = document.querySelector("#video_jacket_img");
const coverPictureUrl = coverPicture?.src;
coverPicture?.addEventListener(
"click",
function () {
GM_download({
url: coverPictureUrl,
headers: { referer: coverPictureUrl, origin: coverPictureUrl },
name: newFilename,
onload: function (download) {
const coverPictureBlob = download.blob;
// Create a Blob URL for the image
const blobUrl = window.URL.createObjectURL(coverPictureBlob);
// Create an invisible <a> element
const downloadLink = document.createElement("a");
// Set the Blob URL as the HREF value and the filename for download
downloadLink.href = blobUrl;
downloadLink.setAttribute("download", newFilename);
// Trigger the click event on the invisible <a> element
downloadLink.click();
// Revoke the Blob URL to free up memory
window.URL.revokeObjectURL(blobUrl);
},
onerror: function (error) {
console.error("Download failed:", error);
},
});
},
{
once: true,
}
);
}
function setSearchLinks() {
// add search links and buttons
addSearchLinkAndOpenAllButton(
"DuckDuckGo Screens",
"https://duckduckgo.com/?kp=-2&iax=images&ia=images&q=" + '"' + avid + '"' + " JAV",
""
);
addSearchLinkAndOpenAllButton("DuckDuckGo", "https://duckduckgo.com/?kp=-2&q=" + '"' + avid + '"' + " JAV", "", true);
addSearchLinkAndOpenAllButton("JavPlace | alternative research platform", "https://jav.place/?q=" + avid, "");
addSearchLinkAndOpenAllButton("JAV-Menu | alternative research platform", "https://jjavbooks.com/en/" + avid, "", true);
addSearchLinkAndOpenAllButton("JAV BIGO | Stream", "https://javbigo.com/?s=" + avid, "Stream-Group");
addSearchLinkAndOpenAllButton("JAVHDMost | Stream", "https://javhdmost.com/?s=" + avid, "Stream-Group");
addSearchLinkAndOpenAllButton("Jable | Stream", "https://jable.tv/search/" + avid + "/", "Stream-Group");
addSearchLinkAndOpenAllButton("MDTAIWAN | Stream", "https://mdtaiwan.com/?s=" + avid, "Stream-Group");
addSearchLinkAndOpenAllButton("HORNYJAV | Stream", "https://hornyjav.com/?s=" + avid, "Stream-Group", true);
addSearchLinkAndOpenAllButton("JAV GDRIVE | Google Drive", "https://javx357.com/?s=" + avid, "GDrive-Group");
addSearchLinkAndOpenAllButton("Arc JAV | Google Drive", "https://arcjav.com/?s=" + avid, "GDrive-Group");
addSearchLinkAndOpenAllButton("JAVGG | Google Drive", "https://javgg.me/?s=" + avid, "GDrive-Group", true);
addSearchLinkAndOpenAllButton("JAVDAILY | RG (optional)", "https://javdaily31.blogspot.com/search?q=" + avid, "");
addSearchLinkAndOpenAllButton("BLOGJAV.NET | RG (optional)", "https://blogjav.net/?s=" + avid, "", true);
addSearchLinkAndOpenAllButton("MissAV | RG | Stream", "https://missav.com/en/search/" + avid, "RG-Group");
addSearchLinkAndOpenAllButton("Supjav | RG", "https://supjav.com/?s=" + avid, "RG-Group");
addSearchLinkAndOpenAllButton("JAV Guru | RG | Stream", "https://jav.guru/?s=" + avid, "RG-Group", true);
addSearchLinkAndOpenAllButton("3xPlanet | Preview", "https://3xplanet.com/?s=" + avid, "Preview-Group-2");
addSearchLinkAndOpenAllButton("JAVAkiba | Preview", "https://javakiba.org/?s=" + avid, "Preview-Group-2");
addSearchLinkAndOpenAllButton("Video-JAV | Preview", "http://video-jav.net/?s=" + avid, "Preview-Group-2", true);
addSearchLinkAndOpenAllButton("JAV Max Quality | Preview", "https://maxjav.com/?s=" + avid, "Preview-Group-1");
addSearchLinkAndOpenAllButton(
"Akiba-Online | Preview",
"https://www.akiba-online.com/search/?q=" + avid + "&c%5Btitle_only%5D=1&o=date&search=" + avid,
"Preview-Group-1",
true
);
addSearchLinkAndOpenAllButton("Torrent-Search", "https://bt4g.org/search/" + avid + "&orderby=size", "", true);
}
/**
* Adds a search links and open all links buttons
*
* @param {*} name Name
* @param {*} href URL
* @param {*} separator Adds a space on top
* @param {*} className Adds a class
*/
function addSearchLinkAndOpenAllButton(name, href, className, separator = false) {
// styles in addCSS
if (separator) {
separator = "added-links-separator";
}
if (className === "") className = undefined;
// after the casting container or "search tips" if the search does not return any results
let existingContainer = castContainer() || document.querySelector("#rightcolumn > div.titlebox");
let newElementContainer = document.createElement("div");
newElementContainer.classList.add("added-links");
newElementContainer.classList.add(separator);
newElementContainer.classList.add(className);
newElementContainer.style.display = "flex";
newElementContainer.style.alignItems = "flex-end";
newElementContainer.style.justifyContent = "space-between";
let newElement = document.createElement("a");
newElement.href = href;
newElement.textContent = name;
newElementContainer.appendChild(newElement);
// add open all links buttons
if (separator && className) {
const openAllButton = document.createElement("button");
const buttonTitle = className.replace(/-/g, " ");
openAllButton.textContent = "Open " + buttonTitle;
openAllButton.className = "smallbutton open-group";
openAllButton.addEventListener("click", function () {
let linksToOpen = document.querySelectorAll(`.${className}.added-links a`);
let reversedLinks = Array.from(linksToOpen).reverse();
// allow batch search on external sites
GM_setValue("externalSearchMode", true);
// TODO: needs a more solid solution without brute force
setTimeout(async () => {
GM_setValue("externalSearchMode", false);
console.log("externalSearchMode off");
}, 7000);
reversedLinks.forEach(function (link) {
window.open(link.href);
});
});
newElementContainer.appendChild(openAllButton);
}
existingContainer.insertAdjacentElement("afterend", newElementContainer);
}
// Execute when button pressed with collecting comments for importing into Jdownloader
async function collectingLinksFromCommentsAndRgGroup(event) {
if (event.key === "<") {
// press Open RG Group button
document.querySelector("#video_info > div.added-links.added-links-separator.RG-Group > button")?.click();
// go to all comments, if not already there
const allCommentsLink = document.querySelector("#video_comments_all > a");
if (allCommentsLink) {
// open link
GM_setValue("executingCollectingComments", true);
setTimeout(() => {
window.open(allCommentsLink.href, "_self");
}, 200);
} else if (document.querySelector("#rightcolumn > div.page_selector > a.page.last")) {
// if already on comments page
GM_setValue("executingCollectingComments", true);
location.reload();
} else {
copyContentsToClipboard();
// alert("No more comments!");
}
}
}
// Function to copy the contents of the #video_comments element to the clipboard
// for collecting download links in apps like JDownloader
function copyContentsToClipboard() {
const commentsElement = document.querySelector("#video_comments");
if (commentsElement) {
const commentsContent = commentsElement.innerText;
GM_setClipboard(commentsContent);
}
}
function addImageSearchToCasts() {
// styles in addCSS
let castElements = document.querySelectorAll("[id^=cast]");
castElements.forEach(function (element) {
function addButton(text, link) {
let a = document.createElement("a");
a.target = "_blank";
a.textContent = text;
a.className = "customButton";
let cast = element.querySelector("span.star > a").textContent;
// Reverse the order of names for better search results
if (cast.split(" ").length === 2) {
cast = cast.split(" ").reverse().join(" ");
}
if (link && cast) {
a.href = link + '"' + cast + '"';
}
let span = document.createElement("span");
span.appendChild(a);
element.appendChild(span);
}
addButton("XSList", "https://duckduckgo.com/?iar=images&iax=images&ia=images&q=site:xslist.org ");
addButton("Yandex", "https://yandex.com/images/search?text=");
addButton("V2PH", "https://www.v2ph.com/search/?q=");
addButton("JJGirls", "https://jjgirls.com/match.php?model=");
addButton("KawaiiThong", "https://kawaiithong.com/search_kawaii_pics/");
});
}
function addReverseImageSearchToCasts() {
let castContainer = document.querySelector("#video_cast > table > tbody > tr > td.text");
function addButton(text, link) {
let button = document.createElement("button");
button.textContent = text;
button.className = "smallbutton open-group";
button.style = "width: unset";
button.onclick = function () {
window.open(link, "_blank");
};
let span = document.createElement("span");
span.style = "display: block; margin-top: 5px";
span.appendChild(button);
castContainer.appendChild(span);
}
addButton("Find Cast with Reverse Image Search", "https://xslist.org/en/searchByImage");
}
async function makeFavoriteCastVisible() {
const favoriteClass = "favorite-star";
function addCss() {
GM_addStyle(`
span[class^="icn_fav"].favorite-star {
background-image: url(${favoriteImage});
}
span[class^="icn_fav"] {
background-image: url(${nonFavoriteImage});
background-size: contain;
background-position: unset;
background-color: #252525;
border-radius: 4px;
}
`);
}
function toggleFavoriteCast(event) {
const element = event.target;
const elementId = element.id;
const isFavorite = element.classList.toggle(favoriteClass);
// hide and auto close modal asking if cast should be added to favorites
waitForElement("div.noty_bar.center.alert.default").then(() => {
function addTemporaryCssRule() {
var styleElement = GM_addStyle(`
div.noty_bar.center.alert.default,
div.noty_modal
{
display: none !important;
}
`);
// Remove the CSS after the specified duration
setTimeout(function () {
styleElement.parentNode.removeChild(styleElement);
}, 2000);
}
// Hide modal until we have clicked it away
addTemporaryCssRule();
// The "ok" and "close" buttons are always created immediately in the DOM and then adjusted afterwards.
// This means that the decision as to what must be clicked does not work with the if clause.
// close with ok
let okButton = document.querySelector(
"div.noty_bar.center.alert.default > div.noty_message > div.noty_text > div.noty_buttons > button.button.green"
);
okButton?.click();
// if not closed with ok, then with close button which can only be clicked after a delay
setTimeout(() => {
let closeButton = document.querySelector("div.noty_bar.center.alert.default > div.noty_message > div.noty_close");
closeButton?.click();
}, 1000);
});
if (isFavorite) {
GM_setValue(elementId, true);
} else {
GM_deleteValue(elementId);
}
}
addCss();
const starElements = document.querySelectorAll("[id^=star]");
for (const element of starElements) {
const elementId = element.id;
const isFavoriteStar = await GM_getValue(elementId, false);
if (isFavoriteStar) {
element.classList.add(favoriteClass);
}
element.addEventListener("click", toggleFavoriteCast);
}
}
async function removeResizingOfCoverImage() {
const coverImage = await waitForElement("#video_jacket_img");
if (!coverImage) return;
coverImage.removeAttribute("width");
coverImage.removeAttribute("height");
const observer = new MutationObserver((mutations) => {
for (let mutation of mutations) {
if (mutation.type === "attributes") {
coverImage.removeAttribute("width");
coverImage.removeAttribute("height");
// observer.disconnect();
return;
}
}
});
observer.observe(coverImage, { attributes: true });
}
function setAdvertisingPhotosToFullSize() {
const advertisingPreviewImageLinks = document.querySelectorAll("#rightcolumn > div.previewthumbs > a:not(.btn_videoplayer)");
advertisingPreviewImageLinks.forEach((anchor) => {
const img = anchor.querySelector("img");
if (img) {
img.src = anchor.href;
img.removeAttribute("width");
img.removeAttribute("height");
}
});
}
// ---------------------------------------------------------------------------------------
// external search
// ---------------------------------------------------------------------------------------
class ExternalSearch {
constructor() {
this.currentURL = window.location.href;
this.hostname = window.location.hostname;
}
openLink(url, target = "_self") {
return window.open(url, target);
}
handleSearchResults() {
const selectors = [
//default
"[id^=post]",
//jav.guru
"#main div.row",
//missjav.com
"div.my-2.text-sm.text-nord4.truncate",
//supjav.com
".post",
];
let posts = [];
for (let selector of selectors) {
posts = document.querySelectorAll(selector);
if (posts.length > 0) break;
}
if (posts[0]?.textContent.includes("404") || posts.length === 0) {
if (document.title !== "Just a moment...") {
window.close();
}
} else if (posts.length === 1) {
const link = posts[0].querySelector("a");
if (link && !link.href.includes("#")) {
link.click();
} else {
window.close();
}
} else if (posts.length <= 30) {
let openWindowCheck;
posts.forEach((post, index) => {
const link = post.querySelector("a");
let title = post.textContent;
const paramName = "s";
let searchTerm = new URLSearchParams(window.location.search).get(paramName);
if (!searchTerm) {
const url = window.location.href;
const searchPattern = /\/search\/([^\/]+)/;
searchTerm = url.match(searchPattern)[1];
}
if (link && title) {
const regex = new RegExp(`\\b${searchTerm}\\b`, "i");
if (regex.test(title)) {
setTimeout(() => {
openWindowCheck = window.open(link.href, "_blank");
}, index * 100);
}
}
});
if (openWindowCheck !== null) {
setTimeout(() => window.close(), 2000);
} else {
console.log("window.open blocked");
}
}
}
handleGDrivePages() {
const links = document.querySelectorAll("[id^=post] a");
let isFirstIteration = true;
links.forEach((link) => {
if (
link.textContent.includes("FHD") ||
link.textContent.includes("GOOGLE DRIVE – ALL IN ONE") ||
link.textContent.includes("GB") ||
link.textContent.includes("1080")
) {
if (isFirstIteration) {
isFirstIteration = false;
link.scrollIntoView({ block: "center" });
}
}
});
if (this.hostname === "javgg.me") {
const postContentElement = document.querySelector("article.status-publish.hentry > div > p");
if (postContentElement && postContentElement.textContent.includes("drive.google.com/file/")) {
GM_setClipboard(postContentElement.textContent)
.then(() => window.close())
.catch((err) => console.error("Error copying content:", err));
}
}
}
handleRapidgatorPages() {
console.log("handleRapidgatorPages");
if (this.hostname === "jav.guru") {
const sources = document.querySelectorAll("#dl_jav_free");
for (let source of sources) {
if (source.innerText.includes("Rapidgator")) {
const link = source.querySelector("a");
if (link) {
let onClickContent = link.getAttribute("onclick");
if (onClickContent) {
const match = onClickContent.match(/window\.open\s*\(\s*['"]([^'"]*)['"]/);
if (match) {
const url = match[1];
onClickContent = onClickContent.replace(/window\.open\s*\([^)]*\)/, `window.open('${url}', '_self')`);
link.setAttribute("onclick", onClickContent);
setTimeout(() => link.click(), 100);
} else {
setTimeout(() => window.open(link.href, "_self"), 100);
}
}
}
} else {
window.close();
}
}
} else {
const link = document.querySelector("a[href*=rapidgator]");
if (link) {
console.log("handle details pages to get rapidgator links");
window.open(link.href, "_self");
setTimeout(() => {
link.click();
setTimeout(() => window.close(), 1000);
}, 100);
} else {
window.close();
}
}
}
main() {
switch (true) {
case this.currentURL.includes("/?s=") || this.currentURL.includes("/search"):
this.handleSearchResults();
break;
case ["arcjav.com", "javgg.me", "javx357.com"].includes(this.hostname):
this.handleGDrivePages();
break;
case ["jav.guru", "supjav.com", "missav.com"].includes(this.hostname):
this.handleRapidgatorPages();
break;
}
}
}
// ---------------------------------------------------------------------------------------
// Main
// ---------------------------------------------------------------------------------------
async function main() {
// Cloudflare restricted access
if (/.*/.test(url)) {
if (document.title.includes("Access denied")) {
setTimeout(() => {
location.reload();
}, 10000);
}
}
// do nothing if cloudflare check happens
if (document.title.includes("Just a moment...")) return;
getAvid();
switch (true) {
// JAV Details
case /[a-z]{2}\/\?v=jav.*/.test(url): {
console.log("JAV Details");
// TODO: needs a more solid solution than just a blind timeout
let externalSearchMode = await GM_getValue("externalSearchMode", false);
if (externalSearchMode) {
setTimeout(async () => {
GM_setValue("externalSearchMode", false);
console.log("externalSearchMode off");
}, 5000);
}
// add title textbox
addTitleCopyPerClick();
// adds posibility for local search but disabled by default as needs addinal scripts
addLocalSearch();
// add search links
setSearchLinks();
// increase advertising previews
setAdvertisingPhotosToFullSize();
// add Cover Image Download button
coverImageDownload();
// Remove link by converting <a> to <span> element
(function () {
let linkElement = document.querySelector("#video_title > h3 > a");
if (linkElement) {
let spanElement = document.createElement("span");
spanElement.innerHTML = linkElement.innerHTML;
linkElement.insertAdjacentElement("beforebegin", spanElement);
linkElement.parentNode.removeChild(linkElement);
}
})();
// remove redirects from external links
setTimeout(() => {
removeRedirects();
}, 500);
// copy title to clipboard
const authorsMode = await GM_getValue("authorsMode", false);
if (authorsMode) {
(function () {
// Handle the case when the window is opened in the background
window.addEventListener("focus", function () {
initalCopyVideoTitleToClipboard("EventListener");
});
// Handle the case when the window is opened in the foreground
// IntersectionObserver is used for better performance and reliability
// compared to repeated DOM queries or fixed timeouts
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
initalCopyVideoTitleToClipboard("IntersectionObserver");
}
});
});
// Set up the observer after a short delay to ensure DOM is loaded
setTimeout(() => {
const textElement = getTitleElement();
if (textElement) {
observer.observe(textElement);
}
}, 500);
})();
}
addImageSearchToCasts();
addReverseImageSearchToCasts();
makeFavoriteCastVisible();
// window.addEventListener("keydown", executeCollectingComments, { once: true });
window.addEventListener("keydown", collectingLinksFromCommentsAndRgGroup);
break;
}
// Redirect Page
case /\/redirect.php/.test(url): {
document.querySelector("#ckbSkipURLWarning").click();
document.querySelector("#redirection").click();
break;
}
// Video Star Listings
case /\/vl_star.php/.test(url): {
console.log("Video Star Listings");
// ToDo: highlight visited videos
let videoLinks = document.querySelectorAll('div[id^="vid_"] a');
for (let i = 0; i < videoLinks.length; i++) {
if (videoLinks[i].visited) {
videoLinks[i].parentNode.style.backgroundColor = "#ffe7d3";
}
}
// open in same tab
setTimeout(() => {
document.querySelectorAll(".video > a").forEach(function (element) {
element.removeAttribute("target");
});
}, 2000);
break;
}
// Search Page
case /\/search.php/.test(url): {
console.log("Advanced Search Section");
// open Combination Search
// document.querySelector("#ui-accordion-accordion-header-1 > span")?.click();
break;
}
// if video is not in JAVLibrary
case /\/vl_searchbyid.php/.test(url): {
if (document.querySelector("#rightcolumn > p > em") && document.querySelector("#rightcolumn > div.titlebox")) {
console.log("no search results");
avid = new URLSearchParams(window.location.search).get("keyword");
if (avid) {
setSearchLinks();
}
}
// open found links in same tab
document.querySelectorAll(".video > a")?.forEach(function (element) {
element.removeAttribute("target");
});
break;
}
case /\/videocomments.php/.test(url): {
console.log("Comments Page");
async function loadNextPage() {
copyContentsToClipboard(); // Copy the comments content before loading the next page
let currentPage = new URL(window.location.href).searchParams.get("page");
let lastPageUrl = document.querySelector("#rightcolumn > div.page_selector > a.page.last")?.href;
let lastPage = await GM_getValue("lastPage", null);
// If lastPage is not set and the last page URL is available, extract and store the last page number
if (!lastPage && lastPageUrl) {
lastPage = new URL(lastPageUrl).searchParams.get("page");
GM_setValue("lastPage", lastPage);
}
if (!currentPage) currentPage = 1;
else currentPage = parseInt(currentPage);
// If the current page is not the last page, load the next page
if (currentPage < lastPage) {
let nextPage = currentPage + 1;
let nextUrl = new URL(window.location.href);
nextUrl.searchParams.set("page", nextPage);
window.location.href = nextUrl.href;
} else {
// not if cloudflare check happens
if (!document.title.includes("Just a moment...")) {
GM_deleteValue("lastPage");
GM_deleteValue("executingCollectingComments");
// go back to main page
const mainPageLink = document.querySelector("#video_jacket > a");
console.log(mainPageLink);
if (mainPageLink) {
// open link
window.open(mainPageLink.href, "_self");
}
}
}
}
// initialize
(async function () {
let executingCollectingComments = await GM_getValue("executingCollectingComments", false);
if (executingCollectingComments) {
// await sleep(1000);
loadNextPage();
} else {
window.addEventListener("keydown", collectingLinksFromCommentsAndRgGroup);
}
})();
break;
}
// batch external download link and preview searches
case /^https?:\/\/javx357\.com\/.*/i.test(url):
case /^https?:\/\/arcjav\.com\/.*/i.test(url):
case /^https?:\/\/javgg\.me\/.*/i.test(url):
case /^https?:\/\/maxjav\.com\/.*/i.test(url):
case /^https?:\/\/jav\.guru\/.*/i.test(url):
case /^https?:\/\/supjav\.com\/.*/i.test(url):
case /^https?:\/\/missav\.com\/.*/i.test(url):
case /^https?:\/\/video-jav\.net\/.*/i.test(url):
case /^https?:\/\/javakiba\.org\/.*/i.test(url): {
let externalSearchMode = await GM_getValue("externalSearchMode", false);
if (externalSearchMode) {
const externalSearcher = new ExternalSearch();
externalSearcher.main();
}
break;
}
// copy GDrive & Rapidgator links into clipboard for JDownloader Linkgrabber and auto close
case /^https:\/\/drive\.google\.com\/uc.*/i.test(url):
case /^https:\/\/drive\.google\.com\/file\/.*/i.test(url):
case /^https:\/\/rapidgator\.net\/.*/i.test(url): {
let externalSearchMode = await GM_getValue("externalSearchMode", false);
if (externalSearchMode) {
const urls = ["https://drive.google.com", "https://rapidgator.net/file/*"];
const currentUrl = window.location.href;
const match = urls.some((url) => currentUrl.match(url));
if (match) {
// Clipboard operation and window close function
function copyToClipboardAndClose() {
// (clipboard needs focus else gone)
// navigator.clipboard.writeText(location.href);
GM_setClipboard(location.href);
setTimeout(() => {
window.close();
}, 100);
}
copyToClipboardAndClose();
}
if (document.body.textContent.includes("404 File not found")) {
window.close();
}
}
break;
}
// Akiba auto search and open
case /^https?:\/\/www\.akiba-online\.com\/search\/.*/i.test(url): {
function search() {
// Extract the current parameter
const paramName = "search";
const searchTerm = new URLSearchParams(window.location.search).get(paramName);
if (searchTerm) {
document
.querySelector(
"#top > div.p-body > div > div.uix_contentWrapper > div > div > div > form > div > dl > dd > div > div.formSubmitRow-controls > button"
)
.click();
// close window if no result
setTimeout(() => {
if (document.querySelector("body > div.flashMessage.is-active > div").textContent === "No results found.") {
window.close();
}
}, 200);
}
}
function autoOpenResults() {
let posts = document.querySelectorAll("div.block-container > ol > li");
if (posts.length === 1) {
let post = posts[0];
let childLink = post.querySelector("a");
childLink?.click();
} else if (posts.length >= 2) {
const paramName = "q";
const searchTerm = new URLSearchParams(window.location.search).get(paramName);
let postTitle = document.querySelectorAll("div.block-container > ol > li h3 a");
let fileJokerExclusive = document.querySelector("div.block-container > ol > li span.label--royalBlue");
if (
fileJokerExclusive &&
fileJokerExclusive.parentElement.textContent.toLowerCase().includes(searchTerm.toLowerCase())
) {
fileJokerExclusive.parentElement?.click();
return;
}
for (let i = 0; i < postTitle.length; i++) {
let title = postTitle[i];
if (!title.textContent.includes(searchTerm)) {
title.parentElement.parentElement.parentElement.parentElement.style.display = "none";
} else {
window.open(title.href, "_blank");
}
}
setTimeout(function () {
window.close();
}, 500);
}
}
let externalSearchMode = await GM_getValue("externalSearchMode", false);
if (externalSearchMode) {
// if this url then no result, so close window
if (/^https?:\/\/www\.akiba-online\.com\/search\/search/i.test(url)) {
window.close();
}
// use get parameter for search with form as only post parameters are allowed
search();
// open result if only one result saves clicking
autoOpenResults();
}
break;
}
}
}
function initializeBeforeRender() {
// GM_setValue("authorsMode", true);
addCSS();
switch (true) {
// JAV Details
case /[a-z]{2}\/\?v=jav.*/.test(url):
// on low resolutions cover image get fixed size by site javascript
removeResizingOfCoverImage();
break;
}
}
initializeBeforeRender();
document.addEventListener("DOMContentLoaded", main, { once: true });