Twitch clip datetime userscript
A userscript for displaying the actual date & time (relative to local time) of when a Twitch clip was created.
FYI: It only works on URLs that start with https://clips.twitch.tv/
.
This script does not work with URLs that are on the Twitch "channel pages" (https://www.twitch.tv/CHANNEL_NAME_HERE/clip/...
).
This has been added as of v0.5.0.
"Under the hood" the script uses Date.toLocaleString()
to format the date. The format of the date & time may differ from the screenshots below.
Requirements:
- Something like the Violentmonkey extension installed for your browser.
Installation:
- Install a userscript extension (such as Violentmonkey).
- Click on this link when Violentmonkey is installed, and it will prompt you to install the userscript.
Changelog
- v0.5.0 - 2021-12-30
- Script now supports clips on Twitch channel pages (e.g.
https://www.twitch.tv/$channelName/clip/$clipSlug
)- The
@match
rule will sayhttps://www.twitch.tv/*
to support navigating from the channel page to the clip page, otherwise script wouldn't load on regular Twitch browsing.
- The
- Script now supports clips on Twitch channel pages (e.g.
- v0.4.1 - 2021-08-29
- Make script compatible with Tampermonkey again.
- v0.4.0 - 2021-08-29
- Date/time placement was adjusted due to changes in the page on Twitch's end. See screenshots.
Previously I used Tampermonkey, but later switched to Violentmonkey. During my testing, I tested with Violentmonkey and it worked fine. However, if you're currently using Tampermonkey, v0.4.0 does not work. I'm unsure as to why, but I'll try to look into it.- Fixed in v0.4.1
Screenshots:
As of v0.4.0, the timestamp shows up a bit different compared to earlier versions.
Without chat (beginning of clip)
With chat
Main Twitch website
When browsing Twitch channel clips on the main Twitch website.
Only v0.5.0 (December 30th, 2021) and newer
1 | // ==UserScript== |
2 | // @name Twitch Clips - Show date & time |
3 | // @version 0.5.0 |
4 | // @description Displays the actual date & time of when a clip was created, instead of the useless "xxx days/months/weeks ago" |
5 | // @author Decicus |
6 | // @updateURL https://gist.github.com/Decicus/ec4745e680e06cfff5b1fa0a53fcff72/raw/twitch-clips-datetime.user.js |
7 | // @downloadURL https://gist.github.com/Decicus/ec4745e680e06cfff5b1fa0a53fcff72/raw/twitch-clips-datetime.user.js |
8 | // @homepageURL https://gist.github.com/Decicus/ec4745e680e06cfff5b1fa0a53fcff72 |
9 | // @icon https://i.alex.lol/2021-08-29_PmO4zo.png |
10 | // @match https://clips.twitch.tv/* |
11 | // @match https://www.twitch.tv/* |
12 | // @license MIT |
13 | // ==/UserScript== |
14 | |
15 | /** |
16 | * Insert timestamp for the `clips.twitch.tv` website. |
17 | */ |
18 | function clipsSubdomain(createdAt, dateAndTime) |
19 | { |
20 | const box = document.querySelector('.clips-sidebar-info'); |
21 | const layoutClass = box.classList[0]; |
22 | |
23 | const textElement = document.querySelector('span[class*="CoreText"]'); |
24 | const textClass = textElement.classList[0]; |
25 | |
26 | const element = document.createElement('div'); |
27 | element.className = layoutClass; |
28 | element.setAttribute('style', 'text-align: center; margin-top: 1em;'); |
29 | element.innerHTML = `<span class="${textClass}" title="${createdAt}"><strong>Clip created:</strong> ${dateAndTime}</span>`; |
30 | |
31 | box.insertAdjacentElement('afterbegin', element); |
32 | } |
33 | |
34 | /** |
35 | * Insert timestamp for the `www.twitch.tv` (main) website. |
36 | * E.g. https://www.twitch.tv/$channelName/clip/$clipSlug |
37 | */ |
38 | function clipsMainWebsite(createdAt, dateAndTime) |
39 | { |
40 | const timestampBar = document.querySelector('.timestamp-metadata__bar'); |
41 | const parent = timestampBar.parentElement; |
42 | |
43 | // Use for text styling |
44 | const textElement = document.querySelector('p[class*="CoreText"]'); |
45 | const textClass = textElement.classList[0]; |
46 | |
47 | const element = document.createElement('p'); |
48 | element.className = textClass; |
49 | element.innerHTML = `- Clip created: <strong>${dateAndTime}</strong>`; |
50 | element.setAttribute('title', createdAt); |
51 | element.setAttribute('style', 'margin-left: 0.25em;'); |
52 | |
53 | parent.insertAdjacentElement('beforeend', element); |
54 | } |
55 | |
56 | async function fetchClip() { |
57 | const url = new URL(window.location.href); |
58 | const pathFragments = url.pathname.split('/'); |
59 | const clipSlug = pathFragments[pathFragments.length - 1]; |
60 | |
61 | const slug = clipSlug.match(/([A-z0-9-_]+)/m)[1]; |
62 | |
63 | if (!slug) { |
64 | return; |
65 | } |
66 | |
67 | const response = await fetch(`https://api.twitch.tv/kraken/clips/${slug}`, { |
68 | headers: { |
69 | Accept: 'application/vnd.twitchtv.v5+json', |
70 | 'Client-ID': 'zs377ogpzz01ogfx26pvbddx9jodg1', |
71 | }, |
72 | }); |
73 | |
74 | const data = await response.json(); |
75 | |
76 | if (!data.created_at) { |
77 | return; |
78 | } |
79 | |
80 | const createdAt = data.created_at; |
81 | const created = new Date(createdAt); |
82 | const dateAndTime = created.toLocaleString(); |
83 | |
84 | if (url.hostname === 'clips.twitch.tv') { |
85 | clipsSubdomain(createdAt, dateAndTime); |
86 | return; |
87 | } |
88 | |
89 | clipsMainWebsite(createdAt, dateAndTime); |
90 | } |
91 | |
92 | /** |
93 | * Observe the DOM until we find the element we're interested in. |
94 | * Once complete, disconnect the observer and call the `fetchClip()` function which actually inserts the |
95 | * timestamp into the DOM. |
96 | */ |
97 | function observerHandler(mutations, observer) |
98 | { |
99 | // clips.twitch.tv |
100 | const clipsInfo = document.querySelector('.clips-chat-info span[class*="CoreText"]'); |
101 | |
102 | // www.twitch.tv |
103 | const timestampBar = document.querySelector('.timestamp-metadata__bar'); |
104 | |
105 | if (!clipsInfo && !timestampBar) { |
106 | return; |
107 | } |
108 | |
109 | console.log('Clips page', clipsInfo !== null); |
110 | console.log('Main website', timestampBar !== null); |
111 | |
112 | fetchClip(); |
113 | observer.disconnect(); |
114 | } |
115 | |
116 | const observer = new MutationObserver(observerHandler); |
117 | observer.observe(document, { |
118 | attributes: false, |
119 | childList: true, |
120 | characterData: false, |
121 | subtree: true, |
122 | }); |