-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Replace Track.artist
and Track.extraArtists
with Track.artists
#1681
base: master
Are you sure you want to change the base?
Conversation
Thanks for contributing this. Is it ready for review? |
Yes. Please let me know when you find something non-functional. Edit: |
// @ts-expect-error NuclearMeta is not a descendant of Track, but Track.artists should be available here anyway | ||
'xesam:artist': track.artists ?? [track.artist] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NuclearMeta should be updated the same way. I'm afraid this is also going to need that:
- Scanned local library tracks will need to be migrated on first run
- Scanner will need to return data in this new format
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I adjusted the obvious bits, although blind (no manual testing).
As written in the Discord thread and as I have no usage experience with that part of the program, I think it's better you take this part.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Line 203 should be updated to artists
as well, otherwise it crashes on start.
function getDownloadsBackwardsCompatible(): Download[] { | ||
const downloads: Download[] = store.get('downloads'); | ||
|
||
downloads.forEach(download => { | ||
// @ts-expect-error For backwards compatibility we're trying to parse an invalid field | ||
if (download.track.artists || !download.track.artist) { | ||
return; | ||
} | ||
|
||
// @ts-expect-error For backwards compatibility we're trying to parse an invalid field | ||
if (download.track.artist) { | ||
// @ts-expect-error For backwards compatibility we're trying to parse an invalid field | ||
download.track.artists = _.isString(download.track.artist) ? [download.track.artist] : [download.track.artist.name]; | ||
} | ||
|
||
// Assuming we have `extraArtists` on a track, we must had an `artist` which | ||
// was already saved into `artists`, so this `track.artists` shouldn't be undefined | ||
// @ts-expect-error For backwards compatibility we're trying to parse an invalid field | ||
download.track.extraArtists?.forEach(artist => { | ||
download.track.artists.push(artist); | ||
}); | ||
}); | ||
|
||
return downloads; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function does the exact same thing as the one in playlists.ts
and almost the same as the one in favorites.ts
. The common parts of this logic should be extracted and can live in e.g. packages/app/app/actions/helpers.ts
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not quite. All three are slightly different:
Download[]: { track: Track }[]
Playlist[]: { tracks: Track[] }[]
Favorites: { tracks: Track[] }
I tried combining all into a generic function but wasn't successful with it.
Just for laughs
export interface TrackMember {
track: Track;
}
export interface TracksMember {
tracks: Track[];
}
export interface Favorites {
albums: Album[];
artists: Artist[];
tracks: Track[];
}
function isTracksMember(config: TrackMember | TracksMember): config is TracksMember {
return (config as TracksMember).tracks !== undefined;
}
function isTrackMember(config: TrackMember | TracksMember): config is TrackMember {
return (config as TrackMember).track !== undefined;
}
function isTracksArrayMember(config: TrackMember | TracksMember | Favorites): config is Favorites {
return (config as Favorites).tracks !== undefined && (config as Favorites).tracks instanceof Array;
}
function rewriteTrackArtists(track: Track) {
// @ts-expect-error For backwards compatibility we're trying to parse an invalid field
if (track.artists || !track.artist) {
// New format already present, do nothing
return;
}
// @ts-expect-error For backwards compatibility we're trying to parse an invalid field
track.artists = _.isString(track.artist) ? [track.artist] : [track.artist.name];
// @ts-expect-error For backwards compatibility we're trying to parse an invalid field
track.artists = track.artists.concat(track.extraArtists?.map(artist));
}
/**
+* Helper function to read the old track format into the new format.
+*
+* `Track.artist` and `Track.extraArtists` are written into {@link Track.artists}
+*/
export function getFromStoreBackwardsCompatible<T extends Favorites | TrackMember[] | TracksMember[]>(configName: 'downloads' | 'favorites' | 'playlists'): T {
let config: T = store.get(configName);
if (config instanceof Array) {
// Playlist[] || Download[]
config = config.map((member: TrackMember | TracksMember) => {
if (isTrackMember(member)) {
// Download[]
rewriteTrackArtists(member.track);
} else if (isTracksMember(member)) {
// Playlist[]
member.tracks.forEach(track => {
rewriteTrackArtists(track);
});
}
return member;
});
} else if (isTracksArrayMember(config)) {
// Favorites: { tracks: Track[] }
config.tracks = config.tracks.map(track => {
rewriteTrackArtists(track);
return track;
});
config.albums = config.albums.map(album => {
album.tracklist = album.tracklist?.map(track => {
rewriteTrackArtists(track);
return track;
});
return album;
});
}
return config;
}
album.tracklist?.forEach(track => { | ||
if (track.artists || !track.artist) { | ||
return; | ||
} | ||
|
||
if (track.artist) { | ||
track.artists = _.isString(track.artist) ? [track.artist] : [track.artist.name]; | ||
} | ||
|
||
// Assuming we have `extraArtists` on a track, we must had an `artist` which | ||
// was already saved into `artists`, so this `track.artists` shouldn't be undefined | ||
track.extraArtists?.forEach(artist => { | ||
track.artists.push(artist); | ||
}); | ||
}); | ||
}); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This part can use the function that can be extracted from all getXBackwardsCompatible
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As mentioned in the Discord thread, this is very strange.
I moved it in a separate helper function and then the tests start failing because the track is suddenly undefined.
download.track.extraArtists?.forEach(artist => { | ||
download.track.artists.push(artist); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of mutating by using forEach
/push
, you can use map
and assign what map
returns.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This also applies to other places where forEach
is used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed them all to use map
now.
Aside from inside the helper functions there was only one single occurrence when reading from the DB.
I'm still working on the track table refactor, after that I'll get back to this. |
Finally got some time (and willpower) to get this into a working shape! I'll do it and merge it asap. |
After these changes everything should be ready. If the CI passes I'll merge it tomorrow. |
This breaks lastfm scrobbling, all song artists are |
Thanks for testing. I'll take a look at this (once I'm back from 1955). |
Bad news. This is going to need replacing several JS files with TS, they also need to be typed correctly. These changes also break autoradio in addition to scrobbling. I'm currently working on this. The most troublesome component to update is going to be the |
Ok, I managed to fix this. |
There's still one more problem, the local library tracks still have a single |
Queue playback doesn't seem to really work now. It seems that the YouTube playback source expires after a few minutes, so, after listening to a song, all the next ones will show a: |
Can you create an issue with more details please? I have also observed this problem and it is not related to this pr. |
I am pretty sure ytdl-core needs to be updated: https://www.npmjs.com/package/@distube/ytdl-core Youtube changed something a couple of days ago. |
I'm not giving up yet, working on a one time migration for existing libraries that will hopefully solve this. |
Ref: https://discord.com/channels/647373417091170314/1275405997736726609
Fix #1668