feat(library and playback): Support for serverside channel plugins and hls streaming
This commit is contained in:
+38
-2
@@ -1134,6 +1134,24 @@ async repositoryGetVideoStreamUrl(handle: string, itemId: string, mediaSourceId:
|
||||
async repositoryGetAudioStreamUrl(handle: string, itemId: string) : Promise<string> {
|
||||
return await TAURI_INVOKE("repository_get_audio_stream_url", { handle, itemId });
|
||||
},
|
||||
/**
|
||||
* Get Live TV channels (broadcast / IPTV) for browsing
|
||||
*/
|
||||
async repositoryGetLiveTvChannels(handle: string) : Promise<MediaItem[]> {
|
||||
return await TAURI_INVOKE("repository_get_live_tv_channels", { handle });
|
||||
},
|
||||
/**
|
||||
* Get the root list of plugin "Channels"
|
||||
*/
|
||||
async repositoryGetChannels(handle: string) : Promise<SearchResult> {
|
||||
return await TAURI_INVOKE("repository_get_channels", { handle });
|
||||
},
|
||||
/**
|
||||
* Open a live stream for a Live TV channel / live item
|
||||
*/
|
||||
async repositoryOpenLiveStream(handle: string, itemId: string) : Promise<LiveStreamInfo> {
|
||||
return await TAURI_INVOKE("repository_open_live_stream", { handle, itemId });
|
||||
},
|
||||
/**
|
||||
* Report playback start
|
||||
*/
|
||||
@@ -1539,6 +1557,14 @@ export type ImageType = "Primary" | "Backdrop" | "Banner" | "Thumb" | "Logo"
|
||||
* Library (media collection)
|
||||
*/
|
||||
export type Library = { id: string; name: string; collectionType: string; imageTag?: string | null }
|
||||
/**
|
||||
* Live stream information returned from opening a Live TV / channel stream.
|
||||
*
|
||||
* Unlike on-demand video, a live channel must be "opened" before it can be
|
||||
* streamed; the server returns a transcoding URL (already absolute) plus a
|
||||
* `live_stream_id` that can later be used to close the stream.
|
||||
*/
|
||||
export type LiveStreamInfo = { streamUrl: string; playSessionId: string | null; liveStreamId: string | null; mediaSourceId: string | null }
|
||||
/**
|
||||
* An LMS multi-room sync group, as returned by JellyLMS `/JellyLms/SyncGroups`.
|
||||
*
|
||||
@@ -1549,7 +1575,12 @@ export type LmsSyncGroup = { masterMac: string; masterName?: string; slaveMacs?:
|
||||
/**
|
||||
* Media item
|
||||
*/
|
||||
export type MediaItem = { id: string; name: string; type: string; serverId: string; parentId?: string | null; libraryId?: string | null; overview?: string | null; genres?: string[] | null; productionYear?: number | null; communityRating?: number | null; officialRating?: string | null; runTimeTicks?: number | null; primaryImageTag?: string | null; backdropImageTags?: string[] | null; parentBackdropImageTags?: string[] | null; albumId?: string | null; albumName?: string | null; albumArtist?: string | null; artists?: string[] | null; artistItems?: ArtistItem[] | null; indexNumber?: number | null; parentIndexNumber?: number | null; seriesId?: string | null; seriesName?: string | null; seasonId?: string | null; seasonName?: string | null; userData?: UserData | null; mediaStreams?: MediaStream[] | null; mediaSources?: MediaSource[] | null; people?: Person[] | null }
|
||||
export type MediaItem = { id: string; name: string; type: string;
|
||||
/**
|
||||
* Whether this item is a folder/container (vs a playable leaf). Used to
|
||||
* decide whether a channel item drills into a list or plays directly.
|
||||
*/
|
||||
isFolder?: boolean; serverId: string; parentId?: string | null; libraryId?: string | null; overview?: string | null; genres?: string[] | null; productionYear?: number | null; communityRating?: number | null; officialRating?: string | null; runTimeTicks?: number | null; primaryImageTag?: string | null; backdropImageTags?: string[] | null; parentBackdropImageTags?: string[] | null; albumId?: string | null; albumName?: string | null; albumArtist?: string | null; artists?: string[] | null; artistItems?: ArtistItem[] | null; indexNumber?: number | null; parentIndexNumber?: number | null; seriesId?: string | null; seriesName?: string | null; seasonId?: string | null; seasonName?: string | null; userData?: UserData | null; mediaStreams?: MediaStream[] | null; mediaSources?: MediaSource[] | null; people?: Person[] | null }
|
||||
/**
|
||||
* Media session type tracking the high-level playback context
|
||||
*/
|
||||
@@ -1927,7 +1958,12 @@ export type PlaylistEntry =
|
||||
/**
|
||||
* The underlying media item
|
||||
*/
|
||||
({ id: string; name: string; type: string; serverId: string; parentId?: string | null; libraryId?: string | null; overview?: string | null; genres?: string[] | null; productionYear?: number | null; communityRating?: number | null; officialRating?: string | null; runTimeTicks?: number | null; primaryImageTag?: string | null; backdropImageTags?: string[] | null; parentBackdropImageTags?: string[] | null; albumId?: string | null; albumName?: string | null; albumArtist?: string | null; artists?: string[] | null; artistItems?: ArtistItem[] | null; indexNumber?: number | null; parentIndexNumber?: number | null; seriesId?: string | null; seriesName?: string | null; seasonId?: string | null; seasonName?: string | null; userData?: UserData | null; mediaStreams?: MediaStream[] | null; mediaSources?: MediaSource[] | null; people?: Person[] | null }) & {
|
||||
({ id: string; name: string; type: string;
|
||||
/**
|
||||
* Whether this item is a folder/container (vs a playable leaf). Used to
|
||||
* decide whether a channel item drills into a list or plays directly.
|
||||
*/
|
||||
isFolder?: boolean; serverId: string; parentId?: string | null; libraryId?: string | null; overview?: string | null; genres?: string[] | null; productionYear?: number | null; communityRating?: number | null; officialRating?: string | null; runTimeTicks?: number | null; primaryImageTag?: string | null; backdropImageTags?: string[] | null; parentBackdropImageTags?: string[] | null; albumId?: string | null; albumName?: string | null; albumArtist?: string | null; artists?: string[] | null; artistItems?: ArtistItem[] | null; indexNumber?: number | null; parentIndexNumber?: number | null; seriesId?: string | null; seriesName?: string | null; seasonId?: string | null; seasonName?: string | null; userData?: UserData | null; mediaStreams?: MediaStream[] | null; mediaSources?: MediaSource[] | null; people?: Person[] | null }) & {
|
||||
/**
|
||||
* The playlist-scoped entry ID (Jellyfin's PlaylistItemId)
|
||||
*/
|
||||
|
||||
@@ -11,6 +11,7 @@ import type {
|
||||
GetItemsOptions,
|
||||
SearchOptions,
|
||||
PlaybackInfo,
|
||||
LiveStreamInfo,
|
||||
ImageType,
|
||||
ImageOptions,
|
||||
Genre,
|
||||
@@ -161,6 +162,23 @@ export class RepositoryClient {
|
||||
);
|
||||
}
|
||||
|
||||
// ===== Live TV / Channels =====
|
||||
|
||||
/** Browse Live TV channels (broadcast / IPTV). */
|
||||
async getLiveTvChannels(): Promise<MediaItem[]> {
|
||||
return commands.repositoryGetLiveTvChannels(this.ensureHandle());
|
||||
}
|
||||
|
||||
/** Browse the root list of plugin "Channels". Drill-down uses getItems(channelId). */
|
||||
async getChannels(): Promise<SearchResult> {
|
||||
return commands.repositoryGetChannels(this.ensureHandle());
|
||||
}
|
||||
|
||||
/** Open a live stream for a Live TV channel / live item before HLS playback. */
|
||||
async openLiveStream(itemId: string): Promise<LiveStreamInfo> {
|
||||
return commands.repositoryOpenLiveStream(this.ensureHandle(), itemId);
|
||||
}
|
||||
|
||||
// ===== URL Construction Methods (sync, no server call) =====
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,6 +12,7 @@ export type {
|
||||
ImageOptions,
|
||||
ImageType,
|
||||
Library,
|
||||
LiveStreamInfo,
|
||||
MediaItem,
|
||||
MediaSource,
|
||||
MediaStream,
|
||||
@@ -51,6 +52,7 @@ export type ItemType =
|
||||
| "CollectionFolder"
|
||||
| "Channel"
|
||||
| "ChannelFolderItem"
|
||||
| "TvChannel"
|
||||
| "Person";
|
||||
|
||||
export type LibraryType =
|
||||
@@ -63,6 +65,7 @@ export type LibraryType =
|
||||
| "boxsets"
|
||||
| "playlists"
|
||||
| "channels"
|
||||
| "livetv"
|
||||
| "unknown";
|
||||
|
||||
export type PersonType =
|
||||
|
||||
Reference in New Issue
Block a user