// ==UserScript== // @name Twitch Clips - Show date & time // @version 0.5.0 // @description Displays the actual date & time of when a clip was created, instead of the useless "xxx days/months/weeks ago" // @author Decicus // @updateURL https://gist.github.com/Decicus/ec4745e680e06cfff5b1fa0a53fcff72/raw/twitch-clips-datetime.user.js // @downloadURL https://gist.github.com/Decicus/ec4745e680e06cfff5b1fa0a53fcff72/raw/twitch-clips-datetime.user.js // @homepageURL https://gist.github.com/Decicus/ec4745e680e06cfff5b1fa0a53fcff72 // @icon https://i.alex.lol/2021-08-29_PmO4zo.png // @match https://clips.twitch.tv/* // @match https://www.twitch.tv/* // @license MIT // ==/UserScript== /** * Insert timestamp for the `clips.twitch.tv` website. */ function clipsSubdomain(createdAt, dateAndTime) { const box = document.querySelector('.clips-sidebar-info'); const layoutClass = box.classList[0]; const textElement = document.querySelector('span[class*="CoreText"]'); const textClass = textElement.classList[0]; const element = document.createElement('div'); element.className = layoutClass; element.setAttribute('style', 'text-align: center; margin-top: 1em;'); element.innerHTML = `Clip created: ${dateAndTime}`; box.insertAdjacentElement('afterbegin', element); } /** * Insert timestamp for the `www.twitch.tv` (main) website. * E.g. https://www.twitch.tv/$channelName/clip/$clipSlug */ function clipsMainWebsite(createdAt, dateAndTime) { const timestampBar = document.querySelector('.timestamp-metadata__bar'); const parent = timestampBar.parentElement; // Use for text styling const textElement = document.querySelector('p[class*="CoreText"]'); const textClass = textElement.classList[0]; const element = document.createElement('p'); element.className = textClass; element.innerHTML = `- Clip created: ${dateAndTime}`; element.setAttribute('title', createdAt); element.setAttribute('style', 'margin-left: 0.25em;'); parent.insertAdjacentElement('beforeend', element); } async function fetchClip() { const url = new URL(window.location.href); const pathFragments = url.pathname.split('/'); const clipSlug = pathFragments[pathFragments.length - 1]; const slug = clipSlug.match(/([A-z0-9-_]+)/m)[1]; if (!slug) { return; } const response = await fetch(`https://api.twitch.tv/kraken/clips/${slug}`, { headers: { Accept: 'application/vnd.twitchtv.v5+json', 'Client-ID': 'zs377ogpzz01ogfx26pvbddx9jodg1', }, }); const data = await response.json(); if (!data.created_at) { return; } const createdAt = data.created_at; const created = new Date(createdAt); const dateAndTime = created.toLocaleString(); if (url.hostname === 'clips.twitch.tv') { clipsSubdomain(createdAt, dateAndTime); return; } clipsMainWebsite(createdAt, dateAndTime); } /** * Observe the DOM until we find the element we're interested in. * Once complete, disconnect the observer and call the `fetchClip()` function which actually inserts the * timestamp into the DOM. */ function observerHandler(mutations, observer) { // clips.twitch.tv const clipsInfo = document.querySelector('.clips-chat-info span[class*="CoreText"]'); // www.twitch.tv const timestampBar = document.querySelector('.timestamp-metadata__bar'); if (!clipsInfo && !timestampBar) { return; } console.log('Clips page', clipsInfo !== null); console.log('Main website', timestampBar !== null); fetchClip(); observer.disconnect(); } const observer = new MutationObserver(observerHandler); observer.observe(document, { attributes: false, childList: true, characterData: false, subtree: true, });