[sorting] Derive browse date from filename, fall back to tag
This commit is contained in:
107
__tests__/util/arrays.test.js
Normal file
107
__tests__/util/arrays.test.js
Normal 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',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -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']);
|
||||||
|
|||||||
Reference in New Issue
Block a user