Skip to content

Instantly share code, notes, and snippets.

@sidneys
Last active February 11, 2023 17:37
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sidneys/13433a0c726895597fb4571405c7ca15 to your computer and use it in GitHub Desktop.
Save sidneys/13433a0c726895597fb4571405c7ca15 to your computer and use it in GitHub Desktop.
UserScript | GIPHY | Direct GIF Image Download Button
// ==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()
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment