From 1b682addf21ea73e66030d2d81d5372e73d73314 Mon Sep 17 00:00:00 2001 From: Colin Powell Date: Tue, 22 Sep 2026 15:53:52 -0400 Subject: [PATCH] [sorting] Derive browse date from filename, fall back to tag --- __tests__/util/arrays.test.js | 107 ++++++++++++++++++++++++++++++++++ src/js/util/arrays.js | 24 +++++++- 2 files changed, 128 insertions(+), 3 deletions(-) create mode 100644 __tests__/util/arrays.test.js diff --git a/__tests__/util/arrays.test.js b/__tests__/util/arrays.test.js new file mode 100644 index 00000000..5aa97a91 --- /dev/null +++ b/__tests__/util/arrays.test.js @@ -0,0 +1,107 @@ +import { sortItems } from '../../src/js/util/arrays'; + +describe('sortItems', () => { + describe('date sorting', () => { + it('should prefer a date embedded in the filename over the tag date', () => { + const tracks = [ + { + name: 'Talk Python', + uri: 'file:///media/podcasts/563-2026-09-22-EVE-Online.mp3', + release_date: '2023-10-13', + }, + { + name: 'Up First', + uri: 'file:///media/podcasts/2026-09-01_Up%20First.mp3', + release_date: '2026-09-01', + }, + ]; + + expect(sortItems(tracks, 'date').map((track) => track.name)).toEqual([ + 'Up First', + 'Talk Python', + ]); + }); + + it('should fall back to the tag date when the filename has no date', () => { + const tracks = [ + { + name: 'Newer', + uri: 'spotify:track:newer', + release_date: '2001-05-01', + }, + { + name: 'Older', + uri: 'spotify:track:older', + release_date: '1999-01-01', + }, + ]; + + expect(sortItems(tracks, 'date').map((track) => track.name)).toEqual([ + 'Older', + 'Newer', + ]); + }); + + it('should sort year-only tag dates within their year', () => { + const tracks = [ + { + name: 'Year only', + uri: 'spotify:track:year', + release_date: '2026', + }, + { + name: 'Previous year', + uri: 'spotify:track:previous', + release_date: '2025-12-31', + }, + ]; + + expect(sortItems(tracks, 'date').map((track) => track.name)).toEqual([ + 'Previous year', + 'Year only', + ]); + }); + + it('should sort tracks sharing a date by album and track number', () => { + const tracks = [ + { + name: 'Second', + uri: 'file:///media/podcasts/2026-09-22_second.mp3', + album: { name: 'Podcast' }, + track_number: 2, + }, + { + name: 'First', + uri: 'file:///media/podcasts/2026-09-22_first.mp3', + album: { name: 'Podcast' }, + track_number: 1, + }, + ]; + + expect(sortItems(tracks, 'date').map((track) => track.name)).toEqual([ + 'First', + 'Second', + ]); + }); + + it('should reverse the order', () => { + const tracks = [ + { + name: 'Talk Python', + uri: 'file:///media/podcasts/563-2026-09-22-EVE-Online.mp3', + release_date: '2023-10-13', + }, + { + name: 'Up First', + uri: 'file:///media/podcasts/2026-09-01_Up%20First.mp3', + release_date: '2026-09-01', + }, + ]; + + expect(sortItems(tracks, 'date', true).map((track) => track.name)).toEqual([ + 'Talk Python', + 'Up First', + ]); + }); + }); +}); diff --git a/src/js/util/arrays.js b/src/js/util/arrays.js index 3ffba4c0..41e84f5b 100755 --- a/src/js/util/arrays.js +++ b/src/js/util/arrays.js @@ -134,6 +134,23 @@ const createRange = function (indexes) { }; }; +/** + * Extract a YYYY-MM-DD date from an item's filename (URI) + * + * Tag dates can be year-only (or missing) for podcasts, so prefer a date embedded in + * the file's name (eg 2026-09-22_Episode.mp3) where available. + * + * @param uri = string + * @return string | null + * */ +const dateFromUri = (uri) => { + if (!uri) return null; + + const filename = String(uri).split('/').pop() || ''; + const match = filename.match(/(\d{4}-\d{2}-\d{2})/); + return match ? match[1] : null; +}; + /** * Sort an array of objects * @param array = array to sort @@ -160,16 +177,17 @@ const sortItems = (array, property, reverse = false) => { case 'user': return item.user ? item.user.id : undefined; case 'date': - return item.release_date || ''; + return dateFromUri(item.uri) || item.release_date || ''; default: return item[property]; } }; - // Special handling for date sort: sort by release date first, then track number + // Special handling for date sort: sort by the date in the filename (where available), + // then the tag date, album name and track number if (property === 'date') { return orderBy(array, [ - item => item.release_date || '', + item => dateFromUri(item.uri) || item.release_date || '', item => item.album?.name || '', item => item.track_number || 0, ], [reverse ? 'desc' : 'asc', 'asc', 'asc']);