diff --git a/src/app/data/repositories/track-repository.base.ts b/src/app/data/repositories/track-repository.base.ts index 73c6213f0..8c7c5ceab 100644 --- a/src/app/data/repositories/track-repository.base.ts +++ b/src/app/data/repositories/track-repository.base.ts @@ -11,6 +11,7 @@ export abstract class TrackRepositoryBase { public abstract getTracksForTrackArtists(trackArtists: string[]): Track[] | undefined; public abstract getTracksForAlbumArtists(albumArtists: string[]): Track[] | undefined; public abstract getTracksForGenres(genres: string[]): Track[] | undefined; + public abstract getTracksForPaths(paths: string[]): Track[] | undefined; public abstract getAlbumDataForAlbumKey(albumKeyIndex: string, albumKey: string): AlbumData[] | undefined; public abstract getAllAlbumData(albumKeyIndex: string): AlbumData[] | undefined; public abstract getAlbumDataForTrackArtists(albumKeyIndex: string, trackArtists: string[]): AlbumData[] | undefined; diff --git a/src/app/data/repositories/track-repository.ts b/src/app/data/repositories/track-repository.ts index 3abc09753..343f53f74 100644 --- a/src/app/data/repositories/track-repository.ts +++ b/src/app/data/repositories/track-repository.ts @@ -107,6 +107,22 @@ export class TrackRepository implements TrackRepositoryBase { return tracks; } + public getTracksForPaths(paths: string[]): Track[] | undefined { + const database: any = this.databaseFactory.create(); + + let filterQuery: string = ''; + + if (paths != undefined && paths.length > 0) { + filterQuery = ` AND ${ClauseCreator.createTextInClause('t.Path', paths)}`; + } + + const statement = database.prepare(`${QueryParts.selectTracksQueryPart(true)} ${filterQuery};`); + + const tracks: Track[] | undefined = statement.all(); + + return tracks; + } + public getAlbumDataThatNeedsIndexing(albumKeyIndex: string): AlbumData[] | undefined { const database = this.databaseFactory.create(); diff --git a/src/app/services/playback/queue-persister.ts b/src/app/services/playback/queue-persister.ts index 8595aa328..966a68601 100644 --- a/src/app/services/playback/queue-persister.ts +++ b/src/app/services/playback/queue-persister.ts @@ -6,12 +6,16 @@ import { QueuedTrack } from '../../data/entities/queued-track'; import { TrackModel } from '../track/track-model'; import { QueueRestoreInfo } from './queue-restore-info'; import { TrackModelFactory } from '../track/track-model-factory'; -import {SettingsBase} from "../../common/settings/settings.base"; +import { SettingsBase } from '../../common/settings/settings.base'; +import { Track } from '../../data/entities/track'; +import { TrackRepositoryBase } from '../../data/repositories/track-repository.base'; +import { Timer } from '../../common/scheduling/timer'; @Injectable({ providedIn: 'root' }) export class QueuePersister { public constructor( private queuedTrackRepository: QueuedTrackRepositoryBase, + private trackRepository: TrackRepositoryBase, private trackModelFactory: TrackModelFactory, private settings: SettingsBase, private logger: Logger, @@ -60,28 +64,61 @@ export class QueuePersister { return new QueueRestoreInfo([], [], undefined, 0); } - const tracks: TrackModel[] = []; + const tracksModels: TrackModel[] = []; const playbackOrder: number[] = new Array(savedQueuedTracks.length).fill(0); let playingTrack: TrackModel | undefined = undefined; let progressSeconds: number = 0; - const albumKeyIndex = this.settings.albumKeyIndex; + const tracks: Track[] | undefined = this.trackRepository.getTracksForPaths(savedQueuedTracks.map((x) => x.path)); + + if (!tracks || tracks.length === 0) { + this.logger.info(`No tracks found for saved queued tracks.`, 'QueuePersister', 'restore'); + return new QueueRestoreInfo([], [], undefined, 0); + } - for (const savedQueuedTrack of savedQueuedTracks) { - const track: TrackModel = await this.trackModelFactory.createFromFileAsync(savedQueuedTrack.path, albumKeyIndex); + const timer = new Timer(); + timer.start(); - tracks.push(track); - playbackOrder[savedQueuedTrack.orderId] = tracks.length - 1; + const albumKeyIndex = this.settings.albumKeyIndex; - if (savedQueuedTrack.isPlaying) { - playingTrack = track; - progressSeconds = savedQueuedTrack.progressSeconds; + const trackMap = new Map(savedQueuedTracks.map((track) => [track.path, track])); + const orderedTracks = tracks + .sort((a, b) => { + const orderA = trackMap.get(a.path)?.orderId ?? 0; + const orderB = trackMap.get(b.path)?.orderId ?? 0; + return orderA - orderB; + }) + .map((track) => { + const savedTrack = trackMap.get(track.path); + return { + ...track, + isPlaying: savedTrack?.isPlaying ?? 0, + orderId: savedTrack?.orderId ?? 0, + progressSeconds: savedTrack?.progressSeconds ?? 0, + }; + }); + + for (const orderedTrack of orderedTracks) { + const trackModel: TrackModel = this.trackModelFactory.createFromTrack(orderedTrack, albumKeyIndex); + + tracksModels.push(trackModel); + playbackOrder[orderedTrack.orderId] = tracksModels.length - 1; + + if (orderedTrack.isPlaying) { + playingTrack = trackModel; + progressSeconds = orderedTrack.progressSeconds; } } - this.logger.info(`Restored queue of ${savedQueuedTracks.length} tracks`, 'QueuePersister', 'restore'); + timer.stop(); + + this.logger.info( + `Restored queue of ${savedQueuedTracks.length} tracks. Time required: ${timer.elapsedMilliseconds} ms`, + 'QueuePersister', + 'restore', + ); - return new QueueRestoreInfo(tracks, playbackOrder, playingTrack, progressSeconds); + return new QueueRestoreInfo(tracksModels, playbackOrder, playingTrack, progressSeconds); } catch (e: unknown) { if (e instanceof Error) { this.logger.error(e, 'Failed to restore queue', 'QueuePersister', 'restore');