import {z} from 'zod' export type HeaderMatchType = 'Equals' | 'Regex' | 'Substring'; export type HttpHeaderInfo = {Name?: (string | null) | undefined, Value?: (string | null) | undefined, Match?: HeaderMatchType | undefined}; export type DeviceIdentification = {FriendlyName?: (string | null) | undefined, ModelNumber?: (string | null) | undefined, SerialNumber?: (string | null) | undefined, ModelName?: (string | null) | undefined, ModelDescription?: (string | null) | undefined, ModelUrl?: (string | null) | undefined, Manufacturer?: (string | null) | undefined, ManufacturerUrl?: (string | null) | undefined, Headers?: (Array | null) | undefined}; export type XmlAttribute = {Name?: (string | null) | undefined, Value?: (string | null) | undefined}; export type DlnaProfileType = 'Audio' | 'Video' | 'Photo'; export type DirectPlayProfile = {Container?: (string | null) | undefined, AudioCodec?: (string | null) | undefined, VideoCodec?: (string | null) | undefined, Type?: DlnaProfileType | undefined}; export type TranscodeSeekInfo = 'Auto' | 'Bytes'; export type EncodingContext = 'Streaming' | 'Static'; export type TranscodingProfile = {Container?: (string | null) | undefined, Type?: DlnaProfileType | undefined, VideoCodec?: (string | null) | undefined, AudioCodec?: (string | null) | undefined, Protocol?: (string | null) | undefined, EstimateContentLength?: boolean | undefined, EnableMpegtsM2TsMode?: boolean | undefined, TranscodeSeekInfo?: TranscodeSeekInfo | undefined, CopyTimestamps?: boolean | undefined, Context?: EncodingContext | undefined, EnableSubtitlesInManifest?: boolean | undefined, MaxAudioChannels?: (string | null) | undefined, MinSegments?: number | undefined, SegmentLength?: number | undefined, BreakOnNonKeyFrames?: boolean | undefined}; export type ProfileConditionType = 'Equals' | 'NotEquals' | 'LessThanEqual' | 'GreaterThanEqual' | 'EqualsAny'; export type ProfileConditionValue = 'AudioChannels' | 'AudioBitrate' | 'AudioProfile' | 'Width' | 'Height' | 'Has64BitOffsets' | 'PacketLength' | 'VideoBitDepth' | 'VideoBitrate' | 'VideoFramerate' | 'VideoLevel' | 'VideoProfile' | 'VideoTimestamp' | 'IsAnamorphic' | 'RefFrames' | 'NumAudioStreams' | 'NumVideoStreams' | 'IsSecondaryAudio' | 'VideoCodecTag' | 'IsAvc' | 'IsInterlaced' | 'AudioSampleRate' | 'AudioBitDepth'; export type ProfileCondition = {Condition?: ProfileConditionType | undefined, Property?: ProfileConditionValue | undefined, Value?: (string | null) | undefined, IsRequired?: boolean | undefined}; export type ContainerProfile = {Type?: DlnaProfileType | undefined, Conditions?: (Array | null) | undefined, Container?: (string | null) | undefined}; export type CodecType = 'Video' | 'VideoAudio' | 'Audio'; export type CodecProfile = {Type?: CodecType | undefined, Conditions?: (Array | null) | undefined, ApplyConditions?: (Array | null) | undefined, Codec?: (string | null) | undefined, Container?: (string | null) | undefined}; export type ResponseProfile = {Container?: (string | null) | undefined, AudioCodec?: (string | null) | undefined, VideoCodec?: (string | null) | undefined, Type?: DlnaProfileType | undefined, OrgPn?: (string | null) | undefined, MimeType?: (string | null) | undefined, Conditions?: (Array | null) | undefined}; export type SubtitleDeliveryMethod = 'Encode' | 'Embed' | 'External' | 'Hls'; export type SubtitleProfile = {Format?: (string | null) | undefined, Method?: SubtitleDeliveryMethod | undefined, DidlMode?: (string | null) | undefined, Language?: (string | null) | undefined, Container?: (string | null) | undefined}; export type DeviceProfile = {Name?: (string | null) | undefined, Id?: (string | null) | undefined, Identification?: DeviceIdentification | undefined, FriendlyName?: (string | null) | undefined, Manufacturer?: (string | null) | undefined, ManufacturerUrl?: (string | null) | undefined, ModelName?: (string | null) | undefined, ModelDescription?: (string | null) | undefined, ModelNumber?: (string | null) | undefined, ModelUrl?: (string | null) | undefined, SerialNumber?: (string | null) | undefined, EnableAlbumArtInDidl?: boolean | undefined, EnableSingleAlbumArtLimit?: boolean | undefined, EnableSingleSubtitleLimit?: boolean | undefined, SupportedMediaTypes?: (string | null) | undefined, UserId?: (string | null) | undefined, AlbumArtPn?: (string | null) | undefined, MaxAlbumArtWidth?: number | undefined, MaxAlbumArtHeight?: number | undefined, MaxIconWidth?: (number | null) | undefined, MaxIconHeight?: (number | null) | undefined, MaxStreamingBitrate?: (number | null) | undefined, MaxStaticBitrate?: (number | null) | undefined, MusicStreamingTranscodingBitrate?: (number | null) | undefined, MaxStaticMusicBitrate?: (number | null) | undefined, SonyAggregationFlags?: (string | null) | undefined, ProtocolInfo?: (string | null) | undefined, TimelineOffsetSeconds?: number | undefined, RequiresPlainVideoItems?: boolean | undefined, RequiresPlainFolders?: boolean | undefined, EnableMSMediaReceiverRegistrar?: boolean | undefined, IgnoreTranscodeByteRangeRequests?: boolean | undefined, XmlRootAttributes?: (Array | null) | undefined, DirectPlayProfiles?: (Array | null) | undefined, TranscodingProfiles?: (Array | null) | undefined, ContainerProfiles?: (Array | null) | undefined, CodecProfiles?: (Array | null) | undefined, ResponseProfiles?: (Array | null) | undefined, SubtitleProfiles?: (Array | null) | undefined}; export type PlaybackInfoDto = {UserId?: (string | null) | undefined, MaxStreamingBitrate?: (number | null) | undefined, StartTimeTicks?: (number | null) | undefined, AudioStreamIndex?: (number | null) | undefined, SubtitleStreamIndex?: (number | null) | undefined, MaxAudioChannels?: (number | null) | undefined, MediaSourceId?: (string | null) | undefined, LiveStreamId?: (string | null) | undefined, DeviceProfile?: DeviceProfile | undefined, EnableDirectPlay?: (boolean | null) | undefined, EnableDirectStream?: (boolean | null) | undefined, EnableTranscoding?: (boolean | null) | undefined, AllowVideoStreamCopy?: (boolean | null) | undefined, AllowAudioStreamCopy?: (boolean | null) | undefined, AutoOpenLiveStream?: (boolean | null) | undefined}; export type CreateApiItemsItemIdPlaybackInfoArgs = {itemId: string, userId?: (string | null) | undefined, maxStreamingBitrate?: (number | null) | undefined, startTimeTicks?: (number | null) | undefined, audioStreamIndex?: (number | null) | undefined, subtitleStreamIndex?: (number | null) | undefined, maxAudioChannels?: (number | null) | undefined, mediaSourceId?: (string | null) | undefined, liveStreamId?: (string | null) | undefined, autoOpenLiveStream?: (boolean | null) | undefined, enableDirectPlay?: (boolean | null) | undefined, enableDirectStream?: (boolean | null) | undefined, enableTranscoding?: (boolean | null) | undefined, allowVideoStreamCopy?: (boolean | null) | undefined, allowAudioStreamCopy?: (boolean | null) | undefined, body: PlaybackInfoDto}; export type CreateApiItemsItemIdPlaybackInfoOptions = {baseUrl?: string | undefined, CustomAuthentication?: string | undefined, fetch?: typeof fetch | undefined}; export const mediaProtocol = z.enum(["File", "Http", "Rtmp", "Rtsp", "Udp", "Rtp", "Ftp"]); export const mediaSourceType = z.enum(["Default", "Grouping", "Placeholder"]); export const videoType = z.enum(["VideoFile", "Iso", "Dvd", "BluRay"]); export const isoType = z.enum(["Dvd", "BluRay"]); export const video3DFormat = z.enum(["HalfSideBySide", "FullSideBySide", "FullTopAndBottom", "HalfTopAndBottom", "MVC"]); export const mediaStreamType = z.enum(["Audio", "Video", "Subtitle", "EmbeddedImage"]); export const subtitleDeliveryMethod = z.enum(["Encode", "Embed", "External", "Hls"]); export const mediaStream = z.object({Codec: z.string().nullable().optional(), CodecTag: z.string().nullable().optional(), Language: z.string().nullable().optional(), ColorRange: z.string().nullable().optional(), ColorSpace: z.string().nullable().optional(), ColorTransfer: z.string().nullable().optional(), ColorPrimaries: z.string().nullable().optional(), Comment: z.string().nullable().optional(), TimeBase: z.string().nullable().optional(), CodecTimeBase: z.string().nullable().optional(), Title: z.string().nullable().optional(), VideoRange: z.string().nullable().optional(), localizedUndefined: z.string().nullable().optional(), localizedDefault: z.string().nullable().optional(), localizedForced: z.string().nullable().optional(), DisplayTitle: z.string().nullable().optional(), NalLengthSize: z.string().nullable().optional(), IsInterlaced: z.boolean().optional(), IsAVC: z.boolean().nullable().optional(), ChannelLayout: z.string().nullable().optional(), BitRate: z.number().int().nullable().optional(), BitDepth: z.number().int().nullable().optional(), RefFrames: z.number().int().nullable().optional(), PacketLength: z.number().int().nullable().optional(), Channels: z.number().int().nullable().optional(), SampleRate: z.number().int().nullable().optional(), IsDefault: z.boolean().optional(), IsForced: z.boolean().optional(), Height: z.number().int().nullable().optional(), Width: z.number().int().nullable().optional(), AverageFrameRate: z.number().nullable().optional(), RealFrameRate: z.number().nullable().optional(), Profile: z.string().nullable().optional(), Type: mediaStreamType.optional(), AspectRatio: z.string().nullable().optional(), Index: z.number().int().optional(), Score: z.number().int().nullable().optional(), IsExternal: z.boolean().optional(), DeliveryMethod: subtitleDeliveryMethod.optional(), DeliveryUrl: z.string().nullable().optional(), IsExternalUrl: z.boolean().nullable().optional(), IsTextSubtitleStream: z.boolean().optional(), SupportsExternalStream: z.boolean().optional(), Path: z.string().nullable().optional(), PixelFormat: z.string().nullable().optional(), Level: z.number().nullable().optional(), IsAnamorphic: z.boolean().nullable().optional()}); export const mediaAttachment = z.object({Codec: z.string().nullable().optional(), CodecTag: z.string().nullable().optional(), Comment: z.string().nullable().optional(), Index: z.number().int().optional(), FileName: z.string().nullable().optional(), MimeType: z.string().nullable().optional(), DeliveryUrl: z.string().nullable().optional()}); export const transportStreamTimestamp = z.enum(["None", "Zero", "Valid"]); export const mediaSourceInfo = z.object({Protocol: mediaProtocol.optional(), Id: z.string().nullable().optional(), Path: z.string().nullable().optional(), EncoderPath: z.string().nullable().optional(), EncoderProtocol: mediaProtocol.optional(), Type: mediaSourceType.optional(), Container: z.string().nullable().optional(), Size: z.number().int().nullable().optional(), Name: z.string().nullable().optional(), IsRemote: z.boolean().optional(), ETag: z.string().nullable().optional(), RunTimeTicks: z.number().int().nullable().optional(), ReadAtNativeFramerate: z.boolean().optional(), IgnoreDts: z.boolean().optional(), IgnoreIndex: z.boolean().optional(), GenPtsInput: z.boolean().optional(), SupportsTranscoding: z.boolean().optional(), SupportsDirectStream: z.boolean().optional(), SupportsDirectPlay: z.boolean().optional(), IsInfiniteStream: z.boolean().optional(), RequiresOpening: z.boolean().optional(), OpenToken: z.string().nullable().optional(), RequiresClosing: z.boolean().optional(), LiveStreamId: z.string().nullable().optional(), BufferMs: z.number().int().nullable().optional(), RequiresLooping: z.boolean().optional(), SupportsProbing: z.boolean().optional(), VideoType: videoType.optional(), IsoType: isoType.optional(), Video3DFormat: video3DFormat.optional(), MediaStreams: z.array(mediaStream).nullable().optional(), MediaAttachments: z.array(mediaAttachment).nullable().optional(), Formats: z.array(z.string()).nullable().optional(), Bitrate: z.number().int().nullable().optional(), Timestamp: transportStreamTimestamp.optional(), RequiredHttpHeaders: z.record(z.string(), z.string()).nullable().optional(), TranscodingUrl: z.string().nullable().optional(), TranscodingSubProtocol: z.string().nullable().optional(), TranscodingContainer: z.string().nullable().optional(), AnalyzeDurationMs: z.number().int().nullable().optional(), DefaultAudioStreamIndex: z.number().int().nullable().optional(), DefaultSubtitleStreamIndex: z.number().int().nullable().optional()}); export const playbackErrorCode = z.enum(["NotAllowed", "NoCompatibleStream", "RateLimitExceeded"]); export const playbackInfoResponse = z.object({MediaSources: z.array(mediaSourceInfo).nullable().optional(), PlaySessionId: z.string().nullable().optional(), ErrorCode: playbackErrorCode.optional()}); export const validateResponse = async ( schema: { readonly '~standard': { readonly validate: (value: unknown) => ({ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }) | Promise<{ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }> } }, value: unknown ): Promise => { const result = await schema['~standard'].validate(value) if (result.issues !== undefined) { const detail = result.issues .map(({ message, path }) => path?.length ? `${path.map((segment) => String(typeof segment === 'object' ? segment.key : segment)).join('.')}: ${message}` : message ) .join('; ') throw new Error(`Response does not match the schema: ${detail}`, { cause: { issues: result.issues } }) } return result.value }; /** * Gets live playback media info for an item. * * POST /Items/{itemId}/PlaybackInfo */ export const createApiItemsItemIdPlaybackInfo = async (args: CreateApiItemsItemIdPlaybackInfoArgs, options?: CreateApiItemsItemIdPlaybackInfoOptions) => { const {baseUrl = "http://localhost", fetch: fetchFn = fetch, CustomAuthentication} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('content-type', "application/json") headers.set('accept', "application/json") if (args.userId != null) query.append("userId", String(args.userId)) if (args.maxStreamingBitrate != null) query.append("maxStreamingBitrate", String(args.maxStreamingBitrate)) if (args.startTimeTicks != null) query.append("startTimeTicks", String(args.startTimeTicks)) if (args.audioStreamIndex != null) query.append("audioStreamIndex", String(args.audioStreamIndex)) if (args.subtitleStreamIndex != null) query.append("subtitleStreamIndex", String(args.subtitleStreamIndex)) if (args.maxAudioChannels != null) query.append("maxAudioChannels", String(args.maxAudioChannels)) if (args.mediaSourceId != null) query.append("mediaSourceId", String(args.mediaSourceId)) if (args.liveStreamId != null) query.append("liveStreamId", String(args.liveStreamId)) if (args.autoOpenLiveStream != null) query.append("autoOpenLiveStream", String(args.autoOpenLiveStream)) if (args.enableDirectPlay != null) query.append("enableDirectPlay", String(args.enableDirectPlay)) if (args.enableDirectStream != null) query.append("enableDirectStream", String(args.enableDirectStream)) if (args.enableTranscoding != null) query.append("enableTranscoding", String(args.enableTranscoding)) if (args.allowVideoStreamCopy != null) query.append("allowVideoStreamCopy", String(args.allowVideoStreamCopy)) if (args.allowAudioStreamCopy != null) query.append("allowAudioStreamCopy", String(args.allowAudioStreamCopy)) if (CustomAuthentication !== undefined) headers.set("X-Emby-Authorization", CustomAuthentication) const url = `${baseUrl.replace(/\/+$/, '')}/Items/${encodeURIComponent(String(args.itemId))}/PlaybackInfo${query.size > 0 ? `?${query}` : ''}` const res = await fetchFn(url, {method: "POST", headers, body: JSON.stringify(args.body)}) if (!res.ok) throw new Error(`Request failed with status ${res.status}`, {cause: {status: res.status, body: await res.text()}}) return validateResponse(playbackInfoResponse, await res.json()) };