Skip to content

Commit

Permalink
Improve qualities & add Low quality tag
Browse files Browse the repository at this point in the history
  • Loading branch information
Inrixia committed Apr 21, 2024
1 parent 5420e12 commit 924881a
Show file tree
Hide file tree
Showing 7 changed files with 55 additions and 48 deletions.
47 changes: 21 additions & 26 deletions lib/AudioQuality.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,33 @@
import { ValueOf } from "@inrixia/helpers/ts";

export type PlaybackContext = {
actualAudioQuality: AudioQuality;
actualProductId: number;
};

export type AudioQuality = ValueOf<typeof PlaybackContextAudioQuality>;

export enum PlaybackContextAudioQuality {
export enum AudioQuality {
HiRes = "HI_RES_LOSSLESS",
MQA = "HI_RES",
High = "LOSSLESS",
Low = "HIGH",
Lowest = "LOW",
}

export enum MediaMetadataQuality {
High = "LOSSLESS",
MQA = "MQA",
export enum QualityTag {
HiRes = "HIRES_LOSSLESS",
Atmos = "DOLBY_ATMOS",
Sony360 = "SONY_360RA",
MQA = "MQA",
High = "LOSSLESS",
DolbyAtmos = "DOLBY_ATMOS",
Sony630 = "SONY_360RA",
}

export type PlaybackContext = {
actualAudioQuality: AudioQuality;
actualProductId: number;
};

export const QualityMeta = {
[MediaMetadataQuality.MQA]: { className: "quality-tag", textContent: "MQA", color: "#F9BA7A" },
[MediaMetadataQuality.HiRes]: { className: "quality-tag", textContent: "HiRes", color: "#ffd432" },
[MediaMetadataQuality.Atmos]: { className: "quality-tag", textContent: "Atmos", color: "#0052a3" },
[MediaMetadataQuality.Sony360]: undefined,
[MediaMetadataQuality.High]: undefined,
[QualityTag.MQA]: { className: "quality-tag", textContent: "MQA", color: "#F9BA7A" },
[QualityTag.HiRes]: { className: "quality-tag", textContent: "HiRes", color: "#ffd432" },
[QualityTag.DolbyAtmos]: { className: "quality-tag", textContent: "Atmos", color: "#0052a3" },
[QualityTag.Sony630]: undefined,
[QualityTag.High]: undefined,
} as const;

export const validQualities = Object.values(PlaybackContextAudioQuality);
export const validQualitiesSet = new Set(validQualities);

export const audioQualities = Object.values(AudioQuality);
// Dont show MQA as a option as if HiRes is avalible itl always be served even if MQA is requested.
export const validQualitiesSettings = [PlaybackContextAudioQuality.HiRes, PlaybackContextAudioQuality.High];
export const validQualitiesSettings: AudioQuality[] = [AudioQuality.HiRes, AudioQuality.High];

export const AudioQualityInverse = Object.fromEntries(Object.entries(PlaybackContextAudioQuality).map(([key, value]) => [value, key]));
export const AudioQualityInverse = Object.fromEntries(Object.entries(AudioQuality).map(([key, value]) => [value, key]));
6 changes: 3 additions & 3 deletions lib/download.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { getStreamInfo } from "./getStreamInfo";
import { decryptBuffer } from "./decryptBuffer";
import { OnProgress, fetchy } from "./fetchy";
import { saveFile } from "./saveFile";
import { AudioQualityInverse, PlaybackContextAudioQuality } from "./AudioQuality";
import { AudioQualityInverse, AudioQuality } from "./AudioQuality";

export const downloadSong = async (songId: number, fileName: string, quality: PlaybackContextAudioQuality, onProgress: OnProgress) => {
export const downloadSong = async (songId: number, fileName: string, quality: AudioQuality, onProgress: OnProgress) => {
const streamInfo = await getStreamInfo(songId, quality);

const { key, nonce } = streamInfo.cryptKey;
Expand All @@ -19,7 +19,7 @@ export const downloadSong = async (songId: number, fileName: string, quality: Pl
saveFile(new Blob([decodedBuffer], { type: "application/octet-stream" }), `${fileName} [${AudioQualityInverse[streamInfo.audioQuality]}].flac`);
};

export const downloadBytes = async (songId: number, quality: PlaybackContextAudioQuality, byteRangeStart = 0, byteRangeEnd: number, onProgress: OnProgress) => {
export const downloadBytes = async (songId: number, quality: AudioQuality, byteRangeStart = 0, byteRangeEnd: number, onProgress: OnProgress) => {
const streamInfo = await getStreamInfo(songId, quality);

const { key, nonce } = streamInfo.cryptKey;
Expand Down
6 changes: 3 additions & 3 deletions lib/getStreamInfo.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { decryptKeyId } from "./decryptKeyId";
import { getHeaders } from "./fetchy";
import { validQualitiesSet, validQualities, PlaybackContextAudioQuality } from "./AudioQuality";
import { audioQualities, AudioQuality } from "./AudioQuality";

export const getStreamInfo = async (trackId: number, audioQuality: PlaybackContextAudioQuality) => {
if (!validQualitiesSet.has(audioQuality)) throw new Error(`Cannot get Stream Info! Invalid audio quality: ${audioQuality}, should be one of ${validQualities.join(", ")}`);
export const getStreamInfo = async (trackId: number, audioQuality: AudioQuality) => {
if (!audioQualities.includes(audioQuality)) throw new Error(`Cannot get Stream Info! Invalid audio quality: ${audioQuality}, should be one of ${audioQualities.join(", ")}`);
if (trackId === undefined) throw new Error("Cannot get Stream Info! trackId is missing");

try {
Expand Down
4 changes: 2 additions & 2 deletions plugins/SongDownloader/src/Settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { html } from "@neptune/voby";

// @ts-expect-error Remove this when types are available
import { storage } from "@plugin";
import { AudioQualityInverse, PlaybackContextAudioQuality, validQualitiesSettings } from "../../../lib/AudioQuality";
import { AudioQualityInverse, AudioQuality, validQualitiesSettings } from "../../../lib/AudioQuality";

storage.desiredDownloadQuality ??= PlaybackContextAudioQuality.HiRes;
storage.desiredDownloadQuality ??= AudioQuality.HiRes;
export const Settings = () => html`<div class="settings-section">
<h3 class="settings-header">Download Quality</h3>
<p class="settings-explainer">Select the desired max download quality:</p>
Expand Down
12 changes: 6 additions & 6 deletions plugins/TidalTags/src/streamQualitySelector.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { QualityMeta, MediaMetadataQuality } from "../../../lib/AudioQuality";
import { audioQualities, QualityMeta } from "../../../lib/AudioQuality";
import { downloadBytes } from "../../../lib/download";
import { PlaybackContextAudioQuality, PlaybackContext, validQualitiesSet } from "../../../lib/AudioQuality";
import { AudioQuality, PlaybackContext } from "../../../lib/AudioQuality";

// @ts-expect-error Remove this when types are available
import { storage } from "@plugin";
Expand All @@ -17,7 +17,7 @@ interface ExtendedFormat extends IFormat {
}

const qualityCache = new Map();
const getFLACInfo = (id: number, quality: PlaybackContextAudioQuality) => {
const getFLACInfo = (id: number, quality: AudioQuality) => {
const key = `${id}-${quality}`;

// If a promise for this key is already in the cache, await it
Expand Down Expand Up @@ -66,15 +66,15 @@ export const setStreamQualityIndicator = async () => {
const { actualAudioQuality, actualProductId } = playbackContext;

switch (actualAudioQuality) {
case PlaybackContextAudioQuality.MQA:
qualityElement.style.color = QualityMeta[MediaMetadataQuality.MQA].color;
case AudioQuality.MQA:
qualityElement.style.color = QualityMeta["MQA"].color;
break;
default:
qualityElement.style.color = "";
break;
}

const invalidState = !validQualitiesSet.has(actualAudioQuality) || flacInfoElem === undefined || qualitySelector.parentElement === null;
const invalidState = !audioQualities.includes(actualAudioQuality) || flacInfoElem === undefined || qualitySelector.parentElement === null;
if (!storage.showFLACInfo || invalidState) return removeElems(qualitySelector);

flacInfoElem.textContent = "";
Expand Down
4 changes: 2 additions & 2 deletions plugins/TidalTags/src/style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ appendStyle(`
${settingsCSS}
.quality-tag-container {
display: inline-flex;
height: 20px;
height: 24px;
font-size: 12px;
line-height: 20px;
line-height: 24px;
}
.quality-tag {
justify-content: center;
Expand Down
24 changes: 18 additions & 6 deletions plugins/TidalTags/src/updateTrackElements.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { store } from "@neptune";
// @ts-expect-error Remove this when types are available
import { storage } from "@plugin";
import { QualityMeta, MediaMetadataQuality } from "../../../lib/AudioQuality";
import { AudioQuality, QualityMeta, QualityTag } from "../../../lib/AudioQuality";
import type { MediaItem } from "neptune-types/tidal";

const queryAllAndAttribute = (selector: string) => {
Expand All @@ -24,9 +24,11 @@ export const updateTrackLists = () => {
const mediaItem = mediaItems[+trackId]?.item;
if (mediaItem?.contentType !== "track") continue;

const isLowQuality = mediaItem.audioQuality === AudioQuality.Low || mediaItem.audioQuality === AudioQuality.Lowest;

let trackTags = mediaItem.mediaMetadata?.tags;
if (trackTags === undefined) continue;
if (trackTags.length === 1 && trackTags[0] === MediaMetadataQuality.High) continue;
if (trackTags.length === 1 && trackTags[0] === QualityTag.High && !isLowQuality) continue;

const trackList = trackElem.querySelector(`[data-test="table-row-title"], [data-test="list-item-track"]`);
if (trackList === null) continue;
Expand All @@ -38,18 +40,28 @@ export const updateTrackLists = () => {
span.className = "quality-tag-container";
span.setAttribute("track-id", trackId);

if (trackTags.includes(MediaMetadataQuality.HiRes) && !storage.showAllQualities) trackTags = trackTags.filter((tag) => tag !== MediaMetadataQuality.MQA);
if (isLowQuality) {
const tagElement = document.createElement("span");

tagElement.className = "quality-tag";
tagElement.textContent = "Low";
tagElement.style.color = "#b9b9b9";

span.appendChild(tagElement);
}

if (trackTags.includes(QualityTag.HiRes) && !storage.showAllQualities) trackTags = trackTags.filter((tag) => tag !== QualityTag.MQA);

for (const tag of trackTags) {
if (tag === MediaMetadataQuality.High) continue;
if (!storage.showAtmosQuality && tag === MediaMetadataQuality.Atmos) continue;
if (tag === QualityTag.High) continue;
if (!storage.showAtmosQuality && tag === QualityTag.DolbyAtmos) continue;

const data = QualityMeta[tag];
if (data === undefined) continue;

const tagElement = document.createElement("span");

tagElement.className = data.className;
tagElement.className = "quality-tag";
tagElement.textContent = data.textContent;
tagElement.style.color = data.color;

Expand Down

0 comments on commit 924881a

Please sign in to comment.