Fix root-relative playable path normalization
This commit is contained in:
@@ -23,6 +23,8 @@ interface RootState {
|
||||
reconnectTimer: ReturnType<typeof setTimeout> | null
|
||||
}
|
||||
|
||||
const MERGED_KEY_DELIMITER = "::"
|
||||
|
||||
/**
|
||||
* Composable that connects to per-root MediaHive WebSockets and keeps
|
||||
* a merged media index updated in real time.
|
||||
@@ -104,20 +106,28 @@ export function useMediaWebSocket() {
|
||||
k,
|
||||
{
|
||||
...t,
|
||||
playable_file: expandPlayablePath(k, t.playable_file || null),
|
||||
playable_file: expandPlayablePath(resolveFilePathKey(k), t.playable_file || null),
|
||||
root_id: t.root_id || rootId,
|
||||
},
|
||||
]),
|
||||
)
|
||||
}
|
||||
|
||||
function resolveFilePathKey(key: string): string {
|
||||
const delimIndex = key.indexOf(MERGED_KEY_DELIMITER)
|
||||
if (delimIndex >= 0) {
|
||||
return key.slice(delimIndex + MERGED_KEY_DELIMITER.length)
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
function mergeTorrentDicts(
|
||||
a: { [key: string]: Torrent },
|
||||
b: { [key: string]: Torrent },
|
||||
): { [key: string]: Torrent } {
|
||||
const merged: { [key: string]: Torrent } = { ...a }
|
||||
for (const [k, t] of Object.entries(b)) {
|
||||
const uniqueKey = merged[k] ? `${t.root_id || "unknown"}:${k}` : k
|
||||
const uniqueKey = merged[k] ? `${t.root_id || "unknown"}${MERGED_KEY_DELIMITER}${k}` : k
|
||||
merged[uniqueKey] = t
|
||||
}
|
||||
const sorted = Object.entries(merged).sort(([, t1], [, t2]) => {
|
||||
@@ -130,11 +140,11 @@ export function useMediaWebSocket() {
|
||||
}
|
||||
|
||||
function withMovieIdentity(id: string, movie: Movie, rootId: string): MovieUi {
|
||||
return { ...normalizeMovie(movie), id, root_id: rootId }
|
||||
return { ...normalizeMovie(movie, rootId), id, root_id: rootId }
|
||||
}
|
||||
|
||||
function withSeriesIdentity(id: string, series: Series, rootId: string): SeriesUi {
|
||||
return { ...normalizeSeries(series), id, root_id: rootId }
|
||||
return { ...normalizeSeries(series, rootId), id, root_id: rootId }
|
||||
}
|
||||
|
||||
function normalizeCastMember(member: unknown): {
|
||||
@@ -197,16 +207,24 @@ export function useMediaWebSocket() {
|
||||
return next
|
||||
}
|
||||
|
||||
function normalizeMovie(movie: Movie): Movie {
|
||||
function normalizeMovie(movie: Movie, rootId: string | null): Movie {
|
||||
return {
|
||||
...movie,
|
||||
files: annotateFiles(movie.files, rootId),
|
||||
info: normalizeInfo(movie.info),
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeSeries(series: Series): Series {
|
||||
function normalizeSeries(series: Series, rootId: string | null): Series {
|
||||
return {
|
||||
...series,
|
||||
seasons: (series.seasons || []).map((season) => ({
|
||||
...season,
|
||||
episodes: (season.episodes || []).map((episode) => ({
|
||||
...episode,
|
||||
files: annotateFiles(episode.files, rootId),
|
||||
})),
|
||||
})),
|
||||
info: normalizeInfo(series.info),
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -491,7 +491,7 @@ async def play_media(root_id: str, request: Request):
|
||||
"""Open a media file with the selected player."""
|
||||
ctx = _get_context(root_id)
|
||||
req = msgspec.json.decode(await request.body(), type=PlayMediaRequest)
|
||||
file_path = ctx.root_path / req.file_path
|
||||
file_path = _resolve_root_scoped_path(ctx.root_path, req.file_path)
|
||||
|
||||
if not file_path.exists():
|
||||
raise HTTPException(status_code=404, detail=f"File not found: {req.file_path}")
|
||||
@@ -525,7 +525,7 @@ async def open_folder(root_id: str, request: Request):
|
||||
"""Open a folder in the system file explorer."""
|
||||
ctx = _get_context(root_id)
|
||||
req = msgspec.json.decode(await request.body(), type=OpenFolderRequest)
|
||||
target_path = ctx.root_path / req.folder_path
|
||||
target_path = _resolve_root_scoped_path(ctx.root_path, req.folder_path)
|
||||
|
||||
if not target_path.exists():
|
||||
raise HTTPException(
|
||||
|
||||
Reference in New Issue
Block a user