Skip to content

Instantly share code, notes, and snippets.

@uhwot
Last active May 30, 2025 04:24
Show Gist options
  • Select an option

  • Save uhwot/fdccd80db1df46b4569ac1b8cb91523b to your computer and use it in GitHub Desktop.

Select an option

Save uhwot/fdccd80db1df46b4569ac1b8cb91523b to your computer and use it in GitHub Desktop.
Userscript which shows format info on Tidal, tested on Violentmonkey
// ==UserScript==
// @name Tidal Formats
// @namespace io.github.uhwot.tidalformats
// @match https://listen.tidal.com/*
// @match https://listen.stage.tidal.com/*
// @grant none
// @version 1.0.9
// @author uh wot
// @icon https://tidal.com/favicon-192x192.png
// @description Shows format info on Tidal
// @run-at document-start
// @homepageURL https://gist.github.com/uhwot/fdccd80db1df46b4569ac1b8cb91523b
// @downloadURL https://gist.github.com/uhwot/fdccd80db1df46b4569ac1b8cb91523b/raw/tidal_formats.user.js
// ==/UserScript==
const TAGS = {
LOSSLESS: { bgcolor: '21feec1a', color: '3fe' },
MQA: { bgcolor: 'ffbe7d1a', color: 'ffbe7d' },
HIRES_LOSSLESS: { bgcolor: 'ffd4321a', color: 'ffd432', text: 'HIRES' },
SONY_360RA: { text: '360' },
DOLBY_ATMOS: { text: 'ATMOS' },
}
function waitForElem(selector) {
return new Promise(resolve => {
let elem = document.body.querySelector(selector)
if (elem) {
resolve(elem);
}
const observer = new MutationObserver(mutations => {
elem = document.body.querySelector(selector)
if (elem) {
resolve(elem);
observer.disconnect();
}
});
observer.observe(document.body, {
childList: true,
subtree: true,
});
});
}
function setAlbumBadges(json) {
const mediaTags = json.rows[0].modules[0].album.mediaMetadata.tags
if (mediaTags.length > 0) {
waitForElem('span[data-test="meta-release-date"].metaItem--TuWeS').then((date) => {
const badgeContainer = date.parentElement
if (badgeContainer.children.length > 1) {
badgeContainer.removeChild(badgeContainer.children[1])
}
for (tag of mediaTags) {
const div = document.createElement('div')
const badge = document.createElement('span')
badge.setAttribute('class', '_wave-badge_1oxl7_1 _wave-badge-text_1oxl7_32 _wave-text-badge-bold_1jtlh_99 _wave-badge-color-default_1oxl7_7')
badge.textContent = tag
let tagData = TAGS[tag]
if (tagData) {
if (tagData.text) {
badge.textContent = tagData.text
}
let style = []
if (tagData.bgcolor) {
style.push(`background-color: #${tagData.bgcolor}`)
}
if (tagData.color) {
style.push(`color: #${tagData.color}`)
}
if (style) {
badge.setAttribute('style', style.join(';'))
}
}
div.appendChild(badge)
badgeContainer.appendChild(div)
}
})
}
}
window.fetch = (function (fetch) {
return async function (url, init) {
let resp = await fetch(url, init)
if (!resp.ok) {
return resp
}
try {
url = new URL(url)
} catch {
return resp
}
if (url.hostname === location.hostname) {
if (url.pathname === '/v1/pages/album') {
const json = await resp.json()
setAlbumBadges(json)
resp = new Response(JSON.stringify(json), resp)
}
}
return resp
}
})(window.fetch);
@zhordon
Copy link

zhordon commented Nov 2, 2024

I'm thinking this may currently require an update. It no longer shows terms like Lossless or High Res just returning High and Max

image

@uhwot
Copy link
Author

uhwot commented Nov 4, 2024

@zhordon should be fixed now

@zhordon
Copy link

zhordon commented Nov 4, 2024

Thanks again for the update!

@zhordon
Copy link

zhordon commented May 30, 2025

I'm thinking this needs another update for 2025. If at all possible could you update it?.
Screenshot 2025-05-29 at 9 38 21 PM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment