mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-09-21 17:16:20 -07:00
fix(jellyfin): fix jellyfin media metadata (#250)
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
import { createHash } from 'node:crypto';
|
||||
|
||||
const URL_SCHEME = /[a-z][a-z0-9+.-]*:\/\//i;
|
||||
const QUERY_PAIR = /[?&][^=\s&#]+=/;
|
||||
const CREDENTIAL_LABEL = /\b(?:api[_ -]?key|access[_ -]?token|x[_ -]?emby[_ -]?token)\b/i;
|
||||
|
||||
/** mpv can report the URL or its query-bearing basename before metadata arrives. */
|
||||
export function sanitizeMediaTitle(value: string | null | undefined): string | null {
|
||||
const title = value?.trim();
|
||||
if (!title) return null;
|
||||
let decoded = title;
|
||||
try {
|
||||
decoded = decodeURIComponent(title);
|
||||
} catch {
|
||||
// Ordinary titles can contain a literal percent sign.
|
||||
}
|
||||
if (URL_SCHEME.test(decoded) || QUERY_PAIR.test(decoded) || CREDENTIAL_LABEL.test(decoded)) {
|
||||
return null;
|
||||
}
|
||||
return title;
|
||||
}
|
||||
|
||||
export function resolveMediaLookupTarget(
|
||||
mediaPath: string | null,
|
||||
mediaTitle: string | null,
|
||||
): string | null {
|
||||
return sanitizeMediaTitle(mediaPath) ?? sanitizeMediaTitle(mediaTitle);
|
||||
}
|
||||
|
||||
/** Persistent identity, never a URL to use for authenticated media retrieval. */
|
||||
export function toMediaIdentityPath(mediaPath: string): string {
|
||||
const value = mediaPath.trim();
|
||||
if (!URL_SCHEME.test(value)) return sanitizeMediaTitle(value) ?? '';
|
||||
try {
|
||||
const url = new URL(value);
|
||||
const jellyfinItem = url.pathname.match(
|
||||
/\/Videos\/([^/]+)\/(?:stream(?:\.[^/]*)?|master\.m3u8)\/?$/i,
|
||||
);
|
||||
if (jellyfinItem) return `jellyfin://${url.host}/item/${jellyfinItem[1]}`;
|
||||
url.username = '';
|
||||
url.password = '';
|
||||
const query = url.search;
|
||||
const queryFingerprint = /^#query-[a-f0-9]{64}$/.test(url.hash) ? url.hash : '';
|
||||
const youtubeId = /^(?:www\.|m\.)?youtube\.com$/i.test(url.hostname)
|
||||
? url.searchParams.get('v')
|
||||
: null;
|
||||
url.search = '';
|
||||
url.hash = queryFingerprint;
|
||||
if (youtubeId) url.searchParams.set('v', youtubeId);
|
||||
else if (query) {
|
||||
// Different query-selected videos must not collapse into one stats entry.
|
||||
url.hash = `query-${createHash('sha256').update(query).digest('hex')}`;
|
||||
}
|
||||
return url.toString();
|
||||
} catch {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user