Skip to content

Commit

Permalink
wip: remove deleted tracks from target playlist
Browse files Browse the repository at this point in the history
  • Loading branch information
pkarpovich committed Oct 9, 2022
1 parent d5c0917 commit 72c32ff
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 28 deletions.
5 changes: 5 additions & 0 deletions .changeset/kind-rules-exercise.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'playlist-synchronizer': minor
---

Remove deleted tracks from target playlist
2 changes: 2 additions & 0 deletions src/entities/track.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ export type Track = {
name: string;

artists: string[];

source?: unknown;
};
4 changes: 4 additions & 0 deletions src/services/music-providers/base-music.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ export abstract class BaseMusicService implements IBaseMusicService {
trackIds: string[],
playlist: Playlist,
): Promise<void>;
abstract removeTracksFromPlaylist(
tracks: Track[],
playlist: Playlist,
): Promise<void>;

abstract isReady: boolean;
}
15 changes: 15 additions & 0 deletions src/services/music-providers/spotify.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ export class SpotifyService implements BaseMusicService {
id: track?.uri,
name: track?.name as string,
artists: track?.artists.map(({ name }) => name) as string[],
source: track,
}));
}

Expand Down Expand Up @@ -137,6 +138,20 @@ export class SpotifyService implements BaseMusicService {
);
}

async removeTracksFromPlaylist(
tracks: Track[],
playlist: Playlist,
): Promise<void> {
await retry(
() =>
this.client.removeTracksFromPlaylist(
playlist.id,
tracks.map((t) => t.source as SpotifyApi.TrackObjectFull),
),
() => this.refreshAccess(),
);
}

private async searchTrackByQuery(
query: string,
): Promise<SpotifyApi.TrackObjectFull | undefined> {
Expand Down
10 changes: 10 additions & 0 deletions src/services/music-providers/yandex-music.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export class YandexMusicService extends BaseMusicService {
return tracks.map<Track>((track) => ({
name: track.title,
artists: track.artists.map(({ name }) => name),
source: track,
}));
}

Expand All @@ -44,4 +45,13 @@ export class YandexMusicService extends BaseMusicService {
addTracksToPlaylist(trackIds: string[], playlist: Playlist): Promise<void> {
throw new Error('Method not implemented.');
}

async removeTracksFromPlaylist(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
tracks: Track[],
// eslint-disable-next-line @typescript-eslint/no-unused-vars
playlist: Playlist,
): Promise<void> {
throw new Error('Method not implemented.');
}
}
96 changes: 68 additions & 28 deletions src/services/sync.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@ const DefaultStatistics: SyncStatistics = {
totalTracksInTargetPlaylists: 0,
};

interface PlaylistSyncContext {
targetService?: BaseMusicService;
targetPlaylist?: Playlist;
targetPlaylistTracks?: Track[];
sourceService: BaseMusicService;
sourcePlaylist: Playlist;
sourcePlaylistTracks: Track[];
loggerCtx: LoggerContext;
}

export class SyncService {
private _statistics: SyncStatistics;

Expand Down Expand Up @@ -55,32 +65,49 @@ export class SyncService {
return;
}

const originalPlaylistTracks = await this.getPlaylistTracks(
const sourcePlaylistTracks = await this.getPlaylistTracks(
syncConfig.type,
syncConfig.metadata,
loggerCtx,
);

const ctx: PlaylistSyncContext = {
sourceService: this.getMusicServiceByType(syncConfig.type),
sourcePlaylist: syncConfig.metadata,
sourcePlaylistTracks,
loggerCtx,
};

for (const target of syncConfig.targetPlaylists) {
const targetMusicService = this.getMusicServiceByType(target.type);
const tracksForAdding = await this.findTracksInService(
originalPlaylistTracks,
ctx.targetService = this.getMusicServiceByType(target.type);
ctx.targetPlaylist = target.metadata;
ctx.targetPlaylistTracks = await this.getPlaylistTracks(
target.type,
target.metadata,
loggerCtx,
);

const tracksForAdd = await this.findTracksInService(
ctx,
target.type,
ctx.sourcePlaylistTracks,
);
this.logService.success(
`Found ${tracksForAdding.length} tracks in ${target.type} service`,
`Found ${tracksForAdd.length} tracks in ${target.type} service`,
loggerCtx,
);
this._statistics.totalTracksInOriginalPlaylists +=
tracksForAdding.length;
tracksForAdd.length;

this._statistics.totalTracksInTargetPlaylists +=
ctx.targetPlaylistTracks.length;

await this.removeDeletedTracks(ctx, tracksForAdd);

const trackIdsForAdd: string[] = (
await this.filterDuplicates(
target.type,
target.metadata,
tracksForAdding,
loggerCtx,
ctx.targetPlaylistTracks,
tracksForAdd,
)
).map((newTrack) => newTrack.id as string);

Expand All @@ -92,7 +119,7 @@ export class SyncService {
continue;
}

await targetMusicService.addTracksToPlaylist(
await ctx.targetService.addTracksToPlaylist(
trackIdsForAdd,
target.metadata,
);
Expand Down Expand Up @@ -140,14 +167,14 @@ export class SyncService {
}

private async findTracksInService(
tracks: Track[],
ctx: PlaylistSyncContext,
serviceType: MusicServiceTypes,
loggerCtx: LoggerContext,
tracks: Track[],
): Promise<Track[]> {
const service = this.getMusicServiceByType(serviceType);
this.logService.await(
`Try to find tracks in ${serviceType} service`,
loggerCtx,
ctx.loggerCtx,
);
const serviceTracks = [];

Expand All @@ -163,7 +190,7 @@ export class SyncService {
`Track ${track.name} by ${track.artists.join(
', ',
)} not found`,
loggerCtx,
ctx.loggerCtx,
);
continue;
}
Expand All @@ -175,24 +202,37 @@ export class SyncService {
}

private async filterDuplicates(
serviceType: MusicServiceTypes,
playlistMetadata: Playlist,
tracksToAdding: Track[],
loggerCtx: LoggerContext,
playlistTracks: Track[],
tracksToAdd: Track[],
): Promise<Track[]> {
const playlistTracks = await this.getPlaylistTracks(
serviceType,
playlistMetadata,
loggerCtx,
);

this._statistics.totalTracksInTargetPlaylists += playlistTracks.length;

return tracksToAdding.filter(
return tracksToAdd.filter(
(newTrack) =>
!playlistTracks.some(
(playlistTrack) => playlistTrack.id === newTrack.id,
),
);
}

private async removeDeletedTracks(
ctx: PlaylistSyncContext,
tracksToAdd: Track[],
): Promise<void> {
if (!ctx.targetService || !ctx.targetPlaylist) {
return;
}

const tracksToRemove =
ctx.targetPlaylistTracks?.filter(
(t) => !tracksToAdd.some((ta) => ta.id === t.id),
) ?? [];

if (!tracksToRemove.length) {
return;
}

await ctx.targetService.removeTracksFromPlaylist(
tracksToRemove,
ctx.targetPlaylist,
);
}
}

0 comments on commit 72c32ff

Please sign in to comment.