// ==UserScript==
// @name Jav跳转到Emby播放,支持 JavBus/Javdb/library/javbooks/avmoo/avsox/sehuatang/msin图书馆
// @namespace http://tampermonkey.net/
// @version 2025.2.7
// @description 在JavBus/Javdb/library/javbooks/avmoo/avsox/sehuatang/msin图书馆高亮emby存在的视频,并在详情页提供一键跳转功能
// @include /^.*(jav|bus|dmm|see|cdn|fan){2}\..*$/
// @match *://www.javbus.com/*
// @include *://javdb*.com/v/*
// @include *://javdb*.com/search?q=*
// @match *://www.javdb.com/*
// @match *://javdb.com/*
// @include *://*.javlib.com/*
// @include *://*.javlibrary.com/*
// @include *://*/cn/*v=jav*
// @include *://*/en/*v=jav*
// @include *://*/tw/*v=jav*
// @include *://*/ja/*v=jav*
// @include /^.*(avmoo|avsox)\..*$/
// @include *://avmoo.*/*/movie/*
// @include *://avsox.*/*/movie/*
// @match https://www.sehuatang.net/thread-*
// @match https://www.sehuatang.net/forum.php?mod=viewthread&tid=*
// @match https://.*/thread-*
// @match https://.*/forum.php?mod=viewthread&tid=*
// @match https://www.tanhuazu.com/threads/*
// @match *://javbooks.com/content*censored/*.htm
// @match *://jmvbt.com/content*censored/*.htm
// @match *://*.com/content*censored/*.htm
// @include *://*.cc/content_censored/*.htm
// @include /^https:\/\/jbk008\.com\/serchinfo\_(censored|uncensored)\/topicsbt/
// @match *://db.msin.jp/jp.page/movie?id=*
// @match *://db.msin.jp/page/movie?id=*
// @include *://*/works/detail/*
// @match *://xslist.org/search?query=*
// @grant GM_xmlhttpRequest
// @license MIT
// ==/UserScript==
const embyAPI = "279b8961e1fd44b1bf8ea4c025f67075";
const embyBaseUrl = "http://192.168.5.8:35405/";
const defaultColor = "#52b54b"; // HotPink
(function () {
'use strict';
class Base {
fetchEmbyData(code, callback) {
console.log('Fetching data for code:', code);
GM_xmlhttpRequest({
method: "GET",
url: `${embyBaseUrl}emby/Users/${embyAPI}/Items?api_key=${embyAPI}&Recursive=true&IncludeItemTypes=Movie&SearchTerm=${code}`,
headers: { accept: "application/json" },
onload: (res) => {
try {
const data = JSON.parse(res.responseText);
callback(data);
} catch (error) {
console.error("Failed to parse response:", error);
}
},
onerror: (e) => {
console.error("Error fetching Emby data:", e);
},
ontimeout: () => {
console.error("Request to Emby timed out");
}
});
}
insertEmbyLink(targetElement, data) {
data.Items.forEach(item => {
const [embyUrlSpanStyle, embyUrlAStyle] = [`background: ${defaultColor}; border-radius: 3px; padding: 3px 6px;`, `color: white; text-decoration: none;`]
const embyUrl = `${embyBaseUrl}web/index.html#!/item?id=${item.Id}&serverId=${item.ServerId}`;
const embyLink = `<div style="${embyUrlSpanStyle}">
<a href="${embyUrl}" style="${embyUrlAStyle}" target="_blank">
<b>跳转到emby👉</b>
</a>
</div>`;
$(targetElement).after(embyLink);
});
}
highlightAndInsertEmbyLink(videos, extractFanhaoFunction, insertAfterSelector) {
// console.log('Highlighting videos...', videos);
const videoArray = Array.from(videos);
videoArray.forEach(videoElement => {
const fanhaos = extractFanhaoFunction(videoElement);
// console.log('Fanhaos:', fanhaos);
if (!fanhaos || fanhaos.length === 0) return;
const searchNextFanhao = (fanhaoIndex) => {
if (fanhaoIndex >= fanhaos.length) return;
let fanhao = fanhaos[fanhaoIndex];
this.fetchEmbyData(fanhao, (data) => {
if (data.Items.length > 0) {
const targetElement = insertAfterSelector
? videoElement.querySelector(insertAfterSelector)
: videoElement;
this.insertEmbyLink(targetElement, data);
// 高亮
videoElement.style.borderWidth = "3px";
videoElement.style.borderStyle = "solid";
videoElement.style.borderColor = defaultColor;
videoElement.style.backgroundColor = defaultColor;
} else {
searchNextFanhao(fanhaoIndex + 1);
}
});
};
searchNextFanhao(0);
});
}
}
// 定义各站点处理类(保持空类结构)
class JavBus extends Base { }
class JavLibrary extends Base { }
class Javdb extends Base { }
class Javbooks extends Base { }
class Avmoo extends Base { }
class Sehuatang extends Base { }
class Msin extends Base { }
class Main {
constructor() {
console.log('Jav跳转Emby启动...');
this.sites = {
'javBus': {
selector: "footer:contains('JavBus')", // 根据页脚判断JavBus
class: JavBus,
listPageSelector: "#waterfall .item.masonry-brick", // 根据id和class判断瀑布/列表页
listPageInsertAfter: ".item date", // 新增插入位置控制
detailPageSelector: '.col-md-3.info p span:nth-child(2)', // 根据选择器是否获取道识别码/番号判断详情页
detailPageContainer: ".col-md-3.info p span:nth-child(2):first", // 插入Emby链接的容器
listPageExtract: (el) => { // 列表页提取番号
const fanhaoElement = el.querySelector('.item date');
return fanhaoElement ? [fanhaoElement.textContent.trim()] : [];
},
detailPageExtract: () => { // 详情页提取番号
const code = $('.col-md-3.info p').eq(0).find('span').eq(1).html();
return code ? [code] : [];
}
},
'javLibrary': {
selector: "#bottomcopyright:contains('JAVLibrary')",
class: JavLibrary,
listPageSelector: ".video",
listPageInsertAfter: "a",
detailPageSelector: '#content #video_title #video_jacket_info #video_info .item .text',
detailPageContainer: "#video_info",
commentPageSelector: "#video_comments .comment",
commentPageInsertAfter: "strong",
listPageExtract: (el) => {
const fanhao = el.children[0]?.title.split(" ")[0] || el.children[1]?.title.split(" ")[0];
return fanhao ? [fanhao] : [];
},
detailPageExtract: () => {
const code = $('#video_info .item').eq(0).find('.text').html();
return code ? [code] : [];
},
commentPageExtract: (el) => {
const anchorElement = el.querySelector('a[href^="videoreviews.php?v="]');
return anchorElement ? [anchorElement.textContent.split(" ")[0]] : [];
}
},
'javdb': {
selector: "#footer:contains('javdb')",
class: Javdb,
listPageSelector: ".movie-list .item",
listPageInsertAfter: ".video-title strong", // 新增插入位置控制
detailPageSelector: 'body > section > div > div.video-detail > h2 > strong',
detailPageContainer: ".panel.movie-panel-info .value:first",
listPageExtract: (el) => {
const result = [];
const videoTitleElement = el.querySelector('.video-title strong');
if (videoTitleElement) {
const text = videoTitleElement.textContent.trim();
if (/\d/.test(text)) result.push(text);
else {
const title = el.querySelector('.video-title').textContent.trim();
result.push(title);
}
}
return result;
},
detailPageExtract: () => {
const code = $('body > section > div > div.video-detail > h2 > strong').text().trim().split(' ')[0];
return code ? [code] : [];
}
},
'javbooks': {
selector: "#Declare_box:contains('javbooks')",
class: Javbooks,
detailPageSelector: '#info > div:nth-child(2) > font',
detailPageContainer: "#info",
detailPageExtract: () => {
const code = $('#info > div:nth-child(2) > font').text().trim().split(' ')[0];
return code ? [code] : [];
}
},
'avmoo': {
selector: "footer:contains('AVMOO')",
class: Avmoo,
listPageSelector: "#waterfall .item", // 根据id和class判断瀑布/列表页
listPageInsertAfter: ".item date", // 新增插入位置控制
listPageExtract: (el) => { // 列表页提取番号
const fanhaoElement = el.querySelector('.item date');
return fanhaoElement ? [fanhaoElement.textContent.trim()] : [];
},
detailPageSelector: '.col-md-3.info p span:nth-child(2)',
detailPageContainer: ".col-md-3.info",
detailPageExtract: () => {
const code = $('.col-md-3.info p').eq(0).find('span').eq(1).html();
return code ? [code] : [];
}
},
'sehuatang': {
selector: "#flk:contains('色花堂')",
class: Sehuatang,
detailPageCodeRegex: /([a-zA-Z]{2,15}[-\s]?\d{2,15}|FC2PPV-[^\d]{0,5}\d{6,7})/i,
detailPageContainer: "#pgt",
detailPageExtract: () => {
const str = document.title.split(" ")[0];
return str.match(this.detailPageCodeRegex) || [];
}
},
'msin': {
selector: "#footer:contains('db.msin.jp')",
class: Msin,
detailPageSelector: 'div.mv_pn',
detailPageContainer: "#top_content",
detailPageExtract: () => {
const code = $('div.mv_pn').text().trim().split(' ')[0];
return code ? [code] : [];
}
}
};
this.site = Object.keys(this.sites).find(key => $(this.sites[key].selector).length) || null;
console.log('Matched site:', this.site);
this.siteClass = this.site ? this.sites[this.site].class : null;
// console.log('Site class:', this.siteClass);
}
make() {
if (!this.siteClass) return;
const siteConfig = this.sites[this.site];
// console.log('Site Config:', this.siteConfig);
const instance = new siteConfig.class();
// console.log('$(siteConfig.listPageSelector).length:', $(siteConfig.listPageSelector).length);
// console.log('$(siteConfig.commentPageSelector).length:', $(siteConfig.commentPageSelector).length);
// 处理列表页
if ($(siteConfig.listPageSelector).length > 0) {
console.log('处理列表页', $(siteConfig.listPageSelector).length);
instance.highlightAndInsertEmbyLink(
$(siteConfig.listPageSelector),
(el) => siteConfig.listPageExtract ? siteConfig.listPageExtract(el) : [],
siteConfig.listPageInsertAfter // 传递插入位置选择器
);
}
// 处理详情页
else if ($(siteConfig.detailPageSelector).length > 0) {
console.log('处理详情页', $(siteConfig.detailPageSelector).length);
const codes = siteConfig.detailPageExtract ? siteConfig.detailPageExtract() : [];
codes.forEach(code => {
instance.fetchEmbyData(code, (data) => {
if (data.Items.length > 0) {
instance.insertEmbyLink($(siteConfig.detailPageContainer), data);
}
});
});
}
// 处理评论页
else if ($(siteConfig.commentPageSelector).length > 0) {
console.log('处理评论页');
// 添加对应的评论页选择器和处理逻辑
instance.highlightAndInsertEmbyLink(
$(siteConfig.commentPageSelector),
(el) => siteConfig.commentPageExtract ? siteConfig.commentPageExtract(el) : [],
siteConfig.commentPageInsertAfter
);
}
}
}
setTimeout(() => new Main().make(), 1000);
})();