JAV在线播放

JavDB、JavBus、JavLibrary 添加在线播放

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name        JAV在线播放
// @version     0.0.4
// @namespace   http://tampermonkey.net/
// @match       https://javdb.com/v/*
// @match       https://www.javbus.com/*
// @match       https://www.javlibrary.com/*
// @author      none
// @description JavDB、JavBus、JavLibrary 添加在线播放
// @license MIT
// @grant       GM_xmlhttpRequest
// @grant       GM_registerMenuCommand
// @grant       GM_unregisterMenuCommand
// @grant       GM_setValue
// @grant       GM_getValue
// @grant       GM_notification
// @connect     missav.ai, jable.tv, www.av.gl
// @require     https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js

// ==/UserScript==
(function () {
    'use strict';

    const SOURCES = ['MissAV', 'Jable.tv', 'AVgle'];
    const currentSource = GM_getValue('source', SOURCES[0]);
    registerMenu(null, SOURCES);

    const domain = location.hostname.split('.').slice(-2).join('.');
    console.log('<JAV_ONLINE> Detect domain: ' + domain);

    let plate = null;
    switch (domain) {
        case 'javdb.com':
            plate = $('.first-block .value').text().trim();
            break;
        case 'javbus.com':
            plate = $('div.movie > div.info > p:first-child > span:last-child').text().trim();
            break;
        case 'javlibrary.com':
            plate = $('#video_id td.text').text().trim();
            break;
    }

    if (plate) {
        //javlibrary fancybox
        if (domain == 'javlibrary.com') {

            $('head').append('<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fancyapps-ui/6.0.5/fancybox/fancybox.min.css" crossorigin="anonymous" referrerpolicy="no-referrer" />');
            $('.previewthumbs a').attr('data-fancybox', 'gallery');
            $.getScript('https://cdnjs.cloudflare.com/ajax/libs/fancyapps-ui/6.0.5/fancybox/fancybox.umd.js').always(() => Fancybox.bind("[data-fancybox]", {}));
        }

        search_movie(plate, currentSource, (playAlt, playUrl) => {
            if (domain === 'javdb.com') {
                const $cover = $('.column-video-cover > a');
                if ($cover.length === 0) return;

                let $pb = $cover.find('.play-button');
                if ($pb.length === 0) {
                    $pb = $(`
                        <div class="play-button">
                            <span class="icon"><img src="/packs/media/images/btn-play-b414746c.svg"></span>
                            <span class="text">${playAlt}</span>
                        </div>
                    `);
                    $cover.append($pb);
                    $cover.addClass('cover-container');
                    $cover.removeAttr('data-fancybox');
                } else {
                    $pb.find('.text').text(playAlt);
                }

                $cover.attr({
                    href: playUrl,
                    target: '_blank'
                });
            }

            if (domain === 'javbus.com') {
                const $cover = $('div.screencap > a.bigImage');
                if ($cover.find('.overlay-play-btn').length === 0) {
                    const $btn = $(`
                        <a class="overlay-play-btn" href="${playUrl}" target="_blank" onclick="event.stopPropagation();"
                            style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
                            color: white; background-color: rgba(0,0,0,0.5); padding: 8px 16px;
                            text-decoration: none; border-radius: 4px; font-weight: bold; font-size: x-large;">
                            ${playAlt}
                        </a>
                    `);
                    $cover.css('position', 'relative').append($btn);
                }
            }

            if (domain === 'javlibrary.com') {
                const $cover = $('#video_jacket_img');
                const $btn = $(`
                    <a class="overlay-play-btn" href="${playUrl}" target="_blank" onclick="event.stopPropagation();"
                        style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
                        color: white; background-color: rgba(0,0,0,0.5); padding: 8px 16px;
                        text-decoration: none; border-radius: 4px; font-weight: bold; font-size: x-large;">
                        ${playAlt}
                    </a>
                `);
                $cover.css('position', 'relative').after($btn);
            }
        });
    }

    async function search_movie(plate, source, callback) {

        const sourceStrategies = {
            'MissAV': {
                buildSearchUrl: plate => `https://missav.ai/en/search/${plate}`,
                referer: 'https://missav.ai',
                parseResult: doc => {
                    const a = doc.querySelector('.thumbnail div:first-child a');
                    if (!a) return null;
                    const href = a.href;
                    const alt = a.getAttribute('alt') || '';
                    const playAlt = alt.includes('uncensored') ? '跳转播放-无码' : '跳转播放';
                    return { playAlt, playUrl: href };
                }
            },
            'Jable.tv': {
                buildSearchUrl: plate => `https://jable.tv/search/${plate}/`,
                referer: 'https://jable.tv',
                parseResult: doc => {
                    const a = doc.querySelector('#list_videos_videos_list_search_result > div > section > div > div:nth-child(1) > div > div.img-box.cover-md > a');
                    if (!a) return null;
                    return { playAlt: '跳转播放', playUrl: a.href };
                }
            },
            'AVgle': {
                buildSearchUrl: plate => `https://www.av.gl/vod/search.html?wd=${plate}`,
                referer: 'https://www.av.gl/',
                parseResult: doc => {
                    const a = doc.querySelector('div.thumbnail > div.relative > a:first-child');
                    if (!a) return null;
                    return { playAlt: '跳转播放', playUrl: 'https://www.av.gl' + a.getAttribute('href') };
                }
            }
        };

        try {
            const strategy = sourceStrategies[source];
            const searchUrl = strategy.buildSearchUrl(plate);
            const html = await gmFetch(searchUrl, strategy.referer);

            const parser = new DOMParser();
            const doc = parser.parseFromString(html, 'text/html');
            const result = strategy.parseResult(doc);

            if (result) {
                console.log(`[${source}] 搜索成功: ${result.playUrl}`);
                callback(result.playAlt, result.playUrl);
            } else {
                console.log(`[${source}] 没有找到结果`);
                callback('暂无资源', '#');
            }
        } catch (err) {
            console.error(`搜索失败 [${source}]:`, err);
            callback('暂无资源', '#');
        }
    }

    function gmFetch(url, referer = '') {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: 'GET',
                url: url,
                headers: {
                    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36',
                    'Referer': referer
                },
                onload: response => resolve(response.responseText),
                onerror: error => reject(error)
            });
        });
    }

    function registerMenu(prevMenu, sources) {
        let currentSource = GM_getValue('source', sources[0]);
        if (prevMenu) GM_unregisterMenuCommand(prevMenu);

        const menu = GM_registerMenuCommand(`数据源(点击切换): ${currentSource}`, () => {
            const index = sources.indexOf(currentSource);
            const nextSource = sources[(index + 1) % sources.length];
            GM_setValue('source', nextSource);

            GM_notification({
                title: '数据源已切换,刷新页面生效',
                text: '当前数据源: ' + nextSource,
                timeout: 2000
            });

            registerMenu(menu, sources);
        });
    }

})();