您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds a button to open a direct, hotlinked video player in a modal, bypassing Pixeldrain limits. No need for a new tab. Also works with my Pixeldrain SRT Subtitle Injector.
// ==UserScript== // @name Pixeldrain Bypass Player (Modal Overlay) // @namespace pixeldrain-direct-player-modal // @version 3.1.2 // @description Adds a button to open a direct, hotlinked video player in a modal, bypassing Pixeldrain limits. No need for a new tab. Also works with my Pixeldrain SRT Subtitle Injector. // @author medy (logic inspired by Sak32009) // @license MIT // @match *://pixeldrain.com/u/* // @match *://pixeldrain.com/l/* // @require https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js // @require https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js // @grant GM_addStyle // @grant unsafeWindow // @run-at document-end // @noframes // ==/UserScript== /* global bootstrap, jQuery */ // Inject the entire minified Bootstrap 5.3 CSS, scoped to our container class. (t=> { if(typeof GM_addStyle=="function") { GM_addStyle(t); return } const r=document.createElement("style"); r.textContent=t,document.head.append(r) }) (` .pd-player-scope { all:initial } .pd-player-scope * { all:revert } .pd-player-scope,.pd-player-scope[data-bs-theme=light] { --bs-blue:#0d6efd; --bs-indigo:#6610f2; --bs-purple:#6f42c1; --bs-pink:#d63384; --bs-red:#dc3545; --bs-orange:#fd7e14; --bs-yellow:#ffc107; --bs-green:#198754; --bs-teal:#20c997; --bs-cyan:#0dcaf0; --bs-black:#000; --bs-white:#fff; --bs-gray:#6c757d; --bs-gray-dark:#343a40; --bs-gray-100:#f8f9fa; --bs-gray-200:#e9ecef; --bs-gray-300:#dee2e6; --bs-gray-400:#ced4da; --bs-gray-500:#adb5bd; --bs-gray-600:#6c757d; --bs-gray-700:#495057; --bs-gray-800:#343a40; --bs-gray-900:#212529; --bs-primary:#0d6efd; --bs-secondary:#6c757d; --bs-success:#198754; --bs-info:#0dcaf0; --bs-warning:#ffc107; --bs-danger:#dc3545; --bs-light:#f8f9fa; --bs-dark:#212529; --bs-primary-rgb:13,110,253; --bs-secondary-rgb:108,117,125; --bs-success-rgb:25,135,84; --bs-info-rgb:13,202,240; --bs-warning-rgb:255,193,7; --bs-danger-rgb:220,53,69; --bs-light-rgb:248,249,250; --bs-dark-rgb:33,37,41; --bs-primary-text-emphasis:#052c65; --bs-secondary-text-emphasis:#2b2f32; --bs-success-text-emphasis:#0a3622; --bs-info-text-emphasis:#055160; --bs-warning-text-emphasis:#664d03; --bs-danger-text-emphasis:#58151c; --bs-light-text-emphasis:#495057; --bs-dark-text-emphasis:#495057; --bs-primary-bg-subtle:#cfe2ff; --bs-secondary-bg-subtle:#e2e3e5; --bs-success-bg-subtle:#d1e7dd; --bs-info-bg-subtle:#cff4fc; --bs-warning-bg-subtle:#fff3cd; --bs-danger-bg-subtle:#f8d7da; --bs-light-bg-subtle:#fcfcfd; --bs-dark-bg-subtle:#ced4da; --bs-primary-border-subtle:#9ec5fe; --bs-secondary-border-subtle:#c4c8cb; --bs-success-border-subtle:#a3cfbb; --bs-info-border-subtle:#9eeaf9; --bs-warning-border-subtle:#ffe69c; --bs-danger-border-subtle:#f1aeb5; --bs-light-border-subtle:#e9ecef; --bs-dark-border-subtle:#adb5bd; --bs-white-rgb:255,255,255; --bs-black-rgb:0,0,0; --bs-font-sans-serif:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue","Noto Sans","Liberation Sans",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji"; --bs-font-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace; --bs-gradient:linear-gradient(180deg, rgba(255, 255, 255, .15), rgba(255, 255, 255, 0)); --bs-body-font-family:var(--bs-font-sans-serif); --bs-body-font-size:1rem; --bs-body-font-weight:400; --bs-body-line-height:1.5; --bs-body-color:#212529; --bs-body-color-rgb:33,37,41; --bs-body-bg:#fff; --bs-body-bg-rgb:255,255,255; --bs-emphasis-color:#000; --bs-emphasis-color-rgb:0,0,0; --bs-secondary-color:rgba(33,37,41,.75); --bs-secondary-color-rgb:33,37,41; --bs-secondary-bg:#e9ecef; --bs-secondary-bg-rgb:233,236,239; --bs-tertiary-color:rgba(33,37,41,.5); --bs-tertiary-color-rgb:33,37,41; --bs-tertiary-bg:#f8f9fa; --bs-tertiary-bg-rgb:248,249,250; --bs-heading-color:inherit; --bs-link-color:#0d6efd; --bs-link-color-rgb:13,110,253; --bs-link-decoration:underline; --bs-link-hover-color:#0a58ca; --bs-link-hover-color-rgb:10,88,202; --bs-code-color:#d63384; --bs-highlight-color:#212529; --bs-highlight-bg:#fff3cd; --bs-border-width:1px; --bs-border-style:solid; --bs-border-color:#dee2e6; --bs-border-color-translucent:rgba(0,0,0,.175); --bs-border-radius:.375rem; --bs-border-radius-sm:.25rem; --bs-border-radius-lg:.5rem; --bs-border-radius-xl:1rem; --bs-border-radius-xxl:2rem; --bs-border-radius-2xl:var(--bs-border-radius-xxl); --bs-border-radius-pill:50rem; --bs-box-shadow:0 .5rem 1rem rgba(0,0,0,.15); --bs-box-shadow-sm:0 .125rem .25rem rgba(0,0,0,.075); --bs-box-shadow-lg:0 1rem 3rem rgba(0,0,0,.175); --bs-box-shadow-inset:inset 0 1px 2px rgba(0,0,0,.075); --bs-focus-ring-width:.25rem; --bs-focus-ring-opacity:.25; --bs-focus-ring-color:rgba(13,110,253,.25); --bs-form-valid-color:#198754; --bs-form-valid-border-color:#198754; --bs-form-invalid-color:#dc3545; --bs-form-invalid-border-color:#dc3545 } .pd-player-scope[data-bs-theme=dark] { color-scheme:dark; --bs-body-color:#dee2e6; --bs-body-color-rgb:222,226,230; --bs-body-bg:#212529; --bs-body-bg-rgb:33,37,41; --bs-emphasis-color:#fff; --bs-emphasis-color-rgb:255,255,255; --bs-secondary-color:rgba(222,226,230,.75); --bs-secondary-color-rgb:222,226,230; --bs-secondary-bg:#343a40; --bs-secondary-bg-rgb:52,58,64; --bs-tertiary-color:rgba(222,226,230,.5); --bs-tertiary-color-rgb:222,226,230; --bs-tertiary-bg:#2b3035; --bs-tertiary-bg-rgb:43,48,53; --bs-primary-text-emphasis:#6ea8fe; --bs-secondary-text-emphasis:#a7acb1; --bs-success-text-emphasis:#75b798; --bs-info-text-emphasis:#6edff6; --bs-warning-text-emphasis:#ffda6a; --bs-danger-text-emphasis:#ea868f; --bs-light-text-emphasis:#f8f9fa; --bs-dark-text-emphasis:#dee2e6; --bs-primary-bg-subtle:#031633; --bs-secondary-bg-subtle:#161719; --bs-success-bg-subtle:#051b11; --bs-info-bg-subtle:#032830; --bs-warning-bg-subtle:#332701; --bs-danger-bg-subtle:#2c0b0e; --bs-light-bg-subtle:#343a40; --bs-dark-bg-subtle:#1a1d20; --bs-primary-border-subtle:#084298; --bs-secondary-border-subtle:#41464b; --bs-success-border-subtle:#0f5132; --bs-info-border-subtle:#087990; --bs-warning-border-subtle:#997404; --bs-danger-border-subtle:#842029; --bs-light-border-subtle:#495057; --bs-dark-border-subtle:#343a40; --bs-heading-color:inherit; --bs-link-color:#6ea8fe; --bs-link-hover-color:#8bb9fe; --bs-link-color-rgb:110,168,254; --bs-link-hover-color-rgb:139,185,254; --bs-code-color:#e685b5; --bs-highlight-color:#dee2e6; --bs-highlight-bg:#664d03; --bs-border-color:#495057; --bs-border-color-translucent:rgba(255,255,255,.15); --bs-form-valid-color:#75b798; --bs-form-valid-border-color:#75b798; --bs-form-invalid-color:#ea868f; --bs-form-invalid-border-color:#ea868f } .pd-player-scope *,.pd-player-scope :before,.pd-player-scope :after { box-sizing:border-box } .pd-player-scope h1,.pd-player-scope h2,.pd-player-scope h3,.pd-player-scope h4,.pd-player-scope h5,.pd-player-scope h6 { margin-top:0; margin-bottom:.5rem; font-weight:500; line-height:1.2; color:var(--bs-heading-color) } .pd-player-scope .btn-close { --bs-btn-close-color:#000; --bs-btn-close-bg:url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414z'/%3e%3c/svg%3e\"); --bs-btn-close-opacity:.5; --bs-btn-close-hover-opacity:.75; --bs-btn-close-focus-shadow:0 0 0 .25rem rgba(13,110,253,.25); --bs-btn-close-focus-opacity:1; --bs-btn-close-disabled-opacity:.25; --bs-btn-close-white-filter:invert(1) grayscale(100%) brightness(200%); box-sizing:content-box; width:1em; height:1em; padding:.25em .25em; color:var(--bs-btn-close-color); background:transparent var(--bs-btn-close-bg) center/1em auto no-repeat; border:0; border-radius:.375rem; opacity:var(--bs-btn-close-opacity) } .pd-player-scope .btn-close:hover { color:var(--bs-btn-close-color); text-decoration:none; opacity:var(--bs-btn-close-hover-opacity) } .pd-player-scope .btn-close:focus { outline:0; box-shadow:var(--bs-btn-close-focus-shadow); opacity:var(--bs-btn-close-focus-opacity) } .pd-player-scope .btn-close:disabled,.pd-player-scope .btn-close.disabled { pointer-events:none; -webkit-user-select:none; -moz-user-select:none; user-select:none; opacity:var(--bs-btn-close-disabled-opacity) } .pd-player-scope .btn-close-white,.pd-player-scope[data-bs-theme=dark] .btn-close { filter:var(--bs-btn-close-white-filter) } .pd-player-scope .modal { --bs-modal-zindex:1055; --bs-modal-width:500px; --bs-modal-padding:1rem; --bs-modal-margin:.5rem; --bs-modal-color:; --bs-modal-bg:var(--bs-body-bg); --bs-modal-border-color:var(--bs-border-color-translucent); --bs-modal-border-width:var(--bs-border-width); --bs-modal-border-radius:var(--bs-border-radius-lg); --bs-modal-box-shadow:var(--bs-box-shadow-sm); --bs-modal-inner-border-radius:calc(var(--bs-border-radius-lg) - var(--bs-border-width)); --bs-modal-header-padding-x:1rem; --bs-modal-header-padding-y:1rem; --bs-modal-header-padding:1rem 1rem; --bs-modal-header-border-color:var(--bs-border-color); --bs-modal-header-border-width:var(--bs-border-width); --bs-modal-title-line-height:1.5; --bs-modal-footer-gap:.5rem; --bs-modal-footer-bg:; --bs-modal-footer-border-color:var(--bs-border-color); --bs-modal-footer-border-width:var(--bs-border-width); position:fixed; top:0; left:0; z-index:var(--bs-modal-zindex); display:none; width:100%; height:100%; overflow-x:hidden; overflow-y:auto; outline:0 } .pd-player-scope .modal-dialog { position:relative; width:auto; margin:var(--bs-modal-margin); pointer-events:none } .pd-player-scope .modal.fade .modal-dialog { transition:transform .3s ease-out; transform:translate(0,-50px) } @media (prefers-reduced-motion:reduce) { .pd-player-scope .modal.fade .modal-dialog { transition:none } } .pd-player-scope .modal.show .modal-dialog { transform:none } .pd-player-scope .modal.modal-static .modal-dialog { transform:scale(1.02) } .pd-player-scope .modal-dialog-scrollable { height:calc(100% - var(--bs-modal-margin)*2) } .pd-player-scope .modal-dialog-scrollable .modal-content { max-height:100%; overflow:hidden } .pd-player-scope .modal-dialog-scrollable .modal-body { overflow-y:auto } .pd-player-scope .modal-dialog-centered { display:flex; align-items:center; min-height:calc(100% - var(--bs-modal-margin)*2) } .pd-player-scope .modal-content { position:relative; display:flex; flex-direction:column; width:100%; color:var(--bs-modal-color); pointer-events:auto; background-color:var(--bs-modal-bg); background-clip:padding-box; border:var(--bs-modal-border-width) solid var(--bs-modal-border-color); border-radius:var(--bs-modal-border-radius); outline:0 } .modal-backdrop { --bs-backdrop-zindex:1050; --bs-backdrop-bg:#000; --bs-backdrop-opacity:.5; position:fixed; top:0; left:0; z-index:var(--bs-backdrop-zindex); width:100vw; height:100vh; background-color:var(--bs-backdrop-bg) } .modal-backdrop.fade { opacity:0 } .modal-backdrop.show { opacity:var(--bs-backdrop-opacity) } .pd-player-scope .modal-header { display:flex; flex-shrink:0; align-items:center; justify-content:space-between; padding:var(--bs-modal-header-padding); border-bottom:var(--bs-modal-header-border-width) solid var(--bs-modal-header-border-color); border-top-left-radius:var(--bs-modal-inner-border-radius); border-top-right-radius:var(--bs-modal-inner-border-radius) } .pd-player-scope .modal-header .btn-close { padding:calc(var(--bs-modal-header-padding-y)*.5) calc(var(--bs-modal-header-padding-x)*.5); margin:calc(var(--bs-modal-header-padding-y)*-.5) calc(var(--bs-modal-header-padding-x)*-.5) calc(var(--bs-modal-header-padding-y)*-.5) auto } .pd-player-scope .modal-title { margin-bottom:0; line-height:var(--bs-modal-title-line-height) } .pd-player-scope .modal-body { position:relative; flex:1 1 auto; padding:var(--bs-modal-padding) } .pd-player-scope .modal-footer { display:flex; flex-shrink:0; flex-wrap:wrap; align-items:center; justify-content:flex-end; padding:calc(var(--bs-modal-padding) - var(--bs-modal-footer-gap)*.5); background-color:var(--bs-modal-footer-bg); border-top:var(--bs-modal-footer-border-width) solid var(--bs-modal-footer-border-color); border-bottom-right-radius:var(--bs-modal-inner-border-radius); border-bottom-left-radius:var(--bs-modal-inner-border-radius) } .pd-player-scope .modal-footer>* { margin:calc(var(--bs-modal-footer-gap)*.5) } @media (min-width:576px) { .pd-player-scope .modal { --bs-modal-margin:1.75rem; --bs-modal-box-shadow:var(--bs-box-shadow) } .pd-player-scope .modal-dialog { max-width:var(--bs-modal-width); margin-right:auto; margin-left:auto } .pd-player-scope .modal-sm { --bs-modal-width:300px } } @media (min-width:992px) { .pd-player-scope .modal-lg,.pd-player-scope .modal-xl { --bs-modal-width:800px } } @media (min-width:1200px) { .pd-player-scope .modal-xl { --bs-modal-width:1140px } } .pd-player-scope .fade { transition:opacity .15s linear } @media (prefers-reduced-motion:reduce) { .pd-player-scope .fade { transition:none } } .pd-player-scope .fade:not(.show) { opacity:0 } .pd-player-scope .p-0 { padding:0!important } .pd-player-scope .fw-bold { font-weight:700!important } .pd-player-scope .modal-body video { width:100%; max-height:calc(100vh - 100px); display:block; outline:none; background-color:#000; } `); (function($, bootstrap) { 'use strict'; // Use .noConflict to avoid clashes with the site's own jQuery if it has one. $ = $.noConflict(true); const SCRIPT_NAME = 'Pixeldrain Direct Player'; const BYPASS_URL_BASE = 'https://pd.cybar.xyz'; const CONTAINER_CLASS = 'pd-player-scope'; const MODAL_ID = 'pd-direct-player-modal'; let uiInitialized = false; // --- HTML Templates --- const MODAL_HTML = ` <div class="modal fade" id="$ { MODAL_ID } " tabindex="-1"> <div class="modal-dialog modal-xl modal-dialog-centered"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title fw-bold">Direct Player</h5> <button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button> </div> <div class="modal-body p-0"> <!-- Video tag will be injected here --> </div> </div> </div> </div> `; /** * Finds the current file ID and checks if it's a video. * @returns { { id: string, name: string } |null } */ function getCurrentFileInfo() { // Primary Method: Read from the global viewer_data object. It's more comprehensive. if (typeof unsafeWindow.viewer_data?.api_response !== 'undefined') { const data = unsafeWindow.viewer_data; const api = data.api_response; // Handle list pages (/l/...) if (data.type === 'list' && data.file_id && api.files) { const currentFile = api.files.find(f => f.id === data.file_id); if (currentFile && currentFile.mime_type?.startsWith('video/')) { return { id: currentFile.id, name: currentFile.name }; } } // Handle single file pages (/u/...) else if (data.type === 'file' && api.id && api.mime_type?.startsWith('video/')) { return { id: api.id, name: api.name }; } } // Fallback Method: For initial load before viewer_data might be populated. const videoMetaTag = document.querySelector('meta[property="og:video"]'); if (videoMetaTag?.content) { const urlParts = videoMetaTag.content.split('/'); const fileId = urlParts.pop(); const fileName = document.title.split('~')[0].trim(); if (fileId) return { id: fileId, name: fileName }; } return null; } /** * Creates and injects the toolbar button. * @param { { id: string, name: string } } fileInfo */ function createOrUpdateButton(fileInfo) { const toolbar = document.querySelector('.toolbar'); const templateButton = document.querySelector('.toolbar_button'); const separatorTemplate = document.querySelector('.toolbar .separator'); if (!toolbar || !templateButton || !separatorTemplate) { console.log(`[$ { SCRIPT_NAME } ] Toolbar elements not found yet`); return false; } // Check if button already exists let directButton = document.querySelector('#pd-direct-player-btn'); if (!directButton) { // Create the button using Pixeldrain's template directButton = templateButton.cloneNode(true); directButton.id = 'pd-direct-player-btn'; directButton.removeAttribute('href'); directButton.removeAttribute('title'); directButton.querySelector('i').textContent = 'play_arrow'; directButton.querySelector('span').textContent = 'Play Direct'; directButton.addEventListener('click', (e) => { e.preventDefault(); e.stopPropagation(); const currentId = directButton.getAttribute('data-file-id'); const currentName = directButton.getAttribute('data-file-name'); if (currentId) { createAndShowModal(currentId, currentName); } }); // Insert into toolbar (after the last separator) const newSeparator = separatorTemplate.cloneNode(true); separatorTemplate.parentNode.insertBefore(newSeparator, separatorTemplate.nextSibling); newSeparator.parentNode.insertBefore(directButton, newSeparator.nextSibling); console.log(`[$ { SCRIPT_NAME } ] Direct player button created`); } // Update button data directButton.setAttribute('data-file-id', fileInfo.id); directButton.setAttribute('data-file-name', fileInfo.name); return true; } /** * Removes the toolbar button if it exists. */ function removeButton() { const directButton = document.querySelector('#pd-direct-player-btn'); if (directButton) { // Also remove the separator that was added with it const prevSeparator = directButton.previousElementSibling; if (prevSeparator && prevSeparator.classList.contains('separator')) { prevSeparator.remove(); } directButton.remove(); console.log(`[$ { SCRIPT_NAME } ] Direct player button removed`); } } /** * Creates the Bootstrap modal, injects the video player, and shows it. * @param { string } fileId * @param { string } fileName */ function createAndShowModal(fileId, fileName) { // Ensure we have our container let $container = $(`.$ { CONTAINER_CLASS } `); if (!$container.length) { $container = $('<div>').addClass(CONTAINER_CLASS).attr('data-bs-theme', 'dark').appendTo('body'); } // Remove any old modal instance first $(`#$ { MODAL_ID } `).remove(); const videoUrl = `$ { BYPASS_URL_BASE } /$ { fileId } `; const $modal = $(MODAL_HTML); $modal.find('.modal-title').text(fileName); const $video = $('<video>').attr( { src: videoUrl, controls: true, autoplay: true }); $modal.find('.modal-body').append($video); $modal.appendTo($container); const modalInstance = new bootstrap.Modal($modal[0]); // Add event listener for when the modal is closed, to stop the video and clean up. $modal[0].addEventListener('hidden.bs.modal', event => { const videoEl = $modal.find('video')[0]; if (videoEl) { videoEl.pause(); videoEl.src = ''; // Detach the source } modalInstance.dispose(); $modal.remove(); // Remove from DOM }); modalInstance.show(); console.log(`[$ { SCRIPT_NAME } ] Modal opened for $ { fileName } `); } /** * Main execution logic. */ function run() { const fileInfo = getCurrentFileInfo(); if (fileInfo) { console.log(`[$ { SCRIPT_NAME } ] Video detected: $ { fileInfo.name } ($ { fileInfo.id }) `); const buttonCreated = createOrUpdateButton(fileInfo); if (!buttonCreated) { // If we couldn't create the button, try again later setTimeout(run, 1000); } } else { console.log(`[$ { SCRIPT_NAME } ] No video detected on this page/view.`); removeButton(); } } /** * Sets up UI when toolbar is available. */ function setupUI() { if (uiInitialized) return; const toolbar = document.querySelector('.toolbar'); const templateButton = document.querySelector('.toolbar_button'); const separatorTemplate = document.querySelector('.toolbar .separator'); if (!toolbar || !templateButton || !separatorTemplate) { console.log(`[$ { SCRIPT_NAME } ] Toolbar not ready yet, retrying...`); return false; } uiInitialized = true; console.log(`[$ { SCRIPT_NAME } ] UI Initialized`); // Run initial setup run(); return true; } // --- Script Entry Point --- function initializeScript() { // Wait for document body to be available if (!document.body) { setTimeout(initializeScript, 100); return; } // Try to setup UI immediately if (setupUI()) { return; // Success } // If immediate setup failed, use observer const observer = new MutationObserver((mutations, obs) => { if (setupUI()) { obs.disconnect(); } }); observer.observe(document.body, { childList: true, subtree: true }); // Fallback timeout setTimeout(() => { if (!uiInitialized) { console.log(`[$ { SCRIPT_NAME } ] Fallback initialization`); setupUI(); } }, 3000); } // Start initialization if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initializeScript); } else { initializeScript(); } // Listen for hash changes on list pages to update the button for the new file. $(window).on('hashchange', function() { // Give the site's JS time to update viewer_data setTimeout(run, 200); }); }) (jQuery, bootstrap);