Greasy Fork is available in English.

GIPHY | Direct GIF Image Download Button

Adds a button for directly downloding the original GIF images to the side menu. Uses the given GIF title as the local filename.

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください。
// ==UserScript==
// @name            GIPHY | Direct GIF Image Download Button
// @namespace       de.sidneys.userscripts
// @homepage        https://gist.githubusercontent.com/sidneys/13433a0c726895597fb4571405c7ca15/raw/
// @version         0.9.1
// @description     Adds a button for directly downloding the original GIF images to the side menu. Uses the given GIF title as the local filename.
// @author          sidneys
// @icon            https://giphy.com/static/img/favicon.png
// @include         http*://giphy.com/gifs/*
// @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         giphy.com
// @grant           GM.addStyle
// @grant           GM.download
// @run-at          document-end
// ==/UserScript==

/**
 * ESLint
 * @global
 */
/* global onElementReady */
Debug = false


/**
 * @callback saveAsCallback
 * @param {Error} error - Error
 * @param {Number} progress - Progress fraction
 * @param {Boolean} complete - Completion Yes/No
 */

/**
 * Download File via Greasemonkey
 * @param {String} url - Target URL
 * @param {String} fileName - Target Filename
 * @param {saveAsCallback} callback - Callback
 */
let saveAs = (url, fileName, callback = () => {}) => {
    console.debug('saveAs')

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

    // Download
    // noinspection JSValidateTypes
    GM.download({
        url: urlHref,
        name: fileName,
        saveAs: true,
        onerror: (download) => {
            console.debug('saveAs', 'onerror')

            callback(new Error(download.error ? download.error.toUpperCase() : 'Unknown'))
        },
        onload: () => {
            console.debug('saveAs', 'onload')

            callback(null)
        },
        ontimeout: () => {
            console.debug('saveAs', 'ontimeout')

            callback(new Error('Network timeout'))
        }
    })
}

/**
 * Get JSON Schema
 * @return {Object|void} - Giphy JSON Schema
 */
let getSchema = () => {
    console.debug('getSchema')

    let schemaObject

    // Read JSON Schema 'giphy-schema' from <script> tags
    try {
        // Parse property '.image.url' from Schema
        const schemaText = document.querySelector('script[name="giphy-schema"]').textContent
        schemaObject = JSON.parse(schemaText)
    } catch (error) {
        console.error(error)
        return
    }

    // Return
    return schemaObject
}

/**
 * Get GIF image URL
 * @return {String|void} - GIF Image URL
 */
let getGifImageUrl = () => {
    console.debug('getGifImageUrl')

    // Lookup Schema
    const schemaObject = getSchema()

    // Parse Media URL
    const gifMediaUrlObject = new URL(schemaObject.image.url)

    // Extract unique Image Identifier from Media URL, abort on failure
    const matchList = (new RegExp('media/(.*)/giphy.gif')).exec(gifMediaUrlObject.pathname) || []
    if (matchList.length === 0) { return }

    // Construct GIF Direct Download URL from Image Identifier ('https://i.giphy.com/identifier.gif')
    const imageId = matchList[1]
    const gifDirectUrl = `https://i.giphy.com/${imageId}.gif`

    // Status
    console.info('GIF Direct Download Image URL', gifDirectUrl)

    // Return
    return gifDirectUrl
}

/**
 * Get GIF Title
 * @return {String|void} - GIF Image URL
 */
let getGifTitle = () => {
    console.debug('getGifTitle')

    // Lookup Schema
    const schemaObject = getSchema()

    // Return
    return schemaObject.headline
}

/**
 * Add a button in menu on  the right hand side
 * @param {Element} referenceElement - Reference Menu Element
 * @param {String} targetUrl - Target URL for Download
 * @param {String} fileName - Downloaded Filename
 * @param {String} label - Button label
 */
let addMenuButton = (referenceElement, targetUrl, fileName, label) => {
    console.debug('addMenuButton')

    // Create Button: Duplicate previous menu button
    const buttonElement = referenceElement.cloneNode(true)
    referenceElement.parentElement.appendChild(buttonElement)

    // Create Icon
    const iconElement = buttonElement.querySelector('i')
    iconElement.style.float = 'left'
    iconElement.style.background = 'no-repeat center/80% url("https://raw.githubusercontent.com/google/material-design-icons/master/action/2x_web/ic_get_app_white_36dp.png")'
    iconElement.style.animation = 'none'

    // Create Link
    const anchorElement = document.createElement('a')
    anchorElement.target = '_blank'
    anchorElement.href = '#'
    anchorElement.rel = 'noopener noreferrer'
    anchorElement.type = 'image/gif'
    anchorElement.style.display = 'block'
    anchorElement.style.float = 'left'
    buttonElement.appendChild(anchorElement)

    // Create Label
    const textElement = buttonElement.querySelector('span')
    anchorElement.style.color = 'rgb(240, 22, 196)'
    textElement.textContent = label
    anchorElement.appendChild(textElement)

    // Register Download Button Events
    anchorElement.onclick = (event) => {
        // Cancel regular download
        event.preventDefault()

        // Status
        console.info('Downloading:', targetUrl, 'to:', fileName)

        // Start download
        saveAs(targetUrl, fileName, (error) => {
            // Error
            if (error) {
                console.error(error)
                return
            }

            // Status
            console.info('Complete:', targetUrl, 'to:', fileName)
        })
    }
}


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

    // Wait for default right-hand side menu (Favorite / Copy link / Media / Embed)
    onElementReady('.gif-detail-page > div > div:nth-child(4) > div > div:nth-child(2) > div > div:nth-child(2) > div:nth-child(2) > div:last-child', false, (element) => {

        const gifImageUrl = getGifImageUrl()
        const gifTitle = getGifTitle()

        if (!gifImageUrl || !gifTitle) {
            console.warning('Could not find GIF Direct Download URL, aborting.')
            return
        }

        // Add download button to menu
        addMenuButton(element, gifImageUrl, `${gifTitle}.gif`, 'Download GIF')
    })
}

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

    init()
})