您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds a button for BitChute playlist menu to video thumbnails and listings (next to the 'Watch Later' button)
当前为
- // ==UserScript==
- // @name BitChute | 'Add to Playlist' Button Everywhere
- // @namespace de.sidneys.userscripts
- // @homepage https://gist.githubusercontent.com/sidneys//raw/
- // @version 0.9.0
- // @description Adds a button for BitChute playlist menu to video thumbnails and listings (next to the 'Watch Later' button)
- // @author sidneys
- // @icon https://i.imgur.com/4GUWzW5.png
- // @noframes
- // @include *://*.bitchute.com/*
- // @require https://greasyfork.org/scripts/38888-greasemonkey-color-log/code/Greasemonkey%20%7C%20Color%20Log.js
- // @require https://greasyfork.org/scripts/374849-library-onelementready-es6/code/Library%20%7C%20onElementReady%20ES6.js
- // @connect bitchute.com
- // @grant GM.addStyle
- // @grant GM.download
- // @grant unsafeWindow
- // @run-at document-start
- // ==/UserScript==
- /**
- * ESLint
- * @global
- */
- /* global onElementReady */
- Debug = false
- /**
- * Inject Stylesheet
- */
- let injectStylesheet = () => {
- console.debug('injectStylesheet')
- GM.addStyle(`
- /* ==========================================================================
- ELEMENTS
- ========================================================================== */
- /* .show-playlist-modal
- ========================================================================== */
- .action-button.show-playlist-modal
- {
- top: 30px;
- font-size: 24px;
- width: 30px;
- height: 30px;
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .action-button.show-playlist-modal > svg.action-icon:hover
- {
- transform: unset;
- }
- .action-button
- {
- cursor: pointer;
- }
- `)
- }
- /**
- * Monkey Patch History.pushState() to emit a 'pushstate' event on <window>.
- * @see {@link https://stackoverflow.com/a/4585031/1327892}
- * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/History/pushState}
- */
- const originalPushState = unsafeWindow.history.pushState
- unsafeWindow.history.pushState = (state, title = '', url) => {
- console.debug('history.pushState()', 'state:', JSON.stringify(state), 'title:', title, 'url:', url)
- // Create event
- const event = new CustomEvent('pushstate', { 'detail': state, 'bubbles': true, 'cancelable': false })
- // Emit event
- unsafeWindow.dispatchEvent(event)
- // Call original
- originalPushState.call(unsafeWindow.history, state, title, url)
- }
- /**
- * Render Button 'Add to Playlist'
- * @param {Element} element - Target Element
- */
- let renderButtonElement = (element) => {
- console.debug('renderButtonElement')
- // Create Element
- const buttonElement = document.createElement('span')
- buttonElement.className = 'show-playlist-modal'
- buttonElement.innerHTML = `
- <i class="action-icon fas fa-list fa-fw"></i>
- `
- buttonElement.title = 'Add to Playlist'
- buttonElement.dataset.toggle = 'tooltip'
- buttonElement.dataset.placement = 'bottom'
- unsafeWindow.jQuery(buttonElement).data('video', unsafeWindow.jQuery(element).data('video'))
- if (element.classList.contains('action-button')) {
- buttonElement.classList.add('action-button')
- }
- if (element.classList.contains('toolbox-button')) {
- buttonElement.classList.add('toolbox-button')
- }
- // Render Element
- element.after(buttonElement)
- // Status
- console.debug('Rendered', 'Button Element')
- }
- /**
- * Render Modal 'Playlist'
- * @param {Element} element - Target Element
- */
- let renderPlaylistElement = (element) => {
- console.debug('renderPlaylistElement')
- // Abort if exists
- if (document.querySelector('.playlist-modal')) { return }
- // Create Element
- const playlistElement = document.createElement('div')
- playlistElement.setAttribute('role', 'dialog')
- playlistElement.className = 'modal fade playlist-modal'
- playlistElement.innerHTML = `
- <div class="modal-dialog">
- <div class="modal-content">
- <div class="modal-header">
- <button type="button" class="close" data-dismiss="modal" aria-hidden="true">
- <i class="fas fa-times fa-fw"></i>
- </button>
- <a href="/" class="spa">
- <img class="modal-logo logo-full" src="/static/v130/images/logo-full-day.png" alt="BitChute">
- </a>
- </div>
- <div class="modal-body">
- <div class="form-group">
- <div class="create-playlist-entry-error alert alert-danger hidden"></div>
- <label>Add to Playlist</label>
- <span class="options"></span>
- </div>
- <div class="create-playlist-error alert alert-danger hidden"></div>
- <div class="form-group">
- <form method="post" id="create-playlist-form">
- <input type="hidden" name="csrfmiddlewaretoken" value="XA3zoHAEsntOaFTteSV3rhIVboyJf5P5HeYjAioyqn2hKQ8IXcBOcKZCTduYIVc4">
- <label>Create a New Playlist</label>
- <div class="create-playlist-error-playlist alert alert-danger hidden"></div>
- <div class="name">
- <input type="text" name="playlist" class="form-control" autocomplete="off" placeholder="My Playlist" maxlength="100" id="id_playlist">
- <span class="toolbox-button create" data-video="UkfyMnll0o9O" data-toggle="tooltip" data-placement="bottom" title="Create Playlist">
- <span class="fa-layers">
- <i class="fal fa-square"></i>
- <i class="far fa-plus" data-fa-transform="shrink-7"></i>
- </span>
- </span>
- </div>
- <p class="help">This must be unique. Maximum 100 characters.</p>
- </form>
- </div>
- </div>
- </div>
- </div>
- `
- // Render Element
- element.before(playlistElement)
- // Status
- console.debug('Rendered', 'Playlist Element')
- }
- /**
- * Register Event Handlers
- */
- let registerEventHandlers = () => {
- console.debug('registerEventHandlers')
- // Add BitChute's internal event handlers
- unsafeWindow.playlistAttachEvents()
- unsafeWindow.playlistAttachModalEvents()
- unsafeWindow.spaAttachEvents()
- unsafeWindow.scrollerAttachEvents()
- // Log message after Playlist shown
- unsafeWindow.jQuery('.playlist-modal')
- .off('shown.bs.modal')
- .on('shown.bs.modal', (event) => {
- console.debug('.playlist-modal#shown.bs.modal')
- console.info('Shown', 'Playlist')
- })
- // Log message after Playlist hidden
- unsafeWindow.jQuery('.playlist-modal')
- .off('hidden.bs.modal')
- .on('hidden.bs.modal', (event) => {
- console.debug('.playlist-modal#hidden.bs.modal')
- console.info('Hidden', 'Playlist')
- })
- }
- /**
- * Init
- */
- let init = () => {
- console.info('init')
- // Add Stylesheet
- injectStylesheet()
- // Add Playlist
- onElementReady('.container', true, (element) => {
- renderPlaylistElement(element)
- })
- // Add Buttons
- onElementReady('.playlist-watch-later', true, (element) => {
- renderButtonElement(element)
- })
- // Add Events
- registerEventHandlers()
- }
- /**
- * @listens document:Event#readystatechange
- */
- document.addEventListener('readystatechange', () => {
- console.debug('document#readystatechange', document.readyState)
- if (document.readyState !== 'interactive') { return }
- /**
- * @listens window:Event#jQuery(document).ready
- */
- unsafeWindow.jQuery(document).ready(() => {
- console.debug('window#jQuery(document).ready')
- init()
- })
- })
- /**
- * Handle in-page navigation on BitChute
- * @listens window:Event#pushstate
- */
- unsafeWindow.addEventListener('pushstate', (event) => {
- console.debug('window#pushstate', 'event.detail:', JSON.stringify(event.detail))
- init()
- })