Remove stream url, cleanup old audio player (#1269)

* Remove stream url, cleanup old audio player

* remove unused api in playerbar waveform

* make jellyfin transcoding work?
This commit is contained in:
Kendall Garner
2025-11-23 17:12:33 -08:00
committed by jeffvli
parent db110733a4
commit 80419a1edf
13 changed files with 160 additions and 621 deletions
+14 -14
View File
@@ -514,6 +514,20 @@ export const controller: GeneralController = {
query: mergeMusicFolderId(args.query, server),
});
},
getStreamUrl(args) {
const server = getServerById(args.apiClientProps.serverId);
if (!server) {
throw new Error(
`${i18n.t('error.apiRouteError', { postProcess: 'sentenceCase' })}: getStreamUrl`,
);
}
return apiController(
'getStreamUrl',
server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } });
},
getStructuredLyrics(args) {
const server = getServerById(args.apiClientProps.serverId);
@@ -556,20 +570,6 @@ export const controller: GeneralController = {
server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } });
},
getTranscodingUrl(args) {
const server = getServerById(args.apiClientProps.serverId);
if (!server) {
throw new Error(
`${i18n.t('error.apiRouteError', { postProcess: 'sentenceCase' })}: getTranscodingUrl`,
);
}
return apiController(
'getTranscodingUrl',
server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } });
},
getUserList(args) {
const server = getServerById(args.apiClientProps.serverId);
@@ -552,7 +552,7 @@ export const JellyfinController: InternalControllerEndpoint = {
}
return {
items: res.body.Items.map((item) => jfNormalize.song(item, apiClientProps.server, '')),
items: res.body.Items.map((item) => jfNormalize.song(item, apiClientProps.server)),
startIndex: 0,
totalRecordCount: res.body.TotalRecordCount,
};
@@ -602,7 +602,7 @@ export const JellyfinController: InternalControllerEndpoint = {
}
return {
items: res.body.Items.map((item) => jfNormalize.song(item, apiClientProps.server, '')),
items: res.body.Items.map((item) => jfNormalize.song(item, apiClientProps.server)),
startIndex: 0,
totalRecordCount: res.body.Items.length || 0,
};
@@ -647,7 +647,7 @@ export const JellyfinController: InternalControllerEndpoint = {
if (res.status === 200 && res.body.Items.length) {
const results = res.body.Items.reduce<Song[]>((acc, song) => {
if (song.Id !== query.songId) {
acc.push(jfNormalize.song(song, apiClientProps.server, ''));
acc.push(jfNormalize.song(song, apiClientProps.server));
}
return acc;
@@ -676,7 +676,7 @@ export const JellyfinController: InternalControllerEndpoint = {
return mix.body.Items.reduce<Song[]>((acc, song) => {
if (song.Id !== query.songId) {
acc.push(jfNormalize.song(song, apiClientProps.server, ''));
acc.push(jfNormalize.song(song, apiClientProps.server));
}
return acc;
@@ -696,7 +696,7 @@ export const JellyfinController: InternalControllerEndpoint = {
throw new Error('Failed to get song detail');
}
return jfNormalize.song(res.body, apiClientProps.server, '');
return jfNormalize.song(res.body, apiClientProps.server);
},
getSongList: async (args) => {
const { apiClientProps, query } = args;
@@ -809,7 +809,7 @@ export const JellyfinController: InternalControllerEndpoint = {
return {
items: items.map((item) =>
jfNormalize.song(item, apiClientProps.server, '', query.imageSize),
jfNormalize.song(item, apiClientProps.server, query.imageSize),
),
startIndex: query.startIndex,
totalRecordCount,
@@ -820,6 +820,39 @@ export const JellyfinController: InternalControllerEndpoint = {
apiClientProps,
query: { ...query, limit: 1, startIndex: 0 },
}).then((result) => result!.totalRecordCount!),
getStreamUrl: ({ apiClientProps: { server }, query }) => {
const { bitrate, format, id, transcode } = query;
const deviceId = '';
let url =
`${server?.url}/audio` +
`/${id}/universal` +
`?userId=${server?.userId}` +
`&deviceId=${deviceId}` +
'&audioCodec=aac' +
`&apiKey=${server?.credential}` +
`&playSessionId=${deviceId}` +
'&container=opus,mp3,aac,m4a,m4b,flac,wav,ogg';
if (transcode) {
// Some format appears to be required. Fall back to trusty MP3 if not specified
// Otherwise, ffmpeg appears to crash
const realFormat = format || 'mp3';
url += `&transcodingProtocol=http&transcodingContainer=${realFormat}`;
url = url.replace('audioCodec=aac', `audioCodec=${realFormat}`);
url = url.replace(
'&container=opus,mp3,aac,m4a,m4b,flac,wav,ogg',
`&container=${realFormat}`,
);
if (bitrate !== undefined) {
url += `&maxStreamingBitrate=${bitrate * 1000}`;
}
}
return url;
},
getTags: async (args) => {
const { apiClientProps, query } = args;
@@ -873,24 +906,11 @@ export const JellyfinController: InternalControllerEndpoint = {
}
return {
items: res.body.Items.map((item) => jfNormalize.song(item, apiClientProps.server, '')),
items: res.body.Items.map((item) => jfNormalize.song(item, apiClientProps.server)),
startIndex: 0,
totalRecordCount: res.body.TotalRecordCount,
};
},
getTranscodingUrl: (args) => {
const { base, bitrate, format } = args.query;
let url = base.replace('transcodingProtocol=hls', 'transcodingProtocol=http');
if (format) {
url = url.replace('audioCodec=aac', `audioCodec=${format}`);
url = url.replace('transcodingContainer=ts', `transcodingContainer=${format}`);
}
if (bitrate !== undefined) {
url += `&maxStreamingBitrate=${bitrate * 1000}`;
}
return url;
},
movePlaylistItem: async (args) => {
const { apiClientProps, query } = args;
@@ -1082,7 +1102,7 @@ export const JellyfinController: InternalControllerEndpoint = {
jfNormalize.albumArtist(item, apiClientProps.server),
),
albums: albums.map((item) => jfNormalize.album(item, apiClientProps.server)),
songs: songs.map((item) => jfNormalize.song(item, apiClientProps.server, '')),
songs: songs.map((item) => jfNormalize.song(item, apiClientProps.server)),
};
},
updatePlaylist: async (args) => {
@@ -605,6 +605,7 @@ export const NavidromeController: InternalControllerEndpoint = {
apiClientProps,
query: { ...query, limit: 1, startIndex: 0 },
}).then((result) => result!.totalRecordCount!),
getStreamUrl: SubsonicController.getStreamUrl,
getStructuredLyrics: SubsonicController.getStructuredLyrics,
getTags: async (args) => {
const { apiClientProps } = args;
@@ -646,7 +647,6 @@ export const NavidromeController: InternalControllerEndpoint = {
};
},
getTopSongs: SubsonicController.getTopSongs,
getTranscodingUrl: SubsonicController.getTranscodingUrl,
getUserList: async (args) => {
const { apiClientProps, query } = args;
@@ -1246,6 +1246,21 @@ export const SubsonicController: InternalControllerEndpoint = {
return totalRecordCount;
},
getStreamUrl: ({ apiClientProps: { server }, query }) => {
const { bitrate, format, id, transcode } = query;
let url = `${server?.url}/rest/stream.view?id=${id}&v=1.13.0&c=Feishin&${server?.credential}`;
if (transcode) {
if (format) {
url += `&format=${format}`;
}
if (bitrate !== undefined) {
url += `&maxBitRate=${bitrate}`;
}
}
return url;
},
getStructuredLyrics: async (args) => {
const { apiClientProps, query } = args;
@@ -1311,18 +1326,6 @@ export const SubsonicController: InternalControllerEndpoint = {
totalRecordCount: res.body.topSongs?.song?.length || 0,
};
},
getTranscodingUrl: (args) => {
const { base, bitrate, format } = args.query;
let url = base;
if (format) {
url += `&format=${format}`;
}
if (bitrate !== undefined) {
url += `&maxBitRate=${bitrate}`;
}
return url;
},
removeFromPlaylist: async ({ apiClientProps, query }) => {
const res = await ssApiClient(apiClientProps).updatePlaylist({
query: {