mirror of
https://github.com/Jellify-Music/App.git
synced 2025-12-16 16:26:33 -06:00
feat: Add URL validation before file downloads and ensure valid session IDs for tracks and streams. (#813)
This commit is contained in:
@@ -60,6 +60,11 @@ export async function downloadJellyfinFile(
|
||||
setDownloadProgress: JellifyDownloadProgressState,
|
||||
preferredExtension?: string | null,
|
||||
): Promise<DownloadedFileInfo> {
|
||||
// Validate URL before attempting download to prevent NPE in native code
|
||||
if (!url || url.trim() === '') {
|
||||
throw new Error('Invalid download URL: URL is empty or undefined')
|
||||
}
|
||||
|
||||
try {
|
||||
const urlExtension = normalizeExtension(getExtensionFromUrl(url))
|
||||
const hintedExtension = normalizeExtension(preferredExtension)
|
||||
@@ -168,6 +173,12 @@ export const saveAudio = async (
|
||||
//Ignore
|
||||
}
|
||||
|
||||
// Validate track URL before attempting download
|
||||
if (!track.url || track.url.trim() === '') {
|
||||
console.error('Cannot download track: URL is missing', track.item.Id)
|
||||
return false
|
||||
}
|
||||
|
||||
try {
|
||||
const downloadedTrackFile = await downloadJellyfinFile(
|
||||
track.url,
|
||||
@@ -177,10 +188,11 @@ export const saveAudio = async (
|
||||
track.mediaSourceInfo?.Container,
|
||||
)
|
||||
let downloadedArtworkFile: DownloadedFileInfo | undefined
|
||||
if (track.artwork) {
|
||||
// Check for non-empty artwork URL (empty string passes truthy check but fails download)
|
||||
if (track.artwork && typeof track.artwork === 'string' && track.artwork.trim() !== '') {
|
||||
downloadedArtworkFile = await downloadJellyfinFile(
|
||||
track.artwork as string,
|
||||
track.item.Id as string,
|
||||
track.artwork,
|
||||
`${track.item.Id}-artwork`,
|
||||
track.title as string,
|
||||
setDownloadProgress,
|
||||
undefined,
|
||||
|
||||
@@ -23,6 +23,18 @@ import StreamingQuality from '../enums/audio-quality'
|
||||
import { getAudioCache } from '../api/mutations/download/offlineModeUtils'
|
||||
import RNFS from 'react-native-fs'
|
||||
|
||||
/**
|
||||
* Ensures a valid session ID is returned.
|
||||
* The ?? operator doesn't catch empty strings, so we need this helper.
|
||||
* Empty session IDs cause MusicService to crash with "Session ID must be unique. ID="
|
||||
*/
|
||||
function getValidSessionId(sessionId: string | null | undefined): string {
|
||||
if (sessionId && sessionId.trim() !== '') {
|
||||
return sessionId
|
||||
}
|
||||
return uuid.v4().toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the artwork URL for a track, prioritizing the track's own artwork over the album's artwork.
|
||||
* Falls back to artist image if no album artwork is available.
|
||||
@@ -169,16 +181,21 @@ function ensureFileUri(path?: string): string | undefined {
|
||||
}
|
||||
|
||||
function buildDownloadedTrack(downloadedTrack: JellifyDownload): TrackMediaInfo {
|
||||
// Safely build the image path - artwork is optional and may be undefined
|
||||
const imagePath = downloadedTrack.artwork
|
||||
? `file://${RNFS.DocumentDirectoryPath}/${downloadedTrack.artwork.split('/').pop()}`
|
||||
: undefined
|
||||
|
||||
return {
|
||||
type: TrackType.Default,
|
||||
url: `file://${RNFS.DocumentDirectoryPath}/${downloadedTrack.path!.split('/').pop()}`,
|
||||
image: `file://${RNFS.DocumentDirectoryPath}/${downloadedTrack.artwork!.split('/').pop()}`,
|
||||
image: imagePath,
|
||||
duration: convertRunTimeTicksToSeconds(
|
||||
downloadedTrack.mediaSourceInfo?.RunTimeTicks || downloadedTrack.item.RunTimeTicks || 0,
|
||||
),
|
||||
item: downloadedTrack.item,
|
||||
mediaSourceInfo: downloadedTrack.mediaSourceInfo,
|
||||
sessionId: downloadedTrack.sessionId,
|
||||
sessionId: getValidSessionId(downloadedTrack.sessionId),
|
||||
sourceType: 'download',
|
||||
}
|
||||
}
|
||||
@@ -198,7 +215,7 @@ function buildTranscodedTrack(
|
||||
duration: convertRunTimeTicksToSeconds(RunTimeTicks ?? 0),
|
||||
mediaSourceInfo,
|
||||
item,
|
||||
sessionId,
|
||||
sessionId: getValidSessionId(sessionId),
|
||||
sourceType: 'stream',
|
||||
}
|
||||
}
|
||||
@@ -228,7 +245,7 @@ function buildAudioApiUrl(
|
||||
const mediaSource = mediaInfo!.MediaSources![0]
|
||||
|
||||
urlParams = {
|
||||
playSessionId: mediaInfo?.PlaySessionId ?? uuid.v4(),
|
||||
playSessionId: getValidSessionId(mediaInfo?.PlaySessionId),
|
||||
startTimeTicks: '0',
|
||||
static: 'true',
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user