mirror of
https://github.com/jeffvli/feishin.git
synced 2026-07-18 00:16:40 +02:00
Compare commits
2 Commits
da445b815d
...
14bad5dbd7
| Author | SHA1 | Date | |
|---|---|---|---|
| 14bad5dbd7 | |||
| 94aa34f6b2 |
+60
-21
@@ -7,7 +7,10 @@
|
|||||||
"viewPlaylists": "$t(entity.playlist, {\"count\": 2}) megtekintése",
|
"viewPlaylists": "$t(entity.playlist, {\"count\": 2}) megtekintése",
|
||||||
"openIn": {
|
"openIn": {
|
||||||
"lastfm": "Megnyitás Last.fm-ben",
|
"lastfm": "Megnyitás Last.fm-ben",
|
||||||
"musicbrainz": "Megnyitás MusicBrainz-ben"
|
"musicbrainz": "Megnyitás MusicBrainz-ben",
|
||||||
|
"listenbrainz": "Megnyitás ListenBrainz-ben",
|
||||||
|
"qobuz": "Megnyitás Qobuz_ban",
|
||||||
|
"spotify": "Megnyitás Spotify-ban"
|
||||||
},
|
},
|
||||||
"clearQueue": "Műsorlista kiürítése",
|
"clearQueue": "Műsorlista kiürítése",
|
||||||
"createPlaylist": "$t(entity.playlist, {\"count\": 1}) létrehozása",
|
"createPlaylist": "$t(entity.playlist, {\"count\": 1}) létrehozása",
|
||||||
@@ -117,7 +120,7 @@
|
|||||||
"none": "Egyik sem",
|
"none": "Egyik sem",
|
||||||
"restartRequired": "Újraindítás szükséges",
|
"restartRequired": "Újraindítás szükséges",
|
||||||
"setting_one": "Beállítás",
|
"setting_one": "Beállítás",
|
||||||
"setting_other": "",
|
"setting_other": "Beállítások",
|
||||||
"translation": "Fordítás",
|
"translation": "Fordítás",
|
||||||
"rating": "Értékelés",
|
"rating": "Értékelés",
|
||||||
"reload": "Újratöltés",
|
"reload": "Újratöltés",
|
||||||
@@ -154,7 +157,14 @@
|
|||||||
"view": "Nézet",
|
"view": "Nézet",
|
||||||
"noFilters": "Nincs konfigurált szűrő",
|
"noFilters": "Nincs konfigurált szűrő",
|
||||||
"countSelected": "{{count}} kiválasztott",
|
"countSelected": "{{count}} kiválasztott",
|
||||||
"retry": "Újra"
|
"retry": "Újra",
|
||||||
|
"openFolder": "Mappa megnyitás",
|
||||||
|
"example": "Példa",
|
||||||
|
"filter_single": "Egy",
|
||||||
|
"filter_multiple": "Több",
|
||||||
|
"mood": "Hangulat",
|
||||||
|
"numberOfResults": "{{numberOfResults}} eredmény",
|
||||||
|
"grouping": "Csoportosítás"
|
||||||
},
|
},
|
||||||
"entity": {
|
"entity": {
|
||||||
"albumArtist_one": "Zenész",
|
"albumArtist_one": "Zenész",
|
||||||
@@ -207,8 +217,8 @@
|
|||||||
"openError": "A fájl megnyitása sikertelen volt",
|
"openError": "A fájl megnyitása sikertelen volt",
|
||||||
"playbackError": "Hiba történt a média lejátszásakor",
|
"playbackError": "Hiba történt a média lejátszásakor",
|
||||||
"remoteEnableError": "Hiba történt a távoli szerver műveletkor: $t(common.enable)",
|
"remoteEnableError": "Hiba történt a távoli szerver műveletkor: $t(common.enable)",
|
||||||
"remotePortError": "Hiba történt a távoli szerver PORT-jának beállításakor",
|
"remotePortError": "Hiba történt a távoli szerver port-jának beállításakor",
|
||||||
"remotePortWarning": "Indítsd újra a szervert az új PORT használatához",
|
"remotePortWarning": "Indítsd újra a szervert az új port használatához",
|
||||||
"genericError": "Hiba történt",
|
"genericError": "Hiba történt",
|
||||||
"endpointNotImplementedError": "A(z) {{endpoint}} végpont nincs implementálva a következőhöz: {{serverType}}",
|
"endpointNotImplementedError": "A(z) {{endpoint}} végpont nincs implementálva a következőhöz: {{serverType}}",
|
||||||
"badAlbum": "Azért látod ezt az oldalt mert ez a zeneszám nem része egy albumnak. ez általában akkor történik amikor egy szám a zenekönyvtárad gyökerébe kerül. a Jellyfin csak mappákba rendezett számokat csoportosít",
|
"badAlbum": "Azért látod ezt az oldalt mert ez a zeneszám nem része egy albumnak. ez általában akkor történik amikor egy szám a zenekönyvtárad gyökerébe kerül. a Jellyfin csak mappákba rendezett számokat csoportosít",
|
||||||
@@ -226,7 +236,10 @@
|
|||||||
"noNetworkDescription": "Nem tudok csatlakozni a szerverhez",
|
"noNetworkDescription": "Nem tudok csatlakozni a szerverhez",
|
||||||
"saveQueueFailed": "Műsorlista mentése sikertelen",
|
"saveQueueFailed": "Műsorlista mentése sikertelen",
|
||||||
"settingsSyncError": "Eltéréseket találtam a leképző és a fő folyamat beállításai között. Indítsd újra az alkalmazást",
|
"settingsSyncError": "Eltéréseket találtam a leképző és a fő folyamat beállításai között. Indítsd újra az alkalmazást",
|
||||||
"multipleServerSaveQueueError": "A műsorlistában egy vagy több olyan dal található, amely nem az aktuális szerverről származik. Ez nem támogatott"
|
"multipleServerSaveQueueError": "A műsorlistában egy vagy több olyan dal található, amely nem az aktuális szerverről származik. Ez nem támogatott",
|
||||||
|
"invalidJson": "Érvénytelen JSON",
|
||||||
|
"playbackPausedDueToError": "Lejátszás szüneteltetve hiba miatt",
|
||||||
|
"serverLockSingleServer": "A szerver zárolása esetén csak egy szerver engedélyezett"
|
||||||
},
|
},
|
||||||
"filter": {
|
"filter": {
|
||||||
"albumCount": "$t(entity.album, {\"count\": 2}) darab",
|
"albumCount": "$t(entity.album, {\"count\": 2}) darab",
|
||||||
@@ -269,9 +282,11 @@
|
|||||||
"trackNumber": "Sáv",
|
"trackNumber": "Sáv",
|
||||||
"artist": "$t(entity.artist, {\"count\": 1})",
|
"artist": "$t(entity.artist, {\"count\": 1})",
|
||||||
"bpm": "Bpm",
|
"bpm": "Bpm",
|
||||||
"channels": "$t(common.channel_other)",
|
"channels": "$t(common.channel, {\"count\": 2})",
|
||||||
"genre": "$t(entity.genre, {\"count\": 1})",
|
"genre": "$t(entity.genre, {\"count\": 1})",
|
||||||
"explicitStatus": "$t(common.explicitStatus)"
|
"explicitStatus": "$t(common.explicitStatus)",
|
||||||
|
"matchAnd": "és",
|
||||||
|
"matchOr": "vagy"
|
||||||
},
|
},
|
||||||
"form": {
|
"form": {
|
||||||
"addServer": {
|
"addServer": {
|
||||||
@@ -671,7 +686,7 @@
|
|||||||
"customFontPath_description": "Beállítja az alkalmazáshoz használandó egyéni betűtípus elérési útját",
|
"customFontPath_description": "Beállítja az alkalmazáshoz használandó egyéni betűtípus elérési útját",
|
||||||
"contextMenu": "Kontextusmenü (jobb klikk) beállítás",
|
"contextMenu": "Kontextusmenü (jobb klikk) beállítás",
|
||||||
"crossfadeDuration_description": "Beállítja áthúzás effekt időtartamát",
|
"crossfadeDuration_description": "Beállítja áthúzás effekt időtartamát",
|
||||||
"crossfadeDuration": "Áthúzás IDőtartam",
|
"crossfadeDuration": "Áthúzás Idő tartam",
|
||||||
"crossfadeStyle": "Áthúzás stílus",
|
"crossfadeStyle": "Áthúzás stílus",
|
||||||
"crossfadeStyle_description": "Válaszd ki az audiolejátszóhoz használni kívánt áthúzás stílust",
|
"crossfadeStyle_description": "Válaszd ki az audiolejátszóhoz használni kívánt áthúzás stílust",
|
||||||
"releaseChannel_description": "Válassz a stabil kiadás vagy a béta kiadás közül az automatikus frissítésekhez",
|
"releaseChannel_description": "Válassz a stabil kiadás vagy a béta kiadás közül az automatikus frissítésekhez",
|
||||||
@@ -852,7 +867,7 @@
|
|||||||
"sidebarPlaylistList_description": "Lejátszási lista megjelenítése vagy elrejtése az oldalsávban",
|
"sidebarPlaylistList_description": "Lejátszási lista megjelenítése vagy elrejtése az oldalsávban",
|
||||||
"sidebarPlaylistList": "Oldalsáv lejátszási lista",
|
"sidebarPlaylistList": "Oldalsáv lejátszási lista",
|
||||||
"sidePlayQueueStyle_description": "Beállítja az oldalsó műsorlista stílusát",
|
"sidePlayQueueStyle_description": "Beállítja az oldalsó műsorlista stílusát",
|
||||||
"mediaSession_description": "Lehetővé teszi a Windows Media Session integrációját, a médiavezérlők és metaadatok megjelenítését a rendszer hangerő-átfedésben és a zárolási képernyőn (csak Windows)",
|
"mediaSession_description": "Bekapcsolja a media session integrációját, a médiavezérlők és metaadatok megjelenítését a rendszer hangerő-átfedésben és a zárolási képernyőn. (Web audiolejátszó szükséges.)",
|
||||||
"mediaSession": "Média munkamenet engedélyezése",
|
"mediaSession": "Média munkamenet engedélyezése",
|
||||||
"sidePlayQueueStyle": "Oldalsó műsorlista stílus",
|
"sidePlayQueueStyle": "Oldalsó műsorlista stílus",
|
||||||
"skipDuration": "Átugrás hossza",
|
"skipDuration": "Átugrás hossza",
|
||||||
@@ -918,7 +933,7 @@
|
|||||||
"autoDJ": "Auto DJ",
|
"autoDJ": "Auto DJ",
|
||||||
"autoDJ_timing": "Időzítés",
|
"autoDJ_timing": "Időzítés",
|
||||||
"autoDJ_itemCount": "Elem szám",
|
"autoDJ_itemCount": "Elem szám",
|
||||||
"autoDJ_itemCount_description": "Az auto DJ engedélyezésekor a műsorsorba felvenni kívánt elemek száma",
|
"autoDJ_itemCount_description": "A műsorsorba felvenni kívánt elemek száma",
|
||||||
"autoDJ_timing_description": "Az auto DJ elindulása előtt a műsorlistában maradt dalok száma",
|
"autoDJ_timing_description": "Az auto DJ elindulása előtt a műsorlistában maradt dalok száma",
|
||||||
"followCurrentSong_description": "Automatikusan görgesse a műsorlistát az aktuálisan lejátszott dalra",
|
"followCurrentSong_description": "Automatikusan görgesse a műsorlistát az aktuálisan lejátszott dalra",
|
||||||
"followCurrentSong": "Kövesd az aktuális dalt",
|
"followCurrentSong": "Kövesd az aktuális dalt",
|
||||||
@@ -959,13 +974,13 @@
|
|||||||
"biography": "$t(common.biography)",
|
"biography": "$t(common.biography)",
|
||||||
"bitrate": "$t(common.bitrate)",
|
"bitrate": "$t(common.bitrate)",
|
||||||
"bpm": "$t(common.bpm)",
|
"bpm": "$t(common.bpm)",
|
||||||
"channels": "$t(common.channel_other)",
|
"channels": "$t(common.channel, {\"count\": 2})",
|
||||||
"codec": "$t(common.codec)",
|
"codec": "$t(common.codec)",
|
||||||
"dateAdded": "Hozzáadva",
|
"dateAdded": "Hozzáadva",
|
||||||
"discNumber": "Lemezszám",
|
"discNumber": "Lemezszám",
|
||||||
"duration": "$t(common.duration)",
|
"duration": "$t(common.duration)",
|
||||||
"favorite": "$t(common.favorite)",
|
"favorite": "$t(common.favorite)",
|
||||||
"actions": "$t(common.action_other)",
|
"actions": "$t(common.action, {\"count\": 2})",
|
||||||
"album": "$t(entity.album, {\"count\": 1})",
|
"album": "$t(entity.album, {\"count\": 1})",
|
||||||
"albumCount": "$t(entity.album, {\"count\": 2})",
|
"albumCount": "$t(entity.album, {\"count\": 2})",
|
||||||
"genreBadge": "$t(entity.genre, {\"count\": 1}) (jelvények)",
|
"genreBadge": "$t(entity.genre, {\"count\": 1}) (jelvények)",
|
||||||
@@ -1011,33 +1026,33 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"column": {
|
"column": {
|
||||||
"albumCount": "$t(entity.album, {\"count\": 2})",
|
"albumCount": "Albumok",
|
||||||
"artist": "$t(entity.artist, {\"count\": 1})",
|
"artist": "Előadó",
|
||||||
"biography": "Életrajz",
|
"biography": "Életrajz",
|
||||||
"bitrate": "Bitráta",
|
"bitrate": "Bitráta",
|
||||||
"bpm": "BPM",
|
"bpm": "BPM",
|
||||||
"channels": "$t(common.channel_other)",
|
"channels": "Csatornák",
|
||||||
"codec": "$t(common.codec)",
|
"codec": "$t(common.codec)",
|
||||||
"comment": "Komment",
|
"comment": "Komment",
|
||||||
"dateAdded": "Hozzáadva",
|
"dateAdded": "Hozzáadva",
|
||||||
"discNumber": "Lemez",
|
"discNumber": "Lemez",
|
||||||
"favorite": "Kedvenc",
|
"favorite": "Kedvenc",
|
||||||
"genre": "$t(entity.genre, {\"count\": 1})",
|
"genre": "Műfaj",
|
||||||
"lastPlayed": "Utoljára játszott",
|
"lastPlayed": "Utoljára játszott",
|
||||||
"path": "Elérési út",
|
"path": "Elérési út",
|
||||||
"playCount": "Lejátszások",
|
"playCount": "Lejátszások",
|
||||||
"rating": "Értékelés",
|
"rating": "Értékelés",
|
||||||
"releaseDate": "Megjelenés",
|
"releaseDate": "Megjelenés",
|
||||||
"releaseYear": "Év",
|
"releaseYear": "Év",
|
||||||
"size": "$t(common.size)",
|
"size": "Méret",
|
||||||
"songCount": "$t(entity.track, {\"count\": 2})",
|
"songCount": "Sávok",
|
||||||
"title": "Cím",
|
"title": "Cím",
|
||||||
"trackNumber": "Sáv",
|
"trackNumber": "Sáv",
|
||||||
"album": "Album",
|
"album": "Album",
|
||||||
"albumArtist": "Album előadó",
|
"albumArtist": "Album előadó",
|
||||||
"owner": "Tulajdonos",
|
"owner": "Tulajdonos",
|
||||||
"bitDepth": "$t(common.bitDepth)",
|
"bitDepth": "Bitmélység",
|
||||||
"sampleRate": "$t(common.sampleRate)"
|
"sampleRate": "Mintavételi frekvencia"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"queryBuilder": {
|
"queryBuilder": {
|
||||||
@@ -1070,5 +1085,29 @@
|
|||||||
"secondShort": "Mp",
|
"secondShort": "Mp",
|
||||||
"hourShort": "Óra",
|
"hourShort": "Óra",
|
||||||
"dayShort": "Nap"
|
"dayShort": "Nap"
|
||||||
|
},
|
||||||
|
"visualizer": {
|
||||||
|
"options": {
|
||||||
|
"weightingFilter": {
|
||||||
|
"a": "A",
|
||||||
|
"b": "B",
|
||||||
|
"c": "C",
|
||||||
|
"d": "D",
|
||||||
|
"z": "Z",
|
||||||
|
"none": "Nincs"
|
||||||
|
},
|
||||||
|
"mode": {
|
||||||
|
"1": "[1] 1/24 oktáv / 240 sáv",
|
||||||
|
"2": "[2] 1/12 oktáv / 120 sáv",
|
||||||
|
"3": "[3] 1/8 oktáv / 80 sáv",
|
||||||
|
"4": "[4] 1/6 oktáv / 60 sáv",
|
||||||
|
"5": "[5] 1/4 oktáv / 40 sáv",
|
||||||
|
"6": "[6] 1/3 oktáv / 30 sáv",
|
||||||
|
"7": "[7] Fél oktáv / 20 sáv",
|
||||||
|
"8": "[8] Teljes oktáv / 10 sáv",
|
||||||
|
"10": "[10] Vonal / Területdiagram"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"showFPS": "Mutat FPS"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -866,7 +866,9 @@
|
|||||||
"compressorThreshold_description": "開始進行壓縮的訊號電平。",
|
"compressorThreshold_description": "開始進行壓縮的訊號電平。",
|
||||||
"compressorThreshold": "閥值",
|
"compressorThreshold": "閥值",
|
||||||
"enableRomaji_description": "在日文歌詞下方顯示羅馬拼音。",
|
"enableRomaji_description": "在日文歌詞下方顯示羅馬拼音。",
|
||||||
"enableRomaji": "啟用羅馬拼音顯示"
|
"enableRomaji": "啟用羅馬拼音顯示",
|
||||||
|
"windowBarTrackinfo": "在視窗標題列顯示曲目資訊",
|
||||||
|
"windowBarTrackinfo_description": "在視窗標題列中顯示目前播放曲目的標題與藝人、播放佇列中的位置,以及播放/暫停狀態。"
|
||||||
},
|
},
|
||||||
"table": {
|
"table": {
|
||||||
"config": {
|
"config": {
|
||||||
|
|||||||
@@ -546,6 +546,18 @@ export const controller: GeneralController = {
|
|||||||
server.type,
|
server.type,
|
||||||
)?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } }));
|
)?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } }));
|
||||||
},
|
},
|
||||||
|
getPlaylistSongIds(args) {
|
||||||
|
const server = getServerById(args.apiClientProps.serverId);
|
||||||
|
|
||||||
|
if (!server) {
|
||||||
|
throw new Error(`${i18n.t('error.apiRouteError')}: getPlaylistSongIds`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return apiController(
|
||||||
|
'getPlaylistSongIds',
|
||||||
|
server.type,
|
||||||
|
)?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } }));
|
||||||
|
},
|
||||||
getPlaylistSongList(args) {
|
getPlaylistSongList(args) {
|
||||||
const server = getServerById(args.apiClientProps.serverId);
|
const server = getServerById(args.apiClientProps.serverId);
|
||||||
|
|
||||||
|
|||||||
@@ -197,8 +197,8 @@ const JF_FIELDS = {
|
|||||||
'SortName',
|
'SortName',
|
||||||
'ProviderIds',
|
'ProviderIds',
|
||||||
],
|
],
|
||||||
ALBUM_DETAIL: ['Genres', 'DateCreated', 'ChildCount', 'People', 'Tags', 'ProviderIds'],
|
ALBUM_DETAIL: ['Genres', 'DateCreated', 'ChildCount', 'Tags', 'ProviderIds'],
|
||||||
ALBUM_LIST: ['People', 'Tags', 'Studios', 'SortName', 'ProviderIds', 'ChildCount'],
|
ALBUM_LIST: ['Tags', 'Studios', 'SortName', 'ProviderIds', 'ChildCount'],
|
||||||
FOLDER: ['Genres', 'DateCreated', 'MediaSources', 'ParentId'],
|
FOLDER: ['Genres', 'DateCreated', 'MediaSources', 'ParentId'],
|
||||||
GENRE: ['ItemCounts'],
|
GENRE: ['ItemCounts'],
|
||||||
PLAYLIST_DETAIL: [
|
PLAYLIST_DETAIL: [
|
||||||
@@ -210,16 +210,7 @@ const JF_FIELDS = {
|
|||||||
'SortName',
|
'SortName',
|
||||||
],
|
],
|
||||||
PLAYLIST_LIST: ['ChildCount', 'Genres', 'DateCreated', 'ParentId', 'Overview'],
|
PLAYLIST_LIST: ['ChildCount', 'Genres', 'DateCreated', 'ParentId', 'Overview'],
|
||||||
SONG: [
|
SONG: ['Genres', 'DateCreated', 'MediaSources', 'ParentId', 'Tags', 'SortName', 'ProviderIds'],
|
||||||
'Genres',
|
|
||||||
'DateCreated',
|
|
||||||
'MediaSources',
|
|
||||||
'ParentId',
|
|
||||||
'People',
|
|
||||||
'Tags',
|
|
||||||
'SortName',
|
|
||||||
'ProviderIds',
|
|
||||||
],
|
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const JellyfinController: InternalControllerEndpoint = {
|
export const JellyfinController: InternalControllerEndpoint = {
|
||||||
@@ -1056,6 +1047,35 @@ export const JellyfinController: InternalControllerEndpoint = {
|
|||||||
apiClientProps,
|
apiClientProps,
|
||||||
query: { ...query, limit: 1, startIndex: 0 },
|
query: { ...query, limit: 1, startIndex: 0 },
|
||||||
}).then((result) => result!.totalRecordCount!),
|
}).then((result) => result!.totalRecordCount!),
|
||||||
|
getPlaylistSongIds: async (args) => {
|
||||||
|
const { apiClientProps, query } = args;
|
||||||
|
|
||||||
|
if (!apiClientProps.server?.userId) {
|
||||||
|
throw new Error('No userId found');
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await jfApiClient(apiClientProps).getPlaylistSongList({
|
||||||
|
params: {
|
||||||
|
id: query.id,
|
||||||
|
},
|
||||||
|
query: {
|
||||||
|
// XXX: No fields are required for only IDs, which saves processing time between
|
||||||
|
// the Jellyfin server query, network (MBs vs KBs), and in-app parsing.
|
||||||
|
IncludeItemTypes: 'Audio',
|
||||||
|
UserId: apiClientProps.server?.userId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.status !== 200) {
|
||||||
|
throw new Error('Failed to get playlist song list IDs');
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
items: res.body.Items.map((item) => item.Id),
|
||||||
|
startIndex: 0,
|
||||||
|
totalRecordCount: res.body.TotalRecordCount,
|
||||||
|
};
|
||||||
|
},
|
||||||
getPlaylistSongList: async (args) => {
|
getPlaylistSongList: async (args) => {
|
||||||
const { apiClientProps, query } = args;
|
const { apiClientProps, query } = args;
|
||||||
|
|
||||||
@@ -1068,7 +1088,7 @@ export const JellyfinController: InternalControllerEndpoint = {
|
|||||||
id: query.id,
|
id: query.id,
|
||||||
},
|
},
|
||||||
query: {
|
query: {
|
||||||
Fields: JF_FIELDS.SONG,
|
Fields: JF_FIELDS.PLAYLIST_DETAIL,
|
||||||
IncludeItemTypes: 'Audio',
|
IncludeItemTypes: 'Audio',
|
||||||
UserId: apiClientProps.server?.userId,
|
UserId: apiClientProps.server?.userId,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -663,6 +663,11 @@ export const NavidromeController: InternalControllerEndpoint = {
|
|||||||
apiClientProps,
|
apiClientProps,
|
||||||
query: { ...query, limit: 1, startIndex: 0 },
|
query: { ...query, limit: 1, startIndex: 0 },
|
||||||
}).then((result) => result!.totalRecordCount!),
|
}).then((result) => result!.totalRecordCount!),
|
||||||
|
getPlaylistSongIds: async (args) =>
|
||||||
|
NavidromeController.getPlaylistSongList(args).then((result) => ({
|
||||||
|
...result,
|
||||||
|
items: result.items.map((song) => song.id),
|
||||||
|
})),
|
||||||
getPlaylistSongList: async (args: PlaylistSongListArgs): Promise<PlaylistSongListResponse> => {
|
getPlaylistSongList: async (args: PlaylistSongListArgs): Promise<PlaylistSongListResponse> => {
|
||||||
const { apiClientProps, query } = args;
|
const { apiClientProps, query } = args;
|
||||||
|
|
||||||
|
|||||||
@@ -338,6 +338,9 @@ export const queryKeys: Record<
|
|||||||
|
|
||||||
return [serverId, 'playlists', 'songList'] as const;
|
return [serverId, 'playlists', 'songList'] as const;
|
||||||
},
|
},
|
||||||
|
songListIds: (serverId: string, id: string) => {
|
||||||
|
return [serverId, 'playlists', 'songListIds', id] as const;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
radio: {
|
radio: {
|
||||||
list: (serverId: string) => [serverId, 'radio', 'list'] as const,
|
list: (serverId: string) => [serverId, 'radio', 'list'] as const,
|
||||||
|
|||||||
@@ -1229,6 +1229,11 @@ export const SubsonicController: InternalControllerEndpoint = {
|
|||||||
|
|
||||||
return results.length;
|
return results.length;
|
||||||
},
|
},
|
||||||
|
getPlaylistSongIds: async (args) =>
|
||||||
|
SubsonicController.getPlaylistSongList(args).then((result) => ({
|
||||||
|
...result,
|
||||||
|
items: result.items.map((song) => song.id),
|
||||||
|
})),
|
||||||
getPlaylistSongList: async ({ apiClientProps, query }) => {
|
getPlaylistSongList: async ({ apiClientProps, query }) => {
|
||||||
const res = await ssApiClient(apiClientProps).getPlaylist({
|
const res = await ssApiClient(apiClientProps).getPlaylist({
|
||||||
query: {
|
query: {
|
||||||
|
|||||||
@@ -211,11 +211,11 @@ export const AddToPlaylistAction = ({ items, itemType }: AddToPlaylistActionProp
|
|||||||
let songsToAdd: string[] = allSongIds;
|
let songsToAdd: string[] = allSongIds;
|
||||||
|
|
||||||
if (skipDuplicates) {
|
if (skipDuplicates) {
|
||||||
const queryKey = queryKeys.playlists.songList(serverId, playlistId);
|
const queryKey = queryKeys.playlists.songListIds(serverId, playlistId);
|
||||||
|
|
||||||
const playlistSongsRes = await queryClient.fetchQuery({
|
const playlistSongsRes = await queryClient.fetchQuery({
|
||||||
queryFn: ({ signal }) => {
|
queryFn: ({ signal }) => {
|
||||||
return api.controller.getPlaylistSongList({
|
return api.controller.getPlaylistSongIds({
|
||||||
apiClientProps: {
|
apiClientProps: {
|
||||||
serverId,
|
serverId,
|
||||||
signal,
|
signal,
|
||||||
@@ -228,7 +228,7 @@ export const AddToPlaylistAction = ({ items, itemType }: AddToPlaylistActionProp
|
|||||||
queryKey,
|
queryKey,
|
||||||
});
|
});
|
||||||
|
|
||||||
const playlistSongIds = playlistSongsRes?.items?.map((song) => song.id);
|
const playlistSongIds = playlistSongsRes?.items;
|
||||||
const uniqueSongIds: string[] = [];
|
const uniqueSongIds: string[] = [];
|
||||||
|
|
||||||
for (const songId of allSongIds) {
|
for (const songId of allSongIds) {
|
||||||
|
|||||||
@@ -231,11 +231,11 @@ export const AddToPlaylistContextModal = ({
|
|||||||
const uniqueSongIds: string[] = [];
|
const uniqueSongIds: string[] = [];
|
||||||
|
|
||||||
if (values.skipDuplicates) {
|
if (values.skipDuplicates) {
|
||||||
const queryKey = queryKeys.playlists.songList(serverId, playlistId);
|
const queryKey = queryKeys.playlists.songListIds(serverId, playlistId);
|
||||||
|
|
||||||
const playlistSongsRes = await queryClient.fetchQuery({
|
const playlistSongsRes = await queryClient.fetchQuery({
|
||||||
queryFn: ({ signal }) => {
|
queryFn: ({ signal }) => {
|
||||||
return api.controller.getPlaylistSongList({
|
return api.controller.getPlaylistSongIds({
|
||||||
apiClientProps: {
|
apiClientProps: {
|
||||||
serverId,
|
serverId,
|
||||||
signal,
|
signal,
|
||||||
@@ -248,7 +248,7 @@ export const AddToPlaylistContextModal = ({
|
|||||||
queryKey,
|
queryKey,
|
||||||
});
|
});
|
||||||
|
|
||||||
const playlistSongIds = playlistSongsRes?.items?.map((song) => song.id);
|
const playlistSongIds = playlistSongsRes?.items;
|
||||||
|
|
||||||
for (const songId of allSongIds) {
|
for (const songId of allSongIds) {
|
||||||
if (!playlistSongIds?.includes(songId)) {
|
if (!playlistSongIds?.includes(songId)) {
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import {
|
|||||||
LibraryItem,
|
LibraryItem,
|
||||||
MusicFolder,
|
MusicFolder,
|
||||||
Playlist,
|
Playlist,
|
||||||
RelatedArtist,
|
|
||||||
Song,
|
Song,
|
||||||
} from '/@/shared/types/domain-types';
|
} from '/@/shared/types/domain-types';
|
||||||
import { ServerListItem, ServerType } from '/@/shared/types/types';
|
import { ServerListItem, ServerType } from '/@/shared/types/types';
|
||||||
@@ -19,42 +18,6 @@ const TICKS_PER_MS = 10000;
|
|||||||
|
|
||||||
type AlbumOrSong = z.infer<typeof jfType._response.album> | z.infer<typeof jfType._response.song>;
|
type AlbumOrSong = z.infer<typeof jfType._response.album> | z.infer<typeof jfType._response.song>;
|
||||||
|
|
||||||
const KEYS_TO_OMIT = new Set(['AlbumArtist', 'Artist']);
|
|
||||||
|
|
||||||
const getPeople = (item: AlbumOrSong): null | Record<string, RelatedArtist[]> => {
|
|
||||||
if (item.People) {
|
|
||||||
const participants: Record<string, RelatedArtist[]> = {};
|
|
||||||
|
|
||||||
for (const person of item.People) {
|
|
||||||
const key = person.Type || '';
|
|
||||||
if (KEYS_TO_OMIT.has(key)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const item: RelatedArtist = {
|
|
||||||
// for other roles, we just want to display this and not filter.
|
|
||||||
// filtering (and links) would require a separate field, PersonIds
|
|
||||||
id: '',
|
|
||||||
imageId: null,
|
|
||||||
imageUrl: null,
|
|
||||||
name: person.Name,
|
|
||||||
userFavorite: false,
|
|
||||||
userRating: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (key in participants) {
|
|
||||||
participants[key].push(item);
|
|
||||||
} else {
|
|
||||||
participants[key] = [item];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return participants;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getTags = (item: AlbumOrSong): null | Record<string, string[]> => {
|
const getTags = (item: AlbumOrSong): null | Record<string, string[]> => {
|
||||||
if (item.Tags) {
|
if (item.Tags) {
|
||||||
const tags: Record<string, string[]> = {};
|
const tags: Record<string, string[]> = {};
|
||||||
@@ -106,39 +69,6 @@ const getPlaylistImageId = (item: z.infer<typeof jfType._response.playlist>): nu
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getArtists = (
|
|
||||||
item: z.infer<typeof jfType._response.song>,
|
|
||||||
participants?: null | Record<string, RelatedArtist[]>,
|
|
||||||
): RelatedArtist[] => {
|
|
||||||
if (!item?.ArtistItems?.length && !item.AlbumArtists && !participants) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const result: RelatedArtist[] = [];
|
|
||||||
|
|
||||||
(item?.ArtistItems?.length ? item.ArtistItems : item.AlbumArtists)?.forEach((entry) => {
|
|
||||||
result.push({
|
|
||||||
id: entry.Id,
|
|
||||||
imageId: null,
|
|
||||||
imageUrl: null,
|
|
||||||
name: entry.Name,
|
|
||||||
userFavorite: false,
|
|
||||||
userRating: null,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
if (participants?.['Remixer']) {
|
|
||||||
const existingIds = new Set(result.map((artist) => artist.id));
|
|
||||||
for (const participant of participants['Remixer']) {
|
|
||||||
if (!existingIds.has(participant.id)) {
|
|
||||||
result.push(participant);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
const jellyfinPremiereFields = (item: {
|
const jellyfinPremiereFields = (item: {
|
||||||
PremiereDate?: string;
|
PremiereDate?: string;
|
||||||
ProductionYear?: number;
|
ProductionYear?: number;
|
||||||
@@ -189,10 +119,6 @@ const normalizeSong = (
|
|||||||
console.warn('Jellyfin song retrieved with no media sources', item);
|
console.warn('Jellyfin song retrieved with no media sources', item);
|
||||||
}
|
}
|
||||||
|
|
||||||
const participants = getPeople(item);
|
|
||||||
|
|
||||||
const artists = getArtists(item, participants);
|
|
||||||
|
|
||||||
const { releaseDate, releaseYear } = jellyfinPremiereFields(item);
|
const { releaseDate, releaseYear } = jellyfinPremiereFields(item);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -211,7 +137,16 @@ const normalizeSong = (
|
|||||||
})),
|
})),
|
||||||
albumId: item.AlbumId || `dummy/${item.Id}`,
|
albumId: item.AlbumId || `dummy/${item.Id}`,
|
||||||
artistName: item?.ArtistItems?.map((entry) => entry.Name).join(', ') || '',
|
artistName: item?.ArtistItems?.map((entry) => entry.Name).join(', ') || '',
|
||||||
artists,
|
artists: (item?.ArtistItems?.length ? item.ArtistItems : item.AlbumArtists)?.map(
|
||||||
|
(entry) => ({
|
||||||
|
id: entry.Id,
|
||||||
|
imageId: null,
|
||||||
|
imageUrl: null,
|
||||||
|
name: entry.Name,
|
||||||
|
userFavorite: false,
|
||||||
|
userRating: null,
|
||||||
|
}),
|
||||||
|
),
|
||||||
bitDepth,
|
bitDepth,
|
||||||
bitRate,
|
bitRate,
|
||||||
bpm: null,
|
bpm: null,
|
||||||
@@ -253,7 +188,7 @@ const normalizeSong = (
|
|||||||
mbzRecordingId: null,
|
mbzRecordingId: null,
|
||||||
mbzTrackId: item.ProviderIds?.MusicBrainzTrack || null,
|
mbzTrackId: item.ProviderIds?.MusicBrainzTrack || null,
|
||||||
name: item.Name,
|
name: item.Name,
|
||||||
participants,
|
participants: null,
|
||||||
path: path || '',
|
path: path || '',
|
||||||
peak: null,
|
peak: null,
|
||||||
playCount: (item.UserData && item.UserData.PlayCount) || 0,
|
playCount: (item.UserData && item.UserData.PlayCount) || 0,
|
||||||
@@ -328,7 +263,7 @@ const normalizeAlbum = (
|
|||||||
name: item.Name,
|
name: item.Name,
|
||||||
originalDate: releaseDate,
|
originalDate: releaseDate,
|
||||||
originalYear,
|
originalYear,
|
||||||
participants: getPeople(item),
|
participants: null,
|
||||||
playCount: item.UserData?.PlayCount || 0,
|
playCount: item.UserData?.PlayCount || 0,
|
||||||
recordLabels: item.Studios?.map((entry) => entry.Name) || [],
|
recordLabels: item.Studios?.map((entry) => entry.Name) || [],
|
||||||
releaseDate,
|
releaseDate,
|
||||||
|
|||||||
@@ -636,6 +636,8 @@ export type AlbumInfo = {
|
|||||||
notes: null | string;
|
notes: null | string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type SongIdListResponse = BasePaginatedResponse<string[]>;
|
||||||
|
|
||||||
export type SongListArgs = BaseEndpointArgs & { query: SongListQuery };
|
export type SongListArgs = BaseEndpointArgs & { query: SongListQuery };
|
||||||
|
|
||||||
export type SongListCountArgs = BaseEndpointArgs & { query: ListCountQuery<SongListQuery> };
|
export type SongListCountArgs = BaseEndpointArgs & { query: ListCountQuery<SongListQuery> };
|
||||||
@@ -1522,6 +1524,7 @@ export type ControllerEndpoint = {
|
|||||||
getPlaylistDetail: (args: PlaylistDetailArgs) => Promise<PlaylistDetailResponse>;
|
getPlaylistDetail: (args: PlaylistDetailArgs) => Promise<PlaylistDetailResponse>;
|
||||||
getPlaylistList: (args: PlaylistListArgs) => Promise<PlaylistListResponse>;
|
getPlaylistList: (args: PlaylistListArgs) => Promise<PlaylistListResponse>;
|
||||||
getPlaylistListCount: (args: PlaylistListCountArgs) => Promise<number>;
|
getPlaylistListCount: (args: PlaylistListCountArgs) => Promise<number>;
|
||||||
|
getPlaylistSongIds: (args: PlaylistSongListArgs) => Promise<SongIdListResponse>;
|
||||||
getPlaylistSongList: (args: PlaylistSongListArgs) => Promise<SongListResponse>;
|
getPlaylistSongList: (args: PlaylistSongListArgs) => Promise<SongListResponse>;
|
||||||
getPlayQueue: (args: GetQueueArgs) => Promise<GetQueueResponse>;
|
getPlayQueue: (args: GetQueueArgs) => Promise<GetQueueResponse>;
|
||||||
getRandomSongList: (args: RandomSongListArgs) => Promise<SongListResponse>;
|
getRandomSongList: (args: RandomSongListArgs) => Promise<SongListResponse>;
|
||||||
@@ -1676,6 +1679,9 @@ export type InternalControllerEndpoint = {
|
|||||||
args: ReplaceApiClientProps<PlaylistListArgs>,
|
args: ReplaceApiClientProps<PlaylistListArgs>,
|
||||||
) => Promise<PlaylistListResponse>;
|
) => Promise<PlaylistListResponse>;
|
||||||
getPlaylistListCount: (args: ReplaceApiClientProps<PlaylistListCountArgs>) => Promise<number>;
|
getPlaylistListCount: (args: ReplaceApiClientProps<PlaylistListCountArgs>) => Promise<number>;
|
||||||
|
getPlaylistSongIds: (
|
||||||
|
args: ReplaceApiClientProps<PlaylistSongListArgs>,
|
||||||
|
) => Promise<SongIdListResponse>;
|
||||||
getPlaylistSongList: (
|
getPlaylistSongList: (
|
||||||
args: ReplaceApiClientProps<PlaylistSongListArgs>,
|
args: ReplaceApiClientProps<PlaylistSongListArgs>,
|
||||||
) => Promise<SongListResponse>;
|
) => Promise<SongListResponse>;
|
||||||
|
|||||||
Reference in New Issue
Block a user