Last active 1715299225

A userscript for displaying the actual date & time (relative to local time) of when a Twitch clip was created.

Revision 7eb3097219f4a57cf57ef823b0c72e4b680a39ae

README.md Raw

Twitch clip datetime userscript

A userscript for displaying the actual date & time (relative to local time) of when a Twitch clip (and video) 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

  1. Install a userscript extension (such as Violentmonkey).
  2. Click on this link when Violentmonkey is installed, and it will prompt you to install the userscript.

Support / Requests

If you have any requests or suggestions, feel free to join my Discord server for all my projects.

Changelog

  • v0.7.0 - 2024-05-09
    • Fixed the script so it now supports the new clip.twitch.tv layout.
      • Since they now use a similar layout as the main Twitch website, the position of the timestamp has been moved below the player. See screenshots at the bottom of this README.
  • v0.6.0 - 2022-01-03
    • Added support for Twitch videos (VODs, highlights)
    • Switched API to use the new Twitch API (Helix) via an "API proxy" I host using Cloudflare Workers.
    • The observer should no longer disconnect after the first timestamp, allowing browsing to other videos/clips to work.
  • v0.5.1 - 2021-12-30
    • Minor bug where it tried to fetch the date & time of a clip when watching highlights/VODs.
      • At some point I'll update the script to support VODs/highlights, but for now I'm sticking to clips.
  • 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 say https://www.twitch.tv/* to support navigating from the channel page to the clip page, otherwise script wouldn't load on regular Twitch browsing.
  • 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.
As of v0.7.0, the script was updated to support the new clips.twitch.tv layout. The timestamp was moved to be below the video player.

On clips.twitch.tv

Screenshot of userscript on clips.twitch.tv

Main Twitch website

When browsing Twitch channel clips on the main Twitch website.
Only v0.5.0 (December 30th, 2021) and newer

Screenshot of userscript on main Twitch website

As of v0.6.0 (January 3rd, 2022), the script also works with Twitch videos (VODs, highlights).

twitch-clips-datetime.user.js Raw
1// ==UserScript==
2// @name Twitch Clips - Show date & time
3// @version 0.7.0
4// @description Displays the actual date & time of when a clip (or video/VOD/highlight) 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 */
18function clipsSubdomain(createdAt, dateAndTime)
19{
20 // The new layout for clips.twitch.tv (as of May 2024) uses a similar layout to the main website,
21 // so this has been simplified until they change it again.
22 insertMainWebsite(createdAt, dateAndTime, false);
23}
24
25/**
26 * Insert timestamp for the `www.twitch.tv` (main) website.
27 * E.g. https://www.twitch.tv/$channelName/clip/$clipSlug
28 * Or: https://www.twitch.tv/videos/$videoId
29 */
30function insertMainWebsite(createdAt, dateAndTime, isVideo)
31{
32 const timestampBar = document.querySelector('.timestamp-metadata__bar');
33 const parent = timestampBar.parentElement;
34
35 // Use for text styling
36 const textElement = document.querySelector('p[class*="CoreText"]');
37 const textClass = textElement.classList[0];
38
39 let element = document.querySelector('#twitch-datetime-script');
40 let newElement = false;
41 if (!element) {
42 element = document.createElement('p');
43 element.className = textClass;
44 element.setAttribute('style', 'margin-left: 0.25em;');
45 element.setAttribute('id', 'twitch-datetime-script');
46
47 newElement = true;
48 }
49
50 element.innerHTML = `- ${isVideo ? 'Video' : 'Clip'} created: <strong>${dateAndTime}</strong>`;
51 element.setAttribute('title', createdAt);
52
53 if (!newElement) {
54 return;
55 }
56
57 parent.insertAdjacentElement('beforeend', element);
58}
59
60/**
61 * Get the timestamp of a clip or video.
62 *
63 * @param {string} id The id of the clip or video.
64 * @param {string} type Type: `clip` or `video`
65 * @returns {Promise<{createdAt: string, dateAndTime: string}>}
66 */
67async function getTimestamp(id, type)
68{
69 if (!type) {
70 type = 'clip';
71 }
72
73 const response = await fetch(`https://twitch-api-proxy.cactus.workers.dev/timestamps/${type}?id=${id}`);
74
75 const data = await response.json();
76
77 if (!data.created_at) {
78 return;
79 }
80
81 const createdAt = data.created_at;
82 const created = new Date(createdAt);
83 const dateAndTime = created.toLocaleString();
84
85 return {createdAt, dateAndTime};
86}
87
88/**
89 * Fetch clip information via getTimestamp() and insert accordingly.
90 *
91 * @param {URL} url
92 * @returns {void}
93 */
94async function fetchClip(url) {
95 const pathFragments = url.pathname.split('/');
96 const clipSlug = pathFragments[pathFragments.length - 1];
97
98 const slug = clipSlug.match(/([A-z0-9-_]+)/m)[1];
99
100 if (!slug) {
101 return;
102 }
103
104 const { createdAt, dateAndTime } = await getTimestamp(slug, 'clip');
105
106 if (url.hostname === 'clips.twitch.tv') {
107 clipsSubdomain(createdAt, dateAndTime);
108 return;
109 }
110
111 insertMainWebsite(createdAt, dateAndTime, false);
112}
113
114/**
115 * Fetch video information via getTimestamp() and insert accordingly.
116 *
117 * @param {URL} url
118 * @returns {void}
119 */
120async function fetchVideo(url)
121{
122 const pathFragments = url.pathname.split('/');
123 const videoFragment = pathFragments[pathFragments.length - 1];
124
125 const videoId = videoFragment.match(/(^\d+$)/m)[1];
126
127 if (!videoId) {
128 return;
129 }
130
131 const { createdAt, dateAndTime } = await getTimestamp(videoId, 'video');
132 insertMainWebsite(createdAt, dateAndTime, true);
133}
134
135/**
136 * Observe the DOM until we find the element we're interested in.
137 * Once complete, disconnect the observer and call the `fetchClip()` function which actually inserts the
138 * timestamp into the DOM.
139 */
140let checkedUrl = null;
141function observerHandler(mutations, observer)
142{
143 // clips.twitch.tv
144 const clipsInfo = document.querySelector('.clips-chat-info span[class*="CoreText"]');
145
146 // www.twitch.tv
147 const timestampBar = document.querySelector('.timestamp-metadata__bar');
148
149 if (!clipsInfo && !timestampBar) {
150 return;
151 }
152
153 const urlString = window.location.href;
154 if (urlString === checkedUrl) {
155 return;
156 }
157 checkedUrl = urlString;
158
159 console.log('Clips page', clipsInfo !== null);
160 console.log('Main website', timestampBar !== null);
161
162 const url = new URL(urlString);
163 if (url.hostname === 'www.twitch.tv' && url.pathname.includes('/videos/')) {
164 fetchVideo(url);
165 }
166 else {
167 fetchClip(url);
168 }
169}
170
171const observer = new MutationObserver(observerHandler);
172observer.observe(document, {
173 attributes: false,
174 childList: true,
175 characterData: false,
176 subtree: true,
177});