Instantly share code, notes, and snippets.
Last active
February 11, 2023 17:37
-
Star
(2)
2
You must be signed in to star a gist -
Fork
(0)
0
You must be signed in to fork a gist
-
Save sidneys/13433a0c726895597fb4571405c7ca15 to your computer and use it in GitHub Desktop.
UserScript | GIPHY | Direct GIF Image Download Button
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==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