BitChute | Video Download Button

Adds a "Download" button to every BitChute video page

当前为 2018-06-11 提交的版本,查看 最新版本

// ==UserScript==
// @name            BitChute | Video Download Button
// @namespace       de.sidneys.userscripts
// @homepage        https://gist.githubusercontent.com/sidneys/b4783b0450e07e12942aa22b3a11bc00/raw/
// @version         2.1.1
// @description     Adds a "Download" button to every BitChute video page
// @author          sidneys
// @icon            https://www.bitchute.com/static/images/android-icon-192x192.png
// @include         *://*bitchute.com/video/*
// @require         https://gf.qytechs.cn/scripts/38888-greasemonkey-color-log/code/Greasemonkey%20%7C%20Color%20Log.js
// @require         https://gf.qytechs.cn/scripts/38889-greasemonkey-waitforkeyelements-2018/code/Greasemonkey%20%7C%20waitForKeyElements%202018.js
// @require         https://gf.qytechs.cn/scripts/369408-save-as-userscript-library/code/Save-As%20Userscript%20Library.js
// @run-at          document-end
// @grant           unsafeWindow
// ==/UserScript==

/**
 * @default
 * @constant
 */
DEBUG = false


/**
 * File extension
 * @default
 * @constant
 */
const defaultExtension = 'mp4'

/**
 * Inject Stylesheet
 */
let injectStylesheet = () => {
    console.debug('injectStylesheet')

    GM_addStyle(`
    /* ==========================================================================
       ELEMENTS
       ========================================================================== */

    /* #download-button
       ========================================================================== */

    #download-button,
    #download-button:hover
    {
        color: rgb(239, 65, 54);
        display: inline-block;
        animation: fade-in 0.3s;
        pointer-events: all;
        filter: none;
        cursor: pointer;
        white-space: nowrap;
        transition: all 500ms ease-in-out;
    }

    #download-button.busy
    {
        pointer-events: none;
        cursor: default;
        opacity: 0.75;
        animation: pulsating-opacity 2000ms cubic-bezier(0.455, 0.03, 0.515, 0.955) 0s infinite alternate;
    }

    #download-button-label
    {
        overflow: hidden;
        white-space: nowrap;
        text-overflow: ellipsis;
    }

    /* .video-statistics
       ========================================================================== */

    .video-information .video-statistics
    {
        white-space: nowrap;
    }

    /* ==========================================================================
       ANIMATIONS
       ========================================================================== */

    @keyframes pulsating-opacity
    {
        0% { filter: opacity(0.75); }
        25% { filter: opacity(0.75); }
        50% { filter: opacity(0.25); }
        75% { filter: opacity(0.75); }
        100% { filter: opacity(0.75); }
    }

    `)
}


/**
 * Generate Filename
 * @return {String} Filename
 */
let generateFilename = () => {
    console.debug('generateFilename')

    const videoAuthor = document.querySelector('p.video-author > a').innerText.trim()
    const pageTitle = document.querySelector('h1.page-title').innerText.trim()

    return `${videoAuthor} - ${pageTitle}.${defaultExtension}`
}


/**
 * Render download button
 * @param {String} url - Target URL
 */
let renderDownloadButton = (url) => {
    console.debug('renderDownloadButton')

    // Parse URL
    const urlObject = new URL(url)
    const urlHref = urlObject.href

    // Parent
    const parentElement = document.querySelector('.video-information .video-statistics')

    // Button
    const buttonElement = document.createElement('a')
    buttonElement.id = 'download-button'
    buttonElement.innerHTML = `
        <i class="action-icon far fa-cloud-download-alt fa-fw"></i>
        <span id="download-button-label">Start Download</span>
    `

    parentElement.appendChild(buttonElement)

    // Label
    const labelElement = document.querySelector('#download-button-label')

    buttonElement.onclick = () => {
        saveAs(urlHref, generateFilename(), (error, progress, complete) => {
            if (error) {
                labelElement.innerText = `${error}`
                return
            }

            if (progress) {
                const percentage = (progress * 100).toFixed(2)

                labelElement.innerText = `Downloading: ${percentage}%`
                buttonElement.classList.add('busy')
            }

            if (complete) {
                labelElement.innerText = `Download complete.`
                buttonElement.classList.remove('busy')
            }
        })
    }

    /**
     * Filename
     * @type {String}
     */
    console.info('Added download button:', urlHref)
}


/**
 * Init
 */
let init = () => {
    console.debug('init')

    // Add Stylesheet
    injectStylesheet()

    if (!unsafeWindow.client) {
        // Firefox
        waitForKeyElements('source', (element) => {
            renderDownloadButton(element.src)
        })
    } else {
        // Chromium
        waitForKeyElements('video', () => {
            renderDownloadButton(unsafeWindow.client.torrents[0].urlList[0])
        })
    }
}


/**
 * @listens window:Event#load
 */
window.addEventListener('load', () => {
    console.debug('window#load')

    init()
})

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址