[sorting] Derive browse date from filename, fall back to tag
All checks were successful
CI / test (push) Successful in 1m18s
CI / deploy (push) Successful in 1m53s

This commit is contained in:
2026-09-22 15:53:52 -04:00
parent c9c4423fc4
commit 1b682addf2
2 changed files with 128 additions and 3 deletions

View File

@ -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',
]);
});
});
});

View File

@ -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 * Sort an array of objects
* @param array = array to sort * @param array = array to sort
@ -160,16 +177,17 @@ const sortItems = (array, property, reverse = false) => {
case 'user': case 'user':
return item.user ? item.user.id : undefined; return item.user ? item.user.id : undefined;
case 'date': case 'date':
return item.release_date || ''; return dateFromUri(item.uri) || item.release_date || '';
default: default:
return item[property]; 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') { if (property === 'date') {
return orderBy(array, [ return orderBy(array, [
item => item.release_date || '', item => dateFromUri(item.uri) || item.release_date || '',
item => item.album?.name || '', item => item.album?.name || '',
item => item.track_number || 0, item => item.track_number || 0,
], [reverse ? 'desc' : 'asc', 'asc', 'asc']); ], [reverse ? 'desc' : 'asc', 'asc', 'asc']);