refactor: split main runtime flows into focused modules

This commit is contained in:
2026-02-19 16:57:06 -08:00
parent 162be118e1
commit d5d71816ac
31 changed files with 3270 additions and 672 deletions

View File

@@ -0,0 +1,116 @@
import type { CliArgs } from '../../cli/args';
type JellyfinSession = {
serverUrl: string;
accessToken: string;
userId: string;
username: string;
};
type JellyfinClientInfo = {
clientName: string;
clientVersion: string;
deviceId: string;
};
type JellyfinConfig = {
defaultLibraryId: string;
};
export function createHandleJellyfinListCommands(deps: {
listJellyfinLibraries: (
session: JellyfinSession,
clientInfo: JellyfinClientInfo,
) => Promise<Array<{ id: string; name: string; collectionType?: string; type?: string }>>;
listJellyfinItems: (
session: JellyfinSession,
clientInfo: JellyfinClientInfo,
params: { libraryId: string; searchTerm?: string; limit: number },
) => Promise<Array<{ id: string; title: string; type: string }>>;
listJellyfinSubtitleTracks: (
session: JellyfinSession,
clientInfo: JellyfinClientInfo,
itemId: string,
) => Promise<
Array<{
index: number;
language?: string;
title?: string;
deliveryMethod?: string;
codec?: string;
isDefault?: boolean;
isForced?: boolean;
isExternal?: boolean;
deliveryUrl?: string | null;
}>
>;
logInfo: (message: string) => void;
}) {
return async (params: {
args: CliArgs;
session: JellyfinSession;
clientInfo: JellyfinClientInfo;
jellyfinConfig: JellyfinConfig;
}): Promise<boolean> => {
const { args, session, clientInfo, jellyfinConfig } = params;
if (args.jellyfinLibraries) {
const libraries = await deps.listJellyfinLibraries(session, clientInfo);
if (libraries.length === 0) {
deps.logInfo('No Jellyfin libraries found.');
return true;
}
for (const library of libraries) {
deps.logInfo(
`Jellyfin library: ${library.name} [${library.id}] (${library.collectionType || library.type || 'unknown'})`,
);
}
return true;
}
if (args.jellyfinItems) {
const libraryId = args.jellyfinLibraryId || jellyfinConfig.defaultLibraryId;
if (!libraryId) {
throw new Error(
'Missing Jellyfin library id. Use --jellyfin-library-id or set jellyfin.defaultLibraryId.',
);
}
const items = await deps.listJellyfinItems(session, clientInfo, {
libraryId,
searchTerm: args.jellyfinSearch,
limit: args.jellyfinLimit ?? 100,
});
if (items.length === 0) {
deps.logInfo('No Jellyfin items found for the selected library/search.');
return true;
}
for (const item of items) {
deps.logInfo(`Jellyfin item: ${item.title} [${item.id}] (${item.type})`);
}
return true;
}
if (args.jellyfinSubtitles) {
if (!args.jellyfinItemId) {
throw new Error('Missing --jellyfin-item-id for --jellyfin-subtitles.');
}
const tracks = await deps.listJellyfinSubtitleTracks(session, clientInfo, args.jellyfinItemId);
if (tracks.length === 0) {
deps.logInfo('No Jellyfin subtitle tracks found for item.');
return true;
}
for (const track of tracks) {
if (args.jellyfinSubtitleUrlsOnly) {
if (track.deliveryUrl) deps.logInfo(track.deliveryUrl);
continue;
}
deps.logInfo(
`Jellyfin subtitle: index=${track.index} lang=${track.language || 'unknown'} title="${track.title || '-'}" method=${track.deliveryMethod || 'unknown'} codec=${track.codec || 'unknown'} default=${track.isDefault ? 'yes' : 'no'} forced=${track.isForced ? 'yes' : 'no'} external=${track.isExternal ? 'yes' : 'no'} url=${track.deliveryUrl || '-'}`,
);
}
return true;
}
return false;
};
}