Skip to content

Commit

Permalink
RealMAX - Faliure
Browse files Browse the repository at this point in the history
  • Loading branch information
Inrixia committed Jun 20, 2024
1 parent c9e59ad commit dd53d87
Show file tree
Hide file tree
Showing 15 changed files with 172 additions and 71 deletions.
26 changes: 26 additions & 0 deletions lib/Caches/AlbumCache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { actions, store } from "@neptune";
import type { Album } from "neptune-types/tidal";
import { interceptPromise } from "../intercept/interceptPromise";
import { undefinedWarn } from "../undefinedError";

export class AlbumCache {
private static readonly _cache: Record<number, Album> = {};
public static async get(albumId?: number) {
if (albumId === undefined) return undefined;

let mediaItem = this._cache[albumId];
if (mediaItem !== undefined) return mediaItem;

const mediaItems: Record<number, Album> = store.getState().content.albums;
for (const itemId in mediaItems) this._cache[itemId] = mediaItems[itemId];

if (this._cache[albumId] === undefined) {
const album = await interceptPromise(() => actions.content.loadAlbum({ albumId }), ["content/LOAD_ALBUM_SUCCESS"], [])
.then((res) => <Album>res?.[0].album)
.catch(undefinedWarn("AlbumCache.get"));
if (album !== undefined) this._cache[albumId] = album;
}

return this._cache[albumId];
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { actions } from "@neptune";
import { ItemId, TrackItem, Album } from "neptune-types/tidal";
import { interceptPromise } from "../interceptPromise";
import { interceptPromise } from "../intercept/interceptPromise";
import { MusicBrainz } from "../musicbrainzApi";
import { Recording } from "../musicbrainzApi/types/Recording";
import { Release } from "../musicbrainzApi/types/UPCData";
import { TrackItemCache } from "./TrackItemCache";
import { undefinedWarn } from "../undefinedError";
import { AlbumCache } from "./AlbumCache";

export class ExtendedTrackItem {
public readonly trackId: ItemId;
Expand Down Expand Up @@ -38,9 +39,7 @@ export class ExtendedTrackItem {
}
public async album(): Promise<Album | undefined> {
if (this._album !== undefined) return this._album;

actions.content.loadAlbum({ albumId: this.trackItem()?.album?.id! });
return (this._album = await interceptPromise(["content/LOAD_ALBUM_SUCCESS"], []).then((res) => <Album>res?.[0].album));
return (this._album = await AlbumCache.get(this.trackItem()?.album?.id));
}
public async recording(): Promise<Recording | undefined> {
if (this._recording !== undefined) return this._recording;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { store } from "@neptune";
import { TrackItem, MediaItem, ItemId } from "neptune-types/tidal";
import { undefinedWarn } from "../undefinedError";
import type { TrackItem, MediaItem, ItemId } from "neptune-types/tidal";

export class TrackItemCache {
private static readonly _cache: Record<ItemId, TrackItem> = {};
public static get(trackId?: ItemId) {
Expand All @@ -16,7 +16,6 @@ export class TrackItemCache {
this._cache[itemId] = item;
}

mediaItem = this._cache[trackId];
if (mediaItem !== undefined) return mediaItem;
return this._cache[trackId];
}
}
File renamed without changes.
19 changes: 17 additions & 2 deletions lib/interceptPromise.ts → lib/intercept/interceptPromise.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,31 @@
import { intercept } from "@neptune";
import { ActionType, CallbackFunction, PayloadActionTypeTuple } from "neptune-types/api/intercept";

export const interceptPromise = <RESAT extends ActionType, REJAT extends ActionType>(resActionType: RESAT[], rejActionType: REJAT[], timeoutMs = 5000): Promise<PayloadActionTypeTuple<RESAT>> => {
export const interceptPromise = <RESAT extends ActionType, REJAT extends ActionType>(
trigger: Function,
resActionType: RESAT[],
rejActionType: REJAT[],
{ timeoutMs, cancel }: { timeoutMs?: number; cancel?: boolean } = {}
): Promise<PayloadActionTypeTuple<RESAT>> => {
timeoutMs ??= 5000;
cancel ??= false;
let res: CallbackFunction<RESAT>;
let rej: (err: PayloadActionTypeTuple<REJAT> | string) => void;
const p = new Promise<PayloadActionTypeTuple<RESAT>>((_res, _rej) => {
res = _res;
rej = _rej;
});
const unloadRes = intercept(resActionType, res!, true);
const unloadRes = intercept(
resActionType,
(payload) => {
res(payload);
if (cancel) return true;
},
true
);
const unloadRej = intercept(rejActionType, rej!, true);
const timeout = setTimeout(() => rej(`${rejActionType}_TIMEOUT`), timeoutMs);
trigger();
return p.finally(() => {
clearTimeout(timeout);
unloadRes();
Expand Down
4 changes: 2 additions & 2 deletions lib/tidalDevApi/isrc.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ISRCResponse, TrackData } from "./types/ISRC";
import { Datum, ISRCResponse, Resource } from "./types/ISRC";
import { requestStream, rejectNotOk, toJson } from "../fetch";
import { getToken } from "./auth";

Expand All @@ -18,7 +18,7 @@ export const fetchIsrc = async (isrc: string, options?: ISRCOptions) => {
.then(toJson<ISRCResponse>);
};

export async function* fetchIsrcIterable(isrc: string): AsyncIterable<TrackData> {
export async function* fetchIsrcIterable(isrc: string): AsyncIterable<Datum> {
let offset = 0;
const limit = 100;
while (true) {
Expand Down
60 changes: 52 additions & 8 deletions lib/tidalDevApi/types/ISRC.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,58 @@
import type { TrackItem } from "neptune-types/tidal";
export interface Datum {
resource?: Resource;
id?: string;
status?: number;
message?: string;
}

export type TrackData = {
resource: TrackItem;
id: string;
status: number;
message: string;
};
export interface Resource {
artifactType?: string;
id?: string;
title?: string;
artists?: Artist[];
album?: Album;
duration?: number;
trackNumber?: number;
volumeNumber?: number;
isrc?: string;
copyright?: string;
mediaMetadata?: MediaMetadata;
properties?: Record<string, unknown>;
tidalUrl?: string;
}

interface Album {
id?: string;
title?: string;
imageCover?: ImageCover[];
videoCover?: any[];
}

interface ImageCover {
url?: string;
width?: number;
height?: number;
}

interface Artist {
id?: string;
name?: string;
picture?: ImageCover[];
main?: boolean;
}

interface MediaMetadata {
tags?: string[];
}

export interface Metadata {
requested?: number;
success?: number;
failure?: number;
}

export type ISRCResponse = {
data: TrackData[];
data: Datum[];
metadata: {
requested: number;
success: number;
Expand Down
2 changes: 1 addition & 1 deletion plugins/LastFM/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export { Settings } from "./Settings";
// @ts-expect-error Remove this when types are available
import { storage } from "@plugin";
import { undefinedWarn } from "../../../lib/undefinedError";
import { ExtendedTrackItem } from "../../../lib/TrackCache/ExtendedTrackItem";
import { ExtendedTrackItem } from "../../../lib/Caches/ExtendedTrackItem";

let totalPlayTime = 0;
let lastPlayStart: number | null = null;
Expand Down
67 changes: 38 additions & 29 deletions plugins/RealMAX/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { ItemId, TrackItem } from "neptune-types/tidal";
import { TrackItemCache } from "../../../lib/TrackCache/TrackItemCache";
import { TrackItemCache } from "../../../lib/Caches/TrackItemCache";
import { fetchIsrcIterable } from "../../../lib/tidalDevApi/isrc";
import { actions, intercept, store } from "@neptune";
import { PlaybackContext } from "../../../lib/AudioQualityTypes";
import { ExtendedTrackItem } from "../../../lib/TrackCache/ExtendedTrackItem";
import { ExtendedTrackItem } from "../../../lib/Caches/ExtendedTrackItem";
import { Resource } from "../../../lib/tidalDevApi/types/ISRC";
import { interceptPromise } from "../../../lib/intercept/interceptPromise";
import { debounce } from "../../../lib/debounce";

const hasHiRes = (trackItem: TrackItem) => {
const tags = trackItem.mediaMetadata?.tags;
Expand All @@ -12,48 +15,54 @@ const hasHiRes = (trackItem: TrackItem) => {
};

class MaxTrack {
private static readonly _idMap: Record<ItemId, Promise<ItemId | false>> = {};
public static async getMaxId(itemId: ItemId | undefined): Promise<ItemId | false> {
if (itemId === undefined) return false;
private static readonly _idMap: Record<ItemId, Promise<Resource | undefined>> = {};
public static async fastCacheMaxId(itemId: ItemId): Promise<Resource | undefined> {
if (itemId === undefined) return undefined;
return MaxTrack._idMap[itemId];
}
public static async getMaxId(itemId: ItemId | undefined): Promise<Resource | undefined> {
if (itemId === undefined) return undefined;

const idMapping = MaxTrack._idMap[itemId];
if (idMapping !== undefined) return idMapping;

const extTrackItem = await ExtendedTrackItem.get(itemId);
const trackItem = extTrackItem?.trackItem();
if (trackItem !== undefined && hasHiRes(trackItem)) return false;
if (trackItem !== undefined && hasHiRes(trackItem)) return undefined;

const isrcs = await extTrackItem?.isrcs();
if (isrcs === undefined) return (this._idMap[itemId] = Promise.resolve(false));
if (isrcs === undefined) return (this._idMap[itemId] = Promise.resolve(undefined));

return (this._idMap[itemId] = (async () => {
for (const isrc of isrcs) {
for await (const { resource } of fetchIsrcIterable(isrc)) {
if (resource?.id !== undefined && hasHiRes(resource)) return resource.id;
if (resource?.id !== undefined && hasHiRes(<TrackItem>resource)) {
if (resource.artifactType !== "track") continue;
const maxTrackItem = TrackItemCache.get(resource?.id);
if (maxTrackItem !== undefined && !hasHiRes(maxTrackItem)) continue;
else return resource;
}
}
}
return false;
return undefined;
})());
}
}
// @ts-ignore intercept doesnt like async functions
const unloadPlay = intercept("playbackControls/MEDIA_PRODUCT_TRANSITION", async ([{ playbackContext }]: [{ playbackContext: PlaybackContext }]) => {
const { elements, currentIndex } = store.getState().playQueue;
for (let index = currentIndex; index < Math.min(elements.length - 1, currentIndex + 5); index++) {
const mediaItemId = elements[index]?.mediaItemId;
if (mediaItemId === undefined) return;
const maxId = await MaxTrack.getMaxId(mediaItemId);
const maxInjected = elements[index + 1]?.mediaItemId === maxId;
if (maxInjected) {
actions.playQueue.removeAtIndex({ index: index + 1 });
actions.playQueue.movePrevious();
} else if (index === currentIndex && maxId !== false) {
actions.playQueue.addNext(<any>{ mediaItemIds: [maxId] });
actions.playQueue.moveNext();
}
}
});

export const onUnload = () => {
unloadPlay();
};
// export const onUnload = intercept(
// "playbackControls/TIME_UPDATE",
// debounce(async () => {
// const { elements, currentIndex } = store.getState().playQueue;
// const queueId = elements[currentIndex]?.mediaItemId;

// const maxItem = await MaxTrack.getMaxId(queueId);
// if (maxItem !== undefined) {
// actions.playQueue.clearActiveItems();
// await interceptPromise(() => actions.content.fetchAndPlayMediaItem({ itemId: maxItem?.id!, itemType: "track", sourceContext: { type: "user" } }), ["playbackControls/MEDIA_PRODUCT_TRANSITION"], []);
// const mediaItemIds = elements.slice(currentIndex + 1).map(({ mediaItemId }) => mediaItemId);
// actions.playQueue.addMediaItemsToQueue({ mediaItemIds, position: "next", options: { overwritePlayQueue: true }, sourceContext: { type: "user" } });
// }
// // Preload next
// await MaxTrack.getMaxId(elements[currentIndex + 1]?.mediaItemId);
// }, 125)
// );
14 changes: 10 additions & 4 deletions plugins/Shazam/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ init();

import { actions, store } from "@neptune";
import { DecodedSignature } from "shazamio-core";
import { interceptPromise } from "../../../lib/interceptPromise";
import { interceptPromise } from "../../../lib/intercept/interceptPromise";
import { messageError, messageWarn, messageInfo } from "../../../lib/messageLogging";
import { fetchShazamData } from "./shazamApi/fetch";

Expand All @@ -15,8 +15,11 @@ import { fetchIsrc } from "../../../lib/tidalDevApi/isrc";
export { Settings } from "./Settings";

const addToPlaylist = async (playlistUUID: string, mediaItemIdsToAdd: string[]) => {
actions.content.addMediaItemsToPlaylist({ mediaItemIdsToAdd, onDupes: "SKIP", playlistUUID });
await interceptPromise(["etag/SET_PLAYLIST_ETAG", "content/ADD_MEDIA_ITEMS_TO_PLAYLIST_SUCCESS"], ["content/ADD_MEDIA_ITEMS_TO_PLAYLIST_FAIL"]);
await interceptPromise(
() => actions.content.addMediaItemsToPlaylist({ mediaItemIdsToAdd, onDupes: "SKIP", playlistUUID }),
["etag/SET_PLAYLIST_ETAG", "content/ADD_MEDIA_ITEMS_TO_PLAYLIST_SUCCESS"],
["content/ADD_MEDIA_ITEMS_TO_PLAYLIST_FAIL"]
);
actions.content.loadListItemsPage({ listName: `playlists/${playlistUUID}`, listType: "mediaItems", reset: false });
setTimeout(() => actions.content.loadListItemsPage({ listName: `playlists/${playlistUUID}`, listType: "mediaItems", reset: true }), 1000);
};
Expand Down Expand Up @@ -58,7 +61,10 @@ const handleDrop = async (event: DragEvent) => {
const ids = (isrcData?.data ?? []).map((track) => track.id);
if (ids.length > 0) {
messageInfo(`Adding ${trackName} to playlist`);
await addToPlaylist(playlistUUID, ids);
await addToPlaylist(
playlistUUID,
ids.filter((id) => id !== undefined)
);
} else {
console.log("[SHAZAM!]", shazamData);
messageWarn(`Track ${trackName} is not avalible in Tidal`);
Expand Down
13 changes: 5 additions & 8 deletions plugins/SongDownloader/src/addMetadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import { ExtendedPlaybackInfoWithBytes } from "../../../lib/trackBytes/download"
import { rejectNotOk, requestStream, toBuffer } from "../../../lib/fetch";
import { ManifestMimeType } from "../../../lib/trackBytes/getPlaybackInfo";
import { actions } from "@neptune";
import { interceptPromise } from "../../../lib/interceptPromise";
import { interceptPromise } from "../../../lib/intercept/interceptPromise";

import { type FlacTagMap, PictureType, createFlacTagsBuffer } from "./flac-tagger";
import { AlbumCache } from "../../../lib/Caches/AlbumCache";

export async function addMetadata(trackInfo: ExtendedPlaybackInfoWithBytes, track: TrackItem) {
if (trackInfo.manifestMimeType === ManifestMimeType.Tidal) {
Expand All @@ -32,9 +33,8 @@ async function makeTags(track: TrackItem) {
if (track.artist?.name) tagMap.artist = track.artist.name;
tagMap.performer = (track.artists ?? []).map(({ name }) => name).filter((name) => name !== undefined);

if (track.id) {
actions.content.loadItemLyrics({ itemId: track.id, itemType: "track" });
const lyrics = await interceptPromise(["content/LOAD_ITEM_LYRICS_SUCCESS"], ["content/LOAD_ITEM_LYRICS_FAIL"])
if (track.id !== undefined) {
const lyrics = await interceptPromise(() => actions.content.loadItemLyrics({ itemId: track.id!, itemType: "track" }), ["content/LOAD_ITEM_LYRICS_SUCCESS"], ["content/LOAD_ITEM_LYRICS_FAIL"])
.catch(() => undefined)
.then((res) => res?.[0]);
if (lyrics?.lyrics !== undefined) tagMap.lyrics = lyrics.lyrics;
Expand All @@ -43,10 +43,7 @@ async function makeTags(track: TrackItem) {
const albumId = track.album?.id;
let cover = track.album?.cover;
if (albumId !== undefined) {
actions.content.loadAlbum({ albumId });
const album = await interceptPromise(["content/LOAD_ALBUM_SUCCESS"], [])
.catch(() => undefined)
.then((res) => res?.[0].album);
const album = await AlbumCache.get(albumId);
if (album !== undefined) {
tagMap.albumArtist = (album.artists ?? []).map(({ name }) => name).filter((name) => name !== undefined);
if (album.genre) tagMap.genres = album.genre;
Expand Down
Loading

0 comments on commit dd53d87

Please sign in to comment.