JAV在线播放

JavDB、JavBus、JavLibrary 添加在线播放

Dovrai installare un'estensione come Tampermonkey, Greasemonkey o Violentmonkey per installare questo script.

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

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Userscripts per installare questo script.

Dovrai installare un'estensione come ad esempio Tampermonkey per installare questo script.

Dovrai installare un gestore di script utente per installare questo script.

(Ho già un gestore di script utente, lasciamelo installare!)

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

(Ho già un gestore di stile utente, lasciamelo installare!)

// ==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);
        });
    }

})();