add bit depth, sample rate

This commit is contained in:
Kendall Garner 2025-07-13 07:12:13 -07:00
parent 8b141d652c
commit 4b4df28641
No known key found for this signature in database
GPG key ID: 9355F387FE765C94
8 changed files with 65 additions and 16 deletions

View file

@ -36,6 +36,7 @@
"ascending": "ascending", "ascending": "ascending",
"backward": "backward", "backward": "backward",
"biography": "biography", "biography": "biography",
"bitDepth": "bit depth",
"bitrate": "bitrate", "bitrate": "bitrate",
"bpm": "bpm", "bpm": "bpm",
"cancel": "cancel", "cancel": "cancel",
@ -99,6 +100,7 @@
"resetToDefault": "reset to default", "resetToDefault": "reset to default",
"restartRequired": "restart required", "restartRequired": "restart required",
"right": "right", "right": "right",
"sampleRate": "sample rate",
"save": "save", "save": "save",
"saveAndReplace": "save and replace", "saveAndReplace": "save and replace",
"saveAs": "save as", "saveAs": "save as",

View file

@ -274,6 +274,8 @@ const SongPropertyMapping: ItemDetailRow<Song>[] = [
{ label: 'filter.isCompilation', render: (song) => BoolField(song.compilation || false) }, { label: 'filter.isCompilation', render: (song) => BoolField(song.compilation || false) },
{ key: 'container', label: 'common.codec' }, { key: 'container', label: 'common.codec' },
{ key: 'bitRate', label: 'common.bitrate', render: (song) => `${song.bitRate} kbps` }, { key: 'bitRate', label: 'common.bitrate', render: (song) => `${song.bitRate} kbps` },
{ key: 'sampleRate', label: 'common.sampleRate' },
{ key: 'bitDepth', label: 'common.bitDepth' },
{ key: 'channels', label: 'common.channel_other' }, { key: 'channels', label: 'common.channel_other' },
{ key: 'size', label: 'common.size', render: (song) => formatSizeString(song.size) }, { key: 'size', label: 'common.size', render: (song) => formatSizeString(song.size) },
{ {

View file

@ -170,6 +170,47 @@ const normalizeSong = (
deviceId: string, deviceId: string,
imageSize?: number, imageSize?: number,
): Song => { ): Song => {
let bitRate = 0;
let channels: null | number = null;
let container: null | string = null;
let path: null | string = null;
let sampleRate: null | number = null;
let size = 0;
let streamUrl = '';
if (item.MediaSources?.length) {
const source = item.MediaSources[0];
container = source.Container;
path = source.Path;
size = source.Size;
streamUrl = getStreamUrl({
container: container,
deviceId,
eTag: source.ETag,
id: item.Id,
mediaSourceId: source.Id,
server,
});
if ((source.MediaStreams?.length || 0) > 0) {
for (const stream of source.MediaStreams) {
if (stream.Type === 'Audio') {
bitRate =
stream.BitRate !== undefined
? Number(Math.trunc(stream.BitRate / 1000))
: 0;
channels = stream.Channels || null;
sampleRate = stream.SampleRate || null;
break;
}
}
}
} else {
console.warn('Jellyfin song retrieved with no media sources', item);
}
return { return {
album: item.Album, album: item.Album,
albumArtists: item.AlbumArtists?.map((entry) => ({ albumArtists: item.AlbumArtists?.map((entry) => ({
@ -184,14 +225,13 @@ const normalizeSong = (
imageUrl: null, imageUrl: null,
name: entry.Name, name: entry.Name,
})), })),
bitRate: bitDepth: null,
item.MediaSources?.[0].Bitrate && bitRate,
Number(Math.trunc(item.MediaSources[0].Bitrate / 1000)),
bpm: null, bpm: null,
channels: null, channels,
comment: null, comment: null,
compilation: null, compilation: null,
container: (item.MediaSources && item.MediaSources[0]?.Container) || null, container,
createdAt: item.DateCreated, createdAt: item.DateCreated,
discNumber: (item.ParentIndexNumber && item.ParentIndexNumber) || 1, discNumber: (item.ParentIndexNumber && item.ParentIndexNumber) || 1,
discSubtitle: null, discSubtitle: null,
@ -220,7 +260,7 @@ const normalizeSong = (
lyrics: null, lyrics: null,
name: item.Name, name: item.Name,
participants: getPeople(item), participants: getPeople(item),
path: (item.MediaSources && item.MediaSources[0]?.Path) || null, path,
peak: null, peak: null,
playCount: (item.UserData && item.UserData.PlayCount) || 0, playCount: (item.UserData && item.UserData.PlayCount) || 0,
playlistItemId: item.PlaylistItemId, playlistItemId: item.PlaylistItemId,
@ -230,17 +270,11 @@ const normalizeSong = (
? new Date(item.ProductionYear, 0, 1).toISOString() ? new Date(item.ProductionYear, 0, 1).toISOString()
: null, : null,
releaseYear: item.ProductionYear ? String(item.ProductionYear) : null, releaseYear: item.ProductionYear ? String(item.ProductionYear) : null,
sampleRate,
serverId: server?.id || '', serverId: server?.id || '',
serverType: ServerType.JELLYFIN, serverType: ServerType.JELLYFIN,
size: item.MediaSources && item.MediaSources[0]?.Size, size,
streamUrl: getStreamUrl({ streamUrl,
container: item.MediaSources?.[0]?.Container,
deviceId,
eTag: item.MediaSources?.[0]?.ETag,
id: item.Id,
mediaSourceId: item.MediaSources?.[0]?.Id,
server,
}),
tags: getTags(item), tags: getTags(item),
trackNumber: item.IndexNumber, trackNumber: item.IndexNumber,
uniqueId: nanoid(), uniqueId: nanoid(),

View file

@ -148,6 +148,7 @@ const normalizeSong = (
albumId: item.albumId, albumId: item.albumId,
...getArtists(item), ...getArtists(item),
artistName: item.artist, artistName: item.artist,
bitDepth: item.bitDepth || null,
bitRate: item.bitRate, bitRate: item.bitRate,
bpm: item.bpm ? item.bpm : null, bpm: item.bpm ? item.bpm : null,
channels: item.channels ? item.channels : null, channels: item.channels ? item.channels : null,
@ -189,6 +190,7 @@ const normalizeSong = (
: new Date(Date.UTC(item.year, 0, 1)) : new Date(Date.UTC(item.year, 0, 1))
).toISOString(), ).toISOString(),
releaseYear: String(item.year), releaseYear: String(item.year),
sampleRate: item.sampleRate || null,
serverId: server?.id || 'unknown', serverId: server?.id || 'unknown',
serverType: ServerType.NAVIDROME, serverType: ServerType.NAVIDROME,
size: item.size, size: item.size,

View file

@ -184,6 +184,7 @@ const song = z.object({
albumId: z.string(), albumId: z.string(),
artist: z.string(), artist: z.string(),
artistId: z.string(), artistId: z.string(),
bitDepth: z.number().optional(),
bitRate: z.number(), bitRate: z.number(),
bookmarkPosition: z.number(), bookmarkPosition: z.number(),
bpm: z.number().optional(), bpm: z.number().optional(),
@ -226,6 +227,7 @@ const song = z.object({
rgAlbumPeak: z.number().optional(), rgAlbumPeak: z.number().optional(),
rgTrackGain: z.number().optional(), rgTrackGain: z.number().optional(),
rgTrackPeak: z.number().optional(), rgTrackPeak: z.number().optional(),
sampleRate: z.number(),
size: z.number(), size: z.number(),
smallImageUrl: z.string().optional(), smallImageUrl: z.string().optional(),
sortAlbumArtistName: z.string(), sortAlbumArtistName: z.string(),

View file

@ -135,9 +135,10 @@ const normalizeSong = (
albumId: item.albumId?.toString() || '', albumId: item.albumId?.toString() || '',
artistName: item.artist || '', artistName: item.artist || '',
artists: getArtistList(item.artists, item.artistId, item.artist), artists: getArtistList(item.artists, item.artistId, item.artist),
bitDepth: item.bitDepth || null,
bitRate: item.bitRate || 0, bitRate: item.bitRate || 0,
bpm: item.bpm || null, bpm: item.bpm || null,
channels: null, channels: item.channelCount || null,
comment: null, comment: null,
compilation: null, compilation: null,
container: item.contentType, container: item.contentType,
@ -172,6 +173,7 @@ const normalizeSong = (
playCount: item?.playCount || 0, playCount: item?.playCount || 0,
releaseDate: null, releaseDate: null,
releaseYear: item.year ? String(item.year) : null, releaseYear: item.year ? String(item.year) : null,
sampleRate: item.samplingRate || null,
serverId: server?.id || 'unknown', serverId: server?.id || 'unknown',
serverType: ServerType.SUBSONIC, serverType: ServerType.SUBSONIC,
size: item.size, size: item.size,

View file

@ -85,8 +85,10 @@ const song = z.object({
artistId: id.optional(), artistId: id.optional(),
artists: z.array(simpleArtist), artists: z.array(simpleArtist),
averageRating: z.number().optional(), averageRating: z.number().optional(),
bitDepth: z.number().optional(),
bitRate: z.number().optional(), bitRate: z.number().optional(),
bpm: z.number().optional(), bpm: z.number().optional(),
channelCount: z.number().optional(),
contentType: z.string(), contentType: z.string(),
contributors: z.array(contributor).optional(), contributors: z.array(contributor).optional(),
coverArt: z.string().optional(), coverArt: z.string().optional(),
@ -103,6 +105,7 @@ const song = z.object({
path: z.string(), path: z.string(),
playCount: z.number().optional(), playCount: z.number().optional(),
replayGain: songGain.optional(), replayGain: songGain.optional(),
samplingRate: z.number().optional(),
size: z.number(), size: z.number(),
starred: z.boolean().optional(), starred: z.boolean().optional(),
suffix: z.string(), suffix: z.string(),

View file

@ -320,6 +320,7 @@ export type Song = {
albumId: string; albumId: string;
artistName: string; artistName: string;
artists: RelatedArtist[]; artists: RelatedArtist[];
bitDepth: null | number;
bitRate: number; bitRate: number;
bpm: null | number; bpm: null | number;
channels: null | number; channels: null | number;
@ -346,6 +347,7 @@ export type Song = {
playlistItemId?: string; playlistItemId?: string;
releaseDate: null | string; releaseDate: null | string;
releaseYear: null | string; releaseYear: null | string;
sampleRate: null | number;
serverId: string; serverId: string;
serverType: ServerType; serverType: ServerType;
size: number; size: number;