diff --git a/Plugins/CreateAPI/GeneratePlugin.swift b/Plugins/CreateAPI/GeneratePlugin.swift index 0d7ad619..2a8f7f94 100644 --- a/Plugins/CreateAPI/GeneratePlugin.swift +++ b/Plugins/CreateAPI/GeneratePlugin.swift @@ -22,6 +22,7 @@ struct Plugin: CommandPlugin { // Apply post patches try await patchRemoteSearchResult(context: context) try await patchAnyJSON(context: context) + try await patchGroupUpdateDiscriminator(context: context) // Move patch files try await addSpecialFeatureType(context: context) @@ -103,8 +104,8 @@ struct Plugin: CommandPlugin { try FileManager.default.removeItem(atPath: filePath.string) } - // BaseItemDto: add SpecialFeatureType string format and CollectionTypeOptions - // object reference to properties prior to generation + // TODO: remove when BaseItemDto uses `ExtraType` or other + // BaseItemDto: add SpecialFeatureType string format to property prior to generation private func patchBaseItemDtoSchema(context: PluginContext) async throws { let contents = try await parseOriginalSchema(context: context) @@ -122,16 +123,6 @@ struct Plugin: CommandPlugin { ] ) - // TODO: Uncomment once Swiftfin has refactored how it uses the existing CollectionType - // property for library management -// properties["CollectionType"] = AnyJSON.object( -// [ -// "allOf": .array([.object(["$ref": .string("#/components/schemas/CollectionTypeOptions")])]), -// "nullable": .bool(true), -// "description": .string("Gets or sets the type of the collection."), -// ] -// ) - baseItemDto["properties"] = .object(properties) schemas["BaseItemDto"] = .object(baseItemDto) components["schemas"] = .object(schemas) @@ -195,4 +186,20 @@ struct Plugin: CommandPlugin { try sourceData.write(to: URL(fileURLWithPath: finalFilePath.string)) } + + // TODO: Remove if/when fixed within CreateAPI + // Entities/GroupUpdate.swift: change generated `Type` name to `_Type` + private func patchGroupUpdateDiscriminator(context: PluginContext) async throws { + let filePath = context + .package + .directory + .appending(["Sources", "Entities", "GroupUpdate.swift"]) + + let contents = try String(contentsOfFile: filePath.string) + .replacingOccurrences(of: "Type", with: "_Type") + + try contents + .data(using: .utf8)? + .write(to: URL(fileURLWithPath: filePath.string)) + } } diff --git a/Plugins/CreateAPI/PatchFiles/SpecialFeatureType.swift b/Plugins/CreateAPI/PatchFiles/SpecialFeatureType.swift index 4bcb1ad8..37a3da6d 100644 --- a/Plugins/CreateAPI/PatchFiles/SpecialFeatureType.swift +++ b/Plugins/CreateAPI/PatchFiles/SpecialFeatureType.swift @@ -19,4 +19,6 @@ public enum SpecialFeatureType: String, Codable, CaseIterable { case sample = "Sample" case themeSong = "ThemeSong" case themeVideo = "ThemeVideo" + case featurette = "Featurette" + case short = "Short" } diff --git a/Sources/Entities/ActivityLogEntryMessage.swift b/Sources/Entities/ActivityLogEntryMessage.swift new file mode 100644 index 00000000..4adb61a0 --- /dev/null +++ b/Sources/Entities/ActivityLogEntryMessage.swift @@ -0,0 +1,39 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Activity log created message. +public struct ActivityLogEntryMessage: Codable, Hashable { + /// Gets or sets the data. + public var data: [ActivityLogEntry]? + /// Gets or sets the message id. + public var messageID: String? + /// The different kinds of messages that are used in the WebSocket api. + public var messageType: SessionMessageType? + + public init(data: [ActivityLogEntry]? = nil, messageID: String? = nil, messageType: SessionMessageType? = nil) { + self.data = data + self.messageID = messageID + self.messageType = messageType + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.data = try values.decodeIfPresent([ActivityLogEntry].self, forKey: "Data") + self.messageID = try values.decodeIfPresent(String.self, forKey: "MessageId") + self.messageType = try values.decodeIfPresent(SessionMessageType.self, forKey: "MessageType") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(data, forKey: "Data") + try values.encodeIfPresent(messageID, forKey: "MessageId") + try values.encodeIfPresent(messageType, forKey: "MessageType") + } +} diff --git a/Sources/Entities/ActivityLogEntryStartMessage.swift b/Sources/Entities/ActivityLogEntryStartMessage.swift new file mode 100644 index 00000000..9bec583a --- /dev/null +++ b/Sources/Entities/ActivityLogEntryStartMessage.swift @@ -0,0 +1,36 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Activity log entry start message. +/// +/// Data is the timing data encoded as "$initialDelay,$interval" in ms. +public struct ActivityLogEntryStartMessage: Codable, Hashable { + /// Gets or sets the data. + public var data: String? + /// The different kinds of messages that are used in the WebSocket api. + public var messageType: SessionMessageType? + + public init(data: String? = nil, messageType: SessionMessageType? = nil) { + self.data = data + self.messageType = messageType + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.data = try values.decodeIfPresent(String.self, forKey: "Data") + self.messageType = try values.decodeIfPresent(SessionMessageType.self, forKey: "MessageType") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(data, forKey: "Data") + try values.encodeIfPresent(messageType, forKey: "MessageType") + } +} diff --git a/Sources/Entities/ActivityLogEntryStopMessage.swift b/Sources/Entities/ActivityLogEntryStopMessage.swift new file mode 100644 index 00000000..ee7d9615 --- /dev/null +++ b/Sources/Entities/ActivityLogEntryStopMessage.swift @@ -0,0 +1,29 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Activity log entry stop message. +public struct ActivityLogEntryStopMessage: Codable, Hashable { + /// The different kinds of messages that are used in the WebSocket api. + public var messageType: SessionMessageType? + + public init(messageType: SessionMessageType? = nil) { + self.messageType = messageType + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.messageType = try values.decodeIfPresent(SessionMessageType.self, forKey: "MessageType") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(messageType, forKey: "MessageType") + } +} diff --git a/Sources/Entities/AudioSpatialFormat.swift b/Sources/Entities/AudioSpatialFormat.swift new file mode 100644 index 00000000..c4f96ecf --- /dev/null +++ b/Sources/Entities/AudioSpatialFormat.swift @@ -0,0 +1,16 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// An enum representing formats of spatial audio. +public enum AudioSpatialFormat: String, Codable, CaseIterable { + case none = "None" + case dolbyAtmos = "DolbyAtmos" + case dtsx = "DTSX" +} diff --git a/Sources/Entities/AuthenticateUserByName.swift b/Sources/Entities/AuthenticateUserByName.swift index 3fe912c0..e43e46df 100644 --- a/Sources/Entities/AuthenticateUserByName.swift +++ b/Sources/Entities/AuthenticateUserByName.swift @@ -10,31 +10,24 @@ import Foundation /// The authenticate user by name request body. public struct AuthenticateUserByName: Codable, Hashable { - /// Gets or sets the sha1-hashed password. - /// - /// - warning: Deprecated. - public var password: String? /// Gets or sets the plain text password. public var pw: String? /// Gets or sets the username. public var username: String? - public init(password: String? = nil, pw: String? = nil, username: String? = nil) { - self.password = password + public init(pw: String? = nil, username: String? = nil) { self.pw = pw self.username = username } public init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: StringCodingKey.self) - self.password = try values.decodeIfPresent(String.self, forKey: "Password") self.pw = try values.decodeIfPresent(String.self, forKey: "Pw") self.username = try values.decodeIfPresent(String.self, forKey: "Username") } public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: StringCodingKey.self) - try values.encodeIfPresent(password, forKey: "Password") try values.encodeIfPresent(pw, forKey: "Pw") try values.encodeIfPresent(username, forKey: "Username") } diff --git a/Sources/Entities/BaseItemDto.swift b/Sources/Entities/BaseItemDto.swift index 70e375bc..2f670f0a 100644 --- a/Sources/Entities/BaseItemDto.swift +++ b/Sources/Entities/BaseItemDto.swift @@ -61,7 +61,7 @@ public struct BaseItemDto: Codable, Hashable, Identifiable { /// Gets or sets the child count. public var childCount: Int? /// Gets or sets the type of the collection. - public var collectionType: String? + public var collectionType: CollectionType? /// Gets or sets the community rating. public var communityRating: Float? /// Gets or sets the completion percentage. @@ -98,6 +98,7 @@ public struct BaseItemDto: Codable, Hashable, Identifiable { public var genreItems: [NameGuidPair]? /// Gets or sets the genres. public var genres: [String]? + public var hasLyrics: Bool? public var hasSubtitles: Bool? public var height: Int? /// Gets or sets the id. @@ -154,13 +155,15 @@ public struct BaseItemDto: Codable, Hashable, Identifiable { /// Gets or sets the media streams. public var mediaStreams: [MediaStream]? /// Gets or sets the type of the media. - public var mediaType: String? + public var mediaType: MediaType? /// Gets or sets the movie count. public var movieCount: Int? /// Gets or sets the music video count. public var musicVideoCount: Int? /// Gets or sets the name. public var name: String? + /// Gets or sets the gain required for audio normalization. + public var normalizationGain: Float? /// Gets or sets the number. public var number: String? /// Gets or sets the official rating. @@ -170,11 +173,11 @@ public struct BaseItemDto: Codable, Hashable, Identifiable { public var overview: String? /// Gets or sets the parent art image tag. public var parentArtImageTag: String? - /// Gets or sets wether the item has fan art, this will hold the Id of the Parent that has one. + /// Gets or sets whether the item has fan art, this will hold the Id of the Parent that has one. public var parentArtItemID: String? /// Gets or sets the parent backdrop image tags. public var parentBackdropImageTags: [String]? - /// Gets or sets wether the item has any backdrops, this will hold the Id of the Parent that has one. + /// Gets or sets whether the item has any backdrops, this will hold the Id of the Parent that has one. public var parentBackdropItemID: String? /// Gets or sets the parent id. public var parentID: String? @@ -182,7 +185,7 @@ public struct BaseItemDto: Codable, Hashable, Identifiable { public var parentIndexNumber: Int? /// Gets or sets the parent logo image tag. public var parentLogoImageTag: String? - /// Gets or sets wether the item has a logo, this will hold the Id of the Parent that has one. + /// Gets or sets whether the item has a logo, this will hold the Id of the Parent that has one. public var parentLogoItemID: String? /// Gets or sets the parent primary image item identifier. public var parentPrimaryImageItemID: String? @@ -260,8 +263,6 @@ public struct BaseItemDto: Codable, Hashable, Identifiable { public var status: String? /// Gets or sets the studios. public var studios: [NameGuidPair]? - /// Gets or sets a value indicating whether [supports synchronize]. - public var isSupportsSync: Bool? /// Gets or sets the taglines. public var taglines: [String]? /// Gets or sets the tags. @@ -270,6 +271,8 @@ public struct BaseItemDto: Codable, Hashable, Identifiable { public var timerID: String? /// Gets or sets the trailer count. public var trailerCount: Int? + /// Gets or sets the trickplay manifest. + public var trickplay: [String: [String: TrickplayInfo]]? /// Gets or sets the type. public var type: BaseItemKind? /// Gets or sets the user data for this item based on the user it's being requested for. @@ -394,7 +397,7 @@ public struct BaseItemDto: Codable, Hashable, Identifiable { channelType: ChannelType? = nil, chapters: [ChapterInfo]? = nil, childCount: Int? = nil, - collectionType: String? = nil, + collectionType: CollectionType? = nil, communityRating: Float? = nil, completionPercentage: Double? = nil, container: String? = nil, @@ -417,6 +420,7 @@ public struct BaseItemDto: Codable, Hashable, Identifiable { forcedSortName: String? = nil, genreItems: [NameGuidPair]? = nil, genres: [String]? = nil, + hasLyrics: Bool? = nil, hasSubtitles: Bool? = nil, height: Int? = nil, id: String? = nil, @@ -447,10 +451,11 @@ public struct BaseItemDto: Codable, Hashable, Identifiable { mediaSourceCount: Int? = nil, mediaSources: [MediaSourceInfo]? = nil, mediaStreams: [MediaStream]? = nil, - mediaType: String? = nil, + mediaType: MediaType? = nil, movieCount: Int? = nil, musicVideoCount: Int? = nil, name: String? = nil, + normalizationGain: Float? = nil, number: String? = nil, officialRating: String? = nil, originalTitle: String? = nil, @@ -504,11 +509,11 @@ public struct BaseItemDto: Codable, Hashable, Identifiable { startDate: Date? = nil, status: String? = nil, studios: [NameGuidPair]? = nil, - isSupportsSync: Bool? = nil, taglines: [String]? = nil, tags: [String]? = nil, timerID: String? = nil, trailerCount: Int? = nil, + trickplay: [String: [String: TrickplayInfo]]? = nil, type: BaseItemKind? = nil, userData: UserItemDataDto? = nil, video3DFormat: Video3DFormat? = nil, @@ -568,6 +573,7 @@ public struct BaseItemDto: Codable, Hashable, Identifiable { self.forcedSortName = forcedSortName self.genreItems = genreItems self.genres = genres + self.hasLyrics = hasLyrics self.hasSubtitles = hasSubtitles self.height = height self.id = id @@ -602,6 +608,7 @@ public struct BaseItemDto: Codable, Hashable, Identifiable { self.movieCount = movieCount self.musicVideoCount = musicVideoCount self.name = name + self.normalizationGain = normalizationGain self.number = number self.officialRating = officialRating self.originalTitle = originalTitle @@ -655,11 +662,11 @@ public struct BaseItemDto: Codable, Hashable, Identifiable { self.startDate = startDate self.status = status self.studios = studios - self.isSupportsSync = isSupportsSync self.taglines = taglines self.tags = tags self.timerID = timerID self.trailerCount = trailerCount + self.trickplay = trickplay self.type = type self.userData = userData self.video3DFormat = video3DFormat @@ -699,7 +706,7 @@ public struct BaseItemDto: Codable, Hashable, Identifiable { self.channelType = try values.decodeIfPresent(ChannelType.self, forKey: "ChannelType") self.chapters = try values.decodeIfPresent([ChapterInfo].self, forKey: "Chapters") self.childCount = try values.decodeIfPresent(Int.self, forKey: "ChildCount") - self.collectionType = try values.decodeIfPresent(String.self, forKey: "CollectionType") + self.collectionType = try values.decodeIfPresent(CollectionType.self, forKey: "CollectionType") self.communityRating = try values.decodeIfPresent(Float.self, forKey: "CommunityRating") self.completionPercentage = try values.decodeIfPresent(Double.self, forKey: "CompletionPercentage") self.container = try values.decodeIfPresent(String.self, forKey: "Container") @@ -722,6 +729,7 @@ public struct BaseItemDto: Codable, Hashable, Identifiable { self.forcedSortName = try values.decodeIfPresent(String.self, forKey: "ForcedSortName") self.genreItems = try values.decodeIfPresent([NameGuidPair].self, forKey: "GenreItems") self.genres = try values.decodeIfPresent([String].self, forKey: "Genres") + self.hasLyrics = try values.decodeIfPresent(Bool.self, forKey: "HasLyrics") self.hasSubtitles = try values.decodeIfPresent(Bool.self, forKey: "HasSubtitles") self.height = try values.decodeIfPresent(Int.self, forKey: "Height") self.id = try values.decodeIfPresent(String.self, forKey: "Id") @@ -752,10 +760,11 @@ public struct BaseItemDto: Codable, Hashable, Identifiable { self.mediaSourceCount = try values.decodeIfPresent(Int.self, forKey: "MediaSourceCount") self.mediaSources = try values.decodeIfPresent([MediaSourceInfo].self, forKey: "MediaSources") self.mediaStreams = try values.decodeIfPresent([MediaStream].self, forKey: "MediaStreams") - self.mediaType = try values.decodeIfPresent(String.self, forKey: "MediaType") + self.mediaType = try values.decodeIfPresent(MediaType.self, forKey: "MediaType") self.movieCount = try values.decodeIfPresent(Int.self, forKey: "MovieCount") self.musicVideoCount = try values.decodeIfPresent(Int.self, forKey: "MusicVideoCount") self.name = try values.decodeIfPresent(String.self, forKey: "Name") + self.normalizationGain = try values.decodeIfPresent(Float.self, forKey: "NormalizationGain") self.number = try values.decodeIfPresent(String.self, forKey: "Number") self.officialRating = try values.decodeIfPresent(String.self, forKey: "OfficialRating") self.originalTitle = try values.decodeIfPresent(String.self, forKey: "OriginalTitle") @@ -809,11 +818,11 @@ public struct BaseItemDto: Codable, Hashable, Identifiable { self.startDate = try values.decodeIfPresent(Date.self, forKey: "StartDate") self.status = try values.decodeIfPresent(String.self, forKey: "Status") self.studios = try values.decodeIfPresent([NameGuidPair].self, forKey: "Studios") - self.isSupportsSync = try values.decodeIfPresent(Bool.self, forKey: "SupportsSync") self.taglines = try values.decodeIfPresent([String].self, forKey: "Taglines") self.tags = try values.decodeIfPresent([String].self, forKey: "Tags") self.timerID = try values.decodeIfPresent(String.self, forKey: "TimerId") self.trailerCount = try values.decodeIfPresent(Int.self, forKey: "TrailerCount") + self.trickplay = try values.decodeIfPresent([String: [String: TrickplayInfo]].self, forKey: "Trickplay") self.type = try values.decodeIfPresent(BaseItemKind.self, forKey: "Type") self.userData = try values.decodeIfPresent(UserItemDataDto.self, forKey: "UserData") self.video3DFormat = try values.decodeIfPresent(Video3DFormat.self, forKey: "Video3DFormat") @@ -876,6 +885,7 @@ public struct BaseItemDto: Codable, Hashable, Identifiable { try values.encodeIfPresent(forcedSortName, forKey: "ForcedSortName") try values.encodeIfPresent(genreItems, forKey: "GenreItems") try values.encodeIfPresent(genres, forKey: "Genres") + try values.encodeIfPresent(hasLyrics, forKey: "HasLyrics") try values.encodeIfPresent(hasSubtitles, forKey: "HasSubtitles") try values.encodeIfPresent(height, forKey: "Height") try values.encodeIfPresent(id, forKey: "Id") @@ -910,6 +920,7 @@ public struct BaseItemDto: Codable, Hashable, Identifiable { try values.encodeIfPresent(movieCount, forKey: "MovieCount") try values.encodeIfPresent(musicVideoCount, forKey: "MusicVideoCount") try values.encodeIfPresent(name, forKey: "Name") + try values.encodeIfPresent(normalizationGain, forKey: "NormalizationGain") try values.encodeIfPresent(number, forKey: "Number") try values.encodeIfPresent(officialRating, forKey: "OfficialRating") try values.encodeIfPresent(originalTitle, forKey: "OriginalTitle") @@ -963,11 +974,11 @@ public struct BaseItemDto: Codable, Hashable, Identifiable { try values.encodeIfPresent(startDate, forKey: "StartDate") try values.encodeIfPresent(status, forKey: "Status") try values.encodeIfPresent(studios, forKey: "Studios") - try values.encodeIfPresent(isSupportsSync, forKey: "SupportsSync") try values.encodeIfPresent(taglines, forKey: "Taglines") try values.encodeIfPresent(tags, forKey: "Tags") try values.encodeIfPresent(timerID, forKey: "TimerId") try values.encodeIfPresent(trailerCount, forKey: "TrailerCount") + try values.encodeIfPresent(trickplay, forKey: "Trickplay") try values.encodeIfPresent(type, forKey: "Type") try values.encodeIfPresent(userData, forKey: "UserData") try values.encodeIfPresent(video3DFormat, forKey: "Video3DFormat") diff --git a/Sources/Entities/BaseItemPerson.swift b/Sources/Entities/BaseItemPerson.swift index ef1c87f3..07c5b05b 100644 --- a/Sources/Entities/BaseItemPerson.swift +++ b/Sources/Entities/BaseItemPerson.swift @@ -21,7 +21,7 @@ public struct BaseItemPerson: Codable, Hashable, Identifiable { /// Gets or sets the role. public var role: String? /// Gets or sets the type. - public var type: String? + public var type: PersonKind? /// Gets or sets the primary image blurhash. public struct ImageBlurHashes: Codable, Hashable { @@ -110,7 +110,7 @@ public struct BaseItemPerson: Codable, Hashable, Identifiable { name: String? = nil, primaryImageTag: String? = nil, role: String? = nil, - type: String? = nil + type: PersonKind? = nil ) { self.id = id self.imageBlurHashes = imageBlurHashes @@ -127,7 +127,7 @@ public struct BaseItemPerson: Codable, Hashable, Identifiable { self.name = try values.decodeIfPresent(String.self, forKey: "Name") self.primaryImageTag = try values.decodeIfPresent(String.self, forKey: "PrimaryImageTag") self.role = try values.decodeIfPresent(String.self, forKey: "Role") - self.type = try values.decodeIfPresent(String.self, forKey: "Type") + self.type = try values.decodeIfPresent(PersonKind.self, forKey: "Type") } public func encode(to encoder: Encoder) throws { diff --git a/Sources/Entities/CastReceiverApplication.swift b/Sources/Entities/CastReceiverApplication.swift new file mode 100644 index 00000000..362905e2 --- /dev/null +++ b/Sources/Entities/CastReceiverApplication.swift @@ -0,0 +1,34 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// The cast receiver application model. +public struct CastReceiverApplication: Codable, Hashable, Identifiable { + /// Gets or sets the cast receiver application id. + public var id: String? + /// Gets or sets the cast receiver application name. + public var name: String? + + public init(id: String? = nil, name: String? = nil) { + self.id = id + self.name = name + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.id = try values.decodeIfPresent(String.self, forKey: "Id") + self.name = try values.decodeIfPresent(String.self, forKey: "Name") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(id, forKey: "Id") + try values.encodeIfPresent(name, forKey: "Name") + } +} diff --git a/Sources/Entities/ClientCapabilities.swift b/Sources/Entities/ClientCapabilities.swift index 807d4ef6..11558b6b 100644 --- a/Sources/Entities/ClientCapabilities.swift +++ b/Sources/Entities/ClientCapabilities.swift @@ -26,20 +26,20 @@ public struct ClientCapabilities: Codable, Hashable { /// case it isn't. public var deviceProfile: DeviceProfile? public var iconURL: String? - public var messageCallbackURL: String? - public var playableMediaTypes: [String]? + public var playableMediaTypes: [MediaType]? public var supportedCommands: [GeneralCommandType]? - public var isSupportsContentUploading: Bool? + /// - warning: Deprecated. + public var isSupportsContentUploading: Bool public var isSupportsMediaControl: Bool? public var isSupportsPersistentIdentifier: Bool? - public var isSupportsSync: Bool? + /// - warning: Deprecated. + public var isSupportsSync: Bool public init( appStoreURL: String? = nil, deviceProfile: DeviceProfile? = nil, iconURL: String? = nil, - messageCallbackURL: String? = nil, - playableMediaTypes: [String]? = nil, + playableMediaTypes: [MediaType]? = nil, supportedCommands: [GeneralCommandType]? = nil, isSupportsContentUploading: Bool? = nil, isSupportsMediaControl: Bool? = nil, @@ -49,13 +49,12 @@ public struct ClientCapabilities: Codable, Hashable { self.appStoreURL = appStoreURL self.deviceProfile = deviceProfile self.iconURL = iconURL - self.messageCallbackURL = messageCallbackURL self.playableMediaTypes = playableMediaTypes self.supportedCommands = supportedCommands - self.isSupportsContentUploading = isSupportsContentUploading + self.isSupportsContentUploading = isSupportsContentUploading ?? false self.isSupportsMediaControl = isSupportsMediaControl self.isSupportsPersistentIdentifier = isSupportsPersistentIdentifier - self.isSupportsSync = isSupportsSync + self.isSupportsSync = isSupportsSync ?? false } public init(from decoder: Decoder) throws { @@ -63,13 +62,12 @@ public struct ClientCapabilities: Codable, Hashable { self.appStoreURL = try values.decodeIfPresent(String.self, forKey: "AppStoreUrl") self.deviceProfile = try values.decodeIfPresent(DeviceProfile.self, forKey: "DeviceProfile") self.iconURL = try values.decodeIfPresent(String.self, forKey: "IconUrl") - self.messageCallbackURL = try values.decodeIfPresent(String.self, forKey: "MessageCallbackUrl") - self.playableMediaTypes = try values.decodeIfPresent([String].self, forKey: "PlayableMediaTypes") + self.playableMediaTypes = try values.decodeIfPresent([MediaType].self, forKey: "PlayableMediaTypes") self.supportedCommands = try values.decodeIfPresent([GeneralCommandType].self, forKey: "SupportedCommands") - self.isSupportsContentUploading = try values.decodeIfPresent(Bool.self, forKey: "SupportsContentUploading") + self.isSupportsContentUploading = try values.decodeIfPresent(Bool.self, forKey: "SupportsContentUploading") ?? false self.isSupportsMediaControl = try values.decodeIfPresent(Bool.self, forKey: "SupportsMediaControl") self.isSupportsPersistentIdentifier = try values.decodeIfPresent(Bool.self, forKey: "SupportsPersistentIdentifier") - self.isSupportsSync = try values.decodeIfPresent(Bool.self, forKey: "SupportsSync") + self.isSupportsSync = try values.decodeIfPresent(Bool.self, forKey: "SupportsSync") ?? false } public func encode(to encoder: Encoder) throws { @@ -77,7 +75,6 @@ public struct ClientCapabilities: Codable, Hashable { try values.encodeIfPresent(appStoreURL, forKey: "AppStoreUrl") try values.encodeIfPresent(deviceProfile, forKey: "DeviceProfile") try values.encodeIfPresent(iconURL, forKey: "IconUrl") - try values.encodeIfPresent(messageCallbackURL, forKey: "MessageCallbackUrl") try values.encodeIfPresent(playableMediaTypes, forKey: "PlayableMediaTypes") try values.encodeIfPresent(supportedCommands, forKey: "SupportedCommands") try values.encodeIfPresent(isSupportsContentUploading, forKey: "SupportsContentUploading") diff --git a/Sources/Entities/ClientCapabilitiesDto.swift b/Sources/Entities/ClientCapabilitiesDto.swift index c0bc1396..4cec67d9 100644 --- a/Sources/Entities/ClientCapabilitiesDto.swift +++ b/Sources/Entities/ClientCapabilitiesDto.swift @@ -29,27 +29,24 @@ public struct ClientCapabilitiesDto: Codable, Hashable { public var deviceProfile: DeviceProfile? /// Gets or sets the icon url. public var iconURL: String? - /// Gets or sets the message callback url. - public var messageCallbackURL: String? /// Gets or sets the list of playable media types. - public var playableMediaTypes: [String]? + public var playableMediaTypes: [MediaType]? /// Gets or sets the list of supported commands. public var supportedCommands: [GeneralCommandType]? - /// Gets or sets a value indicating whether session supports content uploading. - public var isSupportsContentUploading: Bool? + /// - warning: Deprecated. + public var isSupportsContentUploading: Bool /// Gets or sets a value indicating whether session supports media control. public var isSupportsMediaControl: Bool? /// Gets or sets a value indicating whether session supports a persistent identifier. public var isSupportsPersistentIdentifier: Bool? - /// Gets or sets a value indicating whether session supports sync. - public var isSupportsSync: Bool? + /// - warning: Deprecated. + public var isSupportsSync: Bool public init( appStoreURL: String? = nil, deviceProfile: DeviceProfile? = nil, iconURL: String? = nil, - messageCallbackURL: String? = nil, - playableMediaTypes: [String]? = nil, + playableMediaTypes: [MediaType]? = nil, supportedCommands: [GeneralCommandType]? = nil, isSupportsContentUploading: Bool? = nil, isSupportsMediaControl: Bool? = nil, @@ -59,13 +56,12 @@ public struct ClientCapabilitiesDto: Codable, Hashable { self.appStoreURL = appStoreURL self.deviceProfile = deviceProfile self.iconURL = iconURL - self.messageCallbackURL = messageCallbackURL self.playableMediaTypes = playableMediaTypes self.supportedCommands = supportedCommands - self.isSupportsContentUploading = isSupportsContentUploading + self.isSupportsContentUploading = isSupportsContentUploading ?? false self.isSupportsMediaControl = isSupportsMediaControl self.isSupportsPersistentIdentifier = isSupportsPersistentIdentifier - self.isSupportsSync = isSupportsSync + self.isSupportsSync = isSupportsSync ?? false } public init(from decoder: Decoder) throws { @@ -73,13 +69,12 @@ public struct ClientCapabilitiesDto: Codable, Hashable { self.appStoreURL = try values.decodeIfPresent(String.self, forKey: "AppStoreUrl") self.deviceProfile = try values.decodeIfPresent(DeviceProfile.self, forKey: "DeviceProfile") self.iconURL = try values.decodeIfPresent(String.self, forKey: "IconUrl") - self.messageCallbackURL = try values.decodeIfPresent(String.self, forKey: "MessageCallbackUrl") - self.playableMediaTypes = try values.decodeIfPresent([String].self, forKey: "PlayableMediaTypes") + self.playableMediaTypes = try values.decodeIfPresent([MediaType].self, forKey: "PlayableMediaTypes") self.supportedCommands = try values.decodeIfPresent([GeneralCommandType].self, forKey: "SupportedCommands") - self.isSupportsContentUploading = try values.decodeIfPresent(Bool.self, forKey: "SupportsContentUploading") + self.isSupportsContentUploading = try values.decodeIfPresent(Bool.self, forKey: "SupportsContentUploading") ?? false self.isSupportsMediaControl = try values.decodeIfPresent(Bool.self, forKey: "SupportsMediaControl") self.isSupportsPersistentIdentifier = try values.decodeIfPresent(Bool.self, forKey: "SupportsPersistentIdentifier") - self.isSupportsSync = try values.decodeIfPresent(Bool.self, forKey: "SupportsSync") + self.isSupportsSync = try values.decodeIfPresent(Bool.self, forKey: "SupportsSync") ?? false } public func encode(to encoder: Encoder) throws { @@ -87,7 +82,6 @@ public struct ClientCapabilitiesDto: Codable, Hashable { try values.encodeIfPresent(appStoreURL, forKey: "AppStoreUrl") try values.encodeIfPresent(deviceProfile, forKey: "DeviceProfile") try values.encodeIfPresent(iconURL, forKey: "IconUrl") - try values.encodeIfPresent(messageCallbackURL, forKey: "MessageCallbackUrl") try values.encodeIfPresent(playableMediaTypes, forKey: "PlayableMediaTypes") try values.encodeIfPresent(supportedCommands, forKey: "SupportedCommands") try values.encodeIfPresent(isSupportsContentUploading, forKey: "SupportsContentUploading") diff --git a/Sources/Entities/CollectionType.swift b/Sources/Entities/CollectionType.swift new file mode 100644 index 00000000..ffc33906 --- /dev/null +++ b/Sources/Entities/CollectionType.swift @@ -0,0 +1,26 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Collection type. +public enum CollectionType: String, Codable, CaseIterable { + case unknown + case movies + case tvshows + case music + case musicvideos + case trailers + case homevideos + case boxsets + case books + case photos + case livetv + case playlists + case folders +} diff --git a/Sources/Entities/CollectionTypeOptions.swift b/Sources/Entities/CollectionTypeOptions.swift index 7223c74e..545d88ef 100644 --- a/Sources/Entities/CollectionTypeOptions.swift +++ b/Sources/Entities/CollectionTypeOptions.swift @@ -8,13 +8,14 @@ import Foundation +/// The collection type options. public enum CollectionTypeOptions: String, Codable, CaseIterable { - case movies = "Movies" - case tvShows = "TvShows" - case music = "Music" - case musicVideos = "MusicVideos" - case homeVideos = "HomeVideos" - case boxSets = "BoxSets" - case books = "Books" - case mixed = "Mixed" + case movies + case tvshows + case music + case musicvideos + case homevideos + case boxsets + case books + case mixed } diff --git a/Sources/Entities/CreatePlaylistDto.swift b/Sources/Entities/CreatePlaylistDto.swift index b45f8fad..b09adad5 100644 --- a/Sources/Entities/CreatePlaylistDto.swift +++ b/Sources/Entities/CreatePlaylistDto.swift @@ -12,33 +12,50 @@ import Foundation public struct CreatePlaylistDto: Codable, Hashable { /// Gets or sets item ids to add to the playlist. public var ids: [String]? + /// Gets or sets a value indicating whether the playlist is public. + public var isPublic: Bool? /// Gets or sets the media type. - public var mediaType: String? + public var mediaType: MediaType? /// Gets or sets the name of the new playlist. public var name: String? /// Gets or sets the user id. public var userID: String? + /// Gets or sets the playlist users. + public var users: [PlaylistUserPermissions]? - public init(ids: [String]? = nil, mediaType: String? = nil, name: String? = nil, userID: String? = nil) { + public init( + ids: [String]? = nil, + isPublic: Bool? = nil, + mediaType: MediaType? = nil, + name: String? = nil, + userID: String? = nil, + users: [PlaylistUserPermissions]? = nil + ) { self.ids = ids + self.isPublic = isPublic self.mediaType = mediaType self.name = name self.userID = userID + self.users = users } public init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: StringCodingKey.self) self.ids = try values.decodeIfPresent([String].self, forKey: "Ids") - self.mediaType = try values.decodeIfPresent(String.self, forKey: "MediaType") + self.isPublic = try values.decodeIfPresent(Bool.self, forKey: "IsPublic") + self.mediaType = try values.decodeIfPresent(MediaType.self, forKey: "MediaType") self.name = try values.decodeIfPresent(String.self, forKey: "Name") self.userID = try values.decodeIfPresent(String.self, forKey: "UserId") + self.users = try values.decodeIfPresent([PlaylistUserPermissions].self, forKey: "Users") } public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: StringCodingKey.self) try values.encodeIfPresent(ids, forKey: "Ids") + try values.encodeIfPresent(isPublic, forKey: "IsPublic") try values.encodeIfPresent(mediaType, forKey: "MediaType") try values.encodeIfPresent(name, forKey: "Name") try values.encodeIfPresent(userID, forKey: "UserId") + try values.encodeIfPresent(users, forKey: "Users") } } diff --git a/Sources/Entities/CreateUserByName.swift b/Sources/Entities/CreateUserByName.swift index a3fda59d..0a75402a 100644 --- a/Sources/Entities/CreateUserByName.swift +++ b/Sources/Entities/CreateUserByName.swift @@ -11,24 +11,24 @@ import Foundation /// The create user by name request body. public struct CreateUserByName: Codable, Hashable { /// Gets or sets the username. - public var name: String? + public var name: String /// Gets or sets the password. public var password: String? - public init(name: String? = nil, password: String? = nil) { + public init(name: String, password: String? = nil) { self.name = name self.password = password } public init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: StringCodingKey.self) - self.name = try values.decodeIfPresent(String.self, forKey: "Name") + self.name = try values.decode(String.self, forKey: "Name") self.password = try values.decodeIfPresent(String.self, forKey: "Password") } public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: StringCodingKey.self) - try values.encodeIfPresent(name, forKey: "Name") + try values.encode(name, forKey: "Name") try values.encodeIfPresent(password, forKey: "Password") } } diff --git a/Sources/Entities/DeviceInfo.swift b/Sources/Entities/DeviceInfo.swift index a58cdc3f..fa8f109d 100644 --- a/Sources/Entities/DeviceInfo.swift +++ b/Sources/Entities/DeviceInfo.swift @@ -17,6 +17,7 @@ public struct DeviceInfo: Codable, Hashable, Identifiable { public var appVersion: String? /// Gets or sets the capabilities. public var capabilities: ClientCapabilities? + public var customName: String? /// Gets or sets the date last modified. public var dateLastActivity: Date? public var iconURL: String? @@ -33,6 +34,7 @@ public struct DeviceInfo: Codable, Hashable, Identifiable { appName: String? = nil, appVersion: String? = nil, capabilities: ClientCapabilities? = nil, + customName: String? = nil, dateLastActivity: Date? = nil, iconURL: String? = nil, id: String? = nil, @@ -44,6 +46,7 @@ public struct DeviceInfo: Codable, Hashable, Identifiable { self.appName = appName self.appVersion = appVersion self.capabilities = capabilities + self.customName = customName self.dateLastActivity = dateLastActivity self.iconURL = iconURL self.id = id @@ -58,6 +61,7 @@ public struct DeviceInfo: Codable, Hashable, Identifiable { self.appName = try values.decodeIfPresent(String.self, forKey: "AppName") self.appVersion = try values.decodeIfPresent(String.self, forKey: "AppVersion") self.capabilities = try values.decodeIfPresent(ClientCapabilities.self, forKey: "Capabilities") + self.customName = try values.decodeIfPresent(String.self, forKey: "CustomName") self.dateLastActivity = try values.decodeIfPresent(Date.self, forKey: "DateLastActivity") self.iconURL = try values.decodeIfPresent(String.self, forKey: "IconUrl") self.id = try values.decodeIfPresent(String.self, forKey: "Id") @@ -72,6 +76,7 @@ public struct DeviceInfo: Codable, Hashable, Identifiable { try values.encodeIfPresent(appName, forKey: "AppName") try values.encodeIfPresent(appVersion, forKey: "AppVersion") try values.encodeIfPresent(capabilities, forKey: "Capabilities") + try values.encodeIfPresent(customName, forKey: "CustomName") try values.encodeIfPresent(dateLastActivity, forKey: "DateLastActivity") try values.encodeIfPresent(iconURL, forKey: "IconUrl") try values.encodeIfPresent(id, forKey: "Id") diff --git a/Sources/Entities/DeviceProfile.swift b/Sources/Entities/DeviceProfile.swift index f3b7cf19..c8520fe5 100644 --- a/Sources/Entities/DeviceProfile.swift +++ b/Sources/Entities/DeviceProfile.swift @@ -21,250 +21,82 @@ import Foundation /// as well as which containers/codecs to transcode to in case /// it isn't. public struct DeviceProfile: Codable, Hashable, Identifiable { - /// Gets or sets the AlbumArtPn. - public var albumArtPn: String? /// Gets or sets the codec profiles. public var codecProfiles: [CodecProfile]? /// Gets or sets the container profiles. public var containerProfiles: [ContainerProfile]? /// Gets or sets the direct play profiles. public var directPlayProfiles: [DirectPlayProfile]? - /// Gets or sets a value indicating whether EnableAlbumArtInDidl. - public var enableAlbumArtInDidl: Bool - /// Gets or sets a value indicating whether EnableMSMediaReceiverRegistrar. - public var enableMSMediaReceiverRegistrar: Bool - /// Gets or sets a value indicating whether EnableSingleAlbumArtLimit. - public var enableSingleAlbumArtLimit: Bool - /// Gets or sets a value indicating whether EnableSingleSubtitleLimit. - public var enableSingleSubtitleLimit: Bool - /// Gets or sets the friendly name of the device profile, which can be shown to users. - public var friendlyName: String? /// Gets or sets the Id. public var id: String? - /// Gets or sets the Identification. - public var identification: DeviceIdentification? - /// Gets or sets a value indicating whether IgnoreTranscodeByteRangeRequests. - public var isIgnoreTranscodeByteRangeRequests: Bool - /// Gets or sets the manufacturer of the device which this profile represents. - public var manufacturer: String? - /// Gets or sets an url for the manufacturer of the device which this profile represents. - public var manufacturerURL: String? - /// Gets or sets the MaxAlbumArtHeight. - public var maxAlbumArtHeight: Int? - /// Gets or sets the MaxAlbumArtWidth. - public var maxAlbumArtWidth: Int? - /// Gets or sets the maximum allowed height of embedded icons. - public var maxIconHeight: Int? - /// Gets or sets the maximum allowed width of embedded icons. - public var maxIconWidth: Int? /// Gets or sets the maximum allowed bitrate for statically streamed content (= direct played files). public var maxStaticBitrate: Int? /// Gets or sets the maximum allowed bitrate for statically streamed (= direct played) music files. public var maxStaticMusicBitrate: Int? /// Gets or sets the maximum allowed bitrate for all streamed content. public var maxStreamingBitrate: Int? - /// Gets or sets the model description of the device which this profile represents. - public var modelDescription: String? - /// Gets or sets the model name of the device which this profile represents. - public var modelName: String? - /// Gets or sets the model number of the device which this profile represents. - public var modelNumber: String? - /// Gets or sets the ModelUrl. - public var modelURL: String? /// Gets or sets the maximum allowed bitrate for transcoded music streams. public var musicStreamingTranscodingBitrate: Int? /// Gets or sets the name of this device profile. public var name: String? - /// Gets or sets the ProtocolInfo. - public var protocolInfo: String? - /// Gets or sets a value indicating whether RequiresPlainFolders. - public var requiresPlainFolders: Bool - /// Gets or sets a value indicating whether RequiresPlainVideoItems. - public var requiresPlainVideoItems: Bool - /// Gets or sets the ResponseProfiles. - public var responseProfiles: [ResponseProfile]? - /// Gets or sets the serial number of the device which this profile represents. - public var serialNumber: String? - /// Gets or sets the content of the aggregationFlags element in the urn:schemas-sonycom:av namespace. - public var sonyAggregationFlags: String? /// Gets or sets the subtitle profiles. public var subtitleProfiles: [SubtitleProfile]? - /// Gets or sets the SupportedMediaTypes. - public var supportedMediaTypes: String? - /// Gets or sets the TimelineOffsetSeconds. - public var timelineOffsetSeconds: Int? /// Gets or sets the transcoding profiles. public var transcodingProfiles: [TranscodingProfile]? - /// Gets or sets the UserId. - public var userID: String? - /// Gets or sets the XmlRootAttributes. - public var xmlRootAttributes: [XmlAttribute]? public init( - albumArtPn: String? = nil, codecProfiles: [CodecProfile]? = nil, containerProfiles: [ContainerProfile]? = nil, directPlayProfiles: [DirectPlayProfile]? = nil, - enableAlbumArtInDidl: Bool? = nil, - enableMSMediaReceiverRegistrar: Bool? = nil, - enableSingleAlbumArtLimit: Bool? = nil, - enableSingleSubtitleLimit: Bool? = nil, - friendlyName: String? = nil, id: String? = nil, - identification: DeviceIdentification? = nil, - isIgnoreTranscodeByteRangeRequests: Bool? = nil, - manufacturer: String? = nil, - manufacturerURL: String? = nil, - maxAlbumArtHeight: Int? = nil, - maxAlbumArtWidth: Int? = nil, - maxIconHeight: Int? = nil, - maxIconWidth: Int? = nil, maxStaticBitrate: Int? = nil, maxStaticMusicBitrate: Int? = nil, maxStreamingBitrate: Int? = nil, - modelDescription: String? = nil, - modelName: String? = nil, - modelNumber: String? = nil, - modelURL: String? = nil, musicStreamingTranscodingBitrate: Int? = nil, name: String? = nil, - protocolInfo: String? = nil, - requiresPlainFolders: Bool? = nil, - requiresPlainVideoItems: Bool? = nil, - responseProfiles: [ResponseProfile]? = nil, - serialNumber: String? = nil, - sonyAggregationFlags: String? = nil, subtitleProfiles: [SubtitleProfile]? = nil, - supportedMediaTypes: String? = nil, - timelineOffsetSeconds: Int? = nil, - transcodingProfiles: [TranscodingProfile]? = nil, - userID: String? = nil, - xmlRootAttributes: [XmlAttribute]? = nil + transcodingProfiles: [TranscodingProfile]? = nil ) { - self.albumArtPn = albumArtPn self.codecProfiles = codecProfiles self.containerProfiles = containerProfiles self.directPlayProfiles = directPlayProfiles - self.enableAlbumArtInDidl = enableAlbumArtInDidl ?? false - self.enableMSMediaReceiverRegistrar = enableMSMediaReceiverRegistrar ?? false - self.enableSingleAlbumArtLimit = enableSingleAlbumArtLimit ?? false - self.enableSingleSubtitleLimit = enableSingleSubtitleLimit ?? false - self.friendlyName = friendlyName self.id = id - self.identification = identification - self.isIgnoreTranscodeByteRangeRequests = isIgnoreTranscodeByteRangeRequests ?? false - self.manufacturer = manufacturer - self.manufacturerURL = manufacturerURL - self.maxAlbumArtHeight = maxAlbumArtHeight - self.maxAlbumArtWidth = maxAlbumArtWidth - self.maxIconHeight = maxIconHeight - self.maxIconWidth = maxIconWidth self.maxStaticBitrate = maxStaticBitrate self.maxStaticMusicBitrate = maxStaticMusicBitrate self.maxStreamingBitrate = maxStreamingBitrate - self.modelDescription = modelDescription - self.modelName = modelName - self.modelNumber = modelNumber - self.modelURL = modelURL self.musicStreamingTranscodingBitrate = musicStreamingTranscodingBitrate self.name = name - self.protocolInfo = protocolInfo - self.requiresPlainFolders = requiresPlainFolders ?? false - self.requiresPlainVideoItems = requiresPlainVideoItems ?? false - self.responseProfiles = responseProfiles - self.serialNumber = serialNumber - self.sonyAggregationFlags = sonyAggregationFlags self.subtitleProfiles = subtitleProfiles - self.supportedMediaTypes = supportedMediaTypes - self.timelineOffsetSeconds = timelineOffsetSeconds self.transcodingProfiles = transcodingProfiles - self.userID = userID - self.xmlRootAttributes = xmlRootAttributes } public init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: StringCodingKey.self) - self.albumArtPn = try values.decodeIfPresent(String.self, forKey: "AlbumArtPn") self.codecProfiles = try values.decodeIfPresent([CodecProfile].self, forKey: "CodecProfiles") self.containerProfiles = try values.decodeIfPresent([ContainerProfile].self, forKey: "ContainerProfiles") self.directPlayProfiles = try values.decodeIfPresent([DirectPlayProfile].self, forKey: "DirectPlayProfiles") - self.enableAlbumArtInDidl = try values.decodeIfPresent(Bool.self, forKey: "EnableAlbumArtInDidl") ?? false - self.enableMSMediaReceiverRegistrar = try values.decodeIfPresent(Bool.self, forKey: "EnableMSMediaReceiverRegistrar") ?? false - self.enableSingleAlbumArtLimit = try values.decodeIfPresent(Bool.self, forKey: "EnableSingleAlbumArtLimit") ?? false - self.enableSingleSubtitleLimit = try values.decodeIfPresent(Bool.self, forKey: "EnableSingleSubtitleLimit") ?? false - self.friendlyName = try values.decodeIfPresent(String.self, forKey: "FriendlyName") self.id = try values.decodeIfPresent(String.self, forKey: "Id") - self.identification = try values.decodeIfPresent(DeviceIdentification.self, forKey: "Identification") - self.isIgnoreTranscodeByteRangeRequests = try values.decodeIfPresent(Bool.self, forKey: "IgnoreTranscodeByteRangeRequests") ?? false - self.manufacturer = try values.decodeIfPresent(String.self, forKey: "Manufacturer") - self.manufacturerURL = try values.decodeIfPresent(String.self, forKey: "ManufacturerUrl") - self.maxAlbumArtHeight = try values.decodeIfPresent(Int.self, forKey: "MaxAlbumArtHeight") - self.maxAlbumArtWidth = try values.decodeIfPresent(Int.self, forKey: "MaxAlbumArtWidth") - self.maxIconHeight = try values.decodeIfPresent(Int.self, forKey: "MaxIconHeight") - self.maxIconWidth = try values.decodeIfPresent(Int.self, forKey: "MaxIconWidth") self.maxStaticBitrate = try values.decodeIfPresent(Int.self, forKey: "MaxStaticBitrate") self.maxStaticMusicBitrate = try values.decodeIfPresent(Int.self, forKey: "MaxStaticMusicBitrate") self.maxStreamingBitrate = try values.decodeIfPresent(Int.self, forKey: "MaxStreamingBitrate") - self.modelDescription = try values.decodeIfPresent(String.self, forKey: "ModelDescription") - self.modelName = try values.decodeIfPresent(String.self, forKey: "ModelName") - self.modelNumber = try values.decodeIfPresent(String.self, forKey: "ModelNumber") - self.modelURL = try values.decodeIfPresent(String.self, forKey: "ModelUrl") self.musicStreamingTranscodingBitrate = try values.decodeIfPresent(Int.self, forKey: "MusicStreamingTranscodingBitrate") self.name = try values.decodeIfPresent(String.self, forKey: "Name") - self.protocolInfo = try values.decodeIfPresent(String.self, forKey: "ProtocolInfo") - self.requiresPlainFolders = try values.decodeIfPresent(Bool.self, forKey: "RequiresPlainFolders") ?? false - self.requiresPlainVideoItems = try values.decodeIfPresent(Bool.self, forKey: "RequiresPlainVideoItems") ?? false - self.responseProfiles = try values.decodeIfPresent([ResponseProfile].self, forKey: "ResponseProfiles") - self.serialNumber = try values.decodeIfPresent(String.self, forKey: "SerialNumber") - self.sonyAggregationFlags = try values.decodeIfPresent(String.self, forKey: "SonyAggregationFlags") self.subtitleProfiles = try values.decodeIfPresent([SubtitleProfile].self, forKey: "SubtitleProfiles") - self.supportedMediaTypes = try values.decodeIfPresent(String.self, forKey: "SupportedMediaTypes") - self.timelineOffsetSeconds = try values.decodeIfPresent(Int.self, forKey: "TimelineOffsetSeconds") self.transcodingProfiles = try values.decodeIfPresent([TranscodingProfile].self, forKey: "TranscodingProfiles") - self.userID = try values.decodeIfPresent(String.self, forKey: "UserId") - self.xmlRootAttributes = try values.decodeIfPresent([XmlAttribute].self, forKey: "XmlRootAttributes") } public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: StringCodingKey.self) - try values.encodeIfPresent(albumArtPn, forKey: "AlbumArtPn") try values.encodeIfPresent(codecProfiles, forKey: "CodecProfiles") try values.encodeIfPresent(containerProfiles, forKey: "ContainerProfiles") try values.encodeIfPresent(directPlayProfiles, forKey: "DirectPlayProfiles") - try values.encodeIfPresent(enableAlbumArtInDidl, forKey: "EnableAlbumArtInDidl") - try values.encodeIfPresent(enableMSMediaReceiverRegistrar, forKey: "EnableMSMediaReceiverRegistrar") - try values.encodeIfPresent(enableSingleAlbumArtLimit, forKey: "EnableSingleAlbumArtLimit") - try values.encodeIfPresent(enableSingleSubtitleLimit, forKey: "EnableSingleSubtitleLimit") - try values.encodeIfPresent(friendlyName, forKey: "FriendlyName") try values.encodeIfPresent(id, forKey: "Id") - try values.encodeIfPresent(identification, forKey: "Identification") - try values.encodeIfPresent(isIgnoreTranscodeByteRangeRequests, forKey: "IgnoreTranscodeByteRangeRequests") - try values.encodeIfPresent(manufacturer, forKey: "Manufacturer") - try values.encodeIfPresent(manufacturerURL, forKey: "ManufacturerUrl") - try values.encodeIfPresent(maxAlbumArtHeight, forKey: "MaxAlbumArtHeight") - try values.encodeIfPresent(maxAlbumArtWidth, forKey: "MaxAlbumArtWidth") - try values.encodeIfPresent(maxIconHeight, forKey: "MaxIconHeight") - try values.encodeIfPresent(maxIconWidth, forKey: "MaxIconWidth") try values.encodeIfPresent(maxStaticBitrate, forKey: "MaxStaticBitrate") try values.encodeIfPresent(maxStaticMusicBitrate, forKey: "MaxStaticMusicBitrate") try values.encodeIfPresent(maxStreamingBitrate, forKey: "MaxStreamingBitrate") - try values.encodeIfPresent(modelDescription, forKey: "ModelDescription") - try values.encodeIfPresent(modelName, forKey: "ModelName") - try values.encodeIfPresent(modelNumber, forKey: "ModelNumber") - try values.encodeIfPresent(modelURL, forKey: "ModelUrl") try values.encodeIfPresent(musicStreamingTranscodingBitrate, forKey: "MusicStreamingTranscodingBitrate") try values.encodeIfPresent(name, forKey: "Name") - try values.encodeIfPresent(protocolInfo, forKey: "ProtocolInfo") - try values.encodeIfPresent(requiresPlainFolders, forKey: "RequiresPlainFolders") - try values.encodeIfPresent(requiresPlainVideoItems, forKey: "RequiresPlainVideoItems") - try values.encodeIfPresent(responseProfiles, forKey: "ResponseProfiles") - try values.encodeIfPresent(serialNumber, forKey: "SerialNumber") - try values.encodeIfPresent(sonyAggregationFlags, forKey: "SonyAggregationFlags") try values.encodeIfPresent(subtitleProfiles, forKey: "SubtitleProfiles") - try values.encodeIfPresent(supportedMediaTypes, forKey: "SupportedMediaTypes") - try values.encodeIfPresent(timelineOffsetSeconds, forKey: "TimelineOffsetSeconds") try values.encodeIfPresent(transcodingProfiles, forKey: "TranscodingProfiles") - try values.encodeIfPresent(userID, forKey: "UserId") - try values.encodeIfPresent(xmlRootAttributes, forKey: "XmlRootAttributes") } } diff --git a/Sources/Entities/DlnaProfileType.swift b/Sources/Entities/DlnaProfileType.swift index 82548d5d..66b7d01f 100644 --- a/Sources/Entities/DlnaProfileType.swift +++ b/Sources/Entities/DlnaProfileType.swift @@ -13,4 +13,5 @@ public enum DlnaProfileType: String, Codable, CaseIterable { case video = "Video" case photo = "Photo" case subtitle = "Subtitle" + case lyric = "Lyric" } diff --git a/Sources/Entities/DownMixStereoAlgorithms.swift b/Sources/Entities/DownMixStereoAlgorithms.swift new file mode 100644 index 00000000..a1db98c8 --- /dev/null +++ b/Sources/Entities/DownMixStereoAlgorithms.swift @@ -0,0 +1,18 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// An enum representing an algorithm to downmix 6ch+ to stereo. +/// +/// Algorithms sourced from https://superuser.com/questions/852400/properly-downmix-5-1-to-stereo-using-ffmpeg/1410620#1410620. +public enum DownMixStereoAlgorithms: String, Codable, CaseIterable { + case none = "None" + case dave750 = "Dave750" + case nightmodeDialogue = "NightmodeDialogue" +} diff --git a/Sources/Entities/EncodingOptions.swift b/Sources/Entities/EncodingOptions.swift index 0d620d0d..1a5bc2e1 100644 --- a/Sources/Entities/EncodingOptions.swift +++ b/Sources/Entities/EncodingOptions.swift @@ -8,54 +8,106 @@ import Foundation +/// Class EncodingOptions. public struct EncodingOptions: Codable, Hashable { + /// Gets or sets a value indicating whether AV1 encoding is enabled. + public var allowAv1Encoding: Bool? + /// Gets or sets a value indicating whether HEVC encoding is enabled. public var allowHevcEncoding: Bool? + /// Gets or sets the file extensions on-demand metadata based keyframe extraction is enabled for. public var allowOnDemandMetadataBasedKeyframeExtractionForExtensions: [String]? + /// Gets or sets a value indicating whether the framerate is doubled when deinterlacing. public var isDeinterlaceDoubleRate: Bool? + /// Gets or sets the deinterlace method. public var deinterlaceMethod: String? + /// Gets or sets the audio boost applied when downmixing audio. public var downMixAudioBoost: Double? + /// Gets or sets the algorithm used for downmixing audio to stereo. + public var downMixStereoAlgorithm: DownMixStereoAlgorithms? + /// Gets or sets a value indicating whether audio VBR is enabled. + public var enableAudioVbr: Bool? + /// Gets or sets a value indicating whether 10bit HEVC decoding is enabled. public var enableDecodingColorDepth10Hevc: Bool? + /// Gets or sets a value indicating whether 10bit VP9 decoding is enabled. public var enableDecodingColorDepth10Vp9: Bool? + /// Gets or sets a value indicating whether the enhanced NVDEC is enabled. public var enableEnhancedNvdecDecoder: Bool? + /// Gets or sets a value indicating whether to use the fallback font. public var enableFallbackFont: Bool? + /// Gets or sets a value indicating whether hardware encoding is enabled. public var enableHardwareEncoding: Bool? + /// Gets or sets a value indicating whether the Intel H264 low-power hardware encoder should be used. public var enableIntelLowPowerH264HwEncoder: Bool? + /// Gets or sets a value indicating whether the Intel HEVC low-power hardware encoder should be used. public var enableIntelLowPowerHevcHwEncoder: Bool? + /// Gets or sets a value indicating whether segment deletion is enabled. + public var enableSegmentDeletion: Bool? + /// Gets or sets a value indicating whether subtitle extraction is enabled. public var enableSubtitleExtraction: Bool? + /// Gets or sets a value indicating whether throttling is enabled. public var enableThrottling: Bool? + /// Gets or sets a value indicating whether tonemapping is enabled. public var enableTonemapping: Bool? + /// Gets or sets a value indicating whether videotoolbox tonemapping is enabled. + public var enableVideoToolboxTonemapping: Bool? + /// Gets or sets a value indicating whether VPP tonemapping is enabled. public var enableVppTonemapping: Bool? /// Gets or sets the FFmpeg path as set by the user via the UI. public var encoderAppPath: String? /// Gets or sets the current FFmpeg path being used by the system and displayed on the transcode page. public var encoderAppPathDisplay: String? + /// Gets or sets the encoder preset. public var encoderPreset: String? + /// Gets or sets the thread count used for encoding. public var encodingThreadCount: Int? + /// Gets or sets the path to the fallback font. public var fallbackFontPath: String? + /// Gets or sets the H264 CRF. public var h264Crf: Int? + /// Gets or sets the H265 CRF. public var h265Crf: Int? + /// Gets or sets the hardware acceleration type. public var hardwareAccelerationType: String? + /// Gets or sets the codecs hardware encoding is used for. public var hardwareDecodingCodecs: [String]? + /// Gets or sets the maximum size of the muxing queue. public var maxMuxingQueueSize: Int? + /// Gets or sets a value indicating whether the system native hardware decoder should be used. public var isPreferSystemNativeHwDecoder: Bool? + /// Gets or sets seconds for which segments should be kept before being deleted. + public var segmentKeepSeconds: Int? + /// Gets or sets the delay after which throttling happens. public var throttleDelaySeconds: Int? + /// Gets or sets the tone-mapping algorithm. public var tonemappingAlgorithm: String? + /// Gets or sets the tone-mapping desaturation. public var tonemappingDesat: Double? + /// Gets or sets the tone-mapping mode. public var tonemappingMode: String? + /// Gets or sets the tone-mapping parameters. public var tonemappingParam: Double? + /// Gets or sets the tone-mapping peak. public var tonemappingPeak: Double? + /// Gets or sets the tone-mapping range. public var tonemappingRange: String? + /// Gets or sets the temporary transcoding path. public var transcodingTempPath: String? + /// Gets or sets the VA-API device. public var vaapiDevice: String? + /// Gets or sets the VPP tone-mapping brightness. public var vppTonemappingBrightness: Double? + /// Gets or sets the VPP tone-mapping contrast. public var vppTonemappingContrast: Double? public init( + allowAv1Encoding: Bool? = nil, allowHevcEncoding: Bool? = nil, allowOnDemandMetadataBasedKeyframeExtractionForExtensions: [String]? = nil, isDeinterlaceDoubleRate: Bool? = nil, deinterlaceMethod: String? = nil, downMixAudioBoost: Double? = nil, + downMixStereoAlgorithm: DownMixStereoAlgorithms? = nil, + enableAudioVbr: Bool? = nil, enableDecodingColorDepth10Hevc: Bool? = nil, enableDecodingColorDepth10Vp9: Bool? = nil, enableEnhancedNvdecDecoder: Bool? = nil, @@ -63,9 +115,11 @@ public struct EncodingOptions: Codable, Hashable { enableHardwareEncoding: Bool? = nil, enableIntelLowPowerH264HwEncoder: Bool? = nil, enableIntelLowPowerHevcHwEncoder: Bool? = nil, + enableSegmentDeletion: Bool? = nil, enableSubtitleExtraction: Bool? = nil, enableThrottling: Bool? = nil, enableTonemapping: Bool? = nil, + enableVideoToolboxTonemapping: Bool? = nil, enableVppTonemapping: Bool? = nil, encoderAppPath: String? = nil, encoderAppPathDisplay: String? = nil, @@ -78,6 +132,7 @@ public struct EncodingOptions: Codable, Hashable { hardwareDecodingCodecs: [String]? = nil, maxMuxingQueueSize: Int? = nil, isPreferSystemNativeHwDecoder: Bool? = nil, + segmentKeepSeconds: Int? = nil, throttleDelaySeconds: Int? = nil, tonemappingAlgorithm: String? = nil, tonemappingDesat: Double? = nil, @@ -90,11 +145,14 @@ public struct EncodingOptions: Codable, Hashable { vppTonemappingBrightness: Double? = nil, vppTonemappingContrast: Double? = nil ) { + self.allowAv1Encoding = allowAv1Encoding self.allowHevcEncoding = allowHevcEncoding self.allowOnDemandMetadataBasedKeyframeExtractionForExtensions = allowOnDemandMetadataBasedKeyframeExtractionForExtensions self.isDeinterlaceDoubleRate = isDeinterlaceDoubleRate self.deinterlaceMethod = deinterlaceMethod self.downMixAudioBoost = downMixAudioBoost + self.downMixStereoAlgorithm = downMixStereoAlgorithm + self.enableAudioVbr = enableAudioVbr self.enableDecodingColorDepth10Hevc = enableDecodingColorDepth10Hevc self.enableDecodingColorDepth10Vp9 = enableDecodingColorDepth10Vp9 self.enableEnhancedNvdecDecoder = enableEnhancedNvdecDecoder @@ -102,9 +160,11 @@ public struct EncodingOptions: Codable, Hashable { self.enableHardwareEncoding = enableHardwareEncoding self.enableIntelLowPowerH264HwEncoder = enableIntelLowPowerH264HwEncoder self.enableIntelLowPowerHevcHwEncoder = enableIntelLowPowerHevcHwEncoder + self.enableSegmentDeletion = enableSegmentDeletion self.enableSubtitleExtraction = enableSubtitleExtraction self.enableThrottling = enableThrottling self.enableTonemapping = enableTonemapping + self.enableVideoToolboxTonemapping = enableVideoToolboxTonemapping self.enableVppTonemapping = enableVppTonemapping self.encoderAppPath = encoderAppPath self.encoderAppPathDisplay = encoderAppPathDisplay @@ -117,6 +177,7 @@ public struct EncodingOptions: Codable, Hashable { self.hardwareDecodingCodecs = hardwareDecodingCodecs self.maxMuxingQueueSize = maxMuxingQueueSize self.isPreferSystemNativeHwDecoder = isPreferSystemNativeHwDecoder + self.segmentKeepSeconds = segmentKeepSeconds self.throttleDelaySeconds = throttleDelaySeconds self.tonemappingAlgorithm = tonemappingAlgorithm self.tonemappingDesat = tonemappingDesat @@ -132,6 +193,7 @@ public struct EncodingOptions: Codable, Hashable { public init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: StringCodingKey.self) + self.allowAv1Encoding = try values.decodeIfPresent(Bool.self, forKey: "AllowAv1Encoding") self.allowHevcEncoding = try values.decodeIfPresent(Bool.self, forKey: "AllowHevcEncoding") self.allowOnDemandMetadataBasedKeyframeExtractionForExtensions = try values.decodeIfPresent( [String].self, @@ -140,6 +202,8 @@ public struct EncodingOptions: Codable, Hashable { self.isDeinterlaceDoubleRate = try values.decodeIfPresent(Bool.self, forKey: "DeinterlaceDoubleRate") self.deinterlaceMethod = try values.decodeIfPresent(String.self, forKey: "DeinterlaceMethod") self.downMixAudioBoost = try values.decodeIfPresent(Double.self, forKey: "DownMixAudioBoost") + self.downMixStereoAlgorithm = try values.decodeIfPresent(DownMixStereoAlgorithms.self, forKey: "DownMixStereoAlgorithm") + self.enableAudioVbr = try values.decodeIfPresent(Bool.self, forKey: "EnableAudioVbr") self.enableDecodingColorDepth10Hevc = try values.decodeIfPresent(Bool.self, forKey: "EnableDecodingColorDepth10Hevc") self.enableDecodingColorDepth10Vp9 = try values.decodeIfPresent(Bool.self, forKey: "EnableDecodingColorDepth10Vp9") self.enableEnhancedNvdecDecoder = try values.decodeIfPresent(Bool.self, forKey: "EnableEnhancedNvdecDecoder") @@ -147,9 +211,11 @@ public struct EncodingOptions: Codable, Hashable { self.enableHardwareEncoding = try values.decodeIfPresent(Bool.self, forKey: "EnableHardwareEncoding") self.enableIntelLowPowerH264HwEncoder = try values.decodeIfPresent(Bool.self, forKey: "EnableIntelLowPowerH264HwEncoder") self.enableIntelLowPowerHevcHwEncoder = try values.decodeIfPresent(Bool.self, forKey: "EnableIntelLowPowerHevcHwEncoder") + self.enableSegmentDeletion = try values.decodeIfPresent(Bool.self, forKey: "EnableSegmentDeletion") self.enableSubtitleExtraction = try values.decodeIfPresent(Bool.self, forKey: "EnableSubtitleExtraction") self.enableThrottling = try values.decodeIfPresent(Bool.self, forKey: "EnableThrottling") self.enableTonemapping = try values.decodeIfPresent(Bool.self, forKey: "EnableTonemapping") + self.enableVideoToolboxTonemapping = try values.decodeIfPresent(Bool.self, forKey: "EnableVideoToolboxTonemapping") self.enableVppTonemapping = try values.decodeIfPresent(Bool.self, forKey: "EnableVppTonemapping") self.encoderAppPath = try values.decodeIfPresent(String.self, forKey: "EncoderAppPath") self.encoderAppPathDisplay = try values.decodeIfPresent(String.self, forKey: "EncoderAppPathDisplay") @@ -162,6 +228,7 @@ public struct EncodingOptions: Codable, Hashable { self.hardwareDecodingCodecs = try values.decodeIfPresent([String].self, forKey: "HardwareDecodingCodecs") self.maxMuxingQueueSize = try values.decodeIfPresent(Int.self, forKey: "MaxMuxingQueueSize") self.isPreferSystemNativeHwDecoder = try values.decodeIfPresent(Bool.self, forKey: "PreferSystemNativeHwDecoder") + self.segmentKeepSeconds = try values.decodeIfPresent(Int.self, forKey: "SegmentKeepSeconds") self.throttleDelaySeconds = try values.decodeIfPresent(Int.self, forKey: "ThrottleDelaySeconds") self.tonemappingAlgorithm = try values.decodeIfPresent(String.self, forKey: "TonemappingAlgorithm") self.tonemappingDesat = try values.decodeIfPresent(Double.self, forKey: "TonemappingDesat") @@ -177,6 +244,7 @@ public struct EncodingOptions: Codable, Hashable { public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(allowAv1Encoding, forKey: "AllowAv1Encoding") try values.encodeIfPresent(allowHevcEncoding, forKey: "AllowHevcEncoding") try values.encodeIfPresent( allowOnDemandMetadataBasedKeyframeExtractionForExtensions, @@ -185,6 +253,8 @@ public struct EncodingOptions: Codable, Hashable { try values.encodeIfPresent(isDeinterlaceDoubleRate, forKey: "DeinterlaceDoubleRate") try values.encodeIfPresent(deinterlaceMethod, forKey: "DeinterlaceMethod") try values.encodeIfPresent(downMixAudioBoost, forKey: "DownMixAudioBoost") + try values.encodeIfPresent(downMixStereoAlgorithm, forKey: "DownMixStereoAlgorithm") + try values.encodeIfPresent(enableAudioVbr, forKey: "EnableAudioVbr") try values.encodeIfPresent(enableDecodingColorDepth10Hevc, forKey: "EnableDecodingColorDepth10Hevc") try values.encodeIfPresent(enableDecodingColorDepth10Vp9, forKey: "EnableDecodingColorDepth10Vp9") try values.encodeIfPresent(enableEnhancedNvdecDecoder, forKey: "EnableEnhancedNvdecDecoder") @@ -192,9 +262,11 @@ public struct EncodingOptions: Codable, Hashable { try values.encodeIfPresent(enableHardwareEncoding, forKey: "EnableHardwareEncoding") try values.encodeIfPresent(enableIntelLowPowerH264HwEncoder, forKey: "EnableIntelLowPowerH264HwEncoder") try values.encodeIfPresent(enableIntelLowPowerHevcHwEncoder, forKey: "EnableIntelLowPowerHevcHwEncoder") + try values.encodeIfPresent(enableSegmentDeletion, forKey: "EnableSegmentDeletion") try values.encodeIfPresent(enableSubtitleExtraction, forKey: "EnableSubtitleExtraction") try values.encodeIfPresent(enableThrottling, forKey: "EnableThrottling") try values.encodeIfPresent(enableTonemapping, forKey: "EnableTonemapping") + try values.encodeIfPresent(enableVideoToolboxTonemapping, forKey: "EnableVideoToolboxTonemapping") try values.encodeIfPresent(enableVppTonemapping, forKey: "EnableVppTonemapping") try values.encodeIfPresent(encoderAppPath, forKey: "EncoderAppPath") try values.encodeIfPresent(encoderAppPathDisplay, forKey: "EncoderAppPathDisplay") @@ -207,6 +279,7 @@ public struct EncodingOptions: Codable, Hashable { try values.encodeIfPresent(hardwareDecodingCodecs, forKey: "HardwareDecodingCodecs") try values.encodeIfPresent(maxMuxingQueueSize, forKey: "MaxMuxingQueueSize") try values.encodeIfPresent(isPreferSystemNativeHwDecoder, forKey: "PreferSystemNativeHwDecoder") + try values.encodeIfPresent(segmentKeepSeconds, forKey: "SegmentKeepSeconds") try values.encodeIfPresent(throttleDelaySeconds, forKey: "ThrottleDelaySeconds") try values.encodeIfPresent(tonemappingAlgorithm, forKey: "TonemappingAlgorithm") try values.encodeIfPresent(tonemappingDesat, forKey: "TonemappingDesat") diff --git a/Sources/Entities/ExternalIDMediaType.swift b/Sources/Entities/ExternalIDMediaType.swift index 6bbee6a2..127c1be2 100644 --- a/Sources/Entities/ExternalIDMediaType.swift +++ b/Sources/Entities/ExternalIDMediaType.swift @@ -22,4 +22,5 @@ public enum ExternalIDMediaType: String, Codable, CaseIterable { case season = "Season" case series = "Series" case track = "Track" + case book = "Book" } diff --git a/Sources/Entities/ExtraType.swift b/Sources/Entities/ExtraType.swift new file mode 100644 index 00000000..4e033467 --- /dev/null +++ b/Sources/Entities/ExtraType.swift @@ -0,0 +1,24 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +public enum ExtraType: String, Codable, CaseIterable { + case unknown = "Unknown" + case clip = "Clip" + case trailer = "Trailer" + case behindTheScenes = "BehindTheScenes" + case deletedScene = "DeletedScene" + case interview = "Interview" + case scene = "Scene" + case sample = "Sample" + case themeSong = "ThemeSong" + case themeVideo = "ThemeVideo" + case featurette = "Featurette" + case short = "Short" +} diff --git a/Sources/Entities/ForceKeepAliveMessage.swift b/Sources/Entities/ForceKeepAliveMessage.swift new file mode 100644 index 00000000..e7c5267c --- /dev/null +++ b/Sources/Entities/ForceKeepAliveMessage.swift @@ -0,0 +1,39 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Force keep alive websocket messages. +public struct ForceKeepAliveMessage: Codable, Hashable { + /// Gets or sets the data. + public var data: Int? + /// Gets or sets the message id. + public var messageID: String? + /// The different kinds of messages that are used in the WebSocket api. + public var messageType: SessionMessageType? + + public init(data: Int? = nil, messageID: String? = nil, messageType: SessionMessageType? = nil) { + self.data = data + self.messageID = messageID + self.messageType = messageType + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.data = try values.decodeIfPresent(Int.self, forKey: "Data") + self.messageID = try values.decodeIfPresent(String.self, forKey: "MessageId") + self.messageType = try values.decodeIfPresent(SessionMessageType.self, forKey: "MessageType") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(data, forKey: "Data") + try values.encodeIfPresent(messageID, forKey: "MessageId") + try values.encodeIfPresent(messageType, forKey: "MessageType") + } +} diff --git a/Sources/Entities/GeneralCommandMessage.swift b/Sources/Entities/GeneralCommandMessage.swift new file mode 100644 index 00000000..fc9939cf --- /dev/null +++ b/Sources/Entities/GeneralCommandMessage.swift @@ -0,0 +1,39 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// General command websocket message. +public struct GeneralCommandMessage: Codable, Hashable { + /// Gets or sets the data. + public var data: GeneralCommand? + /// Gets or sets the message id. + public var messageID: String? + /// The different kinds of messages that are used in the WebSocket api. + public var messageType: SessionMessageType? + + public init(data: GeneralCommand? = nil, messageID: String? = nil, messageType: SessionMessageType? = nil) { + self.data = data + self.messageID = messageID + self.messageType = messageType + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.data = try values.decodeIfPresent(GeneralCommand.self, forKey: "Data") + self.messageID = try values.decodeIfPresent(String.self, forKey: "MessageId") + self.messageType = try values.decodeIfPresent(SessionMessageType.self, forKey: "MessageType") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(data, forKey: "Data") + try values.encodeIfPresent(messageID, forKey: "MessageId") + try values.encodeIfPresent(messageType, forKey: "MessageType") + } +} diff --git a/Sources/Entities/GeneralCommandType.swift b/Sources/Entities/GeneralCommandType.swift index d6b37f0f..66f16c15 100644 --- a/Sources/Entities/GeneralCommandType.swift +++ b/Sources/Entities/GeneralCommandType.swift @@ -52,4 +52,5 @@ public enum GeneralCommandType: String, Codable, CaseIterable { case toggleOsdMenu = "ToggleOsdMenu" case play = "Play" case setMaxStreamingBitrate = "SetMaxStreamingBitrate" + case setPlaybackOrder = "SetPlaybackOrder" } diff --git a/Sources/Entities/GetProgramsDto.swift b/Sources/Entities/GetProgramsDto.swift index 0a7b8701..8b1534a0 100644 --- a/Sources/Entities/GetProgramsDto.swift +++ b/Sources/Entities/GetProgramsDto.swift @@ -99,7 +99,7 @@ public struct GetProgramsDto: Codable, Hashable { /// Gets or sets specify one or more sort orders, comma delimited. Options: Name, StartDate. /// /// Optional. - public var sortBy: [String]? + public var sortBy: [ItemSortBy]? /// Gets or sets sort Order - Ascending,Descending. public var sortOrder: [SortOrder]? /// Gets or sets the record index to start at. All items with a lower index will be dropped from the results. @@ -133,7 +133,7 @@ public struct GetProgramsDto: Codable, Hashable { minEndDate: Date? = nil, minStartDate: Date? = nil, seriesTimerID: String? = nil, - sortBy: [String]? = nil, + sortBy: [ItemSortBy]? = nil, sortOrder: [SortOrder]? = nil, startIndex: Int? = nil, userID: String? = nil @@ -192,7 +192,7 @@ public struct GetProgramsDto: Codable, Hashable { self.minEndDate = try values.decodeIfPresent(Date.self, forKey: "MinEndDate") self.minStartDate = try values.decodeIfPresent(Date.self, forKey: "MinStartDate") self.seriesTimerID = try values.decodeIfPresent(String.self, forKey: "SeriesTimerId") - self.sortBy = try values.decodeIfPresent([String].self, forKey: "SortBy") + self.sortBy = try values.decodeIfPresent([ItemSortBy].self, forKey: "SortBy") self.sortOrder = try values.decodeIfPresent([SortOrder].self, forKey: "SortOrder") self.startIndex = try values.decodeIfPresent(Int.self, forKey: "StartIndex") self.userID = try values.decodeIfPresent(String.self, forKey: "UserId") diff --git a/Sources/Entities/GroupInfoDtoGroupUpdate.swift b/Sources/Entities/GroupInfoDtoGroupUpdate.swift new file mode 100644 index 00000000..20c47dc0 --- /dev/null +++ b/Sources/Entities/GroupInfoDtoGroupUpdate.swift @@ -0,0 +1,39 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Class GroupUpdate. +public struct GroupInfoDtoGroupUpdate: Codable, Hashable { + /// Gets the update data. + public var data: GroupInfoDto? + /// Gets the group identifier. + public var groupID: String? + /// Gets the update type. + public var type: GroupUpdateType? + + public init(data: GroupInfoDto? = nil, groupID: String? = nil, type: GroupUpdateType? = nil) { + self.data = data + self.groupID = groupID + self.type = type + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.data = try values.decodeIfPresent(GroupInfoDto.self, forKey: "Data") + self.groupID = try values.decodeIfPresent(String.self, forKey: "GroupId") + self.type = try values.decodeIfPresent(GroupUpdateType.self, forKey: "Type") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(data, forKey: "Data") + try values.encodeIfPresent(groupID, forKey: "GroupId") + try values.encodeIfPresent(type, forKey: "Type") + } +} diff --git a/Sources/Entities/GroupStateUpdate.swift b/Sources/Entities/GroupStateUpdate.swift new file mode 100644 index 00000000..218cb6b7 --- /dev/null +++ b/Sources/Entities/GroupStateUpdate.swift @@ -0,0 +1,34 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Class GroupStateUpdate. +public struct GroupStateUpdate: Codable, Hashable { + /// Gets the reason of the state change. + public var reason: PlaybackRequestType? + /// Gets the state of the group. + public var state: GroupStateType? + + public init(reason: PlaybackRequestType? = nil, state: GroupStateType? = nil) { + self.reason = reason + self.state = state + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.reason = try values.decodeIfPresent(PlaybackRequestType.self, forKey: "Reason") + self.state = try values.decodeIfPresent(GroupStateType.self, forKey: "State") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(reason, forKey: "Reason") + try values.encodeIfPresent(state, forKey: "State") + } +} diff --git a/Sources/Entities/GroupStateUpdateGroupUpdate.swift b/Sources/Entities/GroupStateUpdateGroupUpdate.swift new file mode 100644 index 00000000..e925503f --- /dev/null +++ b/Sources/Entities/GroupStateUpdateGroupUpdate.swift @@ -0,0 +1,39 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Class GroupUpdate. +public struct GroupStateUpdateGroupUpdate: Codable, Hashable { + /// Gets the update data. + public var data: GroupStateUpdate? + /// Gets the group identifier. + public var groupID: String? + /// Gets the update type. + public var type: GroupUpdateType? + + public init(data: GroupStateUpdate? = nil, groupID: String? = nil, type: GroupUpdateType? = nil) { + self.data = data + self.groupID = groupID + self.type = type + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.data = try values.decodeIfPresent(GroupStateUpdate.self, forKey: "Data") + self.groupID = try values.decodeIfPresent(String.self, forKey: "GroupId") + self.type = try values.decodeIfPresent(GroupUpdateType.self, forKey: "Type") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(data, forKey: "Data") + try values.encodeIfPresent(groupID, forKey: "GroupId") + try values.encodeIfPresent(type, forKey: "Type") + } +} diff --git a/Sources/Entities/GroupUpdate.swift b/Sources/Entities/GroupUpdate.swift new file mode 100644 index 00000000..c26d6085 --- /dev/null +++ b/Sources/Entities/GroupUpdate.swift @@ -0,0 +1,55 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Group update without data. +public enum GroupUpdate: Codable, Hashable { + case groupInfoDtoGroupUpdate(GroupInfoDtoGroupUpdate) + case groupStateUpdateGroupUpdate(GroupStateUpdateGroupUpdate) + case stringGroupUpdate(StringGroupUpdate) + case playQueueUpdateGroupUpdate(PlayQueueUpdateGroupUpdate) + + public init(from decoder: Decoder) throws { + + struct Discriminator: Decodable { + let _Type: String + } + + let container = try decoder.singleValueContainer() + let discriminatorValue = try container.decode(Discriminator.self)._Type + + switch discriminatorValue { + case "GroupJoined": self = try .groupInfoDtoGroupUpdate(container.decode(GroupInfoDtoGroupUpdate.self)) + case "StateUpdate": self = try .groupStateUpdateGroupUpdate(container.decode(GroupStateUpdateGroupUpdate.self)) + case "GroupDoesNotExist": self = try .stringGroupUpdate(container.decode(StringGroupUpdate.self)) + case "GroupLeft": self = try .stringGroupUpdate(container.decode(StringGroupUpdate.self)) + case "LibraryAccessDenied": self = try .stringGroupUpdate(container.decode(StringGroupUpdate.self)) + case "NotInGroup": self = try .stringGroupUpdate(container.decode(StringGroupUpdate.self)) + case "UserJoined": self = try .stringGroupUpdate(container.decode(StringGroupUpdate.self)) + case "UserLeft": self = try .stringGroupUpdate(container.decode(StringGroupUpdate.self)) + case "PlayQueue": self = try .playQueueUpdateGroupUpdate(container.decode(PlayQueueUpdateGroupUpdate.self)) + + default: + throw DecodingError.dataCorruptedError( + in: container, + debugDescription: "Discriminator value '\(discriminatorValue)' does not match any expected values (GroupJoined, StateUpdate, GroupDoesNotExist, GroupLeft, LibraryAccessDenied, NotInGroup, UserJoined, UserLeft, PlayQueue)." + ) + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + switch self { + case let .groupInfoDtoGroupUpdate(value): try container.encode(value) + case let .groupStateUpdateGroupUpdate(value): try container.encode(value) + case let .stringGroupUpdate(value): try container.encode(value) + case let .playQueueUpdateGroupUpdate(value): try container.encode(value) + } + } +} diff --git a/Sources/Entities/HardwareEncodingType.swift b/Sources/Entities/HardwareEncodingType.swift index f6c6c23d..d4cd9550 100644 --- a/Sources/Entities/HardwareEncodingType.swift +++ b/Sources/Entities/HardwareEncodingType.swift @@ -16,4 +16,5 @@ public enum HardwareEncodingType: String, Codable, CaseIterable { case v4l2m2m = "V4L2M2M" case vaapi = "VAAPI" case videoToolBox = "VideoToolBox" + case rkmpp = "RKMPP" } diff --git a/Sources/Entities/ImageFormat.swift b/Sources/Entities/ImageFormat.swift index 31af4148..a1a4906c 100644 --- a/Sources/Entities/ImageFormat.swift +++ b/Sources/Entities/ImageFormat.swift @@ -15,4 +15,5 @@ public enum ImageFormat: String, Codable, CaseIterable { case jpg = "Jpg" case png = "Png" case webp = "Webp" + case svg = "Svg" } diff --git a/Sources/Entities/ImageResolution.swift b/Sources/Entities/ImageResolution.swift new file mode 100644 index 00000000..a7086031 --- /dev/null +++ b/Sources/Entities/ImageResolution.swift @@ -0,0 +1,22 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Enum ImageResolution. +public enum ImageResolution: String, Codable, CaseIterable { + case matchSource = "MatchSource" + case p144 = "P144" + case p240 = "P240" + case p360 = "P360" + case p480 = "P480" + case p720 = "P720" + case p1080 = "P1080" + case p1440 = "P1440" + case p2160 = "P2160" +} diff --git a/Sources/Entities/InboundKeepAliveMessage.swift b/Sources/Entities/InboundKeepAliveMessage.swift new file mode 100644 index 00000000..9b198886 --- /dev/null +++ b/Sources/Entities/InboundKeepAliveMessage.swift @@ -0,0 +1,29 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Keep alive websocket messages. +public struct InboundKeepAliveMessage: Codable, Hashable { + /// The different kinds of messages that are used in the WebSocket api. + public var messageType: SessionMessageType? + + public init(messageType: SessionMessageType? = nil) { + self.messageType = messageType + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.messageType = try values.decodeIfPresent(SessionMessageType.self, forKey: "MessageType") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(messageType, forKey: "MessageType") + } +} diff --git a/Sources/Entities/InboundWebSocketMessage.swift b/Sources/Entities/InboundWebSocketMessage.swift new file mode 100644 index 00000000..0af9d5bb --- /dev/null +++ b/Sources/Entities/InboundWebSocketMessage.swift @@ -0,0 +1,59 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Represents the list of possible inbound websocket types +public enum InboundWebSocketMessage: Codable, Hashable { + case activityLogEntryStartMessage(ActivityLogEntryStartMessage) + case activityLogEntryStopMessage(ActivityLogEntryStopMessage) + case inboundKeepAliveMessage(InboundKeepAliveMessage) + case scheduledTasksInfoStartMessage(ScheduledTasksInfoStartMessage) + case scheduledTasksInfoStopMessage(ScheduledTasksInfoStopMessage) + case sessionsStartMessage(SessionsStartMessage) + case sessionsStopMessage(SessionsStopMessage) + + public init(from decoder: Decoder) throws { + + struct Discriminator: Decodable { + let MessageType: String + } + + let container = try decoder.singleValueContainer() + let discriminatorValue = try container.decode(Discriminator.self).MessageType + + switch discriminatorValue { + case "ActivityLogEntryStart": self = try .activityLogEntryStartMessage(container.decode(ActivityLogEntryStartMessage.self)) + case "ActivityLogEntryStop": self = try .activityLogEntryStopMessage(container.decode(ActivityLogEntryStopMessage.self)) + case "KeepAlive": self = try .inboundKeepAliveMessage(container.decode(InboundKeepAliveMessage.self)) + case "ScheduledTasksInfoStart": self = try .scheduledTasksInfoStartMessage(container.decode(ScheduledTasksInfoStartMessage.self)) + case "ScheduledTasksInfoStop": self = try .scheduledTasksInfoStopMessage(container.decode(ScheduledTasksInfoStopMessage.self)) + case "SessionsStart": self = try .sessionsStartMessage(container.decode(SessionsStartMessage.self)) + case "SessionsStop": self = try .sessionsStopMessage(container.decode(SessionsStopMessage.self)) + + default: + throw DecodingError.dataCorruptedError( + in: container, + debugDescription: "Discriminator value '\(discriminatorValue)' does not match any expected values (ActivityLogEntryStart, ActivityLogEntryStop, KeepAlive, ScheduledTasksInfoStart, ScheduledTasksInfoStop, SessionsStart, SessionsStop)." + ) + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + switch self { + case let .activityLogEntryStartMessage(value): try container.encode(value) + case let .activityLogEntryStopMessage(value): try container.encode(value) + case let .inboundKeepAliveMessage(value): try container.encode(value) + case let .scheduledTasksInfoStartMessage(value): try container.encode(value) + case let .scheduledTasksInfoStopMessage(value): try container.encode(value) + case let .sessionsStartMessage(value): try container.encode(value) + case let .sessionsStopMessage(value): try container.encode(value) + } + } +} diff --git a/Sources/Entities/ItemFields.swift b/Sources/Entities/ItemFields.swift index ab61d811..6201493f 100644 --- a/Sources/Entities/ItemFields.swift +++ b/Sources/Entities/ItemFields.swift @@ -15,6 +15,7 @@ public enum ItemFields: String, Codable, CaseIterable { case canDownload = "CanDownload" case channelInfo = "ChannelInfo" case chapters = "Chapters" + case trickplay = "Trickplay" case childCount = "ChildCount" case cumulativeRunTimeTicks = "CumulativeRunTimeTicks" case customRating = "CustomRating" @@ -45,8 +46,6 @@ public enum ItemFields: String, Codable, CaseIterable { case sortName = "SortName" case specialEpisodeNumbers = "SpecialEpisodeNumbers" case studios = "Studios" - case basicSyncInfo = "BasicSyncInfo" - case syncInfo = "SyncInfo" case taglines = "Taglines" case tags = "Tags" case remoteTrailers = "RemoteTrailers" diff --git a/Sources/Entities/ItemSortBy.swift b/Sources/Entities/ItemSortBy.swift new file mode 100644 index 00000000..a1ee7cf7 --- /dev/null +++ b/Sources/Entities/ItemSortBy.swift @@ -0,0 +1,45 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// These represent sort orders. +public enum ItemSortBy: String, Codable, CaseIterable { + case `default` = "Default" + case airedEpisodeOrder = "AiredEpisodeOrder" + case album = "Album" + case albumArtist = "AlbumArtist" + case artist = "Artist" + case dateCreated = "DateCreated" + case officialRating = "OfficialRating" + case datePlayed = "DatePlayed" + case premiereDate = "PremiereDate" + case startDate = "StartDate" + case sortName = "SortName" + case name = "Name" + case random = "Random" + case runtime = "Runtime" + case communityRating = "CommunityRating" + case productionYear = "ProductionYear" + case playCount = "PlayCount" + case criticRating = "CriticRating" + case isFolder = "IsFolder" + case isUnplayed = "IsUnplayed" + case isPlayed = "IsPlayed" + case seriesSortName = "SeriesSortName" + case videoBitRate = "VideoBitRate" + case airTime = "AirTime" + case studio = "Studio" + case isFavoriteOrLiked = "IsFavoriteOrLiked" + case dateLastContentAdded = "DateLastContentAdded" + case seriesDatePlayed = "SeriesDatePlayed" + case parentIndexNumber = "ParentIndexNumber" + case indexNumber = "IndexNumber" + case similarityScore = "SimilarityScore" + case searchScore = "SearchScore" +} diff --git a/Sources/Entities/LibraryChangedMessage.swift b/Sources/Entities/LibraryChangedMessage.swift new file mode 100644 index 00000000..1fe17a38 --- /dev/null +++ b/Sources/Entities/LibraryChangedMessage.swift @@ -0,0 +1,39 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Library changed message. +public struct LibraryChangedMessage: Codable, Hashable { + /// Class LibraryUpdateInfo. + public var data: LibraryUpdateInfo? + /// Gets or sets the message id. + public var messageID: String? + /// The different kinds of messages that are used in the WebSocket api. + public var messageType: SessionMessageType? + + public init(data: LibraryUpdateInfo? = nil, messageID: String? = nil, messageType: SessionMessageType? = nil) { + self.data = data + self.messageID = messageID + self.messageType = messageType + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.data = try values.decodeIfPresent(LibraryUpdateInfo.self, forKey: "Data") + self.messageID = try values.decodeIfPresent(String.self, forKey: "MessageId") + self.messageType = try values.decodeIfPresent(SessionMessageType.self, forKey: "MessageType") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(data, forKey: "Data") + try values.encodeIfPresent(messageID, forKey: "MessageId") + try values.encodeIfPresent(messageType, forKey: "MessageType") + } +} diff --git a/Sources/Entities/LibraryOptions.swift b/Sources/Entities/LibraryOptions.swift index 5d0b0a82..249def6d 100644 --- a/Sources/Entities/LibraryOptions.swift +++ b/Sources/Entities/LibraryOptions.swift @@ -18,12 +18,17 @@ public struct LibraryOptions: Codable, Hashable { public var enableAutomaticSeriesGrouping: Bool? public var enableChapterImageExtraction: Bool? public var enableEmbeddedEpisodeInfos: Bool? + public var enableEmbeddedExtrasTitles: Bool? public var enableEmbeddedTitles: Bool? /// - warning: Deprecated. public var enableInternetProviders: Bool? + public var enableLUFSScan: Bool? public var enablePhotos: Bool? public var enableRealtimeMonitor: Bool? + public var enableTrickplayImageExtraction: Bool? + public var isEnabled: Bool? public var isExtractChapterImagesDuringLibraryScan: Bool? + public var isExtractTrickplayImagesDuringLibraryScan: Bool? public var localMetadataReaderOrder: [String]? /// Gets or sets the metadata country code. public var metadataCountryCode: String? @@ -33,6 +38,7 @@ public struct LibraryOptions: Codable, Hashable { public var preferredMetadataLanguage: String? public var requirePerfectSubtitleMatch: Bool? public var isSaveLocalMetadata: Bool? + public var isSaveLyricsWithMedia: Bool public var isSaveSubtitlesWithMedia: Bool? public var seasonZeroDisplayName: String? public var isSkipSubtitlesIfAudioTrackMatches: Bool? @@ -50,11 +56,16 @@ public struct LibraryOptions: Codable, Hashable { enableAutomaticSeriesGrouping: Bool? = nil, enableChapterImageExtraction: Bool? = nil, enableEmbeddedEpisodeInfos: Bool? = nil, + enableEmbeddedExtrasTitles: Bool? = nil, enableEmbeddedTitles: Bool? = nil, enableInternetProviders: Bool? = nil, + enableLUFSScan: Bool? = nil, enablePhotos: Bool? = nil, enableRealtimeMonitor: Bool? = nil, + enableTrickplayImageExtraction: Bool? = nil, + isEnabled: Bool? = nil, isExtractChapterImagesDuringLibraryScan: Bool? = nil, + isExtractTrickplayImagesDuringLibraryScan: Bool? = nil, localMetadataReaderOrder: [String]? = nil, metadataCountryCode: String? = nil, metadataSavers: [String]? = nil, @@ -62,6 +73,7 @@ public struct LibraryOptions: Codable, Hashable { preferredMetadataLanguage: String? = nil, requirePerfectSubtitleMatch: Bool? = nil, isSaveLocalMetadata: Bool? = nil, + isSaveLyricsWithMedia: Bool? = nil, isSaveSubtitlesWithMedia: Bool? = nil, seasonZeroDisplayName: String? = nil, isSkipSubtitlesIfAudioTrackMatches: Bool? = nil, @@ -78,11 +90,16 @@ public struct LibraryOptions: Codable, Hashable { self.enableAutomaticSeriesGrouping = enableAutomaticSeriesGrouping self.enableChapterImageExtraction = enableChapterImageExtraction self.enableEmbeddedEpisodeInfos = enableEmbeddedEpisodeInfos + self.enableEmbeddedExtrasTitles = enableEmbeddedExtrasTitles self.enableEmbeddedTitles = enableEmbeddedTitles self.enableInternetProviders = enableInternetProviders + self.enableLUFSScan = enableLUFSScan self.enablePhotos = enablePhotos self.enableRealtimeMonitor = enableRealtimeMonitor + self.enableTrickplayImageExtraction = enableTrickplayImageExtraction + self.isEnabled = isEnabled self.isExtractChapterImagesDuringLibraryScan = isExtractChapterImagesDuringLibraryScan + self.isExtractTrickplayImagesDuringLibraryScan = isExtractTrickplayImagesDuringLibraryScan self.localMetadataReaderOrder = localMetadataReaderOrder self.metadataCountryCode = metadataCountryCode self.metadataSavers = metadataSavers @@ -90,6 +107,7 @@ public struct LibraryOptions: Codable, Hashable { self.preferredMetadataLanguage = preferredMetadataLanguage self.requirePerfectSubtitleMatch = requirePerfectSubtitleMatch self.isSaveLocalMetadata = isSaveLocalMetadata + self.isSaveLyricsWithMedia = isSaveLyricsWithMedia ?? false self.isSaveSubtitlesWithMedia = isSaveSubtitlesWithMedia self.seasonZeroDisplayName = seasonZeroDisplayName self.isSkipSubtitlesIfAudioTrackMatches = isSkipSubtitlesIfAudioTrackMatches @@ -109,14 +127,22 @@ public struct LibraryOptions: Codable, Hashable { self.enableAutomaticSeriesGrouping = try values.decodeIfPresent(Bool.self, forKey: "EnableAutomaticSeriesGrouping") self.enableChapterImageExtraction = try values.decodeIfPresent(Bool.self, forKey: "EnableChapterImageExtraction") self.enableEmbeddedEpisodeInfos = try values.decodeIfPresent(Bool.self, forKey: "EnableEmbeddedEpisodeInfos") + self.enableEmbeddedExtrasTitles = try values.decodeIfPresent(Bool.self, forKey: "EnableEmbeddedExtrasTitles") self.enableEmbeddedTitles = try values.decodeIfPresent(Bool.self, forKey: "EnableEmbeddedTitles") self.enableInternetProviders = try values.decodeIfPresent(Bool.self, forKey: "EnableInternetProviders") + self.enableLUFSScan = try values.decodeIfPresent(Bool.self, forKey: "EnableLUFSScan") self.enablePhotos = try values.decodeIfPresent(Bool.self, forKey: "EnablePhotos") self.enableRealtimeMonitor = try values.decodeIfPresent(Bool.self, forKey: "EnableRealtimeMonitor") + self.enableTrickplayImageExtraction = try values.decodeIfPresent(Bool.self, forKey: "EnableTrickplayImageExtraction") + self.isEnabled = try values.decodeIfPresent(Bool.self, forKey: "Enabled") self.isExtractChapterImagesDuringLibraryScan = try values.decodeIfPresent( Bool.self, forKey: "ExtractChapterImagesDuringLibraryScan" ) + self.isExtractTrickplayImagesDuringLibraryScan = try values.decodeIfPresent( + Bool.self, + forKey: "ExtractTrickplayImagesDuringLibraryScan" + ) self.localMetadataReaderOrder = try values.decodeIfPresent([String].self, forKey: "LocalMetadataReaderOrder") self.metadataCountryCode = try values.decodeIfPresent(String.self, forKey: "MetadataCountryCode") self.metadataSavers = try values.decodeIfPresent([String].self, forKey: "MetadataSavers") @@ -124,6 +150,7 @@ public struct LibraryOptions: Codable, Hashable { self.preferredMetadataLanguage = try values.decodeIfPresent(String.self, forKey: "PreferredMetadataLanguage") self.requirePerfectSubtitleMatch = try values.decodeIfPresent(Bool.self, forKey: "RequirePerfectSubtitleMatch") self.isSaveLocalMetadata = try values.decodeIfPresent(Bool.self, forKey: "SaveLocalMetadata") + self.isSaveLyricsWithMedia = try values.decodeIfPresent(Bool.self, forKey: "SaveLyricsWithMedia") ?? false self.isSaveSubtitlesWithMedia = try values.decodeIfPresent(Bool.self, forKey: "SaveSubtitlesWithMedia") self.seasonZeroDisplayName = try values.decodeIfPresent(String.self, forKey: "SeasonZeroDisplayName") self.isSkipSubtitlesIfAudioTrackMatches = try values.decodeIfPresent(Bool.self, forKey: "SkipSubtitlesIfAudioTrackMatches") @@ -146,11 +173,16 @@ public struct LibraryOptions: Codable, Hashable { try values.encodeIfPresent(enableAutomaticSeriesGrouping, forKey: "EnableAutomaticSeriesGrouping") try values.encodeIfPresent(enableChapterImageExtraction, forKey: "EnableChapterImageExtraction") try values.encodeIfPresent(enableEmbeddedEpisodeInfos, forKey: "EnableEmbeddedEpisodeInfos") + try values.encodeIfPresent(enableEmbeddedExtrasTitles, forKey: "EnableEmbeddedExtrasTitles") try values.encodeIfPresent(enableEmbeddedTitles, forKey: "EnableEmbeddedTitles") try values.encodeIfPresent(enableInternetProviders, forKey: "EnableInternetProviders") + try values.encodeIfPresent(enableLUFSScan, forKey: "EnableLUFSScan") try values.encodeIfPresent(enablePhotos, forKey: "EnablePhotos") try values.encodeIfPresent(enableRealtimeMonitor, forKey: "EnableRealtimeMonitor") + try values.encodeIfPresent(enableTrickplayImageExtraction, forKey: "EnableTrickplayImageExtraction") + try values.encodeIfPresent(isEnabled, forKey: "Enabled") try values.encodeIfPresent(isExtractChapterImagesDuringLibraryScan, forKey: "ExtractChapterImagesDuringLibraryScan") + try values.encodeIfPresent(isExtractTrickplayImagesDuringLibraryScan, forKey: "ExtractTrickplayImagesDuringLibraryScan") try values.encodeIfPresent(localMetadataReaderOrder, forKey: "LocalMetadataReaderOrder") try values.encodeIfPresent(metadataCountryCode, forKey: "MetadataCountryCode") try values.encodeIfPresent(metadataSavers, forKey: "MetadataSavers") @@ -158,6 +190,7 @@ public struct LibraryOptions: Codable, Hashable { try values.encodeIfPresent(preferredMetadataLanguage, forKey: "PreferredMetadataLanguage") try values.encodeIfPresent(requirePerfectSubtitleMatch, forKey: "RequirePerfectSubtitleMatch") try values.encodeIfPresent(isSaveLocalMetadata, forKey: "SaveLocalMetadata") + try values.encodeIfPresent(isSaveLyricsWithMedia, forKey: "SaveLyricsWithMedia") try values.encodeIfPresent(isSaveSubtitlesWithMedia, forKey: "SaveSubtitlesWithMedia") try values.encodeIfPresent(seasonZeroDisplayName, forKey: "SeasonZeroDisplayName") try values.encodeIfPresent(isSkipSubtitlesIfAudioTrackMatches, forKey: "SkipSubtitlesIfAudioTrackMatches") diff --git a/Sources/Entities/LiveTvOptions.swift b/Sources/Entities/LiveTvOptions.swift index e3c26359..0e4c1c3f 100644 --- a/Sources/Entities/LiveTvOptions.swift +++ b/Sources/Entities/LiveTvOptions.swift @@ -20,6 +20,8 @@ public struct LiveTvOptions: Codable, Hashable { public var recordingPath: String? public var recordingPostProcessor: String? public var recordingPostProcessorArguments: String? + public var isSaveRecordingImages: Bool? + public var isSaveRecordingNFO: Bool? public var seriesRecordingPath: String? public var tunerHosts: [TunerHostInfo]? @@ -35,6 +37,8 @@ public struct LiveTvOptions: Codable, Hashable { recordingPath: String? = nil, recordingPostProcessor: String? = nil, recordingPostProcessorArguments: String? = nil, + isSaveRecordingImages: Bool? = nil, + isSaveRecordingNFO: Bool? = nil, seriesRecordingPath: String? = nil, tunerHosts: [TunerHostInfo]? = nil ) { @@ -49,6 +53,8 @@ public struct LiveTvOptions: Codable, Hashable { self.recordingPath = recordingPath self.recordingPostProcessor = recordingPostProcessor self.recordingPostProcessorArguments = recordingPostProcessorArguments + self.isSaveRecordingImages = isSaveRecordingImages + self.isSaveRecordingNFO = isSaveRecordingNFO self.seriesRecordingPath = seriesRecordingPath self.tunerHosts = tunerHosts } @@ -69,6 +75,8 @@ public struct LiveTvOptions: Codable, Hashable { self.recordingPath = try values.decodeIfPresent(String.self, forKey: "RecordingPath") self.recordingPostProcessor = try values.decodeIfPresent(String.self, forKey: "RecordingPostProcessor") self.recordingPostProcessorArguments = try values.decodeIfPresent(String.self, forKey: "RecordingPostProcessorArguments") + self.isSaveRecordingImages = try values.decodeIfPresent(Bool.self, forKey: "SaveRecordingImages") + self.isSaveRecordingNFO = try values.decodeIfPresent(Bool.self, forKey: "SaveRecordingNFO") self.seriesRecordingPath = try values.decodeIfPresent(String.self, forKey: "SeriesRecordingPath") self.tunerHosts = try values.decodeIfPresent([TunerHostInfo].self, forKey: "TunerHosts") } @@ -86,6 +94,8 @@ public struct LiveTvOptions: Codable, Hashable { try values.encodeIfPresent(recordingPath, forKey: "RecordingPath") try values.encodeIfPresent(recordingPostProcessor, forKey: "RecordingPostProcessor") try values.encodeIfPresent(recordingPostProcessorArguments, forKey: "RecordingPostProcessorArguments") + try values.encodeIfPresent(isSaveRecordingImages, forKey: "SaveRecordingImages") + try values.encodeIfPresent(isSaveRecordingNFO, forKey: "SaveRecordingNFO") try values.encodeIfPresent(seriesRecordingPath, forKey: "SeriesRecordingPath") try values.encodeIfPresent(tunerHosts, forKey: "TunerHosts") } diff --git a/Sources/Entities/LyricDto.swift b/Sources/Entities/LyricDto.swift new file mode 100644 index 00000000..3711f62f --- /dev/null +++ b/Sources/Entities/LyricDto.swift @@ -0,0 +1,34 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// LyricResponse model. +public struct LyricDto: Codable, Hashable { + /// Gets or sets a collection of individual lyric lines. + public var lyrics: [LyricLine]? + /// Gets or sets Metadata for the lyrics. + public var metadata: LyricMetadata? + + public init(lyrics: [LyricLine]? = nil, metadata: LyricMetadata? = nil) { + self.lyrics = lyrics + self.metadata = metadata + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.lyrics = try values.decodeIfPresent([LyricLine].self, forKey: "Lyrics") + self.metadata = try values.decodeIfPresent(LyricMetadata.self, forKey: "Metadata") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(lyrics, forKey: "Lyrics") + try values.encodeIfPresent(metadata, forKey: "Metadata") + } +} diff --git a/Sources/Entities/LyricLine.swift b/Sources/Entities/LyricLine.swift new file mode 100644 index 00000000..446e7ca0 --- /dev/null +++ b/Sources/Entities/LyricLine.swift @@ -0,0 +1,34 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Lyric model. +public struct LyricLine: Codable, Hashable { + /// Gets the start time in ticks. + public var start: Int? + /// Gets the text of this lyric line. + public var text: String? + + public init(start: Int? = nil, text: String? = nil) { + self.start = start + self.text = text + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.start = try values.decodeIfPresent(Int.self, forKey: "Start") + self.text = try values.decodeIfPresent(String.self, forKey: "Text") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(start, forKey: "Start") + try values.encodeIfPresent(text, forKey: "Text") + } +} diff --git a/Sources/Entities/LyricMetadata.swift b/Sources/Entities/LyricMetadata.swift new file mode 100644 index 00000000..58dea8e3 --- /dev/null +++ b/Sources/Entities/LyricMetadata.swift @@ -0,0 +1,85 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// LyricMetadata model. +public struct LyricMetadata: Codable, Hashable { + /// Gets or sets the album this song is on. + public var album: String? + /// Gets or sets the song artist. + public var artist: String? + /// Gets or sets the author of the lyric data. + public var author: String? + /// Gets or sets who the LRC file was created by. + public var by: String? + /// Gets or sets the software used to create the LRC file. + public var creator: String? + /// Gets or sets a value indicating whether this lyric is synced. + public var isSynced: Bool? + /// Gets or sets the length of the song in ticks. + public var length: Int? + /// Gets or sets the lyric offset compared to audio in ticks. + public var offset: Int? + /// Gets or sets the title of the song. + public var title: String? + /// Gets or sets the version of the creator used. + public var version: String? + + public init( + album: String? = nil, + artist: String? = nil, + author: String? = nil, + by: String? = nil, + creator: String? = nil, + isSynced: Bool? = nil, + length: Int? = nil, + offset: Int? = nil, + title: String? = nil, + version: String? = nil + ) { + self.album = album + self.artist = artist + self.author = author + self.by = by + self.creator = creator + self.isSynced = isSynced + self.length = length + self.offset = offset + self.title = title + self.version = version + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.album = try values.decodeIfPresent(String.self, forKey: "Album") + self.artist = try values.decodeIfPresent(String.self, forKey: "Artist") + self.author = try values.decodeIfPresent(String.self, forKey: "Author") + self.by = try values.decodeIfPresent(String.self, forKey: "By") + self.creator = try values.decodeIfPresent(String.self, forKey: "Creator") + self.isSynced = try values.decodeIfPresent(Bool.self, forKey: "IsSynced") + self.length = try values.decodeIfPresent(Int.self, forKey: "Length") + self.offset = try values.decodeIfPresent(Int.self, forKey: "Offset") + self.title = try values.decodeIfPresent(String.self, forKey: "Title") + self.version = try values.decodeIfPresent(String.self, forKey: "Version") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(album, forKey: "Album") + try values.encodeIfPresent(artist, forKey: "Artist") + try values.encodeIfPresent(author, forKey: "Author") + try values.encodeIfPresent(by, forKey: "By") + try values.encodeIfPresent(creator, forKey: "Creator") + try values.encodeIfPresent(isSynced, forKey: "IsSynced") + try values.encodeIfPresent(length, forKey: "Length") + try values.encodeIfPresent(offset, forKey: "Offset") + try values.encodeIfPresent(title, forKey: "Title") + try values.encodeIfPresent(version, forKey: "Version") + } +} diff --git a/Sources/Entities/MediaSourceInfo.swift b/Sources/Entities/MediaSourceInfo.swift index 0ea59ebb..e63adad7 100644 --- a/Sources/Entities/MediaSourceInfo.swift +++ b/Sources/Entities/MediaSourceInfo.swift @@ -49,7 +49,10 @@ public struct MediaSourceInfo: Codable, Hashable, Identifiable { public var isSupportsTranscoding: Bool? public var timestamp: TransportStreamTimestamp? public var transcodingContainer: String? - public var transcodingSubProtocol: String? + /// Media streaming protocol. + /// + /// Lowercase for backwards compatibility. + public var transcodingSubProtocol: MediaStreamProtocol? public var transcodingURL: String? public var type: MediaSourceType? public var video3DFormat: Video3DFormat? @@ -93,7 +96,7 @@ public struct MediaSourceInfo: Codable, Hashable, Identifiable { isSupportsTranscoding: Bool? = nil, timestamp: TransportStreamTimestamp? = nil, transcodingContainer: String? = nil, - transcodingSubProtocol: String? = nil, + transcodingSubProtocol: MediaStreamProtocol? = nil, transcodingURL: String? = nil, type: MediaSourceType? = nil, video3DFormat: Video3DFormat? = nil, @@ -182,7 +185,7 @@ public struct MediaSourceInfo: Codable, Hashable, Identifiable { self.isSupportsTranscoding = try values.decodeIfPresent(Bool.self, forKey: "SupportsTranscoding") self.timestamp = try values.decodeIfPresent(TransportStreamTimestamp.self, forKey: "Timestamp") self.transcodingContainer = try values.decodeIfPresent(String.self, forKey: "TranscodingContainer") - self.transcodingSubProtocol = try values.decodeIfPresent(String.self, forKey: "TranscodingSubProtocol") + self.transcodingSubProtocol = try values.decodeIfPresent(MediaStreamProtocol.self, forKey: "TranscodingSubProtocol") self.transcodingURL = try values.decodeIfPresent(String.self, forKey: "TranscodingUrl") self.type = try values.decodeIfPresent(MediaSourceType.self, forKey: "Type") self.video3DFormat = try values.decodeIfPresent(Video3DFormat.self, forKey: "Video3DFormat") diff --git a/Sources/Entities/MediaStream.swift b/Sources/Entities/MediaStream.swift index 4671d9d4..e1238854 100644 --- a/Sources/Entities/MediaStream.swift +++ b/Sources/Entities/MediaStream.swift @@ -12,6 +12,8 @@ import Foundation public struct MediaStream: Codable, Hashable { /// Gets or sets the aspect ratio. public var aspectRatio: String? + /// Gets the audio spatial format. + public var audioSpatialFormat: AudioSpatialFormat? /// Gets or sets the average frame rate. public var averageFrameRate: Float? /// Gets or sets the bit depth. @@ -72,6 +74,8 @@ public struct MediaStream: Codable, Hashable { public var isExternalURL: Bool? /// Gets or sets a value indicating whether this instance is forced. public var isForced: Bool? + /// Gets or sets a value indicating whether this instance is for the hearing impaired. + public var isHearingImpaired: Bool? /// Gets or sets a value indicating whether this instance is interlaced. public var isInterlaced: Bool? public var isTextSubtitleStream: Bool? @@ -82,6 +86,7 @@ public struct MediaStream: Codable, Hashable { public var localizedDefault: String? public var localizedExternal: String? public var localizedForced: String? + public var localizedHearingImpaired: String? public var localizedUndefined: String? public var nalLengthSize: String? /// Gets or sets the length of the packet. @@ -113,14 +118,15 @@ public struct MediaStream: Codable, Hashable { /// Gets the video dovi title. public var videoDoViTitle: String? /// Gets the video range. - public var videoRange: String? + public var videoRange: VideoRange? /// Gets the video range type. - public var videoRangeType: String? + public var videoRangeType: VideoRangeType? /// Gets or sets the width. public var width: Int? public init( aspectRatio: String? = nil, + audioSpatialFormat: AudioSpatialFormat? = nil, averageFrameRate: Float? = nil, bitDepth: Int? = nil, bitRate: Int? = nil, @@ -152,6 +158,7 @@ public struct MediaStream: Codable, Hashable { isExternal: Bool? = nil, isExternalURL: Bool? = nil, isForced: Bool? = nil, + isHearingImpaired: Bool? = nil, isInterlaced: Bool? = nil, isTextSubtitleStream: Bool? = nil, language: String? = nil, @@ -159,6 +166,7 @@ public struct MediaStream: Codable, Hashable { localizedDefault: String? = nil, localizedExternal: String? = nil, localizedForced: String? = nil, + localizedHearingImpaired: String? = nil, localizedUndefined: String? = nil, nalLengthSize: String? = nil, packetLength: Int? = nil, @@ -175,11 +183,12 @@ public struct MediaStream: Codable, Hashable { title: String? = nil, type: MediaStreamType? = nil, videoDoViTitle: String? = nil, - videoRange: String? = nil, - videoRangeType: String? = nil, + videoRange: VideoRange? = nil, + videoRangeType: VideoRangeType? = nil, width: Int? = nil ) { self.aspectRatio = aspectRatio + self.audioSpatialFormat = audioSpatialFormat self.averageFrameRate = averageFrameRate self.bitDepth = bitDepth self.bitRate = bitRate @@ -211,6 +220,7 @@ public struct MediaStream: Codable, Hashable { self.isExternal = isExternal self.isExternalURL = isExternalURL self.isForced = isForced + self.isHearingImpaired = isHearingImpaired self.isInterlaced = isInterlaced self.isTextSubtitleStream = isTextSubtitleStream self.language = language @@ -218,6 +228,7 @@ public struct MediaStream: Codable, Hashable { self.localizedDefault = localizedDefault self.localizedExternal = localizedExternal self.localizedForced = localizedForced + self.localizedHearingImpaired = localizedHearingImpaired self.localizedUndefined = localizedUndefined self.nalLengthSize = nalLengthSize self.packetLength = packetLength @@ -242,6 +253,7 @@ public struct MediaStream: Codable, Hashable { public init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: StringCodingKey.self) self.aspectRatio = try values.decodeIfPresent(String.self, forKey: "AspectRatio") + self.audioSpatialFormat = try values.decodeIfPresent(AudioSpatialFormat.self, forKey: "AudioSpatialFormat") self.averageFrameRate = try values.decodeIfPresent(Float.self, forKey: "AverageFrameRate") self.bitDepth = try values.decodeIfPresent(Int.self, forKey: "BitDepth") self.bitRate = try values.decodeIfPresent(Int.self, forKey: "BitRate") @@ -273,6 +285,7 @@ public struct MediaStream: Codable, Hashable { self.isExternal = try values.decodeIfPresent(Bool.self, forKey: "IsExternal") self.isExternalURL = try values.decodeIfPresent(Bool.self, forKey: "IsExternalUrl") self.isForced = try values.decodeIfPresent(Bool.self, forKey: "IsForced") + self.isHearingImpaired = try values.decodeIfPresent(Bool.self, forKey: "IsHearingImpaired") self.isInterlaced = try values.decodeIfPresent(Bool.self, forKey: "IsInterlaced") self.isTextSubtitleStream = try values.decodeIfPresent(Bool.self, forKey: "IsTextSubtitleStream") self.language = try values.decodeIfPresent(String.self, forKey: "Language") @@ -280,6 +293,7 @@ public struct MediaStream: Codable, Hashable { self.localizedDefault = try values.decodeIfPresent(String.self, forKey: "LocalizedDefault") self.localizedExternal = try values.decodeIfPresent(String.self, forKey: "LocalizedExternal") self.localizedForced = try values.decodeIfPresent(String.self, forKey: "LocalizedForced") + self.localizedHearingImpaired = try values.decodeIfPresent(String.self, forKey: "LocalizedHearingImpaired") self.localizedUndefined = try values.decodeIfPresent(String.self, forKey: "LocalizedUndefined") self.nalLengthSize = try values.decodeIfPresent(String.self, forKey: "NalLengthSize") self.packetLength = try values.decodeIfPresent(Int.self, forKey: "PacketLength") @@ -296,14 +310,15 @@ public struct MediaStream: Codable, Hashable { self.title = try values.decodeIfPresent(String.self, forKey: "Title") self.type = try values.decodeIfPresent(MediaStreamType.self, forKey: "Type") self.videoDoViTitle = try values.decodeIfPresent(String.self, forKey: "VideoDoViTitle") - self.videoRange = try values.decodeIfPresent(String.self, forKey: "VideoRange") - self.videoRangeType = try values.decodeIfPresent(String.self, forKey: "VideoRangeType") + self.videoRange = try values.decodeIfPresent(VideoRange.self, forKey: "VideoRange") + self.videoRangeType = try values.decodeIfPresent(VideoRangeType.self, forKey: "VideoRangeType") self.width = try values.decodeIfPresent(Int.self, forKey: "Width") } public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: StringCodingKey.self) try values.encodeIfPresent(aspectRatio, forKey: "AspectRatio") + try values.encodeIfPresent(audioSpatialFormat, forKey: "AudioSpatialFormat") try values.encodeIfPresent(averageFrameRate, forKey: "AverageFrameRate") try values.encodeIfPresent(bitDepth, forKey: "BitDepth") try values.encodeIfPresent(bitRate, forKey: "BitRate") @@ -335,6 +350,7 @@ public struct MediaStream: Codable, Hashable { try values.encodeIfPresent(isExternal, forKey: "IsExternal") try values.encodeIfPresent(isExternalURL, forKey: "IsExternalUrl") try values.encodeIfPresent(isForced, forKey: "IsForced") + try values.encodeIfPresent(isHearingImpaired, forKey: "IsHearingImpaired") try values.encodeIfPresent(isInterlaced, forKey: "IsInterlaced") try values.encodeIfPresent(isTextSubtitleStream, forKey: "IsTextSubtitleStream") try values.encodeIfPresent(language, forKey: "Language") @@ -342,6 +358,7 @@ public struct MediaStream: Codable, Hashable { try values.encodeIfPresent(localizedDefault, forKey: "LocalizedDefault") try values.encodeIfPresent(localizedExternal, forKey: "LocalizedExternal") try values.encodeIfPresent(localizedForced, forKey: "LocalizedForced") + try values.encodeIfPresent(localizedHearingImpaired, forKey: "LocalizedHearingImpaired") try values.encodeIfPresent(localizedUndefined, forKey: "LocalizedUndefined") try values.encodeIfPresent(nalLengthSize, forKey: "NalLengthSize") try values.encodeIfPresent(packetLength, forKey: "PacketLength") diff --git a/Sources/Entities/MediaStreamProtocol.swift b/Sources/Entities/MediaStreamProtocol.swift new file mode 100644 index 00000000..c200ae87 --- /dev/null +++ b/Sources/Entities/MediaStreamProtocol.swift @@ -0,0 +1,17 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Media streaming protocol. +/// +/// Lowercase for backwards compatibility. +public enum MediaStreamProtocol: String, Codable, CaseIterable { + case http + case hls +} diff --git a/Sources/Entities/MediaStreamType.swift b/Sources/Entities/MediaStreamType.swift index 7e34c676..07fa13dd 100644 --- a/Sources/Entities/MediaStreamType.swift +++ b/Sources/Entities/MediaStreamType.swift @@ -15,4 +15,5 @@ public enum MediaStreamType: String, Codable, CaseIterable { case subtitle = "Subtitle" case embeddedImage = "EmbeddedImage" case data = "Data" + case lyric = "Lyric" } diff --git a/Sources/Entities/MediaType.swift b/Sources/Entities/MediaType.swift new file mode 100644 index 00000000..df142627 --- /dev/null +++ b/Sources/Entities/MediaType.swift @@ -0,0 +1,18 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Media types. +public enum MediaType: String, Codable, CaseIterable { + case unknown = "Unknown" + case video = "Video" + case audio = "Audio" + case photo = "Photo" + case book = "Book" +} diff --git a/Sources/Entities/MetadataEditorInfo.swift b/Sources/Entities/MetadataEditorInfo.swift index db417816..8774b7f8 100644 --- a/Sources/Entities/MetadataEditorInfo.swift +++ b/Sources/Entities/MetadataEditorInfo.swift @@ -9,7 +9,7 @@ import Foundation public struct MetadataEditorInfo: Codable, Hashable { - public var contentType: String? + public var contentType: CollectionType? public var contentTypeOptions: [NameValuePair]? public var countries: [CountryInfo]? public var cultures: [CultureDto]? @@ -17,7 +17,7 @@ public struct MetadataEditorInfo: Codable, Hashable { public var parentalRatingOptions: [ParentalRating]? public init( - contentType: String? = nil, + contentType: CollectionType? = nil, contentTypeOptions: [NameValuePair]? = nil, countries: [CountryInfo]? = nil, cultures: [CultureDto]? = nil, @@ -34,7 +34,7 @@ public struct MetadataEditorInfo: Codable, Hashable { public init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: StringCodingKey.self) - self.contentType = try values.decodeIfPresent(String.self, forKey: "ContentType") + self.contentType = try values.decodeIfPresent(CollectionType.self, forKey: "ContentType") self.contentTypeOptions = try values.decodeIfPresent([NameValuePair].self, forKey: "ContentTypeOptions") self.countries = try values.decodeIfPresent([CountryInfo].self, forKey: "Countries") self.cultures = try values.decodeIfPresent([CultureDto].self, forKey: "Cultures") diff --git a/Sources/Entities/NetworkConfiguration.swift b/Sources/Entities/NetworkConfiguration.swift index 5dcc22c3..23c64df5 100644 --- a/Sources/Entities/NetworkConfiguration.swift +++ b/Sources/Entities/NetworkConfiguration.swift @@ -8,238 +8,163 @@ import Foundation -/// Defines the Jellyfin.Networking.Configuration.NetworkConfiguration. +/// Defines the MediaBrowser.Common.Net.NetworkConfiguration. public struct NetworkConfiguration: Codable, Hashable { /// Gets or sets a value indicating whether Autodiscovery is enabled. public var isAutoDiscovery: Bool? - /// Gets or sets a value indicating whether Autodiscovery tracing is enabled. - public var isAutoDiscoveryTracing: Bool? /// Gets or sets a value used to specify the URL prefix that your Jellyfin instance can be accessed at. public var baseURL: String? /// Gets or sets the password required to access the X.509 certificate data in the file specified by - /// Jellyfin.Networking.Configuration.NetworkConfiguration.CertificatePath. + /// MediaBrowser.Common.Net.NetworkConfiguration.CertificatePath. public var certificatePassword: String? /// Gets or sets the filesystem path of an X.509 certificate to use for SSL. public var certificatePath: String? /// Gets or sets a value indicating whether to use HTTPS. public var enableHTTPS: Bool? - /// Gets or sets a value indicating whether gets or sets IPV4 capability. - public var enableIPV4: Bool? - /// Gets or sets a value indicating whether gets or sets IPV6 capability. - public var enableIPV6: Bool? - /// Gets a value indicating whether multi-socket binding is available. - public var enableMultiSocketBinding: Bool? + /// Gets or sets a value indicating whether IPv6 is enabled. + public var enableIPv4: Bool? + /// Gets or sets a value indicating whether IPv6 is enabled. + public var enableIPv6: Bool? /// Gets or sets a value indicating whether the published server uri is based on information in HTTP requests. public var enablePublishedServerUriByRequest: Bool? - /// Gets or sets a value indicating whether access outside of the LAN is permitted. + /// Gets or sets a value indicating whether access from outside of the LAN is permitted. public var enableRemoteAccess: Bool? - /// Gets or sets a value indicating whether detailed SSDP logs are sent to the console/log. - /// - /// "Emby.Dlna": "Debug" must be set in logging.default.json for this property to have any effect. - public var enableSSDPTracing: Bool? /// Gets or sets a value indicating whether to enable automatic port forwarding. public var enableUPnP: Bool? - /// Gets or sets the time (in seconds) between the pings of SSDP gateway monitor. - public var gatewayMonitorPeriod: Int? - /// Gets or sets the ports that HDHomerun uses. - public var hDHomerunPortRange: String? - /// Gets or sets the HTTP server port number. - public var httpserverPortNumber: Int? - /// Gets or sets the HTTPS server port number. - public var httpsPortNumber: Int? - /// Gets or sets a value indicating whether address names that match - /// Jellyfin.Networking.Configuration.NetworkConfiguration.VirtualInterfaceNames should be Ignore for the purposes of binding. + /// Gets or sets a value indicating whether address names that match MediaBrowser.Common.Net.NetworkConfiguration.VirtualInterfaceNames + /// should be ignored for the purposes of binding. public var isIgnoreVirtualInterfaces: Bool? - /// Gets or sets a value indicating whether - /// contains a blacklist or a whitelist. Default is a whitelist. + /// Gets or sets the internal HTTP server port. + public var internalHTTPPort: Int? + /// Gets or sets the internal HTTPS server port. + public var internalHTTPSPort: Int? + /// Gets or sets a value indicating whether contains a + /// blacklist or a whitelist. Default is a whitelist. public var isRemoteIPFilterBlacklist: Bool? - /// Gets or sets the known proxies. If the proxy is a network, it's added to the KnownNetworks. + /// Gets or sets the known proxies. public var knownProxies: [String]? /// Gets or sets the interface addresses which Jellyfin will bind to. If empty, all interfaces will be used. public var localNetworkAddresses: [String]? /// Gets or sets the subnets that are deemed to make up the LAN. public var localNetworkSubnets: [String]? + /// Gets or sets the public HTTP port. + public var publicHTTPPort: Int? /// Gets or sets the public HTTPS port. public var publicHTTPSPort: Int? - /// Gets or sets the public mapped port. - public var publicPort: Int? /// Gets or sets the PublishedServerUriBySubnet /// /// Gets or sets PublishedServerUri to advertise for specific subnets. public var publishedServerUriBySubnet: [String]? - /// Gets or sets the filter for remote IP connectivity. Used in conjuntion with . + /// Gets or sets the filter for remote IP connectivity. Used in conjunction with . public var remoteIPFilter: [String]? /// Gets or sets a value indicating whether the server should force connections over HTTPS. public var requireHTTPS: Bool? - /// Gets or sets the SSDPTracingFilter - /// - /// Gets or sets a value indicating whether an IP address is to be used to filter the detailed ssdp logs that are being sent to the - /// console/log. - /// - /// If the setting "Emby.Dlna": "Debug" msut be set in logging.default.json for this property to work. - public var sSDPTracingFilter: String? - /// Gets or sets a value indicating whether all IPv6 interfaces should be treated as on the internal network. - /// - /// Depending on the address range implemented ULA ranges might not be used. - public var isTrustAllIP6Interfaces: Bool? - /// Gets or sets the UDPPortRange. - public var uDPPortRange: String? - /// Gets or sets the number of times SSDP UDP messages are sent. - public var uDPSendCount: Int? - /// Gets or sets the delay between each groups of SSDP messages (in ms). - public var uDPSendDelay: Int? - /// Gets or sets a value indicating whether the http port should be mapped as part of UPnP automatic port forwarding. - public var isUPnPCreateHTTPPortMap: Bool? - /// Gets or sets a value indicating the interfaces that should be ignored. The list can be comma separated. . - public var virtualInterfaceNames: String? + /// Gets or sets a value indicating the interface name prefixes that should be ignored. The list can be comma separated and values are + /// case-insensitive. . + public var virtualInterfaceNames: [String]? public init( isAutoDiscovery: Bool? = nil, - isAutoDiscoveryTracing: Bool? = nil, baseURL: String? = nil, certificatePassword: String? = nil, certificatePath: String? = nil, enableHTTPS: Bool? = nil, - enableIPV4: Bool? = nil, - enableIPV6: Bool? = nil, - enableMultiSocketBinding: Bool? = nil, + enableIPv4: Bool? = nil, + enableIPv6: Bool? = nil, enablePublishedServerUriByRequest: Bool? = nil, enableRemoteAccess: Bool? = nil, - enableSSDPTracing: Bool? = nil, enableUPnP: Bool? = nil, - gatewayMonitorPeriod: Int? = nil, - hDHomerunPortRange: String? = nil, - httpserverPortNumber: Int? = nil, - httpsPortNumber: Int? = nil, isIgnoreVirtualInterfaces: Bool? = nil, + internalHTTPPort: Int? = nil, + internalHTTPSPort: Int? = nil, isRemoteIPFilterBlacklist: Bool? = nil, knownProxies: [String]? = nil, localNetworkAddresses: [String]? = nil, localNetworkSubnets: [String]? = nil, + publicHTTPPort: Int? = nil, publicHTTPSPort: Int? = nil, - publicPort: Int? = nil, publishedServerUriBySubnet: [String]? = nil, remoteIPFilter: [String]? = nil, requireHTTPS: Bool? = nil, - sSDPTracingFilter: String? = nil, - isTrustAllIP6Interfaces: Bool? = nil, - uDPPortRange: String? = nil, - uDPSendCount: Int? = nil, - uDPSendDelay: Int? = nil, - isUPnPCreateHTTPPortMap: Bool? = nil, - virtualInterfaceNames: String? = nil + virtualInterfaceNames: [String]? = nil ) { self.isAutoDiscovery = isAutoDiscovery - self.isAutoDiscoveryTracing = isAutoDiscoveryTracing self.baseURL = baseURL self.certificatePassword = certificatePassword self.certificatePath = certificatePath self.enableHTTPS = enableHTTPS - self.enableIPV4 = enableIPV4 - self.enableIPV6 = enableIPV6 - self.enableMultiSocketBinding = enableMultiSocketBinding + self.enableIPv4 = enableIPv4 + self.enableIPv6 = enableIPv6 self.enablePublishedServerUriByRequest = enablePublishedServerUriByRequest self.enableRemoteAccess = enableRemoteAccess - self.enableSSDPTracing = enableSSDPTracing self.enableUPnP = enableUPnP - self.gatewayMonitorPeriod = gatewayMonitorPeriod - self.hDHomerunPortRange = hDHomerunPortRange - self.httpserverPortNumber = httpserverPortNumber - self.httpsPortNumber = httpsPortNumber self.isIgnoreVirtualInterfaces = isIgnoreVirtualInterfaces + self.internalHTTPPort = internalHTTPPort + self.internalHTTPSPort = internalHTTPSPort self.isRemoteIPFilterBlacklist = isRemoteIPFilterBlacklist self.knownProxies = knownProxies self.localNetworkAddresses = localNetworkAddresses self.localNetworkSubnets = localNetworkSubnets + self.publicHTTPPort = publicHTTPPort self.publicHTTPSPort = publicHTTPSPort - self.publicPort = publicPort self.publishedServerUriBySubnet = publishedServerUriBySubnet self.remoteIPFilter = remoteIPFilter self.requireHTTPS = requireHTTPS - self.sSDPTracingFilter = sSDPTracingFilter - self.isTrustAllIP6Interfaces = isTrustAllIP6Interfaces - self.uDPPortRange = uDPPortRange - self.uDPSendCount = uDPSendCount - self.uDPSendDelay = uDPSendDelay - self.isUPnPCreateHTTPPortMap = isUPnPCreateHTTPPortMap self.virtualInterfaceNames = virtualInterfaceNames } public init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: StringCodingKey.self) self.isAutoDiscovery = try values.decodeIfPresent(Bool.self, forKey: "AutoDiscovery") - self.isAutoDiscoveryTracing = try values.decodeIfPresent(Bool.self, forKey: "AutoDiscoveryTracing") self.baseURL = try values.decodeIfPresent(String.self, forKey: "BaseUrl") self.certificatePassword = try values.decodeIfPresent(String.self, forKey: "CertificatePassword") self.certificatePath = try values.decodeIfPresent(String.self, forKey: "CertificatePath") self.enableHTTPS = try values.decodeIfPresent(Bool.self, forKey: "EnableHttps") - self.enableIPV4 = try values.decodeIfPresent(Bool.self, forKey: "EnableIPV4") - self.enableIPV6 = try values.decodeIfPresent(Bool.self, forKey: "EnableIPV6") - self.enableMultiSocketBinding = try values.decodeIfPresent(Bool.self, forKey: "EnableMultiSocketBinding") + self.enableIPv4 = try values.decodeIfPresent(Bool.self, forKey: "EnableIPv4") + self.enableIPv6 = try values.decodeIfPresent(Bool.self, forKey: "EnableIPv6") self.enablePublishedServerUriByRequest = try values.decodeIfPresent(Bool.self, forKey: "EnablePublishedServerUriByRequest") self.enableRemoteAccess = try values.decodeIfPresent(Bool.self, forKey: "EnableRemoteAccess") - self.enableSSDPTracing = try values.decodeIfPresent(Bool.self, forKey: "EnableSSDPTracing") self.enableUPnP = try values.decodeIfPresent(Bool.self, forKey: "EnableUPnP") - self.gatewayMonitorPeriod = try values.decodeIfPresent(Int.self, forKey: "GatewayMonitorPeriod") - self.hDHomerunPortRange = try values.decodeIfPresent(String.self, forKey: "HDHomerunPortRange") - self.httpserverPortNumber = try values.decodeIfPresent(Int.self, forKey: "HttpServerPortNumber") - self.httpsPortNumber = try values.decodeIfPresent(Int.self, forKey: "HttpsPortNumber") self.isIgnoreVirtualInterfaces = try values.decodeIfPresent(Bool.self, forKey: "IgnoreVirtualInterfaces") + self.internalHTTPPort = try values.decodeIfPresent(Int.self, forKey: "InternalHttpPort") + self.internalHTTPSPort = try values.decodeIfPresent(Int.self, forKey: "InternalHttpsPort") self.isRemoteIPFilterBlacklist = try values.decodeIfPresent(Bool.self, forKey: "IsRemoteIPFilterBlacklist") self.knownProxies = try values.decodeIfPresent([String].self, forKey: "KnownProxies") self.localNetworkAddresses = try values.decodeIfPresent([String].self, forKey: "LocalNetworkAddresses") self.localNetworkSubnets = try values.decodeIfPresent([String].self, forKey: "LocalNetworkSubnets") + self.publicHTTPPort = try values.decodeIfPresent(Int.self, forKey: "PublicHttpPort") self.publicHTTPSPort = try values.decodeIfPresent(Int.self, forKey: "PublicHttpsPort") - self.publicPort = try values.decodeIfPresent(Int.self, forKey: "PublicPort") self.publishedServerUriBySubnet = try values.decodeIfPresent([String].self, forKey: "PublishedServerUriBySubnet") self.remoteIPFilter = try values.decodeIfPresent([String].self, forKey: "RemoteIPFilter") self.requireHTTPS = try values.decodeIfPresent(Bool.self, forKey: "RequireHttps") - self.sSDPTracingFilter = try values.decodeIfPresent(String.self, forKey: "SSDPTracingFilter") - self.isTrustAllIP6Interfaces = try values.decodeIfPresent(Bool.self, forKey: "TrustAllIP6Interfaces") - self.uDPPortRange = try values.decodeIfPresent(String.self, forKey: "UDPPortRange") - self.uDPSendCount = try values.decodeIfPresent(Int.self, forKey: "UDPSendCount") - self.uDPSendDelay = try values.decodeIfPresent(Int.self, forKey: "UDPSendDelay") - self.isUPnPCreateHTTPPortMap = try values.decodeIfPresent(Bool.self, forKey: "UPnPCreateHttpPortMap") - self.virtualInterfaceNames = try values.decodeIfPresent(String.self, forKey: "VirtualInterfaceNames") + self.virtualInterfaceNames = try values.decodeIfPresent([String].self, forKey: "VirtualInterfaceNames") } public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: StringCodingKey.self) try values.encodeIfPresent(isAutoDiscovery, forKey: "AutoDiscovery") - try values.encodeIfPresent(isAutoDiscoveryTracing, forKey: "AutoDiscoveryTracing") try values.encodeIfPresent(baseURL, forKey: "BaseUrl") try values.encodeIfPresent(certificatePassword, forKey: "CertificatePassword") try values.encodeIfPresent(certificatePath, forKey: "CertificatePath") try values.encodeIfPresent(enableHTTPS, forKey: "EnableHttps") - try values.encodeIfPresent(enableIPV4, forKey: "EnableIPV4") - try values.encodeIfPresent(enableIPV6, forKey: "EnableIPV6") - try values.encodeIfPresent(enableMultiSocketBinding, forKey: "EnableMultiSocketBinding") + try values.encodeIfPresent(enableIPv4, forKey: "EnableIPv4") + try values.encodeIfPresent(enableIPv6, forKey: "EnableIPv6") try values.encodeIfPresent(enablePublishedServerUriByRequest, forKey: "EnablePublishedServerUriByRequest") try values.encodeIfPresent(enableRemoteAccess, forKey: "EnableRemoteAccess") - try values.encodeIfPresent(enableSSDPTracing, forKey: "EnableSSDPTracing") try values.encodeIfPresent(enableUPnP, forKey: "EnableUPnP") - try values.encodeIfPresent(gatewayMonitorPeriod, forKey: "GatewayMonitorPeriod") - try values.encodeIfPresent(hDHomerunPortRange, forKey: "HDHomerunPortRange") - try values.encodeIfPresent(httpserverPortNumber, forKey: "HttpServerPortNumber") - try values.encodeIfPresent(httpsPortNumber, forKey: "HttpsPortNumber") try values.encodeIfPresent(isIgnoreVirtualInterfaces, forKey: "IgnoreVirtualInterfaces") + try values.encodeIfPresent(internalHTTPPort, forKey: "InternalHttpPort") + try values.encodeIfPresent(internalHTTPSPort, forKey: "InternalHttpsPort") try values.encodeIfPresent(isRemoteIPFilterBlacklist, forKey: "IsRemoteIPFilterBlacklist") try values.encodeIfPresent(knownProxies, forKey: "KnownProxies") try values.encodeIfPresent(localNetworkAddresses, forKey: "LocalNetworkAddresses") try values.encodeIfPresent(localNetworkSubnets, forKey: "LocalNetworkSubnets") + try values.encodeIfPresent(publicHTTPPort, forKey: "PublicHttpPort") try values.encodeIfPresent(publicHTTPSPort, forKey: "PublicHttpsPort") - try values.encodeIfPresent(publicPort, forKey: "PublicPort") try values.encodeIfPresent(publishedServerUriBySubnet, forKey: "PublishedServerUriBySubnet") try values.encodeIfPresent(remoteIPFilter, forKey: "RemoteIPFilter") try values.encodeIfPresent(requireHTTPS, forKey: "RequireHttps") - try values.encodeIfPresent(sSDPTracingFilter, forKey: "SSDPTracingFilter") - try values.encodeIfPresent(isTrustAllIP6Interfaces, forKey: "TrustAllIP6Interfaces") - try values.encodeIfPresent(uDPPortRange, forKey: "UDPPortRange") - try values.encodeIfPresent(uDPSendCount, forKey: "UDPSendCount") - try values.encodeIfPresent(uDPSendDelay, forKey: "UDPSendDelay") - try values.encodeIfPresent(isUPnPCreateHTTPPortMap, forKey: "UPnPCreateHttpPortMap") try values.encodeIfPresent(virtualInterfaceNames, forKey: "VirtualInterfaceNames") } } diff --git a/Sources/Entities/OutboundKeepAliveMessage.swift b/Sources/Entities/OutboundKeepAliveMessage.swift new file mode 100644 index 00000000..613a518a --- /dev/null +++ b/Sources/Entities/OutboundKeepAliveMessage.swift @@ -0,0 +1,34 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Keep alive websocket messages. +public struct OutboundKeepAliveMessage: Codable, Hashable { + /// Gets or sets the message id. + public var messageID: String? + /// The different kinds of messages that are used in the WebSocket api. + public var messageType: SessionMessageType? + + public init(messageID: String? = nil, messageType: SessionMessageType? = nil) { + self.messageID = messageID + self.messageType = messageType + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.messageID = try values.decodeIfPresent(String.self, forKey: "MessageId") + self.messageType = try values.decodeIfPresent(SessionMessageType.self, forKey: "MessageType") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(messageID, forKey: "MessageId") + try values.encodeIfPresent(messageType, forKey: "MessageType") + } +} diff --git a/Sources/Entities/OutboundWebSocketMessage.swift b/Sources/Entities/OutboundWebSocketMessage.swift new file mode 100644 index 00000000..b0f197d2 --- /dev/null +++ b/Sources/Entities/OutboundWebSocketMessage.swift @@ -0,0 +1,125 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Represents the list of possible outbound websocket types +public enum OutboundWebSocketMessage: Codable, Hashable { + case activityLogEntryMessage(ActivityLogEntryMessage) + case forceKeepAliveMessage(ForceKeepAliveMessage) + case generalCommandMessage(GeneralCommandMessage) + case libraryChangedMessage(LibraryChangedMessage) + case outboundKeepAliveMessage(OutboundKeepAliveMessage) + case playMessage(PlayMessage) + case playstateMessage(PlaystateMessage) + case pluginInstallationCancelledMessage(PluginInstallationCancelledMessage) + case pluginInstallationCompletedMessage(PluginInstallationCompletedMessage) + case pluginInstallationFailedMessage(PluginInstallationFailedMessage) + case pluginInstallingMessage(PluginInstallingMessage) + case pluginUninstalledMessage(PluginUninstalledMessage) + case refreshProgressMessage(RefreshProgressMessage) + case restartRequiredMessage(RestartRequiredMessage) + case scheduledTaskEndedMessage(ScheduledTaskEndedMessage) + case scheduledTasksInfoMessage(ScheduledTasksInfoMessage) + case seriesTimerCancelledMessage(SeriesTimerCancelledMessage) + case seriesTimerCreatedMessage(SeriesTimerCreatedMessage) + case serverRestartingMessage(ServerRestartingMessage) + case serverShuttingDownMessage(ServerShuttingDownMessage) + case sessionsMessage(SessionsMessage) + case syncPlayCommandMessage(SyncPlayCommandMessage) + case syncPlayGroupUpdateCommandMessage(SyncPlayGroupUpdateCommandMessage) + case timerCancelledMessage(TimerCancelledMessage) + case timerCreatedMessage(TimerCreatedMessage) + case userDataChangedMessage(UserDataChangedMessage) + case userDeletedMessage(UserDeletedMessage) + case userUpdatedMessage(UserUpdatedMessage) + + public init(from decoder: Decoder) throws { + + struct Discriminator: Decodable { + let MessageType: String + } + + let container = try decoder.singleValueContainer() + let discriminatorValue = try container.decode(Discriminator.self).MessageType + + switch discriminatorValue { + case "ActivityLogEntry": self = try .activityLogEntryMessage(container.decode(ActivityLogEntryMessage.self)) + case "ForceKeepAlive": self = try .forceKeepAliveMessage(container.decode(ForceKeepAliveMessage.self)) + case "GeneralCommand": self = try .generalCommandMessage(container.decode(GeneralCommandMessage.self)) + case "LibraryChanged": self = try .libraryChangedMessage(container.decode(LibraryChangedMessage.self)) + case "KeepAlive": self = try .outboundKeepAliveMessage(container.decode(OutboundKeepAliveMessage.self)) + case "Play": self = try .playMessage(container.decode(PlayMessage.self)) + case "Playstate": self = try .playstateMessage(container.decode(PlaystateMessage.self)) + case "PackageInstallationCancelled": self = try .pluginInstallationCancelledMessage(container + .decode(PluginInstallationCancelledMessage.self)) + case "PackageInstallationCompleted": self = try .pluginInstallationCompletedMessage(container + .decode(PluginInstallationCompletedMessage.self)) + case "PackageInstallationFailed": self = try .pluginInstallationFailedMessage(container + .decode(PluginInstallationFailedMessage.self)) + case "PackageInstalling": self = try .pluginInstallingMessage(container.decode(PluginInstallingMessage.self)) + case "PackageUninstalled": self = try .pluginUninstalledMessage(container.decode(PluginUninstalledMessage.self)) + case "RefreshProgress": self = try .refreshProgressMessage(container.decode(RefreshProgressMessage.self)) + case "RestartRequired": self = try .restartRequiredMessage(container.decode(RestartRequiredMessage.self)) + case "ScheduledTaskEnded": self = try .scheduledTaskEndedMessage(container.decode(ScheduledTaskEndedMessage.self)) + case "ScheduledTasksInfo": self = try .scheduledTasksInfoMessage(container.decode(ScheduledTasksInfoMessage.self)) + case "SeriesTimerCancelled": self = try .seriesTimerCancelledMessage(container.decode(SeriesTimerCancelledMessage.self)) + case "SeriesTimerCreated": self = try .seriesTimerCreatedMessage(container.decode(SeriesTimerCreatedMessage.self)) + case "ServerRestarting": self = try .serverRestartingMessage(container.decode(ServerRestartingMessage.self)) + case "ServerShuttingDown": self = try .serverShuttingDownMessage(container.decode(ServerShuttingDownMessage.self)) + case "Sessions": self = try .sessionsMessage(container.decode(SessionsMessage.self)) + case "SyncPlayCommand": self = try .syncPlayCommandMessage(container.decode(SyncPlayCommandMessage.self)) + case "SyncPlayGroupUpdate": self = try .syncPlayGroupUpdateCommandMessage(container.decode(SyncPlayGroupUpdateCommandMessage.self)) + case "TimerCancelled": self = try .timerCancelledMessage(container.decode(TimerCancelledMessage.self)) + case "TimerCreated": self = try .timerCreatedMessage(container.decode(TimerCreatedMessage.self)) + case "UserDataChanged": self = try .userDataChangedMessage(container.decode(UserDataChangedMessage.self)) + case "UserDeleted": self = try .userDeletedMessage(container.decode(UserDeletedMessage.self)) + case "UserUpdated": self = try .userUpdatedMessage(container.decode(UserUpdatedMessage.self)) + + default: + throw DecodingError.dataCorruptedError( + in: container, + debugDescription: "Discriminator value '\(discriminatorValue)' does not match any expected values (ActivityLogEntry, ForceKeepAlive, GeneralCommand, LibraryChanged, KeepAlive, Play, Playstate, PackageInstallationCancelled, PackageInstallationCompleted, PackageInstallationFailed, PackageInstalling, PackageUninstalled, RefreshProgress, RestartRequired, ScheduledTaskEnded, ScheduledTasksInfo, SeriesTimerCancelled, SeriesTimerCreated, ServerRestarting, ServerShuttingDown, Sessions, SyncPlayCommand, SyncPlayGroupUpdate, TimerCancelled, TimerCreated, UserDataChanged, UserDeleted, UserUpdated)." + ) + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + switch self { + case let .activityLogEntryMessage(value): try container.encode(value) + case let .forceKeepAliveMessage(value): try container.encode(value) + case let .generalCommandMessage(value): try container.encode(value) + case let .libraryChangedMessage(value): try container.encode(value) + case let .outboundKeepAliveMessage(value): try container.encode(value) + case let .playMessage(value): try container.encode(value) + case let .playstateMessage(value): try container.encode(value) + case let .pluginInstallationCancelledMessage(value): try container.encode(value) + case let .pluginInstallationCompletedMessage(value): try container.encode(value) + case let .pluginInstallationFailedMessage(value): try container.encode(value) + case let .pluginInstallingMessage(value): try container.encode(value) + case let .pluginUninstalledMessage(value): try container.encode(value) + case let .refreshProgressMessage(value): try container.encode(value) + case let .restartRequiredMessage(value): try container.encode(value) + case let .scheduledTaskEndedMessage(value): try container.encode(value) + case let .scheduledTasksInfoMessage(value): try container.encode(value) + case let .seriesTimerCancelledMessage(value): try container.encode(value) + case let .seriesTimerCreatedMessage(value): try container.encode(value) + case let .serverRestartingMessage(value): try container.encode(value) + case let .serverShuttingDownMessage(value): try container.encode(value) + case let .sessionsMessage(value): try container.encode(value) + case let .syncPlayCommandMessage(value): try container.encode(value) + case let .syncPlayGroupUpdateCommandMessage(value): try container.encode(value) + case let .timerCancelledMessage(value): try container.encode(value) + case let .timerCreatedMessage(value): try container.encode(value) + case let .userDataChangedMessage(value): try container.encode(value) + case let .userDeletedMessage(value): try container.encode(value) + case let .userUpdatedMessage(value): try container.encode(value) + } + } +} diff --git a/Sources/Entities/PersonKind.swift b/Sources/Entities/PersonKind.swift new file mode 100644 index 00000000..472ac680 --- /dev/null +++ b/Sources/Entities/PersonKind.swift @@ -0,0 +1,38 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// The person kind. +public enum PersonKind: String, Codable, CaseIterable { + case unknown = "Unknown" + case actor = "Actor" + case director = "Director" + case composer = "Composer" + case writer = "Writer" + case guestStar = "GuestStar" + case producer = "Producer" + case conductor = "Conductor" + case lyricist = "Lyricist" + case arranger = "Arranger" + case engineer = "Engineer" + case mixer = "Mixer" + case remixer = "Remixer" + case creator = "Creator" + case artist = "Artist" + case albumArtist = "AlbumArtist" + case author = "Author" + case illustrator = "Illustrator" + case penciller = "Penciller" + case inker = "Inker" + case colorist = "Colorist" + case letterer = "Letterer" + case coverArtist = "CoverArtist" + case editor = "Editor" + case translator = "Translator" +} diff --git a/Sources/Entities/PlayMessage.swift b/Sources/Entities/PlayMessage.swift new file mode 100644 index 00000000..6ad2abe5 --- /dev/null +++ b/Sources/Entities/PlayMessage.swift @@ -0,0 +1,39 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Play command websocket message. +public struct PlayMessage: Codable, Hashable { + /// Class PlayRequest. + public var data: PlayRequest? + /// Gets or sets the message id. + public var messageID: String? + /// The different kinds of messages that are used in the WebSocket api. + public var messageType: SessionMessageType? + + public init(data: PlayRequest? = nil, messageID: String? = nil, messageType: SessionMessageType? = nil) { + self.data = data + self.messageID = messageID + self.messageType = messageType + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.data = try values.decodeIfPresent(PlayRequest.self, forKey: "Data") + self.messageID = try values.decodeIfPresent(String.self, forKey: "MessageId") + self.messageType = try values.decodeIfPresent(SessionMessageType.self, forKey: "MessageType") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(data, forKey: "Data") + try values.encodeIfPresent(messageID, forKey: "MessageId") + try values.encodeIfPresent(messageType, forKey: "MessageType") + } +} diff --git a/Sources/Entities/PlayQueueUpdate.swift b/Sources/Entities/PlayQueueUpdate.swift new file mode 100644 index 00000000..1e483b42 --- /dev/null +++ b/Sources/Entities/PlayQueueUpdate.swift @@ -0,0 +1,73 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Class PlayQueueUpdate. +public struct PlayQueueUpdate: Codable, Hashable { + /// Gets a value indicating whether the current item is playing. + public var isPlaying: Bool? + /// Gets the UTC time of the last change to the playing queue. + public var lastUpdate: Date? + /// Gets the playing item index in the playlist. + public var playingItemIndex: Int? + /// Gets the playlist. + public var playlist: [SyncPlayQueueItem]? + /// Gets the request type that originated this update. + public var reason: PlayQueueUpdateReason? + /// Gets the repeat mode. + public var repeatMode: GroupRepeatMode? + /// Gets the shuffle mode. + public var shuffleMode: GroupShuffleMode? + /// Gets the start position ticks. + public var startPositionTicks: Int? + + public init( + isPlaying: Bool? = nil, + lastUpdate: Date? = nil, + playingItemIndex: Int? = nil, + playlist: [SyncPlayQueueItem]? = nil, + reason: PlayQueueUpdateReason? = nil, + repeatMode: GroupRepeatMode? = nil, + shuffleMode: GroupShuffleMode? = nil, + startPositionTicks: Int? = nil + ) { + self.isPlaying = isPlaying + self.lastUpdate = lastUpdate + self.playingItemIndex = playingItemIndex + self.playlist = playlist + self.reason = reason + self.repeatMode = repeatMode + self.shuffleMode = shuffleMode + self.startPositionTicks = startPositionTicks + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.isPlaying = try values.decodeIfPresent(Bool.self, forKey: "IsPlaying") + self.lastUpdate = try values.decodeIfPresent(Date.self, forKey: "LastUpdate") + self.playingItemIndex = try values.decodeIfPresent(Int.self, forKey: "PlayingItemIndex") + self.playlist = try values.decodeIfPresent([SyncPlayQueueItem].self, forKey: "Playlist") + self.reason = try values.decodeIfPresent(PlayQueueUpdateReason.self, forKey: "Reason") + self.repeatMode = try values.decodeIfPresent(GroupRepeatMode.self, forKey: "RepeatMode") + self.shuffleMode = try values.decodeIfPresent(GroupShuffleMode.self, forKey: "ShuffleMode") + self.startPositionTicks = try values.decodeIfPresent(Int.self, forKey: "StartPositionTicks") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(isPlaying, forKey: "IsPlaying") + try values.encodeIfPresent(lastUpdate, forKey: "LastUpdate") + try values.encodeIfPresent(playingItemIndex, forKey: "PlayingItemIndex") + try values.encodeIfPresent(playlist, forKey: "Playlist") + try values.encodeIfPresent(reason, forKey: "Reason") + try values.encodeIfPresent(repeatMode, forKey: "RepeatMode") + try values.encodeIfPresent(shuffleMode, forKey: "ShuffleMode") + try values.encodeIfPresent(startPositionTicks, forKey: "StartPositionTicks") + } +} diff --git a/Sources/Entities/PlayQueueUpdateGroupUpdate.swift b/Sources/Entities/PlayQueueUpdateGroupUpdate.swift new file mode 100644 index 00000000..044c8750 --- /dev/null +++ b/Sources/Entities/PlayQueueUpdateGroupUpdate.swift @@ -0,0 +1,39 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Class GroupUpdate. +public struct PlayQueueUpdateGroupUpdate: Codable, Hashable { + /// Gets the update data. + public var data: PlayQueueUpdate? + /// Gets the group identifier. + public var groupID: String? + /// Gets the update type. + public var type: GroupUpdateType? + + public init(data: PlayQueueUpdate? = nil, groupID: String? = nil, type: GroupUpdateType? = nil) { + self.data = data + self.groupID = groupID + self.type = type + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.data = try values.decodeIfPresent(PlayQueueUpdate.self, forKey: "Data") + self.groupID = try values.decodeIfPresent(String.self, forKey: "GroupId") + self.type = try values.decodeIfPresent(GroupUpdateType.self, forKey: "Type") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(data, forKey: "Data") + try values.encodeIfPresent(groupID, forKey: "GroupId") + try values.encodeIfPresent(type, forKey: "Type") + } +} diff --git a/Sources/Entities/PlayQueueUpdateReason.swift b/Sources/Entities/PlayQueueUpdateReason.swift new file mode 100644 index 00000000..226eba57 --- /dev/null +++ b/Sources/Entities/PlayQueueUpdateReason.swift @@ -0,0 +1,23 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Enum PlayQueueUpdateReason. +public enum PlayQueueUpdateReason: String, Codable, CaseIterable { + case newPlaylist = "NewPlaylist" + case setCurrentItem = "SetCurrentItem" + case removeItems = "RemoveItems" + case moveItem = "MoveItem" + case queue = "Queue" + case queueNext = "QueueNext" + case nextItem = "NextItem" + case previousItem = "PreviousItem" + case repeatMode = "RepeatMode" + case shuffleMode = "ShuffleMode" +} diff --git a/Sources/Entities/PlaybackOrder.swift b/Sources/Entities/PlaybackOrder.swift new file mode 100644 index 00000000..df3ced85 --- /dev/null +++ b/Sources/Entities/PlaybackOrder.swift @@ -0,0 +1,15 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Enum PlaybackOrder. +public enum PlaybackOrder: String, Codable, CaseIterable { + case `default` = "Default" + case shuffle = "Shuffle" +} diff --git a/Sources/Entities/PlaybackProgressInfo.swift b/Sources/Entities/PlaybackProgressInfo.swift index a9b38920..731f5faa 100644 --- a/Sources/Entities/PlaybackProgressInfo.swift +++ b/Sources/Entities/PlaybackProgressInfo.swift @@ -33,6 +33,8 @@ public struct PlaybackProgressInfo: Codable, Hashable { public var playMethod: PlayMethod? /// Gets or sets the play session identifier. public var playSessionID: String? + /// Gets or sets the playback order. + public var playbackOrder: PlaybackOrder? public var playbackStartTimeTicks: Int? public var playlistItemID: String? /// Gets or sets the position ticks. @@ -60,6 +62,7 @@ public struct PlaybackProgressInfo: Codable, Hashable { nowPlayingQueue: [QueueItem]? = nil, playMethod: PlayMethod? = nil, playSessionID: String? = nil, + playbackOrder: PlaybackOrder? = nil, playbackStartTimeTicks: Int? = nil, playlistItemID: String? = nil, positionTicks: Int? = nil, @@ -81,6 +84,7 @@ public struct PlaybackProgressInfo: Codable, Hashable { self.nowPlayingQueue = nowPlayingQueue self.playMethod = playMethod self.playSessionID = playSessionID + self.playbackOrder = playbackOrder self.playbackStartTimeTicks = playbackStartTimeTicks self.playlistItemID = playlistItemID self.positionTicks = positionTicks @@ -105,6 +109,7 @@ public struct PlaybackProgressInfo: Codable, Hashable { self.nowPlayingQueue = try values.decodeIfPresent([QueueItem].self, forKey: "NowPlayingQueue") self.playMethod = try values.decodeIfPresent(PlayMethod.self, forKey: "PlayMethod") self.playSessionID = try values.decodeIfPresent(String.self, forKey: "PlaySessionId") + self.playbackOrder = try values.decodeIfPresent(PlaybackOrder.self, forKey: "PlaybackOrder") self.playbackStartTimeTicks = try values.decodeIfPresent(Int.self, forKey: "PlaybackStartTimeTicks") self.playlistItemID = try values.decodeIfPresent(String.self, forKey: "PlaylistItemId") self.positionTicks = try values.decodeIfPresent(Int.self, forKey: "PositionTicks") @@ -129,6 +134,7 @@ public struct PlaybackProgressInfo: Codable, Hashable { try values.encodeIfPresent(nowPlayingQueue, forKey: "NowPlayingQueue") try values.encodeIfPresent(playMethod, forKey: "PlayMethod") try values.encodeIfPresent(playSessionID, forKey: "PlaySessionId") + try values.encodeIfPresent(playbackOrder, forKey: "PlaybackOrder") try values.encodeIfPresent(playbackStartTimeTicks, forKey: "PlaybackStartTimeTicks") try values.encodeIfPresent(playlistItemID, forKey: "PlaylistItemId") try values.encodeIfPresent(positionTicks, forKey: "PositionTicks") diff --git a/Sources/Entities/PlaybackRequestType.swift b/Sources/Entities/PlaybackRequestType.swift new file mode 100644 index 00000000..e4d5a8c3 --- /dev/null +++ b/Sources/Entities/PlaybackRequestType.swift @@ -0,0 +1,30 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Enum PlaybackRequestType. +public enum PlaybackRequestType: String, Codable, CaseIterable { + case play = "Play" + case setPlaylistItem = "SetPlaylistItem" + case removeFromPlaylist = "RemoveFromPlaylist" + case movePlaylistItem = "MovePlaylistItem" + case queue = "Queue" + case unpause = "Unpause" + case pause = "Pause" + case stop = "Stop" + case seek = "Seek" + case buffer = "Buffer" + case ready = "Ready" + case nextItem = "NextItem" + case previousItem = "PreviousItem" + case setRepeatMode = "SetRepeatMode" + case setShuffleMode = "SetShuffleMode" + case ping = "Ping" + case ignoreWait = "IgnoreWait" +} diff --git a/Sources/Entities/PlaybackStartInfo.swift b/Sources/Entities/PlaybackStartInfo.swift index 2ad058e3..11ae897f 100644 --- a/Sources/Entities/PlaybackStartInfo.swift +++ b/Sources/Entities/PlaybackStartInfo.swift @@ -33,6 +33,8 @@ public struct PlaybackStartInfo: Codable, Hashable { public var playMethod: PlayMethod? /// Gets or sets the play session identifier. public var playSessionID: String? + /// Gets or sets the playback order. + public var playbackOrder: PlaybackOrder? public var playbackStartTimeTicks: Int? public var playlistItemID: String? /// Gets or sets the position ticks. @@ -60,6 +62,7 @@ public struct PlaybackStartInfo: Codable, Hashable { nowPlayingQueue: [QueueItem]? = nil, playMethod: PlayMethod? = nil, playSessionID: String? = nil, + playbackOrder: PlaybackOrder? = nil, playbackStartTimeTicks: Int? = nil, playlistItemID: String? = nil, positionTicks: Int? = nil, @@ -81,6 +84,7 @@ public struct PlaybackStartInfo: Codable, Hashable { self.nowPlayingQueue = nowPlayingQueue self.playMethod = playMethod self.playSessionID = playSessionID + self.playbackOrder = playbackOrder self.playbackStartTimeTicks = playbackStartTimeTicks self.playlistItemID = playlistItemID self.positionTicks = positionTicks @@ -105,6 +109,7 @@ public struct PlaybackStartInfo: Codable, Hashable { self.nowPlayingQueue = try values.decodeIfPresent([QueueItem].self, forKey: "NowPlayingQueue") self.playMethod = try values.decodeIfPresent(PlayMethod.self, forKey: "PlayMethod") self.playSessionID = try values.decodeIfPresent(String.self, forKey: "PlaySessionId") + self.playbackOrder = try values.decodeIfPresent(PlaybackOrder.self, forKey: "PlaybackOrder") self.playbackStartTimeTicks = try values.decodeIfPresent(Int.self, forKey: "PlaybackStartTimeTicks") self.playlistItemID = try values.decodeIfPresent(String.self, forKey: "PlaylistItemId") self.positionTicks = try values.decodeIfPresent(Int.self, forKey: "PositionTicks") @@ -129,6 +134,7 @@ public struct PlaybackStartInfo: Codable, Hashable { try values.encodeIfPresent(nowPlayingQueue, forKey: "NowPlayingQueue") try values.encodeIfPresent(playMethod, forKey: "PlayMethod") try values.encodeIfPresent(playSessionID, forKey: "PlaySessionId") + try values.encodeIfPresent(playbackOrder, forKey: "PlaybackOrder") try values.encodeIfPresent(playbackStartTimeTicks, forKey: "PlaybackStartTimeTicks") try values.encodeIfPresent(playlistItemID, forKey: "PlaylistItemId") try values.encodeIfPresent(positionTicks, forKey: "PositionTicks") diff --git a/Sources/Entities/PlayerStateInfo.swift b/Sources/Entities/PlayerStateInfo.swift index cd36785e..2821a7cb 100644 --- a/Sources/Entities/PlayerStateInfo.swift +++ b/Sources/Entities/PlayerStateInfo.swift @@ -23,6 +23,8 @@ public struct PlayerStateInfo: Codable, Hashable { public var mediaSourceID: String? /// Gets or sets the play method. public var playMethod: PlayMethod? + /// Gets or sets the playback order. + public var playbackOrder: PlaybackOrder? /// Gets or sets the now playing position ticks. public var positionTicks: Int? /// Gets or sets the repeat mode. @@ -40,6 +42,7 @@ public struct PlayerStateInfo: Codable, Hashable { liveStreamID: String? = nil, mediaSourceID: String? = nil, playMethod: PlayMethod? = nil, + playbackOrder: PlaybackOrder? = nil, positionTicks: Int? = nil, repeatMode: RepeatMode? = nil, subtitleStreamIndex: Int? = nil, @@ -52,6 +55,7 @@ public struct PlayerStateInfo: Codable, Hashable { self.liveStreamID = liveStreamID self.mediaSourceID = mediaSourceID self.playMethod = playMethod + self.playbackOrder = playbackOrder self.positionTicks = positionTicks self.repeatMode = repeatMode self.subtitleStreamIndex = subtitleStreamIndex @@ -67,6 +71,7 @@ public struct PlayerStateInfo: Codable, Hashable { self.liveStreamID = try values.decodeIfPresent(String.self, forKey: "LiveStreamId") self.mediaSourceID = try values.decodeIfPresent(String.self, forKey: "MediaSourceId") self.playMethod = try values.decodeIfPresent(PlayMethod.self, forKey: "PlayMethod") + self.playbackOrder = try values.decodeIfPresent(PlaybackOrder.self, forKey: "PlaybackOrder") self.positionTicks = try values.decodeIfPresent(Int.self, forKey: "PositionTicks") self.repeatMode = try values.decodeIfPresent(RepeatMode.self, forKey: "RepeatMode") self.subtitleStreamIndex = try values.decodeIfPresent(Int.self, forKey: "SubtitleStreamIndex") @@ -82,6 +87,7 @@ public struct PlayerStateInfo: Codable, Hashable { try values.encodeIfPresent(liveStreamID, forKey: "LiveStreamId") try values.encodeIfPresent(mediaSourceID, forKey: "MediaSourceId") try values.encodeIfPresent(playMethod, forKey: "PlayMethod") + try values.encodeIfPresent(playbackOrder, forKey: "PlaybackOrder") try values.encodeIfPresent(positionTicks, forKey: "PositionTicks") try values.encodeIfPresent(repeatMode, forKey: "RepeatMode") try values.encodeIfPresent(subtitleStreamIndex, forKey: "SubtitleStreamIndex") diff --git a/Sources/Entities/PlaylistUserPermissions.swift b/Sources/Entities/PlaylistUserPermissions.swift new file mode 100644 index 00000000..4831242a --- /dev/null +++ b/Sources/Entities/PlaylistUserPermissions.swift @@ -0,0 +1,34 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Class to hold data on user permissions for playlists. +public struct PlaylistUserPermissions: Codable, Hashable { + /// Gets or sets a value indicating whether the user has edit permissions. + public var canEdit: Bool? + /// Gets or sets the user id. + public var userID: String? + + public init(canEdit: Bool? = nil, userID: String? = nil) { + self.canEdit = canEdit + self.userID = userID + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.canEdit = try values.decodeIfPresent(Bool.self, forKey: "CanEdit") + self.userID = try values.decodeIfPresent(String.self, forKey: "UserId") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(canEdit, forKey: "CanEdit") + try values.encodeIfPresent(userID, forKey: "UserId") + } +} diff --git a/Sources/Entities/PlaystateMessage.swift b/Sources/Entities/PlaystateMessage.swift new file mode 100644 index 00000000..823b66c6 --- /dev/null +++ b/Sources/Entities/PlaystateMessage.swift @@ -0,0 +1,39 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Playstate message. +public struct PlaystateMessage: Codable, Hashable { + /// Gets or sets the data. + public var data: PlaystateRequest? + /// Gets or sets the message id. + public var messageID: String? + /// The different kinds of messages that are used in the WebSocket api. + public var messageType: SessionMessageType? + + public init(data: PlaystateRequest? = nil, messageID: String? = nil, messageType: SessionMessageType? = nil) { + self.data = data + self.messageID = messageID + self.messageType = messageType + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.data = try values.decodeIfPresent(PlaystateRequest.self, forKey: "Data") + self.messageID = try values.decodeIfPresent(String.self, forKey: "MessageId") + self.messageType = try values.decodeIfPresent(SessionMessageType.self, forKey: "MessageType") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(data, forKey: "Data") + try values.encodeIfPresent(messageID, forKey: "MessageId") + try values.encodeIfPresent(messageType, forKey: "MessageType") + } +} diff --git a/Sources/Entities/PluginInstallationCancelledMessage.swift b/Sources/Entities/PluginInstallationCancelledMessage.swift new file mode 100644 index 00000000..eea1fd6c --- /dev/null +++ b/Sources/Entities/PluginInstallationCancelledMessage.swift @@ -0,0 +1,39 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Plugin installation cancelled message. +public struct PluginInstallationCancelledMessage: Codable, Hashable { + /// Class InstallationInfo. + public var data: InstallationInfo? + /// Gets or sets the message id. + public var messageID: String? + /// The different kinds of messages that are used in the WebSocket api. + public var messageType: SessionMessageType? + + public init(data: InstallationInfo? = nil, messageID: String? = nil, messageType: SessionMessageType? = nil) { + self.data = data + self.messageID = messageID + self.messageType = messageType + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.data = try values.decodeIfPresent(InstallationInfo.self, forKey: "Data") + self.messageID = try values.decodeIfPresent(String.self, forKey: "MessageId") + self.messageType = try values.decodeIfPresent(SessionMessageType.self, forKey: "MessageType") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(data, forKey: "Data") + try values.encodeIfPresent(messageID, forKey: "MessageId") + try values.encodeIfPresent(messageType, forKey: "MessageType") + } +} diff --git a/Sources/Entities/PluginInstallationCompletedMessage.swift b/Sources/Entities/PluginInstallationCompletedMessage.swift new file mode 100644 index 00000000..831b5745 --- /dev/null +++ b/Sources/Entities/PluginInstallationCompletedMessage.swift @@ -0,0 +1,39 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Plugin installation completed message. +public struct PluginInstallationCompletedMessage: Codable, Hashable { + /// Class InstallationInfo. + public var data: InstallationInfo? + /// Gets or sets the message id. + public var messageID: String? + /// The different kinds of messages that are used in the WebSocket api. + public var messageType: SessionMessageType? + + public init(data: InstallationInfo? = nil, messageID: String? = nil, messageType: SessionMessageType? = nil) { + self.data = data + self.messageID = messageID + self.messageType = messageType + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.data = try values.decodeIfPresent(InstallationInfo.self, forKey: "Data") + self.messageID = try values.decodeIfPresent(String.self, forKey: "MessageId") + self.messageType = try values.decodeIfPresent(SessionMessageType.self, forKey: "MessageType") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(data, forKey: "Data") + try values.encodeIfPresent(messageID, forKey: "MessageId") + try values.encodeIfPresent(messageType, forKey: "MessageType") + } +} diff --git a/Sources/Entities/PluginInstallationFailedMessage.swift b/Sources/Entities/PluginInstallationFailedMessage.swift new file mode 100644 index 00000000..41698b46 --- /dev/null +++ b/Sources/Entities/PluginInstallationFailedMessage.swift @@ -0,0 +1,39 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Plugin installation failed message. +public struct PluginInstallationFailedMessage: Codable, Hashable { + /// Class InstallationInfo. + public var data: InstallationInfo? + /// Gets or sets the message id. + public var messageID: String? + /// The different kinds of messages that are used in the WebSocket api. + public var messageType: SessionMessageType? + + public init(data: InstallationInfo? = nil, messageID: String? = nil, messageType: SessionMessageType? = nil) { + self.data = data + self.messageID = messageID + self.messageType = messageType + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.data = try values.decodeIfPresent(InstallationInfo.self, forKey: "Data") + self.messageID = try values.decodeIfPresent(String.self, forKey: "MessageId") + self.messageType = try values.decodeIfPresent(SessionMessageType.self, forKey: "MessageType") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(data, forKey: "Data") + try values.encodeIfPresent(messageID, forKey: "MessageId") + try values.encodeIfPresent(messageType, forKey: "MessageType") + } +} diff --git a/Sources/Entities/PluginInstallingMessage.swift b/Sources/Entities/PluginInstallingMessage.swift new file mode 100644 index 00000000..7160b3e4 --- /dev/null +++ b/Sources/Entities/PluginInstallingMessage.swift @@ -0,0 +1,39 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Package installing message. +public struct PluginInstallingMessage: Codable, Hashable { + /// Class InstallationInfo. + public var data: InstallationInfo? + /// Gets or sets the message id. + public var messageID: String? + /// The different kinds of messages that are used in the WebSocket api. + public var messageType: SessionMessageType? + + public init(data: InstallationInfo? = nil, messageID: String? = nil, messageType: SessionMessageType? = nil) { + self.data = data + self.messageID = messageID + self.messageType = messageType + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.data = try values.decodeIfPresent(InstallationInfo.self, forKey: "Data") + self.messageID = try values.decodeIfPresent(String.self, forKey: "MessageId") + self.messageType = try values.decodeIfPresent(SessionMessageType.self, forKey: "MessageType") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(data, forKey: "Data") + try values.encodeIfPresent(messageID, forKey: "MessageId") + try values.encodeIfPresent(messageType, forKey: "MessageType") + } +} diff --git a/Sources/Entities/PluginUninstalledMessage.swift b/Sources/Entities/PluginUninstalledMessage.swift new file mode 100644 index 00000000..9667ceb7 --- /dev/null +++ b/Sources/Entities/PluginUninstalledMessage.swift @@ -0,0 +1,39 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Plugin uninstalled message. +public struct PluginUninstalledMessage: Codable, Hashable { + /// This is a serializable stub class that is used by the api to provide information about installed plugins. + public var data: PluginInfo? + /// Gets or sets the message id. + public var messageID: String? + /// The different kinds of messages that are used in the WebSocket api. + public var messageType: SessionMessageType? + + public init(data: PluginInfo? = nil, messageID: String? = nil, messageType: SessionMessageType? = nil) { + self.data = data + self.messageID = messageID + self.messageType = messageType + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.data = try values.decodeIfPresent(PluginInfo.self, forKey: "Data") + self.messageID = try values.decodeIfPresent(String.self, forKey: "MessageId") + self.messageType = try values.decodeIfPresent(SessionMessageType.self, forKey: "MessageType") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(data, forKey: "Data") + try values.encodeIfPresent(messageID, forKey: "MessageId") + try values.encodeIfPresent(messageType, forKey: "MessageType") + } +} diff --git a/Sources/Entities/ProcessPriorityClass.swift b/Sources/Entities/ProcessPriorityClass.swift new file mode 100644 index 00000000..d4fc70a7 --- /dev/null +++ b/Sources/Entities/ProcessPriorityClass.swift @@ -0,0 +1,18 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +public enum ProcessPriorityClass: String, Codable, CaseIterable { + case normal = "Normal" + case idle = "Idle" + case high = "High" + case realTime = "RealTime" + case belowNormal = "BelowNormal" + case aboveNormal = "AboveNormal" +} diff --git a/Sources/Entities/PublicSystemInfo.swift b/Sources/Entities/PublicSystemInfo.swift index ffd6af6c..0738d92d 100644 --- a/Sources/Entities/PublicSystemInfo.swift +++ b/Sources/Entities/PublicSystemInfo.swift @@ -14,6 +14,8 @@ public struct PublicSystemInfo: Codable, Hashable, Identifiable { /// Gets or sets the local address. public var localAddress: String? /// Gets or sets the operating system. + /// + /// - warning: Deprecated. public var operatingSystem: String? /// Gets or sets the product name. This is the AssemblyProduct name. public var productName: String? diff --git a/Sources/Entities/RefreshProgressMessage.swift b/Sources/Entities/RefreshProgressMessage.swift new file mode 100644 index 00000000..b697d404 --- /dev/null +++ b/Sources/Entities/RefreshProgressMessage.swift @@ -0,0 +1,39 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Refresh progress message. +public struct RefreshProgressMessage: Codable, Hashable { + /// Gets or sets the data. + public var data: [String: String]? + /// Gets or sets the message id. + public var messageID: String? + /// The different kinds of messages that are used in the WebSocket api. + public var messageType: SessionMessageType? + + public init(data: [String: String]? = nil, messageID: String? = nil, messageType: SessionMessageType? = nil) { + self.data = data + self.messageID = messageID + self.messageType = messageType + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.data = try values.decodeIfPresent([String: String].self, forKey: "Data") + self.messageID = try values.decodeIfPresent(String.self, forKey: "MessageId") + self.messageType = try values.decodeIfPresent(SessionMessageType.self, forKey: "MessageType") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(data, forKey: "Data") + try values.encodeIfPresent(messageID, forKey: "MessageId") + try values.encodeIfPresent(messageType, forKey: "MessageType") + } +} diff --git a/Sources/Entities/RemoteLyricInfoDto.swift b/Sources/Entities/RemoteLyricInfoDto.swift new file mode 100644 index 00000000..bf05f41e --- /dev/null +++ b/Sources/Entities/RemoteLyricInfoDto.swift @@ -0,0 +1,39 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// The remote lyric info dto. +public struct RemoteLyricInfoDto: Codable, Hashable, Identifiable { + /// Gets or sets the id for the lyric. + public var id: String? + /// Gets the lyrics. + public var lyrics: LyricDto? + /// Gets the provider name. + public var providerName: String? + + public init(id: String? = nil, lyrics: LyricDto? = nil, providerName: String? = nil) { + self.id = id + self.lyrics = lyrics + self.providerName = providerName + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.id = try values.decodeIfPresent(String.self, forKey: "Id") + self.lyrics = try values.decodeIfPresent(LyricDto.self, forKey: "Lyrics") + self.providerName = try values.decodeIfPresent(String.self, forKey: "ProviderName") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(id, forKey: "Id") + try values.encodeIfPresent(lyrics, forKey: "Lyrics") + try values.encodeIfPresent(providerName, forKey: "ProviderName") + } +} diff --git a/Sources/Entities/RemoteSubtitleInfo.swift b/Sources/Entities/RemoteSubtitleInfo.swift index a6ca4919..2138842d 100644 --- a/Sources/Entities/RemoteSubtitleInfo.swift +++ b/Sources/Entities/RemoteSubtitleInfo.swift @@ -9,39 +9,54 @@ import Foundation public struct RemoteSubtitleInfo: Codable, Hashable, Identifiable { + public var isAiTranslated: Bool? public var author: String? public var comment: String? public var communityRating: Float? public var dateCreated: Date? public var downloadCount: Int? + public var isForced: Bool? public var format: String? + public var frameRate: Float? + public var isHearingImpaired: Bool? public var id: String? public var isHashMatch: Bool? + public var isMachineTranslated: Bool? public var name: String? public var providerName: String? public var threeLetterISOLanguageName: String? public init( + isAiTranslated: Bool? = nil, author: String? = nil, comment: String? = nil, communityRating: Float? = nil, dateCreated: Date? = nil, downloadCount: Int? = nil, + isForced: Bool? = nil, format: String? = nil, + frameRate: Float? = nil, + isHearingImpaired: Bool? = nil, id: String? = nil, isHashMatch: Bool? = nil, + isMachineTranslated: Bool? = nil, name: String? = nil, providerName: String? = nil, threeLetterISOLanguageName: String? = nil ) { + self.isAiTranslated = isAiTranslated self.author = author self.comment = comment self.communityRating = communityRating self.dateCreated = dateCreated self.downloadCount = downloadCount + self.isForced = isForced self.format = format + self.frameRate = frameRate + self.isHearingImpaired = isHearingImpaired self.id = id self.isHashMatch = isHashMatch + self.isMachineTranslated = isMachineTranslated self.name = name self.providerName = providerName self.threeLetterISOLanguageName = threeLetterISOLanguageName @@ -49,14 +64,19 @@ public struct RemoteSubtitleInfo: Codable, Hashable, Identifiable { public init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: StringCodingKey.self) + self.isAiTranslated = try values.decodeIfPresent(Bool.self, forKey: "AiTranslated") self.author = try values.decodeIfPresent(String.self, forKey: "Author") self.comment = try values.decodeIfPresent(String.self, forKey: "Comment") self.communityRating = try values.decodeIfPresent(Float.self, forKey: "CommunityRating") self.dateCreated = try values.decodeIfPresent(Date.self, forKey: "DateCreated") self.downloadCount = try values.decodeIfPresent(Int.self, forKey: "DownloadCount") + self.isForced = try values.decodeIfPresent(Bool.self, forKey: "Forced") self.format = try values.decodeIfPresent(String.self, forKey: "Format") + self.frameRate = try values.decodeIfPresent(Float.self, forKey: "FrameRate") + self.isHearingImpaired = try values.decodeIfPresent(Bool.self, forKey: "HearingImpaired") self.id = try values.decodeIfPresent(String.self, forKey: "Id") self.isHashMatch = try values.decodeIfPresent(Bool.self, forKey: "IsHashMatch") + self.isMachineTranslated = try values.decodeIfPresent(Bool.self, forKey: "MachineTranslated") self.name = try values.decodeIfPresent(String.self, forKey: "Name") self.providerName = try values.decodeIfPresent(String.self, forKey: "ProviderName") self.threeLetterISOLanguageName = try values.decodeIfPresent(String.self, forKey: "ThreeLetterISOLanguageName") @@ -64,14 +84,19 @@ public struct RemoteSubtitleInfo: Codable, Hashable, Identifiable { public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(isAiTranslated, forKey: "AiTranslated") try values.encodeIfPresent(author, forKey: "Author") try values.encodeIfPresent(comment, forKey: "Comment") try values.encodeIfPresent(communityRating, forKey: "CommunityRating") try values.encodeIfPresent(dateCreated, forKey: "DateCreated") try values.encodeIfPresent(downloadCount, forKey: "DownloadCount") + try values.encodeIfPresent(isForced, forKey: "Forced") try values.encodeIfPresent(format, forKey: "Format") + try values.encodeIfPresent(frameRate, forKey: "FrameRate") + try values.encodeIfPresent(isHearingImpaired, forKey: "HearingImpaired") try values.encodeIfPresent(id, forKey: "Id") try values.encodeIfPresent(isHashMatch, forKey: "IsHashMatch") + try values.encodeIfPresent(isMachineTranslated, forKey: "MachineTranslated") try values.encodeIfPresent(name, forKey: "Name") try values.encodeIfPresent(providerName, forKey: "ProviderName") try values.encodeIfPresent(threeLetterISOLanguageName, forKey: "ThreeLetterISOLanguageName") diff --git a/Sources/Entities/RemoveFromPlaylistRequestDto.swift b/Sources/Entities/RemoveFromPlaylistRequestDto.swift index 8debd074..159a48db 100644 --- a/Sources/Entities/RemoveFromPlaylistRequestDto.swift +++ b/Sources/Entities/RemoveFromPlaylistRequestDto.swift @@ -14,7 +14,7 @@ public struct RemoveFromPlaylistRequestDto: Codable, Hashable { public var isClearPlayingItem: Bool? /// Gets or sets a value indicating whether the entire playlist should be cleared. public var isClearPlaylist: Bool? - /// Gets or sets the playlist identifiers ot the items. Ignored when clearing the playlist. + /// Gets or sets the playlist identifiers of the items. Ignored when clearing the playlist. public var playlistItemIDs: [String]? public init(isClearPlayingItem: Bool? = nil, isClearPlaylist: Bool? = nil, playlistItemIDs: [String]? = nil) { diff --git a/Sources/Entities/RestartRequiredMessage.swift b/Sources/Entities/RestartRequiredMessage.swift new file mode 100644 index 00000000..4552101a --- /dev/null +++ b/Sources/Entities/RestartRequiredMessage.swift @@ -0,0 +1,34 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Restart required. +public struct RestartRequiredMessage: Codable, Hashable { + /// Gets or sets the message id. + public var messageID: String? + /// The different kinds of messages that are used in the WebSocket api. + public var messageType: SessionMessageType? + + public init(messageID: String? = nil, messageType: SessionMessageType? = nil) { + self.messageID = messageID + self.messageType = messageType + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.messageID = try values.decodeIfPresent(String.self, forKey: "MessageId") + self.messageType = try values.decodeIfPresent(SessionMessageType.self, forKey: "MessageType") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(messageID, forKey: "MessageId") + try values.encodeIfPresent(messageType, forKey: "MessageType") + } +} diff --git a/Sources/Entities/ScheduledTaskEndedMessage.swift b/Sources/Entities/ScheduledTaskEndedMessage.swift new file mode 100644 index 00000000..8afd74ef --- /dev/null +++ b/Sources/Entities/ScheduledTaskEndedMessage.swift @@ -0,0 +1,39 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Scheduled task ended message. +public struct ScheduledTaskEndedMessage: Codable, Hashable { + /// Class TaskExecutionInfo. + public var data: TaskResult? + /// Gets or sets the message id. + public var messageID: String? + /// The different kinds of messages that are used in the WebSocket api. + public var messageType: SessionMessageType? + + public init(data: TaskResult? = nil, messageID: String? = nil, messageType: SessionMessageType? = nil) { + self.data = data + self.messageID = messageID + self.messageType = messageType + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.data = try values.decodeIfPresent(TaskResult.self, forKey: "Data") + self.messageID = try values.decodeIfPresent(String.self, forKey: "MessageId") + self.messageType = try values.decodeIfPresent(SessionMessageType.self, forKey: "MessageType") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(data, forKey: "Data") + try values.encodeIfPresent(messageID, forKey: "MessageId") + try values.encodeIfPresent(messageType, forKey: "MessageType") + } +} diff --git a/Sources/Entities/ScheduledTasksInfoMessage.swift b/Sources/Entities/ScheduledTasksInfoMessage.swift new file mode 100644 index 00000000..89ce1f3e --- /dev/null +++ b/Sources/Entities/ScheduledTasksInfoMessage.swift @@ -0,0 +1,39 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Scheduled tasks info message. +public struct ScheduledTasksInfoMessage: Codable, Hashable { + /// Gets or sets the data. + public var data: [TaskInfo]? + /// Gets or sets the message id. + public var messageID: String? + /// The different kinds of messages that are used in the WebSocket api. + public var messageType: SessionMessageType? + + public init(data: [TaskInfo]? = nil, messageID: String? = nil, messageType: SessionMessageType? = nil) { + self.data = data + self.messageID = messageID + self.messageType = messageType + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.data = try values.decodeIfPresent([TaskInfo].self, forKey: "Data") + self.messageID = try values.decodeIfPresent(String.self, forKey: "MessageId") + self.messageType = try values.decodeIfPresent(SessionMessageType.self, forKey: "MessageType") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(data, forKey: "Data") + try values.encodeIfPresent(messageID, forKey: "MessageId") + try values.encodeIfPresent(messageType, forKey: "MessageType") + } +} diff --git a/Sources/Entities/ScheduledTasksInfoStartMessage.swift b/Sources/Entities/ScheduledTasksInfoStartMessage.swift new file mode 100644 index 00000000..9fbdc704 --- /dev/null +++ b/Sources/Entities/ScheduledTasksInfoStartMessage.swift @@ -0,0 +1,36 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Scheduled tasks info start message. +/// +/// Data is the timing data encoded as "$initialDelay,$interval" in ms. +public struct ScheduledTasksInfoStartMessage: Codable, Hashable { + /// Gets or sets the data. + public var data: String? + /// The different kinds of messages that are used in the WebSocket api. + public var messageType: SessionMessageType? + + public init(data: String? = nil, messageType: SessionMessageType? = nil) { + self.data = data + self.messageType = messageType + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.data = try values.decodeIfPresent(String.self, forKey: "Data") + self.messageType = try values.decodeIfPresent(SessionMessageType.self, forKey: "MessageType") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(data, forKey: "Data") + try values.encodeIfPresent(messageType, forKey: "MessageType") + } +} diff --git a/Sources/Entities/ScheduledTasksInfoStopMessage.swift b/Sources/Entities/ScheduledTasksInfoStopMessage.swift new file mode 100644 index 00000000..6a1bee45 --- /dev/null +++ b/Sources/Entities/ScheduledTasksInfoStopMessage.swift @@ -0,0 +1,29 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Scheduled tasks info stop message. +public struct ScheduledTasksInfoStopMessage: Codable, Hashable { + /// The different kinds of messages that are used in the WebSocket api. + public var messageType: SessionMessageType? + + public init(messageType: SessionMessageType? = nil) { + self.messageType = messageType + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.messageType = try values.decodeIfPresent(SessionMessageType.self, forKey: "MessageType") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(messageType, forKey: "MessageType") + } +} diff --git a/Sources/Entities/SearchHint.swift b/Sources/Entities/SearchHint.swift index 98db55bc..0e648ec2 100644 --- a/Sources/Entities/SearchHint.swift +++ b/Sources/Entities/SearchHint.swift @@ -14,6 +14,7 @@ public struct SearchHint: Codable, Hashable, Identifiable { public var album: String? /// Gets or sets the album artist. public var albumArtist: String? + /// Gets or sets the album id. public var albumID: String? /// Gets or sets the artists. public var artists: [String]? @@ -25,19 +26,24 @@ public struct SearchHint: Codable, Hashable, Identifiable { public var channelID: String? /// Gets or sets the name of the channel. public var channelName: String? + /// Gets or sets the end date. public var endDate: Date? /// Gets or sets the episode count. public var episodeCount: Int? + /// Gets or sets the item id. public var id: String? /// Gets or sets the index number. public var indexNumber: Int? + /// Gets or sets a value indicating whether this instance is folder. public var isFolder: Bool? /// Gets or sets the item id. + /// + /// - warning: Deprecated. public var itemID: String? /// Gets or sets the matched term. public var matchedTerm: String? /// Gets or sets the type of the media. - public var mediaType: String? + public var mediaType: MediaType? /// Gets or sets the name. public var name: String? /// Gets or sets the parent index number. @@ -54,14 +60,16 @@ public struct SearchHint: Codable, Hashable, Identifiable { public var series: String? /// Gets or sets the song count. public var songCount: Int? + /// Gets or sets the start date. public var startDate: Date? + /// Gets or sets the status. public var status: String? /// Gets or sets the thumb image item identifier. public var thumbImageItemID: String? /// Gets or sets the thumb image tag. public var thumbImageTag: String? /// Gets or sets the type. - public var type: String? + public var type: BaseItemKind? public init( album: String? = nil, @@ -79,7 +87,7 @@ public struct SearchHint: Codable, Hashable, Identifiable { isFolder: Bool? = nil, itemID: String? = nil, matchedTerm: String? = nil, - mediaType: String? = nil, + mediaType: MediaType? = nil, name: String? = nil, parentIndexNumber: Int? = nil, primaryImageAspectRatio: Double? = nil, @@ -92,7 +100,7 @@ public struct SearchHint: Codable, Hashable, Identifiable { status: String? = nil, thumbImageItemID: String? = nil, thumbImageTag: String? = nil, - type: String? = nil + type: BaseItemKind? = nil ) { self.album = album self.albumArtist = albumArtist @@ -142,7 +150,7 @@ public struct SearchHint: Codable, Hashable, Identifiable { self.isFolder = try values.decodeIfPresent(Bool.self, forKey: "IsFolder") self.itemID = try values.decodeIfPresent(String.self, forKey: "ItemId") self.matchedTerm = try values.decodeIfPresent(String.self, forKey: "MatchedTerm") - self.mediaType = try values.decodeIfPresent(String.self, forKey: "MediaType") + self.mediaType = try values.decodeIfPresent(MediaType.self, forKey: "MediaType") self.name = try values.decodeIfPresent(String.self, forKey: "Name") self.parentIndexNumber = try values.decodeIfPresent(Int.self, forKey: "ParentIndexNumber") self.primaryImageAspectRatio = try values.decodeIfPresent(Double.self, forKey: "PrimaryImageAspectRatio") @@ -155,7 +163,7 @@ public struct SearchHint: Codable, Hashable, Identifiable { self.status = try values.decodeIfPresent(String.self, forKey: "Status") self.thumbImageItemID = try values.decodeIfPresent(String.self, forKey: "ThumbImageItemId") self.thumbImageTag = try values.decodeIfPresent(String.self, forKey: "ThumbImageTag") - self.type = try values.decodeIfPresent(String.self, forKey: "Type") + self.type = try values.decodeIfPresent(BaseItemKind.self, forKey: "Type") } public func encode(to encoder: Encoder) throws { diff --git a/Sources/Entities/SeriesStatus.swift b/Sources/Entities/SeriesStatus.swift index 4c2465f4..88e776e5 100644 --- a/Sources/Entities/SeriesStatus.swift +++ b/Sources/Entities/SeriesStatus.swift @@ -8,8 +8,9 @@ import Foundation -/// Enum SeriesStatus. +/// The status of a series. public enum SeriesStatus: String, Codable, CaseIterable { case continuing = "Continuing" case ended = "Ended" + case unreleased = "Unreleased" } diff --git a/Sources/Entities/SeriesTimerCancelledMessage.swift b/Sources/Entities/SeriesTimerCancelledMessage.swift new file mode 100644 index 00000000..f165a034 --- /dev/null +++ b/Sources/Entities/SeriesTimerCancelledMessage.swift @@ -0,0 +1,39 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Series timer cancelled message. +public struct SeriesTimerCancelledMessage: Codable, Hashable { + /// Gets or sets the data. + public var data: TimerEventInfo? + /// Gets or sets the message id. + public var messageID: String? + /// The different kinds of messages that are used in the WebSocket api. + public var messageType: SessionMessageType? + + public init(data: TimerEventInfo? = nil, messageID: String? = nil, messageType: SessionMessageType? = nil) { + self.data = data + self.messageID = messageID + self.messageType = messageType + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.data = try values.decodeIfPresent(TimerEventInfo.self, forKey: "Data") + self.messageID = try values.decodeIfPresent(String.self, forKey: "MessageId") + self.messageType = try values.decodeIfPresent(SessionMessageType.self, forKey: "MessageType") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(data, forKey: "Data") + try values.encodeIfPresent(messageID, forKey: "MessageId") + try values.encodeIfPresent(messageType, forKey: "MessageType") + } +} diff --git a/Sources/Entities/SeriesTimerCreatedMessage.swift b/Sources/Entities/SeriesTimerCreatedMessage.swift new file mode 100644 index 00000000..70944438 --- /dev/null +++ b/Sources/Entities/SeriesTimerCreatedMessage.swift @@ -0,0 +1,39 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Series timer created message. +public struct SeriesTimerCreatedMessage: Codable, Hashable { + /// Gets or sets the data. + public var data: TimerEventInfo? + /// Gets or sets the message id. + public var messageID: String? + /// The different kinds of messages that are used in the WebSocket api. + public var messageType: SessionMessageType? + + public init(data: TimerEventInfo? = nil, messageID: String? = nil, messageType: SessionMessageType? = nil) { + self.data = data + self.messageID = messageID + self.messageType = messageType + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.data = try values.decodeIfPresent(TimerEventInfo.self, forKey: "Data") + self.messageID = try values.decodeIfPresent(String.self, forKey: "MessageId") + self.messageType = try values.decodeIfPresent(SessionMessageType.self, forKey: "MessageType") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(data, forKey: "Data") + try values.encodeIfPresent(messageID, forKey: "MessageId") + try values.encodeIfPresent(messageType, forKey: "MessageType") + } +} diff --git a/Sources/Entities/ServerConfiguration.swift b/Sources/Entities/ServerConfiguration.swift index 72694d0d..ef910d8b 100644 --- a/Sources/Entities/ServerConfiguration.swift +++ b/Sources/Entities/ServerConfiguration.swift @@ -16,12 +16,18 @@ public struct ServerConfiguration: Codable, Hashable { public var allowClientLogUpload: Bool? /// Gets or sets the cache path. public var cachePath: String? + /// Gets or sets the list of cast receiver applications. + public var castReceiverApplications: [CastReceiverApplication]? + /// Gets or sets the chapter image resolution. + public var chapterImageResolution: ImageResolution? public var codecsUsed: [String]? public var contentTypes: [NameValuePair]? /// Gets or sets the cors hosts. public var corsHosts: [String]? public var isDisableLiveTvChannelUserDataName: Bool? public var isDisplaySpecialsWithinSeasons: Bool? + /// Gets or sets the dummy chapter duration in seconds, use 0 (zero) or less to disable generation alltogether. + public var dummyChapterDuration: Int? /// Gets or sets a value indicating whether [enable case sensitive item ids]. public var enableCaseSensitiveItemIDs: Bool? public var enableExternalContentInSuggestions: Bool? @@ -35,6 +41,10 @@ public struct ServerConfiguration: Codable, Hashable { public var imageExtractionTimeoutMs: Int? /// Gets or sets the image saving convention. public var imageSavingConvention: ImageSavingConvention? + /// Gets or sets the threshold in minutes after a inactive session gets closed automatically. + /// + /// If set to 0 the check for inactive sessions gets disabled. + public var inactiveSessionThreshold: Int? /// Gets or sets a value indicating whether this instance is port authorized. public var isPortAuthorized: Bool? /// Gets or sets a value indicating whether this instance is first run. @@ -49,6 +59,9 @@ public struct ServerConfiguration: Codable, Hashable { public var libraryMonitorDelay: Int? /// Gets or sets the how the library scan fans out. public var libraryScanFanoutConcurrency: Int? + /// Gets or sets the duration in seconds that we will wait after a library updated event before executing the library changed + /// notification. + public var libraryUpdateDuration: Int? /// Gets or sets the number of days we should retain log files. public var logFileRetentionDays: Int? /// Gets or sets the remaining minutes of a book that can be played while still saving playstate. If this percentage is crossed @@ -69,6 +82,8 @@ public struct ServerConfiguration: Codable, Hashable { public var minResumeDurationSeconds: Int? /// Gets or sets the minimum percentage of an item that must be played in order for playstate to be updated. public var minResumePct: Int? + /// Gets or sets the limit for parallel image encoding. + public var parallelImageEncodingLimit: Int? public var pathSubstitutions: [PathSubstitution]? public var pluginRepositories: [RepositoryInfo]? /// Gets or sets the preferred metadata language. @@ -95,17 +110,22 @@ public struct ServerConfiguration: Codable, Hashable { public var sortRemoveWords: [String]? /// Gets or sets characters to be replaced with a ' ' in strings to create a sort name. public var sortReplaceCharacters: [String]? + /// Gets or sets the trickplay options. + public var trickplayOptions: TrickplayOptions? public var uICulture: String? public init( activityLogRetentionDays: Int? = nil, allowClientLogUpload: Bool? = nil, cachePath: String? = nil, + castReceiverApplications: [CastReceiverApplication]? = nil, + chapterImageResolution: ImageResolution? = nil, codecsUsed: [String]? = nil, contentTypes: [NameValuePair]? = nil, corsHosts: [String]? = nil, isDisableLiveTvChannelUserDataName: Bool? = nil, isDisplaySpecialsWithinSeasons: Bool? = nil, + dummyChapterDuration: Int? = nil, enableCaseSensitiveItemIDs: Bool? = nil, enableExternalContentInSuggestions: Bool? = nil, enableFolderView: Bool? = nil, @@ -115,11 +135,13 @@ public struct ServerConfiguration: Codable, Hashable { enableSlowResponseWarning: Bool? = nil, imageExtractionTimeoutMs: Int? = nil, imageSavingConvention: ImageSavingConvention? = nil, + inactiveSessionThreshold: Int? = nil, isPortAuthorized: Bool? = nil, isStartupWizardCompleted: Bool? = nil, libraryMetadataRefreshConcurrency: Int? = nil, libraryMonitorDelay: Int? = nil, libraryScanFanoutConcurrency: Int? = nil, + libraryUpdateDuration: Int? = nil, logFileRetentionDays: Int? = nil, maxAudiobookResume: Int? = nil, maxResumePct: Int? = nil, @@ -130,6 +152,7 @@ public struct ServerConfiguration: Codable, Hashable { minAudiobookResume: Int? = nil, minResumeDurationSeconds: Int? = nil, minResumePct: Int? = nil, + parallelImageEncodingLimit: Int? = nil, pathSubstitutions: [PathSubstitution]? = nil, pluginRepositories: [RepositoryInfo]? = nil, preferredMetadataLanguage: String? = nil, @@ -145,16 +168,20 @@ public struct ServerConfiguration: Codable, Hashable { sortRemoveCharacters: [String]? = nil, sortRemoveWords: [String]? = nil, sortReplaceCharacters: [String]? = nil, + trickplayOptions: TrickplayOptions? = nil, uICulture: String? = nil ) { self.activityLogRetentionDays = activityLogRetentionDays self.allowClientLogUpload = allowClientLogUpload self.cachePath = cachePath + self.castReceiverApplications = castReceiverApplications + self.chapterImageResolution = chapterImageResolution self.codecsUsed = codecsUsed self.contentTypes = contentTypes self.corsHosts = corsHosts self.isDisableLiveTvChannelUserDataName = isDisableLiveTvChannelUserDataName self.isDisplaySpecialsWithinSeasons = isDisplaySpecialsWithinSeasons + self.dummyChapterDuration = dummyChapterDuration self.enableCaseSensitiveItemIDs = enableCaseSensitiveItemIDs self.enableExternalContentInSuggestions = enableExternalContentInSuggestions self.enableFolderView = enableFolderView @@ -164,11 +191,13 @@ public struct ServerConfiguration: Codable, Hashable { self.enableSlowResponseWarning = enableSlowResponseWarning self.imageExtractionTimeoutMs = imageExtractionTimeoutMs self.imageSavingConvention = imageSavingConvention + self.inactiveSessionThreshold = inactiveSessionThreshold self.isPortAuthorized = isPortAuthorized self.isStartupWizardCompleted = isStartupWizardCompleted self.libraryMetadataRefreshConcurrency = libraryMetadataRefreshConcurrency self.libraryMonitorDelay = libraryMonitorDelay self.libraryScanFanoutConcurrency = libraryScanFanoutConcurrency + self.libraryUpdateDuration = libraryUpdateDuration self.logFileRetentionDays = logFileRetentionDays self.maxAudiobookResume = maxAudiobookResume self.maxResumePct = maxResumePct @@ -179,6 +208,7 @@ public struct ServerConfiguration: Codable, Hashable { self.minAudiobookResume = minAudiobookResume self.minResumeDurationSeconds = minResumeDurationSeconds self.minResumePct = minResumePct + self.parallelImageEncodingLimit = parallelImageEncodingLimit self.pathSubstitutions = pathSubstitutions self.pluginRepositories = pluginRepositories self.preferredMetadataLanguage = preferredMetadataLanguage @@ -194,6 +224,7 @@ public struct ServerConfiguration: Codable, Hashable { self.sortRemoveCharacters = sortRemoveCharacters self.sortRemoveWords = sortRemoveWords self.sortReplaceCharacters = sortReplaceCharacters + self.trickplayOptions = trickplayOptions self.uICulture = uICulture } @@ -202,11 +233,14 @@ public struct ServerConfiguration: Codable, Hashable { self.activityLogRetentionDays = try values.decodeIfPresent(Int.self, forKey: "ActivityLogRetentionDays") self.allowClientLogUpload = try values.decodeIfPresent(Bool.self, forKey: "AllowClientLogUpload") self.cachePath = try values.decodeIfPresent(String.self, forKey: "CachePath") + self.castReceiverApplications = try values.decodeIfPresent([CastReceiverApplication].self, forKey: "CastReceiverApplications") + self.chapterImageResolution = try values.decodeIfPresent(ImageResolution.self, forKey: "ChapterImageResolution") self.codecsUsed = try values.decodeIfPresent([String].self, forKey: "CodecsUsed") self.contentTypes = try values.decodeIfPresent([NameValuePair].self, forKey: "ContentTypes") self.corsHosts = try values.decodeIfPresent([String].self, forKey: "CorsHosts") self.isDisableLiveTvChannelUserDataName = try values.decodeIfPresent(Bool.self, forKey: "DisableLiveTvChannelUserDataName") self.isDisplaySpecialsWithinSeasons = try values.decodeIfPresent(Bool.self, forKey: "DisplaySpecialsWithinSeasons") + self.dummyChapterDuration = try values.decodeIfPresent(Int.self, forKey: "DummyChapterDuration") self.enableCaseSensitiveItemIDs = try values.decodeIfPresent(Bool.self, forKey: "EnableCaseSensitiveItemIds") self.enableExternalContentInSuggestions = try values.decodeIfPresent(Bool.self, forKey: "EnableExternalContentInSuggestions") self.enableFolderView = try values.decodeIfPresent(Bool.self, forKey: "EnableFolderView") @@ -216,11 +250,13 @@ public struct ServerConfiguration: Codable, Hashable { self.enableSlowResponseWarning = try values.decodeIfPresent(Bool.self, forKey: "EnableSlowResponseWarning") self.imageExtractionTimeoutMs = try values.decodeIfPresent(Int.self, forKey: "ImageExtractionTimeoutMs") self.imageSavingConvention = try values.decodeIfPresent(ImageSavingConvention.self, forKey: "ImageSavingConvention") + self.inactiveSessionThreshold = try values.decodeIfPresent(Int.self, forKey: "InactiveSessionThreshold") self.isPortAuthorized = try values.decodeIfPresent(Bool.self, forKey: "IsPortAuthorized") self.isStartupWizardCompleted = try values.decodeIfPresent(Bool.self, forKey: "IsStartupWizardCompleted") self.libraryMetadataRefreshConcurrency = try values.decodeIfPresent(Int.self, forKey: "LibraryMetadataRefreshConcurrency") self.libraryMonitorDelay = try values.decodeIfPresent(Int.self, forKey: "LibraryMonitorDelay") self.libraryScanFanoutConcurrency = try values.decodeIfPresent(Int.self, forKey: "LibraryScanFanoutConcurrency") + self.libraryUpdateDuration = try values.decodeIfPresent(Int.self, forKey: "LibraryUpdateDuration") self.logFileRetentionDays = try values.decodeIfPresent(Int.self, forKey: "LogFileRetentionDays") self.maxAudiobookResume = try values.decodeIfPresent(Int.self, forKey: "MaxAudiobookResume") self.maxResumePct = try values.decodeIfPresent(Int.self, forKey: "MaxResumePct") @@ -231,6 +267,7 @@ public struct ServerConfiguration: Codable, Hashable { self.minAudiobookResume = try values.decodeIfPresent(Int.self, forKey: "MinAudiobookResume") self.minResumeDurationSeconds = try values.decodeIfPresent(Int.self, forKey: "MinResumeDurationSeconds") self.minResumePct = try values.decodeIfPresent(Int.self, forKey: "MinResumePct") + self.parallelImageEncodingLimit = try values.decodeIfPresent(Int.self, forKey: "ParallelImageEncodingLimit") self.pathSubstitutions = try values.decodeIfPresent([PathSubstitution].self, forKey: "PathSubstitutions") self.pluginRepositories = try values.decodeIfPresent([RepositoryInfo].self, forKey: "PluginRepositories") self.preferredMetadataLanguage = try values.decodeIfPresent(String.self, forKey: "PreferredMetadataLanguage") @@ -246,6 +283,7 @@ public struct ServerConfiguration: Codable, Hashable { self.sortRemoveCharacters = try values.decodeIfPresent([String].self, forKey: "SortRemoveCharacters") self.sortRemoveWords = try values.decodeIfPresent([String].self, forKey: "SortRemoveWords") self.sortReplaceCharacters = try values.decodeIfPresent([String].self, forKey: "SortReplaceCharacters") + self.trickplayOptions = try values.decodeIfPresent(TrickplayOptions.self, forKey: "TrickplayOptions") self.uICulture = try values.decodeIfPresent(String.self, forKey: "UICulture") } @@ -254,11 +292,14 @@ public struct ServerConfiguration: Codable, Hashable { try values.encodeIfPresent(activityLogRetentionDays, forKey: "ActivityLogRetentionDays") try values.encodeIfPresent(allowClientLogUpload, forKey: "AllowClientLogUpload") try values.encodeIfPresent(cachePath, forKey: "CachePath") + try values.encodeIfPresent(castReceiverApplications, forKey: "CastReceiverApplications") + try values.encodeIfPresent(chapterImageResolution, forKey: "ChapterImageResolution") try values.encodeIfPresent(codecsUsed, forKey: "CodecsUsed") try values.encodeIfPresent(contentTypes, forKey: "ContentTypes") try values.encodeIfPresent(corsHosts, forKey: "CorsHosts") try values.encodeIfPresent(isDisableLiveTvChannelUserDataName, forKey: "DisableLiveTvChannelUserDataName") try values.encodeIfPresent(isDisplaySpecialsWithinSeasons, forKey: "DisplaySpecialsWithinSeasons") + try values.encodeIfPresent(dummyChapterDuration, forKey: "DummyChapterDuration") try values.encodeIfPresent(enableCaseSensitiveItemIDs, forKey: "EnableCaseSensitiveItemIds") try values.encodeIfPresent(enableExternalContentInSuggestions, forKey: "EnableExternalContentInSuggestions") try values.encodeIfPresent(enableFolderView, forKey: "EnableFolderView") @@ -268,11 +309,13 @@ public struct ServerConfiguration: Codable, Hashable { try values.encodeIfPresent(enableSlowResponseWarning, forKey: "EnableSlowResponseWarning") try values.encodeIfPresent(imageExtractionTimeoutMs, forKey: "ImageExtractionTimeoutMs") try values.encodeIfPresent(imageSavingConvention, forKey: "ImageSavingConvention") + try values.encodeIfPresent(inactiveSessionThreshold, forKey: "InactiveSessionThreshold") try values.encodeIfPresent(isPortAuthorized, forKey: "IsPortAuthorized") try values.encodeIfPresent(isStartupWizardCompleted, forKey: "IsStartupWizardCompleted") try values.encodeIfPresent(libraryMetadataRefreshConcurrency, forKey: "LibraryMetadataRefreshConcurrency") try values.encodeIfPresent(libraryMonitorDelay, forKey: "LibraryMonitorDelay") try values.encodeIfPresent(libraryScanFanoutConcurrency, forKey: "LibraryScanFanoutConcurrency") + try values.encodeIfPresent(libraryUpdateDuration, forKey: "LibraryUpdateDuration") try values.encodeIfPresent(logFileRetentionDays, forKey: "LogFileRetentionDays") try values.encodeIfPresent(maxAudiobookResume, forKey: "MaxAudiobookResume") try values.encodeIfPresent(maxResumePct, forKey: "MaxResumePct") @@ -283,6 +326,7 @@ public struct ServerConfiguration: Codable, Hashable { try values.encodeIfPresent(minAudiobookResume, forKey: "MinAudiobookResume") try values.encodeIfPresent(minResumeDurationSeconds, forKey: "MinResumeDurationSeconds") try values.encodeIfPresent(minResumePct, forKey: "MinResumePct") + try values.encodeIfPresent(parallelImageEncodingLimit, forKey: "ParallelImageEncodingLimit") try values.encodeIfPresent(pathSubstitutions, forKey: "PathSubstitutions") try values.encodeIfPresent(pluginRepositories, forKey: "PluginRepositories") try values.encodeIfPresent(preferredMetadataLanguage, forKey: "PreferredMetadataLanguage") @@ -298,6 +342,7 @@ public struct ServerConfiguration: Codable, Hashable { try values.encodeIfPresent(sortRemoveCharacters, forKey: "SortRemoveCharacters") try values.encodeIfPresent(sortRemoveWords, forKey: "SortRemoveWords") try values.encodeIfPresent(sortReplaceCharacters, forKey: "SortReplaceCharacters") + try values.encodeIfPresent(trickplayOptions, forKey: "TrickplayOptions") try values.encodeIfPresent(uICulture, forKey: "UICulture") } } diff --git a/Sources/Entities/ServerRestartingMessage.swift b/Sources/Entities/ServerRestartingMessage.swift new file mode 100644 index 00000000..78d516b1 --- /dev/null +++ b/Sources/Entities/ServerRestartingMessage.swift @@ -0,0 +1,34 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Server restarting down message. +public struct ServerRestartingMessage: Codable, Hashable { + /// Gets or sets the message id. + public var messageID: String? + /// The different kinds of messages that are used in the WebSocket api. + public var messageType: SessionMessageType? + + public init(messageID: String? = nil, messageType: SessionMessageType? = nil) { + self.messageID = messageID + self.messageType = messageType + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.messageID = try values.decodeIfPresent(String.self, forKey: "MessageId") + self.messageType = try values.decodeIfPresent(SessionMessageType.self, forKey: "MessageType") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(messageID, forKey: "MessageId") + try values.encodeIfPresent(messageType, forKey: "MessageType") + } +} diff --git a/Sources/Entities/ServerShuttingDownMessage.swift b/Sources/Entities/ServerShuttingDownMessage.swift new file mode 100644 index 00000000..780a5676 --- /dev/null +++ b/Sources/Entities/ServerShuttingDownMessage.swift @@ -0,0 +1,34 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Server shutting down message. +public struct ServerShuttingDownMessage: Codable, Hashable { + /// Gets or sets the message id. + public var messageID: String? + /// The different kinds of messages that are used in the WebSocket api. + public var messageType: SessionMessageType? + + public init(messageID: String? = nil, messageType: SessionMessageType? = nil) { + self.messageID = messageID + self.messageType = messageType + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.messageID = try values.decodeIfPresent(String.self, forKey: "MessageId") + self.messageType = try values.decodeIfPresent(SessionMessageType.self, forKey: "MessageType") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(messageID, forKey: "MessageId") + try values.encodeIfPresent(messageType, forKey: "MessageType") + } +} diff --git a/Sources/Entities/SessionInfo.swift b/Sources/Entities/SessionInfo.swift index ccf13e01..31f7e3a1 100644 --- a/Sources/Entities/SessionInfo.swift +++ b/Sources/Entities/SessionInfo.swift @@ -22,8 +22,6 @@ public struct SessionInfo: Codable, Hashable, Identifiable { public var deviceName: String? /// Gets or sets the type of the device. public var deviceType: String? - /// Class BaseItem. - public var fullNowPlayingItem: BaseItem? public var hasCustomDeviceName: Bool? /// Gets or sets the id. public var id: String? @@ -31,9 +29,13 @@ public struct SessionInfo: Codable, Hashable, Identifiable { public var isActive: Bool? /// Gets or sets the last activity date. public var lastActivityDate: Date? + /// Gets or sets the last paused date. + public var lastPausedDate: Date? /// Gets or sets the last playback check in. public var lastPlaybackCheckIn: Date? - /// Gets or sets the now playing item. + /// This is strictly used as a data transfer object from the api layer. + /// + /// This holds information about a BaseItem in a format that is convenient for the client. public var nowPlayingItem: BaseItemDto? public var nowPlayingQueue: [QueueItem]? public var nowPlayingQueueFullItems: [BaseItemDto]? @@ -43,7 +45,7 @@ public struct SessionInfo: Codable, Hashable, Identifiable { public var nowViewingItem: BaseItemDto? public var playState: PlayerStateInfo? /// Gets the playable media types. - public var playableMediaTypes: [String]? + public var playableMediaTypes: [MediaType]? public var playlistItemID: String? /// Gets or sets the remote end point. public var remoteEndPoint: String? @@ -67,18 +69,18 @@ public struct SessionInfo: Codable, Hashable, Identifiable { deviceID: String? = nil, deviceName: String? = nil, deviceType: String? = nil, - fullNowPlayingItem: BaseItem? = nil, hasCustomDeviceName: Bool? = nil, id: String? = nil, isActive: Bool? = nil, lastActivityDate: Date? = nil, + lastPausedDate: Date? = nil, lastPlaybackCheckIn: Date? = nil, nowPlayingItem: BaseItemDto? = nil, nowPlayingQueue: [QueueItem]? = nil, nowPlayingQueueFullItems: [BaseItemDto]? = nil, nowViewingItem: BaseItemDto? = nil, playState: PlayerStateInfo? = nil, - playableMediaTypes: [String]? = nil, + playableMediaTypes: [MediaType]? = nil, playlistItemID: String? = nil, remoteEndPoint: String? = nil, serverID: String? = nil, @@ -97,11 +99,11 @@ public struct SessionInfo: Codable, Hashable, Identifiable { self.deviceID = deviceID self.deviceName = deviceName self.deviceType = deviceType - self.fullNowPlayingItem = fullNowPlayingItem self.hasCustomDeviceName = hasCustomDeviceName self.id = id self.isActive = isActive self.lastActivityDate = lastActivityDate + self.lastPausedDate = lastPausedDate self.lastPlaybackCheckIn = lastPlaybackCheckIn self.nowPlayingItem = nowPlayingItem self.nowPlayingQueue = nowPlayingQueue @@ -130,18 +132,18 @@ public struct SessionInfo: Codable, Hashable, Identifiable { self.deviceID = try values.decodeIfPresent(String.self, forKey: "DeviceId") self.deviceName = try values.decodeIfPresent(String.self, forKey: "DeviceName") self.deviceType = try values.decodeIfPresent(String.self, forKey: "DeviceType") - self.fullNowPlayingItem = try values.decodeIfPresent(BaseItem.self, forKey: "FullNowPlayingItem") self.hasCustomDeviceName = try values.decodeIfPresent(Bool.self, forKey: "HasCustomDeviceName") self.id = try values.decodeIfPresent(String.self, forKey: "Id") self.isActive = try values.decodeIfPresent(Bool.self, forKey: "IsActive") self.lastActivityDate = try values.decodeIfPresent(Date.self, forKey: "LastActivityDate") + self.lastPausedDate = try values.decodeIfPresent(Date.self, forKey: "LastPausedDate") self.lastPlaybackCheckIn = try values.decodeIfPresent(Date.self, forKey: "LastPlaybackCheckIn") self.nowPlayingItem = try values.decodeIfPresent(BaseItemDto.self, forKey: "NowPlayingItem") self.nowPlayingQueue = try values.decodeIfPresent([QueueItem].self, forKey: "NowPlayingQueue") self.nowPlayingQueueFullItems = try values.decodeIfPresent([BaseItemDto].self, forKey: "NowPlayingQueueFullItems") self.nowViewingItem = try values.decodeIfPresent(BaseItemDto.self, forKey: "NowViewingItem") self.playState = try values.decodeIfPresent(PlayerStateInfo.self, forKey: "PlayState") - self.playableMediaTypes = try values.decodeIfPresent([String].self, forKey: "PlayableMediaTypes") + self.playableMediaTypes = try values.decodeIfPresent([MediaType].self, forKey: "PlayableMediaTypes") self.playlistItemID = try values.decodeIfPresent(String.self, forKey: "PlaylistItemId") self.remoteEndPoint = try values.decodeIfPresent(String.self, forKey: "RemoteEndPoint") self.serverID = try values.decodeIfPresent(String.self, forKey: "ServerId") @@ -163,11 +165,11 @@ public struct SessionInfo: Codable, Hashable, Identifiable { try values.encodeIfPresent(deviceID, forKey: "DeviceId") try values.encodeIfPresent(deviceName, forKey: "DeviceName") try values.encodeIfPresent(deviceType, forKey: "DeviceType") - try values.encodeIfPresent(fullNowPlayingItem, forKey: "FullNowPlayingItem") try values.encodeIfPresent(hasCustomDeviceName, forKey: "HasCustomDeviceName") try values.encodeIfPresent(id, forKey: "Id") try values.encodeIfPresent(isActive, forKey: "IsActive") try values.encodeIfPresent(lastActivityDate, forKey: "LastActivityDate") + try values.encodeIfPresent(lastPausedDate, forKey: "LastPausedDate") try values.encodeIfPresent(lastPlaybackCheckIn, forKey: "LastPlaybackCheckIn") try values.encodeIfPresent(nowPlayingItem, forKey: "NowPlayingItem") try values.encodeIfPresent(nowPlayingQueue, forKey: "NowPlayingQueue") diff --git a/Sources/Entities/SessionsMessage.swift b/Sources/Entities/SessionsMessage.swift new file mode 100644 index 00000000..2983eb88 --- /dev/null +++ b/Sources/Entities/SessionsMessage.swift @@ -0,0 +1,39 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Sessions message. +public struct SessionsMessage: Codable, Hashable { + /// Gets or sets the data. + public var data: [SessionInfo]? + /// Gets or sets the message id. + public var messageID: String? + /// The different kinds of messages that are used in the WebSocket api. + public var messageType: SessionMessageType? + + public init(data: [SessionInfo]? = nil, messageID: String? = nil, messageType: SessionMessageType? = nil) { + self.data = data + self.messageID = messageID + self.messageType = messageType + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.data = try values.decodeIfPresent([SessionInfo].self, forKey: "Data") + self.messageID = try values.decodeIfPresent(String.self, forKey: "MessageId") + self.messageType = try values.decodeIfPresent(SessionMessageType.self, forKey: "MessageType") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(data, forKey: "Data") + try values.encodeIfPresent(messageID, forKey: "MessageId") + try values.encodeIfPresent(messageType, forKey: "MessageType") + } +} diff --git a/Sources/Entities/SessionsStartMessage.swift b/Sources/Entities/SessionsStartMessage.swift new file mode 100644 index 00000000..7e7e5136 --- /dev/null +++ b/Sources/Entities/SessionsStartMessage.swift @@ -0,0 +1,36 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Sessions start message. +/// +/// Data is the timing data encoded as "$initialDelay,$interval" in ms. +public struct SessionsStartMessage: Codable, Hashable { + /// Gets or sets the data. + public var data: String? + /// The different kinds of messages that are used in the WebSocket api. + public var messageType: SessionMessageType? + + public init(data: String? = nil, messageType: SessionMessageType? = nil) { + self.data = data + self.messageType = messageType + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.data = try values.decodeIfPresent(String.self, forKey: "Data") + self.messageType = try values.decodeIfPresent(SessionMessageType.self, forKey: "MessageType") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(data, forKey: "Data") + try values.encodeIfPresent(messageType, forKey: "MessageType") + } +} diff --git a/Sources/Entities/SessionsStopMessage.swift b/Sources/Entities/SessionsStopMessage.swift new file mode 100644 index 00000000..b9ee1dd9 --- /dev/null +++ b/Sources/Entities/SessionsStopMessage.swift @@ -0,0 +1,29 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Sessions stop message. +public struct SessionsStopMessage: Codable, Hashable { + /// The different kinds of messages that are used in the WebSocket api. + public var messageType: SessionMessageType? + + public init(messageType: SessionMessageType? = nil) { + self.messageType = messageType + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.messageType = try values.decodeIfPresent(SessionMessageType.self, forKey: "MessageType") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(messageType, forKey: "MessageType") + } +} diff --git a/Sources/Entities/SpecialFeatureType.swift b/Sources/Entities/SpecialFeatureType.swift index 4bcb1ad8..37a3da6d 100644 --- a/Sources/Entities/SpecialFeatureType.swift +++ b/Sources/Entities/SpecialFeatureType.swift @@ -19,4 +19,6 @@ public enum SpecialFeatureType: String, Codable, CaseIterable { case sample = "Sample" case themeSong = "ThemeSong" case themeVideo = "ThemeVideo" + case featurette = "Featurette" + case short = "Short" } diff --git a/Sources/Entities/StringGroupUpdate.swift b/Sources/Entities/StringGroupUpdate.swift new file mode 100644 index 00000000..f7c719f3 --- /dev/null +++ b/Sources/Entities/StringGroupUpdate.swift @@ -0,0 +1,39 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Class GroupUpdate. +public struct StringGroupUpdate: Codable, Hashable { + /// Gets the update data. + public var data: String? + /// Gets the group identifier. + public var groupID: String? + /// Gets the update type. + public var type: GroupUpdateType? + + public init(data: String? = nil, groupID: String? = nil, type: GroupUpdateType? = nil) { + self.data = data + self.groupID = groupID + self.type = type + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.data = try values.decodeIfPresent(String.self, forKey: "Data") + self.groupID = try values.decodeIfPresent(String.self, forKey: "GroupId") + self.type = try values.decodeIfPresent(GroupUpdateType.self, forKey: "Type") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(data, forKey: "Data") + try values.encodeIfPresent(groupID, forKey: "GroupId") + try values.encodeIfPresent(type, forKey: "Type") + } +} diff --git a/Sources/Entities/SyncPlayCommandMessage.swift b/Sources/Entities/SyncPlayCommandMessage.swift new file mode 100644 index 00000000..5add5371 --- /dev/null +++ b/Sources/Entities/SyncPlayCommandMessage.swift @@ -0,0 +1,39 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Sync play command. +public struct SyncPlayCommandMessage: Codable, Hashable { + /// Class SendCommand. + public var data: SendCommand? + /// Gets or sets the message id. + public var messageID: String? + /// The different kinds of messages that are used in the WebSocket api. + public var messageType: SessionMessageType? + + public init(data: SendCommand? = nil, messageID: String? = nil, messageType: SessionMessageType? = nil) { + self.data = data + self.messageID = messageID + self.messageType = messageType + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.data = try values.decodeIfPresent(SendCommand.self, forKey: "Data") + self.messageID = try values.decodeIfPresent(String.self, forKey: "MessageId") + self.messageType = try values.decodeIfPresent(SessionMessageType.self, forKey: "MessageType") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(data, forKey: "Data") + try values.encodeIfPresent(messageID, forKey: "MessageId") + try values.encodeIfPresent(messageType, forKey: "MessageType") + } +} diff --git a/Sources/Entities/SyncPlayGroupUpdateCommandMessage.swift b/Sources/Entities/SyncPlayGroupUpdateCommandMessage.swift new file mode 100644 index 00000000..f435d7b4 --- /dev/null +++ b/Sources/Entities/SyncPlayGroupUpdateCommandMessage.swift @@ -0,0 +1,39 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Untyped sync play command. +public struct SyncPlayGroupUpdateCommandMessage: Codable, Hashable { + /// Group update without data. + public var data: GroupUpdate? + /// Gets or sets the message id. + public var messageID: String? + /// The different kinds of messages that are used in the WebSocket api. + public var messageType: SessionMessageType? + + public init(data: GroupUpdate? = nil, messageID: String? = nil, messageType: SessionMessageType? = nil) { + self.data = data + self.messageID = messageID + self.messageType = messageType + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.data = try values.decodeIfPresent(GroupUpdate.self, forKey: "Data") + self.messageID = try values.decodeIfPresent(String.self, forKey: "MessageId") + self.messageType = try values.decodeIfPresent(SessionMessageType.self, forKey: "MessageType") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(data, forKey: "Data") + try values.encodeIfPresent(messageID, forKey: "MessageId") + try values.encodeIfPresent(messageType, forKey: "MessageType") + } +} diff --git a/Sources/Entities/SyncPlayQueueItem.swift b/Sources/Entities/SyncPlayQueueItem.swift new file mode 100644 index 00000000..72be5e0d --- /dev/null +++ b/Sources/Entities/SyncPlayQueueItem.swift @@ -0,0 +1,34 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Class QueueItem. +public struct SyncPlayQueueItem: Codable, Hashable { + /// Gets the item identifier. + public var itemID: String? + /// Gets the playlist identifier of the item. + public var playlistItemID: String? + + public init(itemID: String? = nil, playlistItemID: String? = nil) { + self.itemID = itemID + self.playlistItemID = playlistItemID + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.itemID = try values.decodeIfPresent(String.self, forKey: "ItemId") + self.playlistItemID = try values.decodeIfPresent(String.self, forKey: "PlaylistItemId") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(itemID, forKey: "ItemId") + try values.encodeIfPresent(playlistItemID, forKey: "PlaylistItemId") + } +} diff --git a/Sources/Entities/SystemInfo.swift b/Sources/Entities/SystemInfo.swift index 9e60ef93..d67e8679 100644 --- a/Sources/Entities/SystemInfo.swift +++ b/Sources/Entities/SystemInfo.swift @@ -12,21 +12,24 @@ import Foundation public struct SystemInfo: Codable, Hashable, Identifiable { /// Gets or sets the cache path. public var cachePath: String? - public var canLaunchWebBrowser: Bool? + /// - warning: Deprecated. + public var canLaunchWebBrowser: Bool /// Gets or sets a value indicating whether this instance can self restart. - public var canSelfRestart: Bool? - /// Gets or sets the completed installations. - public var completedInstallations: [InstallationInfo]? - /// Enum describing the location of the FFmpeg tool. /// /// - warning: Deprecated. - public var encoderLocation: FFmpegLocation? + public var canSelfRestart: Bool + /// Gets or sets the list of cast receiver applications. + public var castReceiverApplications: [CastReceiverApplication]? + /// Gets or sets the completed installations. + public var completedInstallations: [InstallationInfo]? + /// - warning: Deprecated. + public var encoderLocation: String? /// Gets or sets a value indicating whether this instance has pending restart. public var hasPendingRestart: Bool? /// Gets or sets a value indicating whether this instance has update available. /// /// - warning: Deprecated. - public var hasUpdateAvailable: Bool? + public var hasUpdateAvailable: Bool /// Gets or sets the id. public var id: String? /// Gets or sets the internal metadata path. @@ -39,8 +42,12 @@ public struct SystemInfo: Codable, Hashable, Identifiable { /// Gets or sets the log path. public var logPath: String? /// Gets or sets the operating system. + /// + /// - warning: Deprecated. public var operatingSystem: String? /// Gets or sets the display name of the operating system. + /// + /// - warning: Deprecated. public var operatingSystemDisplayName: String? /// Gets or sets the package name. public var packageName: String? @@ -54,7 +61,8 @@ public struct SystemInfo: Codable, Hashable, Identifiable { public var isStartupWizardCompleted: Bool? /// Gets or sets a value indicating whether [supports library monitor]. public var isSupportsLibraryMonitor: Bool? - public var systemArchitecture: Architecture? + /// - warning: Deprecated. + public var systemArchitecture: String? /// Gets or sets the transcode path. public var transcodingTempPath: String? /// Gets or sets the server version. @@ -68,8 +76,9 @@ public struct SystemInfo: Codable, Hashable, Identifiable { cachePath: String? = nil, canLaunchWebBrowser: Bool? = nil, canSelfRestart: Bool? = nil, + castReceiverApplications: [CastReceiverApplication]? = nil, completedInstallations: [InstallationInfo]? = nil, - encoderLocation: FFmpegLocation? = nil, + encoderLocation: String? = nil, hasPendingRestart: Bool? = nil, hasUpdateAvailable: Bool? = nil, id: String? = nil, @@ -86,19 +95,20 @@ public struct SystemInfo: Codable, Hashable, Identifiable { serverName: String? = nil, isStartupWizardCompleted: Bool? = nil, isSupportsLibraryMonitor: Bool? = nil, - systemArchitecture: Architecture? = nil, + systemArchitecture: String? = nil, transcodingTempPath: String? = nil, version: String? = nil, webPath: String? = nil, webSocketPortNumber: Int? = nil ) { self.cachePath = cachePath - self.canLaunchWebBrowser = canLaunchWebBrowser - self.canSelfRestart = canSelfRestart + self.canLaunchWebBrowser = canLaunchWebBrowser ?? false + self.canSelfRestart = canSelfRestart ?? true + self.castReceiverApplications = castReceiverApplications self.completedInstallations = completedInstallations self.encoderLocation = encoderLocation self.hasPendingRestart = hasPendingRestart - self.hasUpdateAvailable = hasUpdateAvailable + self.hasUpdateAvailable = hasUpdateAvailable ?? false self.id = id self.internalMetadataPath = internalMetadataPath self.isShuttingDown = isShuttingDown @@ -123,12 +133,13 @@ public struct SystemInfo: Codable, Hashable, Identifiable { public init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: StringCodingKey.self) self.cachePath = try values.decodeIfPresent(String.self, forKey: "CachePath") - self.canLaunchWebBrowser = try values.decodeIfPresent(Bool.self, forKey: "CanLaunchWebBrowser") - self.canSelfRestart = try values.decodeIfPresent(Bool.self, forKey: "CanSelfRestart") + self.canLaunchWebBrowser = try values.decodeIfPresent(Bool.self, forKey: "CanLaunchWebBrowser") ?? false + self.canSelfRestart = try values.decodeIfPresent(Bool.self, forKey: "CanSelfRestart") ?? true + self.castReceiverApplications = try values.decodeIfPresent([CastReceiverApplication].self, forKey: "CastReceiverApplications") self.completedInstallations = try values.decodeIfPresent([InstallationInfo].self, forKey: "CompletedInstallations") - self.encoderLocation = try values.decodeIfPresent(FFmpegLocation.self, forKey: "EncoderLocation") + self.encoderLocation = try values.decodeIfPresent(String.self, forKey: "EncoderLocation") self.hasPendingRestart = try values.decodeIfPresent(Bool.self, forKey: "HasPendingRestart") - self.hasUpdateAvailable = try values.decodeIfPresent(Bool.self, forKey: "HasUpdateAvailable") + self.hasUpdateAvailable = try values.decodeIfPresent(Bool.self, forKey: "HasUpdateAvailable") ?? false self.id = try values.decodeIfPresent(String.self, forKey: "Id") self.internalMetadataPath = try values.decodeIfPresent(String.self, forKey: "InternalMetadataPath") self.isShuttingDown = try values.decodeIfPresent(Bool.self, forKey: "IsShuttingDown") @@ -143,7 +154,7 @@ public struct SystemInfo: Codable, Hashable, Identifiable { self.serverName = try values.decodeIfPresent(String.self, forKey: "ServerName") self.isStartupWizardCompleted = try values.decodeIfPresent(Bool.self, forKey: "StartupWizardCompleted") self.isSupportsLibraryMonitor = try values.decodeIfPresent(Bool.self, forKey: "SupportsLibraryMonitor") - self.systemArchitecture = try values.decodeIfPresent(Architecture.self, forKey: "SystemArchitecture") + self.systemArchitecture = try values.decodeIfPresent(String.self, forKey: "SystemArchitecture") self.transcodingTempPath = try values.decodeIfPresent(String.self, forKey: "TranscodingTempPath") self.version = try values.decodeIfPresent(String.self, forKey: "Version") self.webPath = try values.decodeIfPresent(String.self, forKey: "WebPath") @@ -155,6 +166,7 @@ public struct SystemInfo: Codable, Hashable, Identifiable { try values.encodeIfPresent(cachePath, forKey: "CachePath") try values.encodeIfPresent(canLaunchWebBrowser, forKey: "CanLaunchWebBrowser") try values.encodeIfPresent(canSelfRestart, forKey: "CanSelfRestart") + try values.encodeIfPresent(castReceiverApplications, forKey: "CastReceiverApplications") try values.encodeIfPresent(completedInstallations, forKey: "CompletedInstallations") try values.encodeIfPresent(encoderLocation, forKey: "EncoderLocation") try values.encodeIfPresent(hasPendingRestart, forKey: "HasPendingRestart") diff --git a/Sources/Entities/TimerCancelledMessage.swift b/Sources/Entities/TimerCancelledMessage.swift new file mode 100644 index 00000000..f183c6af --- /dev/null +++ b/Sources/Entities/TimerCancelledMessage.swift @@ -0,0 +1,39 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Timer cancelled message. +public struct TimerCancelledMessage: Codable, Hashable { + /// Gets or sets the data. + public var data: TimerEventInfo? + /// Gets or sets the message id. + public var messageID: String? + /// The different kinds of messages that are used in the WebSocket api. + public var messageType: SessionMessageType? + + public init(data: TimerEventInfo? = nil, messageID: String? = nil, messageType: SessionMessageType? = nil) { + self.data = data + self.messageID = messageID + self.messageType = messageType + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.data = try values.decodeIfPresent(TimerEventInfo.self, forKey: "Data") + self.messageID = try values.decodeIfPresent(String.self, forKey: "MessageId") + self.messageType = try values.decodeIfPresent(SessionMessageType.self, forKey: "MessageType") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(data, forKey: "Data") + try values.encodeIfPresent(messageID, forKey: "MessageId") + try values.encodeIfPresent(messageType, forKey: "MessageType") + } +} diff --git a/Sources/Entities/TimerCreatedMessage.swift b/Sources/Entities/TimerCreatedMessage.swift new file mode 100644 index 00000000..3748a1c4 --- /dev/null +++ b/Sources/Entities/TimerCreatedMessage.swift @@ -0,0 +1,39 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Timer created message. +public struct TimerCreatedMessage: Codable, Hashable { + /// Gets or sets the data. + public var data: TimerEventInfo? + /// Gets or sets the message id. + public var messageID: String? + /// The different kinds of messages that are used in the WebSocket api. + public var messageType: SessionMessageType? + + public init(data: TimerEventInfo? = nil, messageID: String? = nil, messageType: SessionMessageType? = nil) { + self.data = data + self.messageID = messageID + self.messageType = messageType + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.data = try values.decodeIfPresent(TimerEventInfo.self, forKey: "Data") + self.messageID = try values.decodeIfPresent(String.self, forKey: "MessageId") + self.messageType = try values.decodeIfPresent(SessionMessageType.self, forKey: "MessageType") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(data, forKey: "Data") + try values.encodeIfPresent(messageID, forKey: "MessageId") + try values.encodeIfPresent(messageType, forKey: "MessageType") + } +} diff --git a/Sources/Entities/TranscodingProfile.swift b/Sources/Entities/TranscodingProfile.swift index ce5f6dac..15985ae9 100644 --- a/Sources/Entities/TranscodingProfile.swift +++ b/Sources/Entities/TranscodingProfile.swift @@ -20,7 +20,10 @@ public struct TranscodingProfile: Codable, Hashable { public var isEstimateContentLength: Bool public var maxAudioChannels: String? public var minSegments: Int? - public var `protocol`: String? + /// Media streaming protocol. + /// + /// Lowercase for backwards compatibility. + public var `protocol`: MediaStreamProtocol? public var segmentLength: Int? public var transcodeSeekInfo: TranscodeSeekInfo? public var type: DlnaProfileType? @@ -38,7 +41,7 @@ public struct TranscodingProfile: Codable, Hashable { isEstimateContentLength: Bool? = nil, maxAudioChannels: String? = nil, minSegments: Int? = nil, - protocol: String? = nil, + protocol: MediaStreamProtocol? = nil, segmentLength: Int? = nil, transcodeSeekInfo: TranscodeSeekInfo? = nil, type: DlnaProfileType? = nil, @@ -75,7 +78,7 @@ public struct TranscodingProfile: Codable, Hashable { self.isEstimateContentLength = try values.decodeIfPresent(Bool.self, forKey: "EstimateContentLength") ?? false self.maxAudioChannels = try values.decodeIfPresent(String.self, forKey: "MaxAudioChannels") self.minSegments = try values.decodeIfPresent(Int.self, forKey: "MinSegments") - self.protocol = try values.decodeIfPresent(String.self, forKey: "Protocol") + self.protocol = try values.decodeIfPresent(MediaStreamProtocol.self, forKey: "Protocol") self.segmentLength = try values.decodeIfPresent(Int.self, forKey: "SegmentLength") self.transcodeSeekInfo = try values.decodeIfPresent(TranscodeSeekInfo.self, forKey: "TranscodeSeekInfo") self.type = try values.decodeIfPresent(DlnaProfileType.self, forKey: "Type") diff --git a/Sources/Entities/TrickplayInfo.swift b/Sources/Entities/TrickplayInfo.swift new file mode 100644 index 00000000..abc32bff --- /dev/null +++ b/Sources/Entities/TrickplayInfo.swift @@ -0,0 +1,67 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// An entity representing the metadata for a group of trickplay tiles. +public struct TrickplayInfo: Codable, Hashable { + /// Gets or sets peak bandwith usage in bits per second. + public var bandwidth: Int? + /// Gets or sets height of an individual thumbnail. + public var height: Int? + /// Gets or sets interval in milliseconds between each trickplay thumbnail. + public var interval: Int? + /// Gets or sets total amount of non-black thumbnails. + public var thumbnailCount: Int? + /// Gets or sets amount of thumbnails per column. + public var tileHeight: Int? + /// Gets or sets amount of thumbnails per row. + public var tileWidth: Int? + /// Gets or sets width of an individual thumbnail. + public var width: Int? + + public init( + bandwidth: Int? = nil, + height: Int? = nil, + interval: Int? = nil, + thumbnailCount: Int? = nil, + tileHeight: Int? = nil, + tileWidth: Int? = nil, + width: Int? = nil + ) { + self.bandwidth = bandwidth + self.height = height + self.interval = interval + self.thumbnailCount = thumbnailCount + self.tileHeight = tileHeight + self.tileWidth = tileWidth + self.width = width + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.bandwidth = try values.decodeIfPresent(Int.self, forKey: "Bandwidth") + self.height = try values.decodeIfPresent(Int.self, forKey: "Height") + self.interval = try values.decodeIfPresent(Int.self, forKey: "Interval") + self.thumbnailCount = try values.decodeIfPresent(Int.self, forKey: "ThumbnailCount") + self.tileHeight = try values.decodeIfPresent(Int.self, forKey: "TileHeight") + self.tileWidth = try values.decodeIfPresent(Int.self, forKey: "TileWidth") + self.width = try values.decodeIfPresent(Int.self, forKey: "Width") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(bandwidth, forKey: "Bandwidth") + try values.encodeIfPresent(height, forKey: "Height") + try values.encodeIfPresent(interval, forKey: "Interval") + try values.encodeIfPresent(thumbnailCount, forKey: "ThumbnailCount") + try values.encodeIfPresent(tileHeight, forKey: "TileHeight") + try values.encodeIfPresent(tileWidth, forKey: "TileWidth") + try values.encodeIfPresent(width, forKey: "Width") + } +} diff --git a/Sources/Entities/TrickplayOptions.swift b/Sources/Entities/TrickplayOptions.swift new file mode 100644 index 00000000..733dab3b --- /dev/null +++ b/Sources/Entities/TrickplayOptions.swift @@ -0,0 +1,91 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Class TrickplayOptions. +public struct TrickplayOptions: Codable, Hashable { + /// Gets or sets a value indicating whether or not to use HW acceleration. + public var enableHwAcceleration: Bool? + /// Gets or sets a value indicating whether or not to use HW accelerated MJPEG encoding. + public var enableHwEncoding: Bool? + /// Gets or sets the interval, in ms, between each new trickplay image. + public var interval: Int? + /// Gets or sets the jpeg quality to use for image tiles. + public var jpegQuality: Int? + /// Gets or sets the process priority for the ffmpeg process. + public var processPriority: ProcessPriorityClass? + /// Gets or sets the number of threads to be used by ffmpeg. + public var processThreads: Int? + /// Gets or sets the ffmpeg output quality level. + public var qscale: Int? + /// Gets or sets the behavior used by trickplay provider on library scan/update. + public var scanBehavior: TrickplayScanBehavior? + /// Gets or sets number of tile images to allow in Y dimension. + public var tileHeight: Int? + /// Gets or sets number of tile images to allow in X dimension. + public var tileWidth: Int? + /// Gets or sets the target width resolutions, in px, to generates preview images for. + public var widthResolutions: [Int]? + + public init( + enableHwAcceleration: Bool? = nil, + enableHwEncoding: Bool? = nil, + interval: Int? = nil, + jpegQuality: Int? = nil, + processPriority: ProcessPriorityClass? = nil, + processThreads: Int? = nil, + qscale: Int? = nil, + scanBehavior: TrickplayScanBehavior? = nil, + tileHeight: Int? = nil, + tileWidth: Int? = nil, + widthResolutions: [Int]? = nil + ) { + self.enableHwAcceleration = enableHwAcceleration + self.enableHwEncoding = enableHwEncoding + self.interval = interval + self.jpegQuality = jpegQuality + self.processPriority = processPriority + self.processThreads = processThreads + self.qscale = qscale + self.scanBehavior = scanBehavior + self.tileHeight = tileHeight + self.tileWidth = tileWidth + self.widthResolutions = widthResolutions + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.enableHwAcceleration = try values.decodeIfPresent(Bool.self, forKey: "EnableHwAcceleration") + self.enableHwEncoding = try values.decodeIfPresent(Bool.self, forKey: "EnableHwEncoding") + self.interval = try values.decodeIfPresent(Int.self, forKey: "Interval") + self.jpegQuality = try values.decodeIfPresent(Int.self, forKey: "JpegQuality") + self.processPriority = try values.decodeIfPresent(ProcessPriorityClass.self, forKey: "ProcessPriority") + self.processThreads = try values.decodeIfPresent(Int.self, forKey: "ProcessThreads") + self.qscale = try values.decodeIfPresent(Int.self, forKey: "Qscale") + self.scanBehavior = try values.decodeIfPresent(TrickplayScanBehavior.self, forKey: "ScanBehavior") + self.tileHeight = try values.decodeIfPresent(Int.self, forKey: "TileHeight") + self.tileWidth = try values.decodeIfPresent(Int.self, forKey: "TileWidth") + self.widthResolutions = try values.decodeIfPresent([Int].self, forKey: "WidthResolutions") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(enableHwAcceleration, forKey: "EnableHwAcceleration") + try values.encodeIfPresent(enableHwEncoding, forKey: "EnableHwEncoding") + try values.encodeIfPresent(interval, forKey: "Interval") + try values.encodeIfPresent(jpegQuality, forKey: "JpegQuality") + try values.encodeIfPresent(processPriority, forKey: "ProcessPriority") + try values.encodeIfPresent(processThreads, forKey: "ProcessThreads") + try values.encodeIfPresent(qscale, forKey: "Qscale") + try values.encodeIfPresent(scanBehavior, forKey: "ScanBehavior") + try values.encodeIfPresent(tileHeight, forKey: "TileHeight") + try values.encodeIfPresent(tileWidth, forKey: "TileWidth") + try values.encodeIfPresent(widthResolutions, forKey: "WidthResolutions") + } +} diff --git a/Sources/Entities/TrickplayScanBehavior.swift b/Sources/Entities/TrickplayScanBehavior.swift new file mode 100644 index 00000000..620db5d1 --- /dev/null +++ b/Sources/Entities/TrickplayScanBehavior.swift @@ -0,0 +1,15 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Enum TrickplayScanBehavior. +public enum TrickplayScanBehavior: String, Codable, CaseIterable { + case blocking = "Blocking" + case nonBlocking = "NonBlocking" +} diff --git a/Sources/Entities/TunerHostInfo.swift b/Sources/Entities/TunerHostInfo.swift index 094a4f49..b424ef09 100644 --- a/Sources/Entities/TunerHostInfo.swift +++ b/Sources/Entities/TunerHostInfo.swift @@ -14,6 +14,7 @@ public struct TunerHostInfo: Codable, Hashable, Identifiable { public var enableStreamLooping: Bool? public var friendlyName: String? public var id: String? + public var isIgnoreDts: Bool? public var isImportFavoritesOnly: Bool? public var source: String? public var tunerCount: Int? @@ -27,6 +28,7 @@ public struct TunerHostInfo: Codable, Hashable, Identifiable { enableStreamLooping: Bool? = nil, friendlyName: String? = nil, id: String? = nil, + isIgnoreDts: Bool? = nil, isImportFavoritesOnly: Bool? = nil, source: String? = nil, tunerCount: Int? = nil, @@ -39,6 +41,7 @@ public struct TunerHostInfo: Codable, Hashable, Identifiable { self.enableStreamLooping = enableStreamLooping self.friendlyName = friendlyName self.id = id + self.isIgnoreDts = isIgnoreDts self.isImportFavoritesOnly = isImportFavoritesOnly self.source = source self.tunerCount = tunerCount @@ -54,6 +57,7 @@ public struct TunerHostInfo: Codable, Hashable, Identifiable { self.enableStreamLooping = try values.decodeIfPresent(Bool.self, forKey: "EnableStreamLooping") self.friendlyName = try values.decodeIfPresent(String.self, forKey: "FriendlyName") self.id = try values.decodeIfPresent(String.self, forKey: "Id") + self.isIgnoreDts = try values.decodeIfPresent(Bool.self, forKey: "IgnoreDts") self.isImportFavoritesOnly = try values.decodeIfPresent(Bool.self, forKey: "ImportFavoritesOnly") self.source = try values.decodeIfPresent(String.self, forKey: "Source") self.tunerCount = try values.decodeIfPresent(Int.self, forKey: "TunerCount") @@ -69,6 +73,7 @@ public struct TunerHostInfo: Codable, Hashable, Identifiable { try values.encodeIfPresent(enableStreamLooping, forKey: "EnableStreamLooping") try values.encodeIfPresent(friendlyName, forKey: "FriendlyName") try values.encodeIfPresent(id, forKey: "Id") + try values.encodeIfPresent(isIgnoreDts, forKey: "IgnoreDts") try values.encodeIfPresent(isImportFavoritesOnly, forKey: "ImportFavoritesOnly") try values.encodeIfPresent(source, forKey: "Source") try values.encodeIfPresent(tunerCount, forKey: "TunerCount") diff --git a/Sources/Entities/UpdatePlaylistDto.swift b/Sources/Entities/UpdatePlaylistDto.swift new file mode 100644 index 00000000..d8507bd6 --- /dev/null +++ b/Sources/Entities/UpdatePlaylistDto.swift @@ -0,0 +1,44 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Update existing playlist dto. Fields set to `null` will not be updated and keep their current values. +public struct UpdatePlaylistDto: Codable, Hashable { + /// Gets or sets item ids of the playlist. + public var ids: [String]? + /// Gets or sets a value indicating whether the playlist is public. + public var isPublic: Bool? + /// Gets or sets the name of the new playlist. + public var name: String? + /// Gets or sets the playlist users. + public var users: [PlaylistUserPermissions]? + + public init(ids: [String]? = nil, isPublic: Bool? = nil, name: String? = nil, users: [PlaylistUserPermissions]? = nil) { + self.ids = ids + self.isPublic = isPublic + self.name = name + self.users = users + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.ids = try values.decodeIfPresent([String].self, forKey: "Ids") + self.isPublic = try values.decodeIfPresent(Bool.self, forKey: "IsPublic") + self.name = try values.decodeIfPresent(String.self, forKey: "Name") + self.users = try values.decodeIfPresent([PlaylistUserPermissions].self, forKey: "Users") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(ids, forKey: "Ids") + try values.encodeIfPresent(isPublic, forKey: "IsPublic") + try values.encodeIfPresent(name, forKey: "Name") + try values.encodeIfPresent(users, forKey: "Users") + } +} diff --git a/Sources/Entities/UpdatePlaylistUserDto.swift b/Sources/Entities/UpdatePlaylistUserDto.swift new file mode 100644 index 00000000..c045aa4b --- /dev/null +++ b/Sources/Entities/UpdatePlaylistUserDto.swift @@ -0,0 +1,29 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Update existing playlist user dto. Fields set to `null` will not be updated and keep their current values. +public struct UpdatePlaylistUserDto: Codable, Hashable { + /// Gets or sets a value indicating whether the user can edit the playlist. + public var canEdit: Bool? + + public init(canEdit: Bool? = nil) { + self.canEdit = canEdit + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.canEdit = try values.decodeIfPresent(Bool.self, forKey: "CanEdit") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(canEdit, forKey: "CanEdit") + } +} diff --git a/Sources/Entities/UpdateUserItemDataDto.swift b/Sources/Entities/UpdateUserItemDataDto.swift new file mode 100644 index 00000000..07e298ea --- /dev/null +++ b/Sources/Entities/UpdateUserItemDataDto.swift @@ -0,0 +1,91 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// This is used by the api to get information about a item user data. +public struct UpdateUserItemDataDto: Codable, Hashable { + /// Gets or sets a value indicating whether this instance is favorite. + public var isFavorite: Bool? + /// Gets or sets the item identifier. + public var itemID: String? + /// Gets or sets the key. + public var key: String? + /// Gets or sets the last played date. + public var lastPlayedDate: Date? + /// Gets or sets a value indicating whether this MediaBrowser.Model.Dto.UpdateUserItemDataDto is likes. + public var isLikes: Bool? + /// Gets or sets the play count. + public var playCount: Int? + /// Gets or sets the playback position ticks. + public var playbackPositionTicks: Int? + /// Gets or sets a value indicating whether this MediaBrowser.Model.Dto.UserItemDataDto is played. + public var isPlayed: Bool? + /// Gets or sets the played percentage. + public var playedPercentage: Double? + /// Gets or sets the rating. + public var rating: Double? + /// Gets or sets the unplayed item count. + public var unplayedItemCount: Int? + + public init( + isFavorite: Bool? = nil, + itemID: String? = nil, + key: String? = nil, + lastPlayedDate: Date? = nil, + isLikes: Bool? = nil, + playCount: Int? = nil, + playbackPositionTicks: Int? = nil, + isPlayed: Bool? = nil, + playedPercentage: Double? = nil, + rating: Double? = nil, + unplayedItemCount: Int? = nil + ) { + self.isFavorite = isFavorite + self.itemID = itemID + self.key = key + self.lastPlayedDate = lastPlayedDate + self.isLikes = isLikes + self.playCount = playCount + self.playbackPositionTicks = playbackPositionTicks + self.isPlayed = isPlayed + self.playedPercentage = playedPercentage + self.rating = rating + self.unplayedItemCount = unplayedItemCount + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.isFavorite = try values.decodeIfPresent(Bool.self, forKey: "IsFavorite") + self.itemID = try values.decodeIfPresent(String.self, forKey: "ItemId") + self.key = try values.decodeIfPresent(String.self, forKey: "Key") + self.lastPlayedDate = try values.decodeIfPresent(Date.self, forKey: "LastPlayedDate") + self.isLikes = try values.decodeIfPresent(Bool.self, forKey: "Likes") + self.playCount = try values.decodeIfPresent(Int.self, forKey: "PlayCount") + self.playbackPositionTicks = try values.decodeIfPresent(Int.self, forKey: "PlaybackPositionTicks") + self.isPlayed = try values.decodeIfPresent(Bool.self, forKey: "Played") + self.playedPercentage = try values.decodeIfPresent(Double.self, forKey: "PlayedPercentage") + self.rating = try values.decodeIfPresent(Double.self, forKey: "Rating") + self.unplayedItemCount = try values.decodeIfPresent(Int.self, forKey: "UnplayedItemCount") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(isFavorite, forKey: "IsFavorite") + try values.encodeIfPresent(itemID, forKey: "ItemId") + try values.encodeIfPresent(key, forKey: "Key") + try values.encodeIfPresent(lastPlayedDate, forKey: "LastPlayedDate") + try values.encodeIfPresent(isLikes, forKey: "Likes") + try values.encodeIfPresent(playCount, forKey: "PlayCount") + try values.encodeIfPresent(playbackPositionTicks, forKey: "PlaybackPositionTicks") + try values.encodeIfPresent(isPlayed, forKey: "Played") + try values.encodeIfPresent(playedPercentage, forKey: "PlayedPercentage") + try values.encodeIfPresent(rating, forKey: "Rating") + try values.encodeIfPresent(unplayedItemCount, forKey: "UnplayedItemCount") + } +} diff --git a/Sources/Entities/UploadSubtitleDto.swift b/Sources/Entities/UploadSubtitleDto.swift index 4ed6e8f0..b5dcf9c9 100644 --- a/Sources/Entities/UploadSubtitleDto.swift +++ b/Sources/Entities/UploadSubtitleDto.swift @@ -16,13 +16,16 @@ public struct UploadSubtitleDto: Codable, Hashable { public var format: String /// Gets or sets a value indicating whether the subtitle is forced. public var isForced: Bool + /// Gets or sets a value indicating whether the subtitle is for hearing impaired. + public var isHearingImpaired: Bool /// Gets or sets the subtitle language. public var language: String - public init(data: String, format: String, isForced: Bool, language: String) { + public init(data: String, format: String, isForced: Bool, isHearingImpaired: Bool, language: String) { self.data = data self.format = format self.isForced = isForced + self.isHearingImpaired = isHearingImpaired self.language = language } @@ -31,6 +34,7 @@ public struct UploadSubtitleDto: Codable, Hashable { self.data = try values.decode(String.self, forKey: "Data") self.format = try values.decode(String.self, forKey: "Format") self.isForced = try values.decode(Bool.self, forKey: "IsForced") + self.isHearingImpaired = try values.decode(Bool.self, forKey: "IsHearingImpaired") self.language = try values.decode(String.self, forKey: "Language") } @@ -39,6 +43,7 @@ public struct UploadSubtitleDto: Codable, Hashable { try values.encode(data, forKey: "Data") try values.encode(format, forKey: "Format") try values.encode(isForced, forKey: "IsForced") + try values.encode(isHearingImpaired, forKey: "IsHearingImpaired") try values.encode(language, forKey: "Language") } } diff --git a/Sources/Entities/UserConfiguration.swift b/Sources/Entities/UserConfiguration.swift index f2658d9c..db99f510 100644 --- a/Sources/Entities/UserConfiguration.swift +++ b/Sources/Entities/UserConfiguration.swift @@ -12,6 +12,8 @@ import Foundation public struct UserConfiguration: Codable, Hashable { /// Gets or sets the audio language preference. public var audioLanguagePreference: String? + /// Gets or sets the id of the selected cast receiver. + public var castReceiverID: String? public var isDisplayCollectionsView: Bool? public var isDisplayMissingEpisodes: Bool? public var enableLocalPassword: Bool? @@ -32,6 +34,7 @@ public struct UserConfiguration: Codable, Hashable { public init( audioLanguagePreference: String? = nil, + castReceiverID: String? = nil, isDisplayCollectionsView: Bool? = nil, isDisplayMissingEpisodes: Bool? = nil, enableLocalPassword: Bool? = nil, @@ -48,6 +51,7 @@ public struct UserConfiguration: Codable, Hashable { subtitleMode: SubtitlePlaybackMode? = nil ) { self.audioLanguagePreference = audioLanguagePreference + self.castReceiverID = castReceiverID self.isDisplayCollectionsView = isDisplayCollectionsView self.isDisplayMissingEpisodes = isDisplayMissingEpisodes self.enableLocalPassword = enableLocalPassword @@ -67,6 +71,7 @@ public struct UserConfiguration: Codable, Hashable { public init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: StringCodingKey.self) self.audioLanguagePreference = try values.decodeIfPresent(String.self, forKey: "AudioLanguagePreference") + self.castReceiverID = try values.decodeIfPresent(String.self, forKey: "CastReceiverId") self.isDisplayCollectionsView = try values.decodeIfPresent(Bool.self, forKey: "DisplayCollectionsView") self.isDisplayMissingEpisodes = try values.decodeIfPresent(Bool.self, forKey: "DisplayMissingEpisodes") self.enableLocalPassword = try values.decodeIfPresent(Bool.self, forKey: "EnableLocalPassword") @@ -86,6 +91,7 @@ public struct UserConfiguration: Codable, Hashable { public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: StringCodingKey.self) try values.encodeIfPresent(audioLanguagePreference, forKey: "AudioLanguagePreference") + try values.encodeIfPresent(castReceiverID, forKey: "CastReceiverId") try values.encodeIfPresent(isDisplayCollectionsView, forKey: "DisplayCollectionsView") try values.encodeIfPresent(isDisplayMissingEpisodes, forKey: "DisplayMissingEpisodes") try values.encodeIfPresent(enableLocalPassword, forKey: "EnableLocalPassword") diff --git a/Sources/Entities/UserDataChangeInfo.swift b/Sources/Entities/UserDataChangeInfo.swift new file mode 100644 index 00000000..2368e60b --- /dev/null +++ b/Sources/Entities/UserDataChangeInfo.swift @@ -0,0 +1,34 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Class UserDataChangeInfo. +public struct UserDataChangeInfo: Codable, Hashable { + /// Gets or sets the user data list. + public var userDataList: [UserItemDataDto]? + /// Gets or sets the user id. + public var userID: String? + + public init(userDataList: [UserItemDataDto]? = nil, userID: String? = nil) { + self.userDataList = userDataList + self.userID = userID + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.userDataList = try values.decodeIfPresent([UserItemDataDto].self, forKey: "UserDataList") + self.userID = try values.decodeIfPresent(String.self, forKey: "UserId") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(userDataList, forKey: "UserDataList") + try values.encodeIfPresent(userID, forKey: "UserId") + } +} diff --git a/Sources/Entities/UserDataChangedMessage.swift b/Sources/Entities/UserDataChangedMessage.swift new file mode 100644 index 00000000..b7bde2f1 --- /dev/null +++ b/Sources/Entities/UserDataChangedMessage.swift @@ -0,0 +1,39 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// User data changed message. +public struct UserDataChangedMessage: Codable, Hashable { + /// Class UserDataChangeInfo. + public var data: UserDataChangeInfo? + /// Gets or sets the message id. + public var messageID: String? + /// The different kinds of messages that are used in the WebSocket api. + public var messageType: SessionMessageType? + + public init(data: UserDataChangeInfo? = nil, messageID: String? = nil, messageType: SessionMessageType? = nil) { + self.data = data + self.messageID = messageID + self.messageType = messageType + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.data = try values.decodeIfPresent(UserDataChangeInfo.self, forKey: "Data") + self.messageID = try values.decodeIfPresent(String.self, forKey: "MessageId") + self.messageType = try values.decodeIfPresent(SessionMessageType.self, forKey: "MessageType") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(data, forKey: "Data") + try values.encodeIfPresent(messageID, forKey: "MessageId") + try values.encodeIfPresent(messageType, forKey: "MessageType") + } +} diff --git a/Sources/Entities/UserDeletedMessage.swift b/Sources/Entities/UserDeletedMessage.swift new file mode 100644 index 00000000..df909bb5 --- /dev/null +++ b/Sources/Entities/UserDeletedMessage.swift @@ -0,0 +1,39 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// User deleted message. +public struct UserDeletedMessage: Codable, Hashable { + /// Gets or sets the data. + public var data: String? + /// Gets or sets the message id. + public var messageID: String? + /// The different kinds of messages that are used in the WebSocket api. + public var messageType: SessionMessageType? + + public init(data: String? = nil, messageID: String? = nil, messageType: SessionMessageType? = nil) { + self.data = data + self.messageID = messageID + self.messageType = messageType + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.data = try values.decodeIfPresent(String.self, forKey: "Data") + self.messageID = try values.decodeIfPresent(String.self, forKey: "MessageId") + self.messageType = try values.decodeIfPresent(SessionMessageType.self, forKey: "MessageType") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(data, forKey: "Data") + try values.encodeIfPresent(messageID, forKey: "MessageId") + try values.encodeIfPresent(messageType, forKey: "MessageType") + } +} diff --git a/Sources/Entities/UserDto.swift b/Sources/Entities/UserDto.swift index 18d88d08..efcd0658 100644 --- a/Sources/Entities/UserDto.swift +++ b/Sources/Entities/UserDto.swift @@ -15,6 +15,8 @@ public struct UserDto: Codable, Hashable, Identifiable { /// Gets or sets whether async login is enabled or not. public var enableAutoLogin: Bool? /// Gets or sets a value indicating whether this instance has configured easy password. + /// + /// - warning: Deprecated. public var hasConfiguredEasyPassword: Bool? /// Gets or sets a value indicating whether this instance has configured password. public var hasConfiguredPassword: Bool? diff --git a/Sources/Entities/UserPolicy.swift b/Sources/Entities/UserPolicy.swift index 8cff0531..548285eb 100644 --- a/Sources/Entities/UserPolicy.swift +++ b/Sources/Entities/UserPolicy.swift @@ -10,7 +10,8 @@ import Foundation public struct UserPolicy: Codable, Hashable { public var accessSchedules: [AccessSchedule]? - public var authenticationProviderID: String? + public var allowedTags: [String]? + public var authenticationProviderID: String public var blockUnratedItems: [UnratedItem]? public var blockedChannels: [String]? public var blockedMediaFolders: [String]? @@ -19,11 +20,15 @@ public struct UserPolicy: Codable, Hashable { public var enableAllDevices: Bool? public var enableAllFolders: Bool? public var enableAudioPlaybackTranscoding: Bool? + /// Gets or sets a value indicating whether this instance can manage collections. + public var enableCollectionManagement: Bool public var enableContentDeletion: Bool? public var enableContentDeletionFromFolders: [String]? public var enableContentDownloading: Bool? public var enableLiveTvAccess: Bool? public var enableLiveTvManagement: Bool? + /// Gets or sets a value indicating whether this user can manage lyrics. + public var enableLyricManagement: Bool public var enableMediaConversion: Bool? public var enableMediaPlayback: Bool? public var enablePlaybackRemuxing: Bool? @@ -31,6 +36,8 @@ public struct UserPolicy: Codable, Hashable { public var enableRemoteAccess: Bool? public var enableRemoteControlOfOtherUsers: Bool? public var enableSharedDeviceControl: Bool? + /// Gets or sets a value indicating whether this instance can manage subtitles. + public var enableSubtitleManagement: Bool /// Gets or sets a value indicating whether [enable synchronize]. public var enableSyncTranscoding: Bool? public var enableUserPreferenceAccess: Bool? @@ -50,14 +57,15 @@ public struct UserPolicy: Codable, Hashable { public var maxActiveSessions: Int? /// Gets or sets the max parental rating. public var maxParentalRating: Int? - public var passwordResetProviderID: String? + public var passwordResetProviderID: String public var remoteClientBitrateLimit: Int? /// Gets or sets a value indicating what SyncPlay features the user can access. public var syncPlayAccess: SyncPlayUserAccessType? public init( accessSchedules: [AccessSchedule]? = nil, - authenticationProviderID: String? = nil, + allowedTags: [String]? = nil, + authenticationProviderID: String, blockUnratedItems: [UnratedItem]? = nil, blockedChannels: [String]? = nil, blockedMediaFolders: [String]? = nil, @@ -66,11 +74,13 @@ public struct UserPolicy: Codable, Hashable { enableAllDevices: Bool? = nil, enableAllFolders: Bool? = nil, enableAudioPlaybackTranscoding: Bool? = nil, + enableCollectionManagement: Bool? = nil, enableContentDeletion: Bool? = nil, enableContentDeletionFromFolders: [String]? = nil, enableContentDownloading: Bool? = nil, enableLiveTvAccess: Bool? = nil, enableLiveTvManagement: Bool? = nil, + enableLyricManagement: Bool? = nil, enableMediaConversion: Bool? = nil, enableMediaPlayback: Bool? = nil, enablePlaybackRemuxing: Bool? = nil, @@ -78,6 +88,7 @@ public struct UserPolicy: Codable, Hashable { enableRemoteAccess: Bool? = nil, enableRemoteControlOfOtherUsers: Bool? = nil, enableSharedDeviceControl: Bool? = nil, + enableSubtitleManagement: Bool? = nil, enableSyncTranscoding: Bool? = nil, enableUserPreferenceAccess: Bool? = nil, enableVideoPlaybackTranscoding: Bool? = nil, @@ -92,11 +103,12 @@ public struct UserPolicy: Codable, Hashable { loginAttemptsBeforeLockout: Int? = nil, maxActiveSessions: Int? = nil, maxParentalRating: Int? = nil, - passwordResetProviderID: String? = nil, + passwordResetProviderID: String, remoteClientBitrateLimit: Int? = nil, syncPlayAccess: SyncPlayUserAccessType? = nil ) { self.accessSchedules = accessSchedules + self.allowedTags = allowedTags self.authenticationProviderID = authenticationProviderID self.blockUnratedItems = blockUnratedItems self.blockedChannels = blockedChannels @@ -106,11 +118,13 @@ public struct UserPolicy: Codable, Hashable { self.enableAllDevices = enableAllDevices self.enableAllFolders = enableAllFolders self.enableAudioPlaybackTranscoding = enableAudioPlaybackTranscoding + self.enableCollectionManagement = enableCollectionManagement ?? false self.enableContentDeletion = enableContentDeletion self.enableContentDeletionFromFolders = enableContentDeletionFromFolders self.enableContentDownloading = enableContentDownloading self.enableLiveTvAccess = enableLiveTvAccess self.enableLiveTvManagement = enableLiveTvManagement + self.enableLyricManagement = enableLyricManagement ?? false self.enableMediaConversion = enableMediaConversion self.enableMediaPlayback = enableMediaPlayback self.enablePlaybackRemuxing = enablePlaybackRemuxing @@ -118,6 +132,7 @@ public struct UserPolicy: Codable, Hashable { self.enableRemoteAccess = enableRemoteAccess self.enableRemoteControlOfOtherUsers = enableRemoteControlOfOtherUsers self.enableSharedDeviceControl = enableSharedDeviceControl + self.enableSubtitleManagement = enableSubtitleManagement ?? false self.enableSyncTranscoding = enableSyncTranscoding self.enableUserPreferenceAccess = enableUserPreferenceAccess self.enableVideoPlaybackTranscoding = enableVideoPlaybackTranscoding @@ -140,7 +155,8 @@ public struct UserPolicy: Codable, Hashable { public init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: StringCodingKey.self) self.accessSchedules = try values.decodeIfPresent([AccessSchedule].self, forKey: "AccessSchedules") - self.authenticationProviderID = try values.decodeIfPresent(String.self, forKey: "AuthenticationProviderId") + self.allowedTags = try values.decodeIfPresent([String].self, forKey: "AllowedTags") + self.authenticationProviderID = try values.decode(String.self, forKey: "AuthenticationProviderId") self.blockUnratedItems = try values.decodeIfPresent([UnratedItem].self, forKey: "BlockUnratedItems") self.blockedChannels = try values.decodeIfPresent([String].self, forKey: "BlockedChannels") self.blockedMediaFolders = try values.decodeIfPresent([String].self, forKey: "BlockedMediaFolders") @@ -149,11 +165,13 @@ public struct UserPolicy: Codable, Hashable { self.enableAllDevices = try values.decodeIfPresent(Bool.self, forKey: "EnableAllDevices") self.enableAllFolders = try values.decodeIfPresent(Bool.self, forKey: "EnableAllFolders") self.enableAudioPlaybackTranscoding = try values.decodeIfPresent(Bool.self, forKey: "EnableAudioPlaybackTranscoding") + self.enableCollectionManagement = try values.decodeIfPresent(Bool.self, forKey: "EnableCollectionManagement") ?? false self.enableContentDeletion = try values.decodeIfPresent(Bool.self, forKey: "EnableContentDeletion") self.enableContentDeletionFromFolders = try values.decodeIfPresent([String].self, forKey: "EnableContentDeletionFromFolders") self.enableContentDownloading = try values.decodeIfPresent(Bool.self, forKey: "EnableContentDownloading") self.enableLiveTvAccess = try values.decodeIfPresent(Bool.self, forKey: "EnableLiveTvAccess") self.enableLiveTvManagement = try values.decodeIfPresent(Bool.self, forKey: "EnableLiveTvManagement") + self.enableLyricManagement = try values.decodeIfPresent(Bool.self, forKey: "EnableLyricManagement") ?? false self.enableMediaConversion = try values.decodeIfPresent(Bool.self, forKey: "EnableMediaConversion") self.enableMediaPlayback = try values.decodeIfPresent(Bool.self, forKey: "EnableMediaPlayback") self.enablePlaybackRemuxing = try values.decodeIfPresent(Bool.self, forKey: "EnablePlaybackRemuxing") @@ -161,6 +179,7 @@ public struct UserPolicy: Codable, Hashable { self.enableRemoteAccess = try values.decodeIfPresent(Bool.self, forKey: "EnableRemoteAccess") self.enableRemoteControlOfOtherUsers = try values.decodeIfPresent(Bool.self, forKey: "EnableRemoteControlOfOtherUsers") self.enableSharedDeviceControl = try values.decodeIfPresent(Bool.self, forKey: "EnableSharedDeviceControl") + self.enableSubtitleManagement = try values.decodeIfPresent(Bool.self, forKey: "EnableSubtitleManagement") ?? false self.enableSyncTranscoding = try values.decodeIfPresent(Bool.self, forKey: "EnableSyncTranscoding") self.enableUserPreferenceAccess = try values.decodeIfPresent(Bool.self, forKey: "EnableUserPreferenceAccess") self.enableVideoPlaybackTranscoding = try values.decodeIfPresent(Bool.self, forKey: "EnableVideoPlaybackTranscoding") @@ -175,7 +194,7 @@ public struct UserPolicy: Codable, Hashable { self.loginAttemptsBeforeLockout = try values.decodeIfPresent(Int.self, forKey: "LoginAttemptsBeforeLockout") self.maxActiveSessions = try values.decodeIfPresent(Int.self, forKey: "MaxActiveSessions") self.maxParentalRating = try values.decodeIfPresent(Int.self, forKey: "MaxParentalRating") - self.passwordResetProviderID = try values.decodeIfPresent(String.self, forKey: "PasswordResetProviderId") + self.passwordResetProviderID = try values.decode(String.self, forKey: "PasswordResetProviderId") self.remoteClientBitrateLimit = try values.decodeIfPresent(Int.self, forKey: "RemoteClientBitrateLimit") self.syncPlayAccess = try values.decodeIfPresent(SyncPlayUserAccessType.self, forKey: "SyncPlayAccess") } @@ -183,7 +202,8 @@ public struct UserPolicy: Codable, Hashable { public func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: StringCodingKey.self) try values.encodeIfPresent(accessSchedules, forKey: "AccessSchedules") - try values.encodeIfPresent(authenticationProviderID, forKey: "AuthenticationProviderId") + try values.encodeIfPresent(allowedTags, forKey: "AllowedTags") + try values.encode(authenticationProviderID, forKey: "AuthenticationProviderId") try values.encodeIfPresent(blockUnratedItems, forKey: "BlockUnratedItems") try values.encodeIfPresent(blockedChannels, forKey: "BlockedChannels") try values.encodeIfPresent(blockedMediaFolders, forKey: "BlockedMediaFolders") @@ -192,11 +212,13 @@ public struct UserPolicy: Codable, Hashable { try values.encodeIfPresent(enableAllDevices, forKey: "EnableAllDevices") try values.encodeIfPresent(enableAllFolders, forKey: "EnableAllFolders") try values.encodeIfPresent(enableAudioPlaybackTranscoding, forKey: "EnableAudioPlaybackTranscoding") + try values.encodeIfPresent(enableCollectionManagement, forKey: "EnableCollectionManagement") try values.encodeIfPresent(enableContentDeletion, forKey: "EnableContentDeletion") try values.encodeIfPresent(enableContentDeletionFromFolders, forKey: "EnableContentDeletionFromFolders") try values.encodeIfPresent(enableContentDownloading, forKey: "EnableContentDownloading") try values.encodeIfPresent(enableLiveTvAccess, forKey: "EnableLiveTvAccess") try values.encodeIfPresent(enableLiveTvManagement, forKey: "EnableLiveTvManagement") + try values.encodeIfPresent(enableLyricManagement, forKey: "EnableLyricManagement") try values.encodeIfPresent(enableMediaConversion, forKey: "EnableMediaConversion") try values.encodeIfPresent(enableMediaPlayback, forKey: "EnableMediaPlayback") try values.encodeIfPresent(enablePlaybackRemuxing, forKey: "EnablePlaybackRemuxing") @@ -204,6 +226,7 @@ public struct UserPolicy: Codable, Hashable { try values.encodeIfPresent(enableRemoteAccess, forKey: "EnableRemoteAccess") try values.encodeIfPresent(enableRemoteControlOfOtherUsers, forKey: "EnableRemoteControlOfOtherUsers") try values.encodeIfPresent(enableSharedDeviceControl, forKey: "EnableSharedDeviceControl") + try values.encodeIfPresent(enableSubtitleManagement, forKey: "EnableSubtitleManagement") try values.encodeIfPresent(enableSyncTranscoding, forKey: "EnableSyncTranscoding") try values.encodeIfPresent(enableUserPreferenceAccess, forKey: "EnableUserPreferenceAccess") try values.encodeIfPresent(enableVideoPlaybackTranscoding, forKey: "EnableVideoPlaybackTranscoding") @@ -218,7 +241,7 @@ public struct UserPolicy: Codable, Hashable { try values.encodeIfPresent(loginAttemptsBeforeLockout, forKey: "LoginAttemptsBeforeLockout") try values.encodeIfPresent(maxActiveSessions, forKey: "MaxActiveSessions") try values.encodeIfPresent(maxParentalRating, forKey: "MaxParentalRating") - try values.encodeIfPresent(passwordResetProviderID, forKey: "PasswordResetProviderId") + try values.encode(passwordResetProviderID, forKey: "PasswordResetProviderId") try values.encodeIfPresent(remoteClientBitrateLimit, forKey: "RemoteClientBitrateLimit") try values.encodeIfPresent(syncPlayAccess, forKey: "SyncPlayAccess") } diff --git a/Sources/Entities/UserUpdatedMessage.swift b/Sources/Entities/UserUpdatedMessage.swift new file mode 100644 index 00000000..b746dc14 --- /dev/null +++ b/Sources/Entities/UserUpdatedMessage.swift @@ -0,0 +1,39 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// User updated message. +public struct UserUpdatedMessage: Codable, Hashable { + /// Class UserDto. + public var data: UserDto? + /// Gets or sets the message id. + public var messageID: String? + /// The different kinds of messages that are used in the WebSocket api. + public var messageType: SessionMessageType? + + public init(data: UserDto? = nil, messageID: String? = nil, messageType: SessionMessageType? = nil) { + self.data = data + self.messageID = messageID + self.messageType = messageType + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: StringCodingKey.self) + self.data = try values.decodeIfPresent(UserDto.self, forKey: "Data") + self.messageID = try values.decodeIfPresent(String.self, forKey: "MessageId") + self.messageType = try values.decodeIfPresent(SessionMessageType.self, forKey: "MessageType") + } + + public func encode(to encoder: Encoder) throws { + var values = encoder.container(keyedBy: StringCodingKey.self) + try values.encodeIfPresent(data, forKey: "Data") + try values.encodeIfPresent(messageID, forKey: "MessageId") + try values.encodeIfPresent(messageType, forKey: "MessageType") + } +} diff --git a/Sources/Entities/VideoRange.swift b/Sources/Entities/VideoRange.swift new file mode 100644 index 00000000..e4b68be0 --- /dev/null +++ b/Sources/Entities/VideoRange.swift @@ -0,0 +1,16 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// An enum representing video ranges. +public enum VideoRange: String, Codable, CaseIterable { + case unknown = "Unknown" + case sdr = "SDR" + case hdr = "HDR" +} diff --git a/Sources/Entities/VideoRangeType.swift b/Sources/Entities/VideoRangeType.swift new file mode 100644 index 00000000..4bee7ff0 --- /dev/null +++ b/Sources/Entities/VideoRangeType.swift @@ -0,0 +1,22 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// An enum representing types of video ranges. +public enum VideoRangeType: String, Codable, CaseIterable { + case unknown = "Unknown" + case sdr = "SDR" + case hdr10 = "HDR10" + case hlg = "HLG" + case dovi = "DOVI" + case dOVIWithHDR10 = "DOVIWithHDR10" + case dOVIWithHLG = "DOVIWithHLG" + case dOVIWithSDR = "DOVIWithSDR" + case hDR10Plus = "HDR10Plus" +} diff --git a/Sources/Entities/WebSocketMessage.swift b/Sources/Entities/WebSocketMessage.swift new file mode 100644 index 00000000..42695ed2 --- /dev/null +++ b/Sources/Entities/WebSocketMessage.swift @@ -0,0 +1,37 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation + +/// Represents the possible websocket types +public enum WebSocketMessage: Codable, Hashable { + case inboundWebSocketMessage(InboundWebSocketMessage) + case outboundWebSocketMessage(OutboundWebSocketMessage) + + public init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + if let value = try? container.decode(InboundWebSocketMessage.self) { + self = .inboundWebSocketMessage(value) + } else if let value = try? container.decode(OutboundWebSocketMessage.self) { + self = .outboundWebSocketMessage(value) + } else { + throw DecodingError.dataCorruptedError( + in: container, + debugDescription: "Data could not be decoded as any of the expected types (InboundWebSocketMessage, OutboundWebSocketMessage)." + ) + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + switch self { + case let .inboundWebSocketMessage(value): try container.encode(value) + case let .outboundWebSocketMessage(value): try container.encode(value) + } + } +} diff --git a/Sources/JellyfinClient.swift b/Sources/JellyfinClient.swift index 5dfe3253..f573dbe6 100644 --- a/Sources/JellyfinClient.swift +++ b/Sources/JellyfinClient.swift @@ -195,8 +195,8 @@ public extension JellyfinClient { /// - Throws: `ClientError.noAccessTokenInResponse` if no access token was supplied in a successful authentication response @discardableResult func signIn(username: String, password: String) async throws -> AuthenticationResult { - let authenticateUserRequest = Paths.authenticateUserByName(.init(password: nil, pw: password, username: username)) - let response = try await send(authenticateUserRequest).value + let request = Paths.authenticateUserByName(.init(pw: password, username: username)) + let response = try await send(request).value if let accessToken = response.accessToken { self.accessToken = accessToken @@ -218,8 +218,8 @@ public extension JellyfinClient { /// - Throws: `ClientError.noAccessTokenInResponse` if no access token was supplied in a successful authentication response @discardableResult func signIn(quickConnectSecret: String) async throws -> AuthenticationResult { - let quickConnectRequest = Paths.authenticateWithQuickConnect(.init(secret: quickConnectSecret)) - let response = try await send(quickConnectRequest).value + let request = Paths.authenticateWithQuickConnect(.init(secret: quickConnectSecret)) + let response = try await send(request).value if let accessToken = response.accessToken { self.accessToken = accessToken diff --git a/Sources/Paths/AddItemToPlaylistAPI.swift b/Sources/Paths/AddItemToPlaylistAPI.swift new file mode 100644 index 00000000..28299233 --- /dev/null +++ b/Sources/Paths/AddItemToPlaylistAPI.swift @@ -0,0 +1,30 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation +import Get +import URLQueryEncoder + +extension Paths { + /// Adds items to a playlist. + public static func addItemToPlaylist(playlistID: String, ids: [String]? = nil, userID: String? = nil) -> Request { + Request( + path: "/Playlists/\(playlistID)/Items", + method: "POST", + query: makeAddItemToPlaylistQuery(ids, userID), + id: "AddItemToPlaylist" + ) + } + + private static func makeAddItemToPlaylistQuery(_ ids: [String]?, _ userID: String?) -> [(String, String?)] { + let encoder = URLQueryEncoder() + encoder.encode(ids, forKey: "ids") + encoder.encode(userID, forKey: "userId") + return encoder.items + } +} diff --git a/Sources/Paths/AuthorizeQuickConnectAPI.swift b/Sources/Paths/AuthorizeQuickConnectAPI.swift new file mode 100644 index 00000000..65dba594 --- /dev/null +++ b/Sources/Paths/AuthorizeQuickConnectAPI.swift @@ -0,0 +1,30 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation +import Get +import URLQueryEncoder + +extension Paths { + /// Authorizes a pending quick connect request. + public static func authorizeQuickConnect(code: String, userID: String? = nil) -> Request { + Request( + path: "/QuickConnect/Authorize", + method: "POST", + query: makeAuthorizeQuickConnectQuery(code, userID), + id: "AuthorizeQuickConnect" + ) + } + + private static func makeAuthorizeQuickConnectQuery(_ code: String, _ userID: String?) -> [(String, String?)] { + let encoder = URLQueryEncoder() + encoder.encode(code, forKey: "code") + encoder.encode(userID, forKey: "userId") + return encoder.items + } +} diff --git a/Sources/Paths/CreatePlaylistAPI.swift b/Sources/Paths/CreatePlaylistAPI.swift index 46f7d8da..758dbb2c 100644 --- a/Sources/Paths/CreatePlaylistAPI.swift +++ b/Sources/Paths/CreatePlaylistAPI.swift @@ -27,9 +27,11 @@ public extension Paths { public var name: String? public var ids: [String]? public var userID: String? - public var mediaType: String? + public var mediaType: MediaType? - public init(name: String? = nil, ids: [String]? = nil, userID: String? = nil, mediaType: String? = nil) { + public typealias MediaType = JellyfinAPI.MediaType + + public init(name: String? = nil, ids: [String]? = nil, userID: String? = nil, mediaType: MediaType? = nil) { self.name = name self.ids = ids self.userID = userID diff --git a/Sources/Paths/DeleteLyricsAPI.swift b/Sources/Paths/DeleteLyricsAPI.swift new file mode 100644 index 00000000..e034f967 --- /dev/null +++ b/Sources/Paths/DeleteLyricsAPI.swift @@ -0,0 +1,18 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation +import Get +import URLQueryEncoder + +public extension Paths { + /// Deletes an external lyric file. + static func deleteLyrics(itemID: String) -> Request { + Request(path: "/Audio/\(itemID)/Lyrics", method: "DELETE", id: "DeleteLyrics") + } +} diff --git a/Sources/Paths/DeleteUserImageAPI.swift b/Sources/Paths/DeleteUserImageAPI.swift index 31aaee0f..09a407ee 100644 --- a/Sources/Paths/DeleteUserImageAPI.swift +++ b/Sources/Paths/DeleteUserImageAPI.swift @@ -12,18 +12,13 @@ import URLQueryEncoder extension Paths { /// Delete the user's image. - public static func deleteUserImage(userID: String, imageType: String, index: Int? = nil) -> Request { - Request( - path: "/Users/\(userID)/Images/\(imageType)", - method: "DELETE", - query: makeDeleteUserImageQuery(index), - id: "DeleteUserImage" - ) + public static func deleteUserImage(userID: String? = nil) -> Request { + Request(path: "/UserImage", method: "DELETE", query: makeDeleteUserImageQuery(userID), id: "DeleteUserImage") } - private static func makeDeleteUserImageQuery(_ index: Int?) -> [(String, String?)] { + private static func makeDeleteUserImageQuery(_ userID: String?) -> [(String, String?)] { let encoder = URLQueryEncoder() - encoder.encode(index, forKey: "index") + encoder.encode(userID, forKey: "userId") return encoder.items } } diff --git a/Sources/Paths/DeleteUserItemRatingAPI.swift b/Sources/Paths/DeleteUserItemRatingAPI.swift index 523885b1..c7261606 100644 --- a/Sources/Paths/DeleteUserItemRatingAPI.swift +++ b/Sources/Paths/DeleteUserItemRatingAPI.swift @@ -10,9 +10,20 @@ import Foundation import Get import URLQueryEncoder -public extension Paths { +extension Paths { /// Deletes a user's saved personal rating for an item. - static func deleteUserItemRating(userID: String, itemID: String) -> Request { - Request(path: "/Users/\(userID)/Items/\(itemID)/Rating", method: "DELETE", id: "DeleteUserItemRating") + public static func deleteUserItemRating(itemID: String, userID: String? = nil) -> Request { + Request( + path: "/UserItems/\(itemID)/Rating", + method: "DELETE", + query: makeDeleteUserItemRatingQuery(userID), + id: "DeleteUserItemRating" + ) + } + + private static func makeDeleteUserItemRatingQuery(_ userID: String?) -> [(String, String?)] { + let encoder = URLQueryEncoder() + encoder.encode(userID, forKey: "userId") + return encoder.items } } diff --git a/Sources/Paths/DownloadRemoteLyricsAPI.swift b/Sources/Paths/DownloadRemoteLyricsAPI.swift new file mode 100644 index 00000000..27c9376d --- /dev/null +++ b/Sources/Paths/DownloadRemoteLyricsAPI.swift @@ -0,0 +1,18 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation +import Get +import URLQueryEncoder + +public extension Paths { + /// Downloads a remote lyric. + static func downloadRemoteLyrics(itemID: String, lyricID: String) -> Request { + Request(path: "/Audio/\(itemID)/RemoteSearch/Lyrics/\(lyricID)", method: "POST", id: "DownloadRemoteLyrics") + } +} diff --git a/Sources/Paths/GetAlbumArtistsAPI.swift b/Sources/Paths/GetAlbumArtistsAPI.swift index c771957f..f8c73cc6 100644 --- a/Sources/Paths/GetAlbumArtistsAPI.swift +++ b/Sources/Paths/GetAlbumArtistsAPI.swift @@ -27,7 +27,7 @@ public extension Paths { public var includeItemTypes: [JellyfinAPI.BaseItemKind]? public var filters: [JellyfinAPI.ItemFilter]? public var isFavorite: Bool? - public var mediaTypes: [String]? + public var mediaTypes: [JellyfinAPI.MediaType]? public var genres: [String]? public var genreIDs: [String]? public var officialRatings: [String]? @@ -45,7 +45,7 @@ public extension Paths { public var nameStartsWithOrGreater: String? public var nameStartsWith: String? public var nameLessThan: String? - public var sortBy: [String]? + public var sortBy: [JellyfinAPI.ItemSortBy]? public var sortOrder: [JellyfinAPI.SortOrder]? public var enableImages: Bool? public var enableTotalRecordCount: Bool? @@ -61,7 +61,7 @@ public extension Paths { includeItemTypes: [JellyfinAPI.BaseItemKind]? = nil, filters: [JellyfinAPI.ItemFilter]? = nil, isFavorite: Bool? = nil, - mediaTypes: [String]? = nil, + mediaTypes: [JellyfinAPI.MediaType]? = nil, genres: [String]? = nil, genreIDs: [String]? = nil, officialRatings: [String]? = nil, @@ -79,7 +79,7 @@ public extension Paths { nameStartsWithOrGreater: String? = nil, nameStartsWith: String? = nil, nameLessThan: String? = nil, - sortBy: [String]? = nil, + sortBy: [JellyfinAPI.ItemSortBy]? = nil, sortOrder: [JellyfinAPI.SortOrder]? = nil, enableImages: Bool? = nil, enableTotalRecordCount: Bool? = nil diff --git a/Sources/Paths/GetArtistImageAPI.swift b/Sources/Paths/GetArtistImageAPI.swift index f78782e5..25562eeb 100644 --- a/Sources/Paths/GetArtistImageAPI.swift +++ b/Sources/Paths/GetArtistImageAPI.swift @@ -33,8 +33,6 @@ public extension Paths { public var quality: Int? public var fillWidth: Int? public var fillHeight: Int? - public var isCropWhitespace: Bool? - public var isAddPlayedIndicator: Bool? public var blur: Int? public var backgroundColor: String? public var foregroundLayer: String? @@ -53,8 +51,6 @@ public extension Paths { quality: Int? = nil, fillWidth: Int? = nil, fillHeight: Int? = nil, - isCropWhitespace: Bool? = nil, - isAddPlayedIndicator: Bool? = nil, blur: Int? = nil, backgroundColor: String? = nil, foregroundLayer: String? = nil @@ -70,8 +66,6 @@ public extension Paths { self.quality = quality self.fillWidth = fillWidth self.fillHeight = fillHeight - self.isCropWhitespace = isCropWhitespace - self.isAddPlayedIndicator = isAddPlayedIndicator self.blur = blur self.backgroundColor = backgroundColor self.foregroundLayer = foregroundLayer @@ -90,8 +84,6 @@ public extension Paths { encoder.encode(quality, forKey: "quality") encoder.encode(fillWidth, forKey: "fillWidth") encoder.encode(fillHeight, forKey: "fillHeight") - encoder.encode(isCropWhitespace, forKey: "cropWhitespace") - encoder.encode(isAddPlayedIndicator, forKey: "addPlayedIndicator") encoder.encode(blur, forKey: "blur") encoder.encode(backgroundColor, forKey: "backgroundColor") encoder.encode(foregroundLayer, forKey: "foregroundLayer") diff --git a/Sources/Paths/GetArtistsAPI.swift b/Sources/Paths/GetArtistsAPI.swift index 382feca3..096bbae9 100644 --- a/Sources/Paths/GetArtistsAPI.swift +++ b/Sources/Paths/GetArtistsAPI.swift @@ -27,7 +27,7 @@ public extension Paths { public var includeItemTypes: [JellyfinAPI.BaseItemKind]? public var filters: [JellyfinAPI.ItemFilter]? public var isFavorite: Bool? - public var mediaTypes: [String]? + public var mediaTypes: [JellyfinAPI.MediaType]? public var genres: [String]? public var genreIDs: [String]? public var officialRatings: [String]? @@ -45,7 +45,7 @@ public extension Paths { public var nameStartsWithOrGreater: String? public var nameStartsWith: String? public var nameLessThan: String? - public var sortBy: [String]? + public var sortBy: [JellyfinAPI.ItemSortBy]? public var sortOrder: [JellyfinAPI.SortOrder]? public var enableImages: Bool? public var enableTotalRecordCount: Bool? @@ -61,7 +61,7 @@ public extension Paths { includeItemTypes: [JellyfinAPI.BaseItemKind]? = nil, filters: [JellyfinAPI.ItemFilter]? = nil, isFavorite: Bool? = nil, - mediaTypes: [String]? = nil, + mediaTypes: [JellyfinAPI.MediaType]? = nil, genres: [String]? = nil, genreIDs: [String]? = nil, officialRatings: [String]? = nil, @@ -79,7 +79,7 @@ public extension Paths { nameStartsWithOrGreater: String? = nil, nameStartsWith: String? = nil, nameLessThan: String? = nil, - sortBy: [String]? = nil, + sortBy: [JellyfinAPI.ItemSortBy]? = nil, sortOrder: [JellyfinAPI.SortOrder]? = nil, enableImages: Bool? = nil, enableTotalRecordCount: Bool? = nil diff --git a/Sources/Paths/GetBrandingCss2API.swift b/Sources/Paths/GetBrandingCss2API.swift index 72b3beeb..7161f479 100644 --- a/Sources/Paths/GetBrandingCss2API.swift +++ b/Sources/Paths/GetBrandingCss2API.swift @@ -12,7 +12,7 @@ import URLQueryEncoder public extension Paths { /// Gets branding css. - static var getBrandingCss2: Request { + static var getBrandingCss2: Request { Request(path: "/Branding/Css.css", method: "GET", id: "GetBrandingCss_2") } } diff --git a/Sources/Paths/GetBrandingCssAPI.swift b/Sources/Paths/GetBrandingCssAPI.swift index 7c23ce1f..5245625e 100644 --- a/Sources/Paths/GetBrandingCssAPI.swift +++ b/Sources/Paths/GetBrandingCssAPI.swift @@ -12,7 +12,7 @@ import URLQueryEncoder public extension Paths { /// Gets branding css. - static var getBrandingCss: Request { + static var getBrandingCss: Request { Request(path: "/Branding/Css", method: "GET", id: "GetBrandingCss") } } diff --git a/Sources/Paths/GetChannelItemsAPI.swift b/Sources/Paths/GetChannelItemsAPI.swift index e1f4a285..ee7ad2f0 100644 --- a/Sources/Paths/GetChannelItemsAPI.swift +++ b/Sources/Paths/GetChannelItemsAPI.swift @@ -26,7 +26,7 @@ public extension Paths { public var limit: Int? public var sortOrder: [JellyfinAPI.SortOrder]? public var filters: [JellyfinAPI.ItemFilter]? - public var sortBy: [String]? + public var sortBy: [JellyfinAPI.ItemSortBy]? public var fields: [JellyfinAPI.ItemFields]? public init( @@ -36,7 +36,7 @@ public extension Paths { limit: Int? = nil, sortOrder: [JellyfinAPI.SortOrder]? = nil, filters: [JellyfinAPI.ItemFilter]? = nil, - sortBy: [String]? = nil, + sortBy: [JellyfinAPI.ItemSortBy]? = nil, fields: [JellyfinAPI.ItemFields]? = nil ) { self.folderID = folderID diff --git a/Sources/Paths/GetDevicesAPI.swift b/Sources/Paths/GetDevicesAPI.swift index 42eb7664..045a01a2 100644 --- a/Sources/Paths/GetDevicesAPI.swift +++ b/Sources/Paths/GetDevicesAPI.swift @@ -12,13 +12,12 @@ import URLQueryEncoder extension Paths { /// Get Devices. - public static func getDevices(isSupportsSync: Bool? = nil, userID: String? = nil) -> Request { - Request(path: "/Devices", method: "GET", query: makeGetDevicesQuery(isSupportsSync, userID), id: "GetDevices") + public static func getDevices(userID: String? = nil) -> Request { + Request(path: "/Devices", method: "GET", query: makeGetDevicesQuery(userID), id: "GetDevices") } - private static func makeGetDevicesQuery(_ isSupportsSync: Bool?, _ userID: String?) -> [(String, String?)] { + private static func makeGetDevicesQuery(_ userID: String?) -> [(String, String?)] { let encoder = URLQueryEncoder() - encoder.encode(isSupportsSync, forKey: "supportsSync") encoder.encode(userID, forKey: "userId") return encoder.items } diff --git a/Sources/Paths/GetDisplayPreferencesAPI.swift b/Sources/Paths/GetDisplayPreferencesAPI.swift index 9916675d..a486f883 100644 --- a/Sources/Paths/GetDisplayPreferencesAPI.swift +++ b/Sources/Paths/GetDisplayPreferencesAPI.swift @@ -10,18 +10,25 @@ import Foundation import Get import URLQueryEncoder -public extension Paths { +extension Paths { /// Get Display Preferences. - static func getDisplayPreferences( + public static func getDisplayPreferences( displayPreferencesID: String, - userID: String, + userID: String? = nil, client: String ) -> Request { Request( path: "/DisplayPreferences/\(displayPreferencesID)", method: "GET", - query: [("userId", userID), ("client", client)], + query: makeGetDisplayPreferencesQuery(userID, client), id: "GetDisplayPreferences" ) } + + private static func makeGetDisplayPreferencesQuery(_ userID: String?, _ client: String) -> [(String, String?)] { + let encoder = URLQueryEncoder() + encoder.encode(userID, forKey: "userId") + encoder.encode(client, forKey: "client") + return encoder.items + } } diff --git a/Sources/Paths/GetEpisodesAPI.swift b/Sources/Paths/GetEpisodesAPI.swift index 383300af..78918af7 100644 --- a/Sources/Paths/GetEpisodesAPI.swift +++ b/Sources/Paths/GetEpisodesAPI.swift @@ -30,7 +30,9 @@ public extension Paths { public var imageTypeLimit: Int? public var enableImageTypes: [JellyfinAPI.ImageType]? public var enableUserData: Bool? - public var sortBy: String? + public var sortBy: SortBy? + + public typealias SortBy = JellyfinAPI.ItemSortBy public init( userID: String? = nil, @@ -46,7 +48,7 @@ public extension Paths { imageTypeLimit: Int? = nil, enableImageTypes: [JellyfinAPI.ImageType]? = nil, enableUserData: Bool? = nil, - sortBy: String? = nil + sortBy: SortBy? = nil ) { self.userID = userID self.fields = fields diff --git a/Sources/Paths/GetGenreImageAPI.swift b/Sources/Paths/GetGenreImageAPI.swift index 4642e71b..ab0ddc22 100644 --- a/Sources/Paths/GetGenreImageAPI.swift +++ b/Sources/Paths/GetGenreImageAPI.swift @@ -28,8 +28,6 @@ public extension Paths { public var quality: Int? public var fillWidth: Int? public var fillHeight: Int? - public var isCropWhitespace: Bool? - public var isAddPlayedIndicator: Bool? public var blur: Int? public var backgroundColor: String? public var foregroundLayer: String? @@ -49,8 +47,6 @@ public extension Paths { quality: Int? = nil, fillWidth: Int? = nil, fillHeight: Int? = nil, - isCropWhitespace: Bool? = nil, - isAddPlayedIndicator: Bool? = nil, blur: Int? = nil, backgroundColor: String? = nil, foregroundLayer: String? = nil, @@ -67,8 +63,6 @@ public extension Paths { self.quality = quality self.fillWidth = fillWidth self.fillHeight = fillHeight - self.isCropWhitespace = isCropWhitespace - self.isAddPlayedIndicator = isAddPlayedIndicator self.blur = blur self.backgroundColor = backgroundColor self.foregroundLayer = foregroundLayer @@ -88,8 +82,6 @@ public extension Paths { encoder.encode(quality, forKey: "quality") encoder.encode(fillWidth, forKey: "fillWidth") encoder.encode(fillHeight, forKey: "fillHeight") - encoder.encode(isCropWhitespace, forKey: "cropWhitespace") - encoder.encode(isAddPlayedIndicator, forKey: "addPlayedIndicator") encoder.encode(blur, forKey: "blur") encoder.encode(backgroundColor, forKey: "backgroundColor") encoder.encode(foregroundLayer, forKey: "foregroundLayer") diff --git a/Sources/Paths/GetGenreImageByIndexAPI.swift b/Sources/Paths/GetGenreImageByIndexAPI.swift index 9c56d23a..d280d4fa 100644 --- a/Sources/Paths/GetGenreImageByIndexAPI.swift +++ b/Sources/Paths/GetGenreImageByIndexAPI.swift @@ -38,8 +38,6 @@ public extension Paths { public var quality: Int? public var fillWidth: Int? public var fillHeight: Int? - public var isCropWhitespace: Bool? - public var isAddPlayedIndicator: Bool? public var blur: Int? public var backgroundColor: String? public var foregroundLayer: String? @@ -58,8 +56,6 @@ public extension Paths { quality: Int? = nil, fillWidth: Int? = nil, fillHeight: Int? = nil, - isCropWhitespace: Bool? = nil, - isAddPlayedIndicator: Bool? = nil, blur: Int? = nil, backgroundColor: String? = nil, foregroundLayer: String? = nil @@ -75,8 +71,6 @@ public extension Paths { self.quality = quality self.fillWidth = fillWidth self.fillHeight = fillHeight - self.isCropWhitespace = isCropWhitespace - self.isAddPlayedIndicator = isAddPlayedIndicator self.blur = blur self.backgroundColor = backgroundColor self.foregroundLayer = foregroundLayer @@ -95,8 +89,6 @@ public extension Paths { encoder.encode(quality, forKey: "quality") encoder.encode(fillWidth, forKey: "fillWidth") encoder.encode(fillHeight, forKey: "fillHeight") - encoder.encode(isCropWhitespace, forKey: "cropWhitespace") - encoder.encode(isAddPlayedIndicator, forKey: "addPlayedIndicator") encoder.encode(blur, forKey: "blur") encoder.encode(backgroundColor, forKey: "backgroundColor") encoder.encode(foregroundLayer, forKey: "foregroundLayer") diff --git a/Sources/Paths/GetGenresAPI.swift b/Sources/Paths/GetGenresAPI.swift index c688ace8..cd46b51e 100644 --- a/Sources/Paths/GetGenresAPI.swift +++ b/Sources/Paths/GetGenresAPI.swift @@ -31,7 +31,7 @@ public extension Paths { public var nameStartsWithOrGreater: String? public var nameStartsWith: String? public var nameLessThan: String? - public var sortBy: [String]? + public var sortBy: [JellyfinAPI.ItemSortBy]? public var sortOrder: [JellyfinAPI.SortOrder]? public var enableImages: Bool? public var enableTotalRecordCount: Bool? @@ -51,7 +51,7 @@ public extension Paths { nameStartsWithOrGreater: String? = nil, nameStartsWith: String? = nil, nameLessThan: String? = nil, - sortBy: [String]? = nil, + sortBy: [JellyfinAPI.ItemSortBy]? = nil, sortOrder: [JellyfinAPI.SortOrder]? = nil, enableImages: Bool? = nil, enableTotalRecordCount: Bool? = nil diff --git a/Sources/Paths/GetGroupingOptionsAPI.swift b/Sources/Paths/GetGroupingOptionsAPI.swift index 68cfdb13..5b7ddefa 100644 --- a/Sources/Paths/GetGroupingOptionsAPI.swift +++ b/Sources/Paths/GetGroupingOptionsAPI.swift @@ -10,9 +10,15 @@ import Foundation import Get import URLQueryEncoder -public extension Paths { +extension Paths { /// Get user view grouping options. - static func getGroupingOptions(userID: String) -> Request<[JellyfinAPI.SpecialViewOptionDto]> { - Request(path: "/Users/\(userID)/GroupingOptions", method: "GET", id: "GetGroupingOptions") + public static func getGroupingOptions(userID: String? = nil) -> Request<[JellyfinAPI.SpecialViewOptionDto]> { + Request(path: "/UserViews/GroupingOptions", method: "GET", query: makeGetGroupingOptionsQuery(userID), id: "GetGroupingOptions") + } + + private static func makeGetGroupingOptionsQuery(_ userID: String?) -> [(String, String?)] { + let encoder = URLQueryEncoder() + encoder.encode(userID, forKey: "userId") + return encoder.items } } diff --git a/Sources/Paths/GetInstantMixFromAlbumAPI.swift b/Sources/Paths/GetInstantMixFromAlbumAPI.swift index 5378af2c..f866cbd1 100644 --- a/Sources/Paths/GetInstantMixFromAlbumAPI.swift +++ b/Sources/Paths/GetInstantMixFromAlbumAPI.swift @@ -13,10 +13,10 @@ import URLQueryEncoder public extension Paths { /// Creates an instant playlist based on a given album. static func getInstantMixFromAlbum( - id: String, + itemID: String, parameters: GetInstantMixFromAlbumParameters? = nil ) -> Request { - Request(path: "/Albums/\(id)/InstantMix", method: "GET", query: parameters?.asQuery, id: "GetInstantMixFromAlbum") + Request(path: "/Albums/\(itemID)/InstantMix", method: "GET", query: parameters?.asQuery, id: "GetInstantMixFromAlbum") } struct GetInstantMixFromAlbumParameters { diff --git a/Sources/Paths/GetInstantMixFromArtistsAPI.swift b/Sources/Paths/GetInstantMixFromArtistsAPI.swift index f4cb2714..faad0c5f 100644 --- a/Sources/Paths/GetInstantMixFromArtistsAPI.swift +++ b/Sources/Paths/GetInstantMixFromArtistsAPI.swift @@ -13,10 +13,10 @@ import URLQueryEncoder public extension Paths { /// Creates an instant playlist based on a given artist. static func getInstantMixFromArtists( - id: String, + itemID: String, parameters: GetInstantMixFromArtistsParameters? = nil ) -> Request { - Request(path: "/Artists/\(id)/InstantMix", method: "GET", query: parameters?.asQuery, id: "GetInstantMixFromArtists") + Request(path: "/Artists/\(itemID)/InstantMix", method: "GET", query: parameters?.asQuery, id: "GetInstantMixFromArtists") } struct GetInstantMixFromArtistsParameters { diff --git a/Sources/Paths/GetInstantMixFromItemAPI.swift b/Sources/Paths/GetInstantMixFromItemAPI.swift index fcef4668..8a7e6ab4 100644 --- a/Sources/Paths/GetInstantMixFromItemAPI.swift +++ b/Sources/Paths/GetInstantMixFromItemAPI.swift @@ -13,10 +13,10 @@ import URLQueryEncoder public extension Paths { /// Creates an instant playlist based on a given item. static func getInstantMixFromItem( - id: String, + itemID: String, parameters: GetInstantMixFromItemParameters? = nil ) -> Request { - Request(path: "/Items/\(id)/InstantMix", method: "GET", query: parameters?.asQuery, id: "GetInstantMixFromItem") + Request(path: "/Items/\(itemID)/InstantMix", method: "GET", query: parameters?.asQuery, id: "GetInstantMixFromItem") } struct GetInstantMixFromItemParameters { diff --git a/Sources/Paths/GetInstantMixFromPlaylistAPI.swift b/Sources/Paths/GetInstantMixFromPlaylistAPI.swift index 2238f7f9..a89c8ae2 100644 --- a/Sources/Paths/GetInstantMixFromPlaylistAPI.swift +++ b/Sources/Paths/GetInstantMixFromPlaylistAPI.swift @@ -13,10 +13,10 @@ import URLQueryEncoder public extension Paths { /// Creates an instant playlist based on a given playlist. static func getInstantMixFromPlaylist( - id: String, + itemID: String, parameters: GetInstantMixFromPlaylistParameters? = nil ) -> Request { - Request(path: "/Playlists/\(id)/InstantMix", method: "GET", query: parameters?.asQuery, id: "GetInstantMixFromPlaylist") + Request(path: "/Playlists/\(itemID)/InstantMix", method: "GET", query: parameters?.asQuery, id: "GetInstantMixFromPlaylist") } struct GetInstantMixFromPlaylistParameters { diff --git a/Sources/Paths/GetInstantMixFromSongAPI.swift b/Sources/Paths/GetInstantMixFromSongAPI.swift index ff5b02f3..6b16ed43 100644 --- a/Sources/Paths/GetInstantMixFromSongAPI.swift +++ b/Sources/Paths/GetInstantMixFromSongAPI.swift @@ -13,10 +13,10 @@ import URLQueryEncoder public extension Paths { /// Creates an instant playlist based on a given song. static func getInstantMixFromSong( - id: String, + itemID: String, parameters: GetInstantMixFromSongParameters? = nil ) -> Request { - Request(path: "/Songs/\(id)/InstantMix", method: "GET", query: parameters?.asQuery, id: "GetInstantMixFromSong") + Request(path: "/Songs/\(itemID)/InstantMix", method: "GET", query: parameters?.asQuery, id: "GetInstantMixFromSong") } struct GetInstantMixFromSongParameters { diff --git a/Sources/Paths/GetIntrosAPI.swift b/Sources/Paths/GetIntrosAPI.swift index 76228cf7..75449122 100644 --- a/Sources/Paths/GetIntrosAPI.swift +++ b/Sources/Paths/GetIntrosAPI.swift @@ -10,9 +10,15 @@ import Foundation import Get import URLQueryEncoder -public extension Paths { +extension Paths { /// Gets intros to play before the main media item plays. - static func getIntros(userID: String, itemID: String) -> Request { - Request(path: "/Users/\(userID)/Items/\(itemID)/Intros", method: "GET", id: "GetIntros") + public static func getIntros(itemID: String, userID: String? = nil) -> Request { + Request(path: "/Items/\(itemID)/Intros", method: "GET", query: makeGetIntrosQuery(userID), id: "GetIntros") + } + + private static func makeGetIntrosQuery(_ userID: String?) -> [(String, String?)] { + let encoder = URLQueryEncoder() + encoder.encode(userID, forKey: "userId") + return encoder.items } } diff --git a/Sources/Paths/GetItemAPI.swift b/Sources/Paths/GetItemAPI.swift index 05b0398d..edebe35b 100644 --- a/Sources/Paths/GetItemAPI.swift +++ b/Sources/Paths/GetItemAPI.swift @@ -10,9 +10,15 @@ import Foundation import Get import URLQueryEncoder -public extension Paths { +extension Paths { /// Gets an item from a user's library. - static func getItem(userID: String, itemID: String) -> Request { - Request(path: "/Users/\(userID)/Items/\(itemID)", method: "GET", id: "GetItem") + public static func getItem(itemID: String, userID: String? = nil) -> Request { + Request(path: "/Items/\(itemID)", method: "GET", query: makeGetItemQuery(userID), id: "GetItem") + } + + private static func makeGetItemQuery(_ userID: String?) -> [(String, String?)] { + let encoder = URLQueryEncoder() + encoder.encode(userID, forKey: "userId") + return encoder.items } } diff --git a/Sources/Paths/GetItemImage2API.swift b/Sources/Paths/GetItemImage2API.swift index fb5947db..aa7f557e 100644 --- a/Sources/Paths/GetItemImage2API.swift +++ b/Sources/Paths/GetItemImage2API.swift @@ -38,8 +38,6 @@ public extension Paths { public var quality: Int? public var fillWidth: Int? public var fillHeight: Int? - public var isCropWhitespace: Bool? - public var isAddPlayedIndicator: Bool? public var blur: Int? public var backgroundColor: String? public var foregroundLayer: String? @@ -50,8 +48,6 @@ public extension Paths { quality: Int? = nil, fillWidth: Int? = nil, fillHeight: Int? = nil, - isCropWhitespace: Bool? = nil, - isAddPlayedIndicator: Bool? = nil, blur: Int? = nil, backgroundColor: String? = nil, foregroundLayer: String? = nil @@ -61,8 +57,6 @@ public extension Paths { self.quality = quality self.fillWidth = fillWidth self.fillHeight = fillHeight - self.isCropWhitespace = isCropWhitespace - self.isAddPlayedIndicator = isAddPlayedIndicator self.blur = blur self.backgroundColor = backgroundColor self.foregroundLayer = foregroundLayer @@ -75,8 +69,6 @@ public extension Paths { encoder.encode(quality, forKey: "quality") encoder.encode(fillWidth, forKey: "fillWidth") encoder.encode(fillHeight, forKey: "fillHeight") - encoder.encode(isCropWhitespace, forKey: "cropWhitespace") - encoder.encode(isAddPlayedIndicator, forKey: "addPlayedIndicator") encoder.encode(blur, forKey: "blur") encoder.encode(backgroundColor, forKey: "backgroundColor") encoder.encode(foregroundLayer, forKey: "foregroundLayer") diff --git a/Sources/Paths/GetItemImageAPI.swift b/Sources/Paths/GetItemImageAPI.swift index 599244fd..b3dd0eec 100644 --- a/Sources/Paths/GetItemImageAPI.swift +++ b/Sources/Paths/GetItemImageAPI.swift @@ -25,9 +25,7 @@ public extension Paths { public var fillWidth: Int? public var fillHeight: Int? public var tag: String? - public var isCropWhitespace: Bool? public var format: Format? - public var isAddPlayedIndicator: Bool? public var percentPlayed: Double? public var unplayedCount: Int? public var blur: Int? @@ -46,9 +44,7 @@ public extension Paths { fillWidth: Int? = nil, fillHeight: Int? = nil, tag: String? = nil, - isCropWhitespace: Bool? = nil, format: Format? = nil, - isAddPlayedIndicator: Bool? = nil, percentPlayed: Double? = nil, unplayedCount: Int? = nil, blur: Int? = nil, @@ -64,9 +60,7 @@ public extension Paths { self.fillWidth = fillWidth self.fillHeight = fillHeight self.tag = tag - self.isCropWhitespace = isCropWhitespace self.format = format - self.isAddPlayedIndicator = isAddPlayedIndicator self.percentPlayed = percentPlayed self.unplayedCount = unplayedCount self.blur = blur @@ -85,9 +79,7 @@ public extension Paths { encoder.encode(fillWidth, forKey: "fillWidth") encoder.encode(fillHeight, forKey: "fillHeight") encoder.encode(tag, forKey: "tag") - encoder.encode(isCropWhitespace, forKey: "cropWhitespace") encoder.encode(format, forKey: "format") - encoder.encode(isAddPlayedIndicator, forKey: "addPlayedIndicator") encoder.encode(percentPlayed, forKey: "percentPlayed") encoder.encode(unplayedCount, forKey: "unplayedCount") encoder.encode(blur, forKey: "blur") diff --git a/Sources/Paths/GetItemImageByIndexAPI.swift b/Sources/Paths/GetItemImageByIndexAPI.swift index fa386550..a4c278ec 100644 --- a/Sources/Paths/GetItemImageByIndexAPI.swift +++ b/Sources/Paths/GetItemImageByIndexAPI.swift @@ -35,9 +35,7 @@ public extension Paths { public var fillWidth: Int? public var fillHeight: Int? public var tag: String? - public var isCropWhitespace: Bool? public var format: Format? - public var isAddPlayedIndicator: Bool? public var percentPlayed: Double? public var unplayedCount: Int? public var blur: Int? @@ -55,9 +53,7 @@ public extension Paths { fillWidth: Int? = nil, fillHeight: Int? = nil, tag: String? = nil, - isCropWhitespace: Bool? = nil, format: Format? = nil, - isAddPlayedIndicator: Bool? = nil, percentPlayed: Double? = nil, unplayedCount: Int? = nil, blur: Int? = nil, @@ -72,9 +68,7 @@ public extension Paths { self.fillWidth = fillWidth self.fillHeight = fillHeight self.tag = tag - self.isCropWhitespace = isCropWhitespace self.format = format - self.isAddPlayedIndicator = isAddPlayedIndicator self.percentPlayed = percentPlayed self.unplayedCount = unplayedCount self.blur = blur @@ -92,9 +86,7 @@ public extension Paths { encoder.encode(fillWidth, forKey: "fillWidth") encoder.encode(fillHeight, forKey: "fillHeight") encoder.encode(tag, forKey: "tag") - encoder.encode(isCropWhitespace, forKey: "cropWhitespace") encoder.encode(format, forKey: "format") - encoder.encode(isAddPlayedIndicator, forKey: "addPlayedIndicator") encoder.encode(percentPlayed, forKey: "percentPlayed") encoder.encode(unplayedCount, forKey: "unplayedCount") encoder.encode(blur, forKey: "blur") diff --git a/Sources/Paths/GetItemUserDataAPI.swift b/Sources/Paths/GetItemUserDataAPI.swift new file mode 100644 index 00000000..d4c86512 --- /dev/null +++ b/Sources/Paths/GetItemUserDataAPI.swift @@ -0,0 +1,24 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation +import Get +import URLQueryEncoder + +extension Paths { + /// Get Item User Data. + public static func getItemUserData(itemID: String, userID: String? = nil) -> Request { + Request(path: "/UserItems/\(itemID)/UserData", method: "GET", query: makeGetItemUserDataQuery(userID), id: "GetItemUserData") + } + + private static func makeGetItemUserDataQuery(_ userID: String?) -> [(String, String?)] { + let encoder = URLQueryEncoder() + encoder.encode(userID, forKey: "userId") + return encoder.items + } +} diff --git a/Sources/Paths/GetItemsAPI.swift b/Sources/Paths/GetItemsAPI.swift index 06b6f0c0..2d42abf3 100644 --- a/Sources/Paths/GetItemsAPI.swift +++ b/Sources/Paths/GetItemsAPI.swift @@ -60,9 +60,9 @@ public extension Paths { public var includeItemTypes: [JellyfinAPI.BaseItemKind]? public var filters: [JellyfinAPI.ItemFilter]? public var isFavorite: Bool? - public var mediaTypes: [String]? + public var mediaTypes: [JellyfinAPI.MediaType]? public var imageTypes: [JellyfinAPI.ImageType]? - public var sortBy: [String]? + public var sortBy: [JellyfinAPI.ItemSortBy]? public var isPlayed: Bool? public var genres: [String]? public var officialRatings: [String]? @@ -147,9 +147,9 @@ public extension Paths { includeItemTypes: [JellyfinAPI.BaseItemKind]? = nil, filters: [JellyfinAPI.ItemFilter]? = nil, isFavorite: Bool? = nil, - mediaTypes: [String]? = nil, + mediaTypes: [JellyfinAPI.MediaType]? = nil, imageTypes: [JellyfinAPI.ImageType]? = nil, - sortBy: [String]? = nil, + sortBy: [JellyfinAPI.ItemSortBy]? = nil, isPlayed: Bool? = nil, genres: [String]? = nil, officialRatings: [String]? = nil, diff --git a/Sources/Paths/GetLatestMediaAPI.swift b/Sources/Paths/GetLatestMediaAPI.swift index b59e717f..cf3f43cc 100644 --- a/Sources/Paths/GetLatestMediaAPI.swift +++ b/Sources/Paths/GetLatestMediaAPI.swift @@ -12,11 +12,12 @@ import URLQueryEncoder public extension Paths { /// Gets latest media. - static func getLatestMedia(userID: String, parameters: GetLatestMediaParameters? = nil) -> Request<[JellyfinAPI.BaseItemDto]> { - Request(path: "/Users/\(userID)/Items/Latest", method: "GET", query: parameters?.asQuery, id: "GetLatestMedia") + static func getLatestMedia(parameters: GetLatestMediaParameters? = nil) -> Request<[JellyfinAPI.BaseItemDto]> { + Request(path: "/Items/Latest", method: "GET", query: parameters?.asQuery, id: "GetLatestMedia") } struct GetLatestMediaParameters { + public var userID: String? public var parentID: String? public var fields: [JellyfinAPI.ItemFields]? public var includeItemTypes: [JellyfinAPI.BaseItemKind]? @@ -29,6 +30,7 @@ public extension Paths { public var isGroupItems: Bool? public init( + userID: String? = nil, parentID: String? = nil, fields: [JellyfinAPI.ItemFields]? = nil, includeItemTypes: [JellyfinAPI.BaseItemKind]? = nil, @@ -40,6 +42,7 @@ public extension Paths { limit: Int? = nil, isGroupItems: Bool? = nil ) { + self.userID = userID self.parentID = parentID self.fields = fields self.includeItemTypes = includeItemTypes @@ -54,6 +57,7 @@ public extension Paths { public var asQuery: [(String, String?)] { let encoder = URLQueryEncoder() + encoder.encode(userID, forKey: "userId") encoder.encode(parentID, forKey: "parentId") encoder.encode(fields, forKey: "fields") encoder.encode(includeItemTypes, forKey: "includeItemTypes") diff --git a/Sources/Paths/GetLibraryOptionsInfoAPI.swift b/Sources/Paths/GetLibraryOptionsInfoAPI.swift index ebbddeca..d7de8e03 100644 --- a/Sources/Paths/GetLibraryOptionsInfoAPI.swift +++ b/Sources/Paths/GetLibraryOptionsInfoAPI.swift @@ -10,24 +10,28 @@ import Foundation import Get import URLQueryEncoder -extension Paths { +public extension Paths { /// Gets the library options info. - public static func getLibraryOptionsInfo( - libraryContentType: String? = nil, - isNewLibrary: Bool? = nil - ) -> Request { - Request( - path: "/Libraries/AvailableOptions", - method: "GET", - query: makeGetLibraryOptionsInfoQuery(libraryContentType, isNewLibrary), - id: "GetLibraryOptionsInfo" - ) + static func getLibraryOptionsInfo(parameters: GetLibraryOptionsInfoParameters? = nil) -> Request { + Request(path: "/Libraries/AvailableOptions", method: "GET", query: parameters?.asQuery, id: "GetLibraryOptionsInfo") } - private static func makeGetLibraryOptionsInfoQuery(_ libraryContentType: String?, _ isNewLibrary: Bool?) -> [(String, String?)] { - let encoder = URLQueryEncoder() - encoder.encode(libraryContentType, forKey: "libraryContentType") - encoder.encode(isNewLibrary, forKey: "isNewLibrary") - return encoder.items + struct GetLibraryOptionsInfoParameters { + public var libraryContentType: LibraryContentType? + public var isNewLibrary: Bool? + + public typealias LibraryContentType = JellyfinAPI.CollectionType + + public init(libraryContentType: LibraryContentType? = nil, isNewLibrary: Bool? = nil) { + self.libraryContentType = libraryContentType + self.isNewLibrary = isNewLibrary + } + + public var asQuery: [(String, String?)] { + let encoder = URLQueryEncoder() + encoder.encode(libraryContentType, forKey: "libraryContentType") + encoder.encode(isNewLibrary, forKey: "isNewLibrary") + return encoder.items + } } } diff --git a/Sources/Paths/GetLiveTvChannelsAPI.swift b/Sources/Paths/GetLiveTvChannelsAPI.swift index 659a6501..df42dcb0 100644 --- a/Sources/Paths/GetLiveTvChannelsAPI.swift +++ b/Sources/Paths/GetLiveTvChannelsAPI.swift @@ -34,7 +34,7 @@ public extension Paths { public var enableImageTypes: [JellyfinAPI.ImageType]? public var fields: [JellyfinAPI.ItemFields]? public var enableUserData: Bool? - public var sortBy: [String]? + public var sortBy: [JellyfinAPI.ItemSortBy]? public var sortOrder: SortOrder? public var enableFavoriteSorting: Bool? public var isAddCurrentProgram: Bool? @@ -61,7 +61,7 @@ public extension Paths { enableImageTypes: [JellyfinAPI.ImageType]? = nil, fields: [JellyfinAPI.ItemFields]? = nil, enableUserData: Bool? = nil, - sortBy: [String]? = nil, + sortBy: [JellyfinAPI.ItemSortBy]? = nil, sortOrder: SortOrder? = nil, enableFavoriteSorting: Bool? = nil, isAddCurrentProgram: Bool? = nil diff --git a/Sources/Paths/GetLiveTvProgramsAPI.swift b/Sources/Paths/GetLiveTvProgramsAPI.swift index ee0b744f..a3c13e35 100644 --- a/Sources/Paths/GetLiveTvProgramsAPI.swift +++ b/Sources/Paths/GetLiveTvProgramsAPI.swift @@ -32,7 +32,7 @@ public extension Paths { public var isSports: Bool? public var startIndex: Int? public var limit: Int? - public var sortBy: [String]? + public var sortBy: [JellyfinAPI.ItemSortBy]? public var sortOrder: [JellyfinAPI.SortOrder]? public var genres: [String]? public var genreIDs: [String]? @@ -61,7 +61,7 @@ public extension Paths { isSports: Bool? = nil, startIndex: Int? = nil, limit: Int? = nil, - sortBy: [String]? = nil, + sortBy: [JellyfinAPI.ItemSortBy]? = nil, sortOrder: [JellyfinAPI.SortOrder]? = nil, genres: [String]? = nil, genreIDs: [String]? = nil, diff --git a/Sources/Paths/GetLocalTrailersAPI.swift b/Sources/Paths/GetLocalTrailersAPI.swift index db1789f4..e1a7efa4 100644 --- a/Sources/Paths/GetLocalTrailersAPI.swift +++ b/Sources/Paths/GetLocalTrailersAPI.swift @@ -10,9 +10,15 @@ import Foundation import Get import URLQueryEncoder -public extension Paths { +extension Paths { /// Gets local trailers for an item. - static func getLocalTrailers(userID: String, itemID: String) -> Request<[JellyfinAPI.BaseItemDto]> { - Request(path: "/Users/\(userID)/Items/\(itemID)/LocalTrailers", method: "GET", id: "GetLocalTrailers") + public static func getLocalTrailers(itemID: String, userID: String? = nil) -> Request<[JellyfinAPI.BaseItemDto]> { + Request(path: "/Items/\(itemID)/LocalTrailers", method: "GET", query: makeGetLocalTrailersQuery(userID), id: "GetLocalTrailers") + } + + private static func makeGetLocalTrailersQuery(_ userID: String?) -> [(String, String?)] { + let encoder = URLQueryEncoder() + encoder.encode(userID, forKey: "userId") + return encoder.items } } diff --git a/Sources/Paths/GetLyricsAPI.swift b/Sources/Paths/GetLyricsAPI.swift new file mode 100644 index 00000000..2c358362 --- /dev/null +++ b/Sources/Paths/GetLyricsAPI.swift @@ -0,0 +1,18 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation +import Get +import URLQueryEncoder + +public extension Paths { + /// Gets an item's lyrics. + static func getLyrics(itemID: String) -> Request { + Request(path: "/Audio/\(itemID)/Lyrics", method: "GET", id: "GetLyrics") + } +} diff --git a/Sources/Paths/GetMasterHlsVideoPlaylistAPI.swift b/Sources/Paths/GetMasterHlsVideoPlaylistAPI.swift index 4c5174f6..a9eaa130 100644 --- a/Sources/Paths/GetMasterHlsVideoPlaylistAPI.swift +++ b/Sources/Paths/GetMasterHlsVideoPlaylistAPI.swift @@ -67,6 +67,7 @@ public extension Paths { public var context: Context? public var streamOptions: StreamOptions? public var enableAdaptiveBitrateStreaming: Bool? + public var enableTrickplay: Bool? public typealias SubtitleMethod = JellyfinAPI.SubtitleDeliveryMethod @@ -124,7 +125,8 @@ public extension Paths { videoStreamIndex: Int? = nil, context: Context? = nil, streamOptions: StreamOptions? = nil, - enableAdaptiveBitrateStreaming: Bool? = nil + enableAdaptiveBitrateStreaming: Bool? = nil, + enableTrickplay: Bool? = nil ) { self.isStatic = isStatic self.params = params @@ -176,6 +178,7 @@ public extension Paths { self.context = context self.streamOptions = streamOptions self.enableAdaptiveBitrateStreaming = enableAdaptiveBitrateStreaming + self.enableTrickplay = enableTrickplay } public var asQuery: [(String, String?)] { @@ -230,6 +233,7 @@ public extension Paths { encoder.encode(context, forKey: "context") encoder.encode(streamOptions, forKey: "streamOptions") encoder.encode(enableAdaptiveBitrateStreaming, forKey: "enableAdaptiveBitrateStreaming") + encoder.encode(enableTrickplay, forKey: "enableTrickplay") return encoder.items } } diff --git a/Sources/Paths/GetMusicGenreImageAPI.swift b/Sources/Paths/GetMusicGenreImageAPI.swift index 0ddbd6c9..dd629527 100644 --- a/Sources/Paths/GetMusicGenreImageAPI.swift +++ b/Sources/Paths/GetMusicGenreImageAPI.swift @@ -28,8 +28,6 @@ public extension Paths { public var quality: Int? public var fillWidth: Int? public var fillHeight: Int? - public var isCropWhitespace: Bool? - public var isAddPlayedIndicator: Bool? public var blur: Int? public var backgroundColor: String? public var foregroundLayer: String? @@ -49,8 +47,6 @@ public extension Paths { quality: Int? = nil, fillWidth: Int? = nil, fillHeight: Int? = nil, - isCropWhitespace: Bool? = nil, - isAddPlayedIndicator: Bool? = nil, blur: Int? = nil, backgroundColor: String? = nil, foregroundLayer: String? = nil, @@ -67,8 +63,6 @@ public extension Paths { self.quality = quality self.fillWidth = fillWidth self.fillHeight = fillHeight - self.isCropWhitespace = isCropWhitespace - self.isAddPlayedIndicator = isAddPlayedIndicator self.blur = blur self.backgroundColor = backgroundColor self.foregroundLayer = foregroundLayer @@ -88,8 +82,6 @@ public extension Paths { encoder.encode(quality, forKey: "quality") encoder.encode(fillWidth, forKey: "fillWidth") encoder.encode(fillHeight, forKey: "fillHeight") - encoder.encode(isCropWhitespace, forKey: "cropWhitespace") - encoder.encode(isAddPlayedIndicator, forKey: "addPlayedIndicator") encoder.encode(blur, forKey: "blur") encoder.encode(backgroundColor, forKey: "backgroundColor") encoder.encode(foregroundLayer, forKey: "foregroundLayer") diff --git a/Sources/Paths/GetMusicGenreImageByIndexAPI.swift b/Sources/Paths/GetMusicGenreImageByIndexAPI.swift index 1412f7fc..81be5789 100644 --- a/Sources/Paths/GetMusicGenreImageByIndexAPI.swift +++ b/Sources/Paths/GetMusicGenreImageByIndexAPI.swift @@ -38,8 +38,6 @@ public extension Paths { public var quality: Int? public var fillWidth: Int? public var fillHeight: Int? - public var isCropWhitespace: Bool? - public var isAddPlayedIndicator: Bool? public var blur: Int? public var backgroundColor: String? public var foregroundLayer: String? @@ -58,8 +56,6 @@ public extension Paths { quality: Int? = nil, fillWidth: Int? = nil, fillHeight: Int? = nil, - isCropWhitespace: Bool? = nil, - isAddPlayedIndicator: Bool? = nil, blur: Int? = nil, backgroundColor: String? = nil, foregroundLayer: String? = nil @@ -75,8 +71,6 @@ public extension Paths { self.quality = quality self.fillWidth = fillWidth self.fillHeight = fillHeight - self.isCropWhitespace = isCropWhitespace - self.isAddPlayedIndicator = isAddPlayedIndicator self.blur = blur self.backgroundColor = backgroundColor self.foregroundLayer = foregroundLayer @@ -95,8 +89,6 @@ public extension Paths { encoder.encode(quality, forKey: "quality") encoder.encode(fillWidth, forKey: "fillWidth") encoder.encode(fillHeight, forKey: "fillHeight") - encoder.encode(isCropWhitespace, forKey: "cropWhitespace") - encoder.encode(isAddPlayedIndicator, forKey: "addPlayedIndicator") encoder.encode(blur, forKey: "blur") encoder.encode(backgroundColor, forKey: "backgroundColor") encoder.encode(foregroundLayer, forKey: "foregroundLayer") diff --git a/Sources/Paths/GetMusicGenresAPI.swift b/Sources/Paths/GetMusicGenresAPI.swift index 419ddd95..10d16d75 100644 --- a/Sources/Paths/GetMusicGenresAPI.swift +++ b/Sources/Paths/GetMusicGenresAPI.swift @@ -32,7 +32,7 @@ public extension Paths { public var nameStartsWithOrGreater: String? public var nameStartsWith: String? public var nameLessThan: String? - public var sortBy: [String]? + public var sortBy: [JellyfinAPI.ItemSortBy]? public var sortOrder: [JellyfinAPI.SortOrder]? public var enableImages: Bool? public var enableTotalRecordCount: Bool? @@ -52,7 +52,7 @@ public extension Paths { nameStartsWithOrGreater: String? = nil, nameStartsWith: String? = nil, nameLessThan: String? = nil, - sortBy: [String]? = nil, + sortBy: [JellyfinAPI.ItemSortBy]? = nil, sortOrder: [JellyfinAPI.SortOrder]? = nil, enableImages: Bool? = nil, enableTotalRecordCount: Bool? = nil diff --git a/Sources/Paths/GetNextUpAPI.swift b/Sources/Paths/GetNextUpAPI.swift index 1e82a9c0..bbd58b01 100644 --- a/Sources/Paths/GetNextUpAPI.swift +++ b/Sources/Paths/GetNextUpAPI.swift @@ -30,6 +30,7 @@ public extension Paths { public var nextUpDateCutoff: Date? public var enableTotalRecordCount: Bool? public var isDisableFirstEpisode: Bool? + public var enableResumable: Bool? public var enableRewatching: Bool? public init( @@ -46,6 +47,7 @@ public extension Paths { nextUpDateCutoff: Date? = nil, enableTotalRecordCount: Bool? = nil, isDisableFirstEpisode: Bool? = nil, + enableResumable: Bool? = nil, enableRewatching: Bool? = nil ) { self.userID = userID @@ -61,6 +63,7 @@ public extension Paths { self.nextUpDateCutoff = nextUpDateCutoff self.enableTotalRecordCount = enableTotalRecordCount self.isDisableFirstEpisode = isDisableFirstEpisode + self.enableResumable = enableResumable self.enableRewatching = enableRewatching } @@ -79,6 +82,7 @@ public extension Paths { encoder.encode(nextUpDateCutoff, forKey: "nextUpDateCutoff") encoder.encode(enableTotalRecordCount, forKey: "enableTotalRecordCount") encoder.encode(isDisableFirstEpisode, forKey: "disableFirstEpisode") + encoder.encode(enableResumable, forKey: "enableResumable") encoder.encode(enableRewatching, forKey: "enableRewatching") return encoder.items } diff --git a/Sources/Paths/GetPersonImageAPI.swift b/Sources/Paths/GetPersonImageAPI.swift index 8b1d0507..7e931788 100644 --- a/Sources/Paths/GetPersonImageAPI.swift +++ b/Sources/Paths/GetPersonImageAPI.swift @@ -28,8 +28,6 @@ public extension Paths { public var quality: Int? public var fillWidth: Int? public var fillHeight: Int? - public var isCropWhitespace: Bool? - public var isAddPlayedIndicator: Bool? public var blur: Int? public var backgroundColor: String? public var foregroundLayer: String? @@ -49,8 +47,6 @@ public extension Paths { quality: Int? = nil, fillWidth: Int? = nil, fillHeight: Int? = nil, - isCropWhitespace: Bool? = nil, - isAddPlayedIndicator: Bool? = nil, blur: Int? = nil, backgroundColor: String? = nil, foregroundLayer: String? = nil, @@ -67,8 +63,6 @@ public extension Paths { self.quality = quality self.fillWidth = fillWidth self.fillHeight = fillHeight - self.isCropWhitespace = isCropWhitespace - self.isAddPlayedIndicator = isAddPlayedIndicator self.blur = blur self.backgroundColor = backgroundColor self.foregroundLayer = foregroundLayer @@ -88,8 +82,6 @@ public extension Paths { encoder.encode(quality, forKey: "quality") encoder.encode(fillWidth, forKey: "fillWidth") encoder.encode(fillHeight, forKey: "fillHeight") - encoder.encode(isCropWhitespace, forKey: "cropWhitespace") - encoder.encode(isAddPlayedIndicator, forKey: "addPlayedIndicator") encoder.encode(blur, forKey: "blur") encoder.encode(backgroundColor, forKey: "backgroundColor") encoder.encode(foregroundLayer, forKey: "foregroundLayer") diff --git a/Sources/Paths/GetPersonImageByIndexAPI.swift b/Sources/Paths/GetPersonImageByIndexAPI.swift index 020dd03f..c35e1605 100644 --- a/Sources/Paths/GetPersonImageByIndexAPI.swift +++ b/Sources/Paths/GetPersonImageByIndexAPI.swift @@ -38,8 +38,6 @@ public extension Paths { public var quality: Int? public var fillWidth: Int? public var fillHeight: Int? - public var isCropWhitespace: Bool? - public var isAddPlayedIndicator: Bool? public var blur: Int? public var backgroundColor: String? public var foregroundLayer: String? @@ -58,8 +56,6 @@ public extension Paths { quality: Int? = nil, fillWidth: Int? = nil, fillHeight: Int? = nil, - isCropWhitespace: Bool? = nil, - isAddPlayedIndicator: Bool? = nil, blur: Int? = nil, backgroundColor: String? = nil, foregroundLayer: String? = nil @@ -75,8 +71,6 @@ public extension Paths { self.quality = quality self.fillWidth = fillWidth self.fillHeight = fillHeight - self.isCropWhitespace = isCropWhitespace - self.isAddPlayedIndicator = isAddPlayedIndicator self.blur = blur self.backgroundColor = backgroundColor self.foregroundLayer = foregroundLayer @@ -95,8 +89,6 @@ public extension Paths { encoder.encode(quality, forKey: "quality") encoder.encode(fillWidth, forKey: "fillWidth") encoder.encode(fillHeight, forKey: "fillHeight") - encoder.encode(isCropWhitespace, forKey: "cropWhitespace") - encoder.encode(isAddPlayedIndicator, forKey: "addPlayedIndicator") encoder.encode(blur, forKey: "blur") encoder.encode(backgroundColor, forKey: "backgroundColor") encoder.encode(foregroundLayer, forKey: "foregroundLayer") diff --git a/Sources/Paths/GetPlaybackInfoAPI.swift b/Sources/Paths/GetPlaybackInfoAPI.swift index ceeb9d69..d332135b 100644 --- a/Sources/Paths/GetPlaybackInfoAPI.swift +++ b/Sources/Paths/GetPlaybackInfoAPI.swift @@ -10,9 +10,15 @@ import Foundation import Get import URLQueryEncoder -public extension Paths { +extension Paths { /// Gets live playback media info for an item. - static func getPlaybackInfo(itemID: String, userID: String) -> Request { - Request(path: "/Items/\(itemID)/PlaybackInfo", method: "GET", query: [("userId", userID)], id: "GetPlaybackInfo") + public static func getPlaybackInfo(itemID: String, userID: String? = nil) -> Request { + Request(path: "/Items/\(itemID)/PlaybackInfo", method: "GET", query: makeGetPlaybackInfoQuery(userID), id: "GetPlaybackInfo") + } + + private static func makeGetPlaybackInfoQuery(_ userID: String?) -> [(String, String?)] { + let encoder = URLQueryEncoder() + encoder.encode(userID, forKey: "userId") + return encoder.items } } diff --git a/Sources/Paths/GetPlaylistItemsAPI.swift b/Sources/Paths/GetPlaylistItemsAPI.swift index 98b7b615..70c8ae06 100644 --- a/Sources/Paths/GetPlaylistItemsAPI.swift +++ b/Sources/Paths/GetPlaylistItemsAPI.swift @@ -14,13 +14,13 @@ public extension Paths { /// Gets the original items of a playlist. static func getPlaylistItems( playlistID: String, - parameters: GetPlaylistItemsParameters + parameters: GetPlaylistItemsParameters? = nil ) -> Request { - Request(path: "/Playlists/\(playlistID)/Items", method: "GET", query: parameters.asQuery, id: "GetPlaylistItems") + Request(path: "/Playlists/\(playlistID)/Items", method: "GET", query: parameters?.asQuery, id: "GetPlaylistItems") } struct GetPlaylistItemsParameters { - public var userID: String + public var userID: String? public var startIndex: Int? public var limit: Int? public var fields: [JellyfinAPI.ItemFields]? @@ -30,7 +30,7 @@ public extension Paths { public var enableImageTypes: [JellyfinAPI.ImageType]? public init( - userID: String, + userID: String? = nil, startIndex: Int? = nil, limit: Int? = nil, fields: [JellyfinAPI.ItemFields]? = nil, diff --git a/Sources/Paths/GetPlaylistUserAPI.swift b/Sources/Paths/GetPlaylistUserAPI.swift new file mode 100644 index 00000000..83c46d29 --- /dev/null +++ b/Sources/Paths/GetPlaylistUserAPI.swift @@ -0,0 +1,18 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation +import Get +import URLQueryEncoder + +public extension Paths { + /// Get a playlist user. + static func getPlaylistUser(playlistID: String, userID: String) -> Request { + Request(path: "/Playlists/\(playlistID)/Users/\(userID)", method: "GET", id: "GetPlaylistUser") + } +} diff --git a/Sources/Paths/GetPlaylistUsersAPI.swift b/Sources/Paths/GetPlaylistUsersAPI.swift new file mode 100644 index 00000000..13567421 --- /dev/null +++ b/Sources/Paths/GetPlaylistUsersAPI.swift @@ -0,0 +1,18 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation +import Get +import URLQueryEncoder + +public extension Paths { + /// Get a playlist's users. + static func getPlaylistUsers(playlistID: String) -> Request<[JellyfinAPI.PlaylistUserPermissions]> { + Request(path: "/Playlists/\(playlistID)/Users", method: "GET", id: "GetPlaylistUsers") + } +} diff --git a/Sources/Paths/GetQueryFiltersLegacyAPI.swift b/Sources/Paths/GetQueryFiltersLegacyAPI.swift index 3c12156f..86938453 100644 --- a/Sources/Paths/GetQueryFiltersLegacyAPI.swift +++ b/Sources/Paths/GetQueryFiltersLegacyAPI.swift @@ -20,13 +20,13 @@ public extension Paths { public var userID: String? public var parentID: String? public var includeItemTypes: [JellyfinAPI.BaseItemKind]? - public var mediaTypes: [String]? + public var mediaTypes: [JellyfinAPI.MediaType]? public init( userID: String? = nil, parentID: String? = nil, includeItemTypes: [JellyfinAPI.BaseItemKind]? = nil, - mediaTypes: [String]? = nil + mediaTypes: [JellyfinAPI.MediaType]? = nil ) { self.userID = userID self.parentID = parentID diff --git a/Sources/Paths/GetQuickConnectEnabledAPI.swift b/Sources/Paths/GetQuickConnectEnabledAPI.swift new file mode 100644 index 00000000..5793ca3d --- /dev/null +++ b/Sources/Paths/GetQuickConnectEnabledAPI.swift @@ -0,0 +1,18 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation +import Get +import URLQueryEncoder + +public extension Paths { + /// Gets the current quick connect state. + static var getQuickConnectEnabled: Request { + Request(path: "/QuickConnect/Enabled", method: "GET", id: "GetQuickConnectEnabled") + } +} diff --git a/Sources/Paths/GetQuickConnectStateAPI.swift b/Sources/Paths/GetQuickConnectStateAPI.swift new file mode 100644 index 00000000..cfee96aa --- /dev/null +++ b/Sources/Paths/GetQuickConnectStateAPI.swift @@ -0,0 +1,18 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation +import Get +import URLQueryEncoder + +public extension Paths { + /// Attempts to retrieve authentication information. + static func getQuickConnectState(secret: String) -> Request { + Request(path: "/QuickConnect/Connect", method: "GET", query: [("secret", secret)], id: "GetQuickConnectState") + } +} diff --git a/Sources/Paths/GetRemoteLyricsAPI.swift b/Sources/Paths/GetRemoteLyricsAPI.swift new file mode 100644 index 00000000..5b3f771b --- /dev/null +++ b/Sources/Paths/GetRemoteLyricsAPI.swift @@ -0,0 +1,18 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation +import Get +import URLQueryEncoder + +public extension Paths { + /// Gets the remote lyrics. + static func getRemoteLyrics(lyricID: String) -> Request { + Request(path: "/Providers/Lyrics/\(lyricID)", method: "GET", id: "GetRemoteLyrics") + } +} diff --git a/Sources/Paths/GetRemoteSubtitlesAPI.swift b/Sources/Paths/GetRemoteSubtitlesAPI.swift index d3975441..7c0e84f4 100644 --- a/Sources/Paths/GetRemoteSubtitlesAPI.swift +++ b/Sources/Paths/GetRemoteSubtitlesAPI.swift @@ -12,7 +12,7 @@ import URLQueryEncoder public extension Paths { /// Gets the remote subtitles. - static func getRemoteSubtitles(id: String) -> Request { - Request(path: "/Providers/Subtitles/Subtitles/\(id)", method: "GET", id: "GetRemoteSubtitles") + static func getRemoteSubtitles(subtitleID: String) -> Request { + Request(path: "/Providers/Subtitles/Subtitles/\(subtitleID)", method: "GET", id: "GetRemoteSubtitles") } } diff --git a/Sources/Paths/GetResumeItemsAPI.swift b/Sources/Paths/GetResumeItemsAPI.swift index c093ee0f..5a0d3993 100644 --- a/Sources/Paths/GetResumeItemsAPI.swift +++ b/Sources/Paths/GetResumeItemsAPI.swift @@ -12,17 +12,18 @@ import URLQueryEncoder public extension Paths { /// Gets items based on a query. - static func getResumeItems(userID: String, parameters: GetResumeItemsParameters? = nil) -> Request { - Request(path: "/Users/\(userID)/Items/Resume", method: "GET", query: parameters?.asQuery, id: "GetResumeItems") + static func getResumeItems(parameters: GetResumeItemsParameters? = nil) -> Request { + Request(path: "/UserItems/Resume", method: "GET", query: parameters?.asQuery, id: "GetResumeItems") } struct GetResumeItemsParameters { + public var userID: String? public var startIndex: Int? public var limit: Int? public var searchTerm: String? public var parentID: String? public var fields: [JellyfinAPI.ItemFields]? - public var mediaTypes: [String]? + public var mediaTypes: [JellyfinAPI.MediaType]? public var enableUserData: Bool? public var imageTypeLimit: Int? public var enableImageTypes: [JellyfinAPI.ImageType]? @@ -33,12 +34,13 @@ public extension Paths { public var excludeActiveSessions: Bool? public init( + userID: String? = nil, startIndex: Int? = nil, limit: Int? = nil, searchTerm: String? = nil, parentID: String? = nil, fields: [JellyfinAPI.ItemFields]? = nil, - mediaTypes: [String]? = nil, + mediaTypes: [JellyfinAPI.MediaType]? = nil, enableUserData: Bool? = nil, imageTypeLimit: Int? = nil, enableImageTypes: [JellyfinAPI.ImageType]? = nil, @@ -48,6 +50,7 @@ public extension Paths { enableImages: Bool? = nil, excludeActiveSessions: Bool? = nil ) { + self.userID = userID self.startIndex = startIndex self.limit = limit self.searchTerm = searchTerm @@ -66,6 +69,7 @@ public extension Paths { public var asQuery: [(String, String?)] { let encoder = URLQueryEncoder() + encoder.encode(userID, forKey: "userId") encoder.encode(startIndex, forKey: "startIndex") encoder.encode(limit, forKey: "limit") encoder.encode(searchTerm, forKey: "searchTerm") diff --git a/Sources/Paths/GetRootFolderAPI.swift b/Sources/Paths/GetRootFolderAPI.swift index 1415c3f0..1eefc4e9 100644 --- a/Sources/Paths/GetRootFolderAPI.swift +++ b/Sources/Paths/GetRootFolderAPI.swift @@ -10,9 +10,15 @@ import Foundation import Get import URLQueryEncoder -public extension Paths { +extension Paths { /// Gets the root folder from a user's library. - static func getRootFolder(userID: String) -> Request { - Request(path: "/Users/\(userID)/Items/Root", method: "GET", id: "GetRootFolder") + public static func getRootFolder(userID: String? = nil) -> Request { + Request(path: "/Items/Root", method: "GET", query: makeGetRootFolderQuery(userID), id: "GetRootFolder") + } + + private static func makeGetRootFolderQuery(_ userID: String?) -> [(String, String?)] { + let encoder = URLQueryEncoder() + encoder.encode(userID, forKey: "userId") + return encoder.items } } diff --git a/Sources/Paths/GetSearchHintsAPI.swift b/Sources/Paths/GetSearchHintsAPI.swift new file mode 100644 index 00000000..229866fe --- /dev/null +++ b/Sources/Paths/GetSearchHintsAPI.swift @@ -0,0 +1,102 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation +import Get +import URLQueryEncoder + +public extension Paths { + /// Gets the search hint result. + static func getSearchHints(parameters: GetSearchHintsParameters) -> Request { + Request(path: "/Search/Hints", method: "GET", query: parameters.asQuery, id: "GetSearchHints") + } + + struct GetSearchHintsParameters { + public var startIndex: Int? + public var limit: Int? + public var userID: String? + public var searchTerm: String + public var includeItemTypes: [JellyfinAPI.BaseItemKind]? + public var excludeItemTypes: [JellyfinAPI.BaseItemKind]? + public var mediaTypes: [JellyfinAPI.MediaType]? + public var parentID: String? + public var isMovie: Bool? + public var isSeries: Bool? + public var isNews: Bool? + public var isKids: Bool? + public var isSports: Bool? + public var isIncludePeople: Bool? + public var isIncludeMedia: Bool? + public var isIncludeGenres: Bool? + public var isIncludeStudios: Bool? + public var isIncludeArtists: Bool? + + public init( + startIndex: Int? = nil, + limit: Int? = nil, + userID: String? = nil, + searchTerm: String, + includeItemTypes: [JellyfinAPI.BaseItemKind]? = nil, + excludeItemTypes: [JellyfinAPI.BaseItemKind]? = nil, + mediaTypes: [JellyfinAPI.MediaType]? = nil, + parentID: String? = nil, + isMovie: Bool? = nil, + isSeries: Bool? = nil, + isNews: Bool? = nil, + isKids: Bool? = nil, + isSports: Bool? = nil, + isIncludePeople: Bool? = nil, + isIncludeMedia: Bool? = nil, + isIncludeGenres: Bool? = nil, + isIncludeStudios: Bool? = nil, + isIncludeArtists: Bool? = nil + ) { + self.startIndex = startIndex + self.limit = limit + self.userID = userID + self.searchTerm = searchTerm + self.includeItemTypes = includeItemTypes + self.excludeItemTypes = excludeItemTypes + self.mediaTypes = mediaTypes + self.parentID = parentID + self.isMovie = isMovie + self.isSeries = isSeries + self.isNews = isNews + self.isKids = isKids + self.isSports = isSports + self.isIncludePeople = isIncludePeople + self.isIncludeMedia = isIncludeMedia + self.isIncludeGenres = isIncludeGenres + self.isIncludeStudios = isIncludeStudios + self.isIncludeArtists = isIncludeArtists + } + + public var asQuery: [(String, String?)] { + let encoder = URLQueryEncoder() + encoder.encode(startIndex, forKey: "startIndex") + encoder.encode(limit, forKey: "limit") + encoder.encode(userID, forKey: "userId") + encoder.encode(searchTerm, forKey: "searchTerm") + encoder.encode(includeItemTypes, forKey: "includeItemTypes") + encoder.encode(excludeItemTypes, forKey: "excludeItemTypes") + encoder.encode(mediaTypes, forKey: "mediaTypes") + encoder.encode(parentID, forKey: "parentId") + encoder.encode(isMovie, forKey: "isMovie") + encoder.encode(isSeries, forKey: "isSeries") + encoder.encode(isNews, forKey: "isNews") + encoder.encode(isKids, forKey: "isKids") + encoder.encode(isSports, forKey: "isSports") + encoder.encode(isIncludePeople, forKey: "includePeople") + encoder.encode(isIncludeMedia, forKey: "includeMedia") + encoder.encode(isIncludeGenres, forKey: "includeGenres") + encoder.encode(isIncludeStudios, forKey: "includeStudios") + encoder.encode(isIncludeArtists, forKey: "includeArtists") + return encoder.items + } + } +} diff --git a/Sources/Paths/GetSpecialFeaturesAPI.swift b/Sources/Paths/GetSpecialFeaturesAPI.swift index 160c1e05..49324424 100644 --- a/Sources/Paths/GetSpecialFeaturesAPI.swift +++ b/Sources/Paths/GetSpecialFeaturesAPI.swift @@ -10,9 +10,20 @@ import Foundation import Get import URLQueryEncoder -public extension Paths { +extension Paths { /// Gets special features for an item. - static func getSpecialFeatures(userID: String, itemID: String) -> Request<[JellyfinAPI.BaseItemDto]> { - Request(path: "/Users/\(userID)/Items/\(itemID)/SpecialFeatures", method: "GET", id: "GetSpecialFeatures") + public static func getSpecialFeatures(itemID: String, userID: String? = nil) -> Request<[JellyfinAPI.BaseItemDto]> { + Request( + path: "/Items/\(itemID)/SpecialFeatures", + method: "GET", + query: makeGetSpecialFeaturesQuery(userID), + id: "GetSpecialFeatures" + ) + } + + private static func makeGetSpecialFeaturesQuery(_ userID: String?) -> [(String, String?)] { + let encoder = URLQueryEncoder() + encoder.encode(userID, forKey: "userId") + return encoder.items } } diff --git a/Sources/Paths/GetStudioImageAPI.swift b/Sources/Paths/GetStudioImageAPI.swift index aaba5a28..5c1656d9 100644 --- a/Sources/Paths/GetStudioImageAPI.swift +++ b/Sources/Paths/GetStudioImageAPI.swift @@ -28,8 +28,6 @@ public extension Paths { public var quality: Int? public var fillWidth: Int? public var fillHeight: Int? - public var isCropWhitespace: Bool? - public var isAddPlayedIndicator: Bool? public var blur: Int? public var backgroundColor: String? public var foregroundLayer: String? @@ -49,8 +47,6 @@ public extension Paths { quality: Int? = nil, fillWidth: Int? = nil, fillHeight: Int? = nil, - isCropWhitespace: Bool? = nil, - isAddPlayedIndicator: Bool? = nil, blur: Int? = nil, backgroundColor: String? = nil, foregroundLayer: String? = nil, @@ -67,8 +63,6 @@ public extension Paths { self.quality = quality self.fillWidth = fillWidth self.fillHeight = fillHeight - self.isCropWhitespace = isCropWhitespace - self.isAddPlayedIndicator = isAddPlayedIndicator self.blur = blur self.backgroundColor = backgroundColor self.foregroundLayer = foregroundLayer @@ -88,8 +82,6 @@ public extension Paths { encoder.encode(quality, forKey: "quality") encoder.encode(fillWidth, forKey: "fillWidth") encoder.encode(fillHeight, forKey: "fillHeight") - encoder.encode(isCropWhitespace, forKey: "cropWhitespace") - encoder.encode(isAddPlayedIndicator, forKey: "addPlayedIndicator") encoder.encode(blur, forKey: "blur") encoder.encode(backgroundColor, forKey: "backgroundColor") encoder.encode(foregroundLayer, forKey: "foregroundLayer") diff --git a/Sources/Paths/GetStudioImageByIndexAPI.swift b/Sources/Paths/GetStudioImageByIndexAPI.swift index 0ccb00c8..464c3a92 100644 --- a/Sources/Paths/GetStudioImageByIndexAPI.swift +++ b/Sources/Paths/GetStudioImageByIndexAPI.swift @@ -38,8 +38,6 @@ public extension Paths { public var quality: Int? public var fillWidth: Int? public var fillHeight: Int? - public var isCropWhitespace: Bool? - public var isAddPlayedIndicator: Bool? public var blur: Int? public var backgroundColor: String? public var foregroundLayer: String? @@ -58,8 +56,6 @@ public extension Paths { quality: Int? = nil, fillWidth: Int? = nil, fillHeight: Int? = nil, - isCropWhitespace: Bool? = nil, - isAddPlayedIndicator: Bool? = nil, blur: Int? = nil, backgroundColor: String? = nil, foregroundLayer: String? = nil @@ -75,8 +71,6 @@ public extension Paths { self.quality = quality self.fillWidth = fillWidth self.fillHeight = fillHeight - self.isCropWhitespace = isCropWhitespace - self.isAddPlayedIndicator = isAddPlayedIndicator self.blur = blur self.backgroundColor = backgroundColor self.foregroundLayer = foregroundLayer @@ -95,8 +89,6 @@ public extension Paths { encoder.encode(quality, forKey: "quality") encoder.encode(fillWidth, forKey: "fillWidth") encoder.encode(fillHeight, forKey: "fillHeight") - encoder.encode(isCropWhitespace, forKey: "cropWhitespace") - encoder.encode(isAddPlayedIndicator, forKey: "addPlayedIndicator") encoder.encode(blur, forKey: "blur") encoder.encode(backgroundColor, forKey: "backgroundColor") encoder.encode(foregroundLayer, forKey: "foregroundLayer") diff --git a/Sources/Paths/GetSuggestionsAPI.swift b/Sources/Paths/GetSuggestionsAPI.swift index 876eda77..7ed9a5e8 100644 --- a/Sources/Paths/GetSuggestionsAPI.swift +++ b/Sources/Paths/GetSuggestionsAPI.swift @@ -12,24 +12,27 @@ import URLQueryEncoder public extension Paths { /// Gets suggestions. - static func getSuggestions(userID: String, parameters: GetSuggestionsParameters? = nil) -> Request { - Request(path: "/Users/\(userID)/Suggestions", method: "GET", query: parameters?.asQuery, id: "GetSuggestions") + static func getSuggestions(parameters: GetSuggestionsParameters? = nil) -> Request { + Request(path: "/Items/Suggestions", method: "GET", query: parameters?.asQuery, id: "GetSuggestions") } struct GetSuggestionsParameters { - public var mediaType: [String]? + public var userID: String? + public var mediaType: [JellyfinAPI.MediaType]? public var type: [JellyfinAPI.BaseItemKind]? public var startIndex: Int? public var limit: Int? public var enableTotalRecordCount: Bool? public init( - mediaType: [String]? = nil, + userID: String? = nil, + mediaType: [JellyfinAPI.MediaType]? = nil, type: [JellyfinAPI.BaseItemKind]? = nil, startIndex: Int? = nil, limit: Int? = nil, enableTotalRecordCount: Bool? = nil ) { + self.userID = userID self.mediaType = mediaType self.type = type self.startIndex = startIndex @@ -39,6 +42,7 @@ public extension Paths { public var asQuery: [(String, String?)] { let encoder = URLQueryEncoder() + encoder.encode(userID, forKey: "userId") encoder.encode(mediaType, forKey: "mediaType") encoder.encode(type, forKey: "type") encoder.encode(startIndex, forKey: "startIndex") diff --git a/Sources/Paths/GetTrailersAPI.swift b/Sources/Paths/GetTrailersAPI.swift index 92fb2024..efb512a7 100644 --- a/Sources/Paths/GetTrailersAPI.swift +++ b/Sources/Paths/GetTrailersAPI.swift @@ -59,9 +59,9 @@ public extension Paths { public var excludeItemTypes: [JellyfinAPI.BaseItemKind]? public var filters: [JellyfinAPI.ItemFilter]? public var isFavorite: Bool? - public var mediaTypes: [String]? + public var mediaTypes: [JellyfinAPI.MediaType]? public var imageTypes: [JellyfinAPI.ImageType]? - public var sortBy: [String]? + public var sortBy: [JellyfinAPI.ItemSortBy]? public var isPlayed: Bool? public var genres: [String]? public var officialRatings: [String]? @@ -145,9 +145,9 @@ public extension Paths { excludeItemTypes: [JellyfinAPI.BaseItemKind]? = nil, filters: [JellyfinAPI.ItemFilter]? = nil, isFavorite: Bool? = nil, - mediaTypes: [String]? = nil, + mediaTypes: [JellyfinAPI.MediaType]? = nil, imageTypes: [JellyfinAPI.ImageType]? = nil, - sortBy: [String]? = nil, + sortBy: [JellyfinAPI.ItemSortBy]? = nil, isPlayed: Bool? = nil, genres: [String]? = nil, officialRatings: [String]? = nil, diff --git a/Sources/Paths/GetTrickplayHlsPlaylistAPI.swift b/Sources/Paths/GetTrickplayHlsPlaylistAPI.swift new file mode 100644 index 00000000..cb4c5006 --- /dev/null +++ b/Sources/Paths/GetTrickplayHlsPlaylistAPI.swift @@ -0,0 +1,29 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation +import Get +import URLQueryEncoder + +extension Paths { + /// Gets an image tiles playlist for trickplay. + public static func getTrickplayHlsPlaylist(itemID: String, width: Int, mediaSourceID: String? = nil) -> Request { + Request( + path: "/Videos/\(itemID)/Trickplay/\(width)/tiles.m3u8", + method: "GET", + query: makeGetTrickplayHlsPlaylistQuery(mediaSourceID), + id: "GetTrickplayHlsPlaylist" + ) + } + + private static func makeGetTrickplayHlsPlaylistQuery(_ mediaSourceID: String?) -> [(String, String?)] { + let encoder = URLQueryEncoder() + encoder.encode(mediaSourceID, forKey: "mediaSourceId") + return encoder.items + } +} diff --git a/Sources/Paths/GetTrickplayTileImageAPI.swift b/Sources/Paths/GetTrickplayTileImageAPI.swift new file mode 100644 index 00000000..bc197928 --- /dev/null +++ b/Sources/Paths/GetTrickplayTileImageAPI.swift @@ -0,0 +1,29 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation +import Get +import URLQueryEncoder + +extension Paths { + /// Gets a trickplay tile image. + public static func getTrickplayTileImage(itemID: String, width: Int, index: Int, mediaSourceID: String? = nil) -> Request { + Request( + path: "/Videos/\(itemID)/Trickplay/\(width)/\(index).jpg", + method: "GET", + query: makeGetTrickplayTileImageQuery(mediaSourceID), + id: "GetTrickplayTileImage" + ) + } + + private static func makeGetTrickplayTileImageQuery(_ mediaSourceID: String?) -> [(String, String?)] { + let encoder = URLQueryEncoder() + encoder.encode(mediaSourceID, forKey: "mediaSourceId") + return encoder.items + } +} diff --git a/Sources/Paths/GetUniversalAudioStreamAPI.swift b/Sources/Paths/GetUniversalAudioStreamAPI.swift index 195f9561..c58f73aa 100644 --- a/Sources/Paths/GetUniversalAudioStreamAPI.swift +++ b/Sources/Paths/GetUniversalAudioStreamAPI.swift @@ -28,13 +28,15 @@ public extension Paths { public var audioBitRate: Int? public var startTimeTicks: Int? public var transcodingContainer: String? - public var transcodingProtocol: String? + public var transcodingProtocol: TranscodingProtocol? public var maxAudioSampleRate: Int? public var maxAudioBitDepth: Int? public var enableRemoteMedia: Bool? public var isBreakOnNonKeyFrames: Bool? public var enableRedirection: Bool? + public typealias TranscodingProtocol = JellyfinAPI.MediaStreamProtocol + public init( container: [String]? = nil, mediaSourceID: String? = nil, @@ -47,7 +49,7 @@ public extension Paths { audioBitRate: Int? = nil, startTimeTicks: Int? = nil, transcodingContainer: String? = nil, - transcodingProtocol: String? = nil, + transcodingProtocol: TranscodingProtocol? = nil, maxAudioSampleRate: Int? = nil, maxAudioBitDepth: Int? = nil, enableRemoteMedia: Bool? = nil, diff --git a/Sources/Paths/GetUserImageAPI.swift b/Sources/Paths/GetUserImageAPI.swift index 91d50d39..97d667d1 100644 --- a/Sources/Paths/GetUserImageAPI.swift +++ b/Sources/Paths/GetUserImageAPI.swift @@ -12,11 +12,12 @@ import URLQueryEncoder public extension Paths { /// Get user profile image. - static func getUserImage(userID: String, imageType: String, parameters: GetUserImageParameters? = nil) -> Request { - Request(path: "/Users/\(userID)/Images/\(imageType)", method: "GET", query: parameters?.asQuery, id: "GetUserImage") + static func getUserImage(parameters: GetUserImageParameters? = nil) -> Request { + Request(path: "/UserImage", method: "GET", query: parameters?.asQuery, id: "GetUserImage") } struct GetUserImageParameters { + public var userID: String? public var tag: String? public var format: Format? public var maxWidth: Int? @@ -28,8 +29,6 @@ public extension Paths { public var quality: Int? public var fillWidth: Int? public var fillHeight: Int? - public var isCropWhitespace: Bool? - public var isAddPlayedIndicator: Bool? public var blur: Int? public var backgroundColor: String? public var foregroundLayer: String? @@ -38,6 +37,7 @@ public extension Paths { public typealias Format = JellyfinAPI.ImageFormat public init( + userID: String? = nil, tag: String? = nil, format: Format? = nil, maxWidth: Int? = nil, @@ -49,13 +49,12 @@ public extension Paths { quality: Int? = nil, fillWidth: Int? = nil, fillHeight: Int? = nil, - isCropWhitespace: Bool? = nil, - isAddPlayedIndicator: Bool? = nil, blur: Int? = nil, backgroundColor: String? = nil, foregroundLayer: String? = nil, imageIndex: Int? = nil ) { + self.userID = userID self.tag = tag self.format = format self.maxWidth = maxWidth @@ -67,8 +66,6 @@ public extension Paths { self.quality = quality self.fillWidth = fillWidth self.fillHeight = fillHeight - self.isCropWhitespace = isCropWhitespace - self.isAddPlayedIndicator = isAddPlayedIndicator self.blur = blur self.backgroundColor = backgroundColor self.foregroundLayer = foregroundLayer @@ -77,6 +74,7 @@ public extension Paths { public var asQuery: [(String, String?)] { let encoder = URLQueryEncoder() + encoder.encode(userID, forKey: "userId") encoder.encode(tag, forKey: "tag") encoder.encode(format, forKey: "format") encoder.encode(maxWidth, forKey: "maxWidth") @@ -88,8 +86,6 @@ public extension Paths { encoder.encode(quality, forKey: "quality") encoder.encode(fillWidth, forKey: "fillWidth") encoder.encode(fillHeight, forKey: "fillHeight") - encoder.encode(isCropWhitespace, forKey: "cropWhitespace") - encoder.encode(isAddPlayedIndicator, forKey: "addPlayedIndicator") encoder.encode(blur, forKey: "blur") encoder.encode(backgroundColor, forKey: "backgroundColor") encoder.encode(foregroundLayer, forKey: "foregroundLayer") diff --git a/Sources/Paths/GetUserViewsAPI.swift b/Sources/Paths/GetUserViewsAPI.swift index 386b083c..e09ed80b 100644 --- a/Sources/Paths/GetUserViewsAPI.swift +++ b/Sources/Paths/GetUserViewsAPI.swift @@ -12,16 +12,23 @@ import URLQueryEncoder public extension Paths { /// Get user views. - static func getUserViews(userID: String, parameters: GetUserViewsParameters? = nil) -> Request { - Request(path: "/Users/\(userID)/Views", method: "GET", query: parameters?.asQuery, id: "GetUserViews") + static func getUserViews(parameters: GetUserViewsParameters? = nil) -> Request { + Request(path: "/UserViews", method: "GET", query: parameters?.asQuery, id: "GetUserViews") } struct GetUserViewsParameters { + public var userID: String? public var isIncludeExternalContent: Bool? - public var presetViews: [String]? + public var presetViews: [JellyfinAPI.CollectionType]? public var isIncludeHidden: Bool? - public init(isIncludeExternalContent: Bool? = nil, presetViews: [String]? = nil, isIncludeHidden: Bool? = nil) { + public init( + userID: String? = nil, + isIncludeExternalContent: Bool? = nil, + presetViews: [JellyfinAPI.CollectionType]? = nil, + isIncludeHidden: Bool? = nil + ) { + self.userID = userID self.isIncludeExternalContent = isIncludeExternalContent self.presetViews = presetViews self.isIncludeHidden = isIncludeHidden @@ -29,6 +36,7 @@ public extension Paths { public var asQuery: [(String, String?)] { let encoder = URLQueryEncoder() + encoder.encode(userID, forKey: "userId") encoder.encode(isIncludeExternalContent, forKey: "includeExternalContent") encoder.encode(presetViews, forKey: "presetViews") encoder.encode(isIncludeHidden, forKey: "includeHidden") diff --git a/Sources/Paths/GetYearsAPI.swift b/Sources/Paths/GetYearsAPI.swift index c4613a6c..8c9b737a 100644 --- a/Sources/Paths/GetYearsAPI.swift +++ b/Sources/Paths/GetYearsAPI.swift @@ -24,8 +24,8 @@ public extension Paths { public var fields: [JellyfinAPI.ItemFields]? public var excludeItemTypes: [JellyfinAPI.BaseItemKind]? public var includeItemTypes: [JellyfinAPI.BaseItemKind]? - public var mediaTypes: [String]? - public var sortBy: [String]? + public var mediaTypes: [JellyfinAPI.MediaType]? + public var sortBy: [JellyfinAPI.ItemSortBy]? public var enableUserData: Bool? public var imageTypeLimit: Int? public var enableImageTypes: [JellyfinAPI.ImageType]? @@ -41,8 +41,8 @@ public extension Paths { fields: [JellyfinAPI.ItemFields]? = nil, excludeItemTypes: [JellyfinAPI.BaseItemKind]? = nil, includeItemTypes: [JellyfinAPI.BaseItemKind]? = nil, - mediaTypes: [String]? = nil, - sortBy: [String]? = nil, + mediaTypes: [JellyfinAPI.MediaType]? = nil, + sortBy: [JellyfinAPI.ItemSortBy]? = nil, enableUserData: Bool? = nil, imageTypeLimit: Int? = nil, enableImageTypes: [JellyfinAPI.ImageType]? = nil, diff --git a/Sources/Paths/HeadArtistImageAPI.swift b/Sources/Paths/HeadArtistImageAPI.swift index 363ee7bf..e5dc848a 100644 --- a/Sources/Paths/HeadArtistImageAPI.swift +++ b/Sources/Paths/HeadArtistImageAPI.swift @@ -38,8 +38,6 @@ public extension Paths { public var quality: Int? public var fillWidth: Int? public var fillHeight: Int? - public var isCropWhitespace: Bool? - public var isAddPlayedIndicator: Bool? public var blur: Int? public var backgroundColor: String? public var foregroundLayer: String? @@ -58,8 +56,6 @@ public extension Paths { quality: Int? = nil, fillWidth: Int? = nil, fillHeight: Int? = nil, - isCropWhitespace: Bool? = nil, - isAddPlayedIndicator: Bool? = nil, blur: Int? = nil, backgroundColor: String? = nil, foregroundLayer: String? = nil @@ -75,8 +71,6 @@ public extension Paths { self.quality = quality self.fillWidth = fillWidth self.fillHeight = fillHeight - self.isCropWhitespace = isCropWhitespace - self.isAddPlayedIndicator = isAddPlayedIndicator self.blur = blur self.backgroundColor = backgroundColor self.foregroundLayer = foregroundLayer @@ -95,8 +89,6 @@ public extension Paths { encoder.encode(quality, forKey: "quality") encoder.encode(fillWidth, forKey: "fillWidth") encoder.encode(fillHeight, forKey: "fillHeight") - encoder.encode(isCropWhitespace, forKey: "cropWhitespace") - encoder.encode(isAddPlayedIndicator, forKey: "addPlayedIndicator") encoder.encode(blur, forKey: "blur") encoder.encode(backgroundColor, forKey: "backgroundColor") encoder.encode(foregroundLayer, forKey: "foregroundLayer") diff --git a/Sources/Paths/HeadGenreImageAPI.swift b/Sources/Paths/HeadGenreImageAPI.swift index d7af1e5b..24ed134d 100644 --- a/Sources/Paths/HeadGenreImageAPI.swift +++ b/Sources/Paths/HeadGenreImageAPI.swift @@ -28,8 +28,6 @@ public extension Paths { public var quality: Int? public var fillWidth: Int? public var fillHeight: Int? - public var isCropWhitespace: Bool? - public var isAddPlayedIndicator: Bool? public var blur: Int? public var backgroundColor: String? public var foregroundLayer: String? @@ -49,8 +47,6 @@ public extension Paths { quality: Int? = nil, fillWidth: Int? = nil, fillHeight: Int? = nil, - isCropWhitespace: Bool? = nil, - isAddPlayedIndicator: Bool? = nil, blur: Int? = nil, backgroundColor: String? = nil, foregroundLayer: String? = nil, @@ -67,8 +63,6 @@ public extension Paths { self.quality = quality self.fillWidth = fillWidth self.fillHeight = fillHeight - self.isCropWhitespace = isCropWhitespace - self.isAddPlayedIndicator = isAddPlayedIndicator self.blur = blur self.backgroundColor = backgroundColor self.foregroundLayer = foregroundLayer @@ -88,8 +82,6 @@ public extension Paths { encoder.encode(quality, forKey: "quality") encoder.encode(fillWidth, forKey: "fillWidth") encoder.encode(fillHeight, forKey: "fillHeight") - encoder.encode(isCropWhitespace, forKey: "cropWhitespace") - encoder.encode(isAddPlayedIndicator, forKey: "addPlayedIndicator") encoder.encode(blur, forKey: "blur") encoder.encode(backgroundColor, forKey: "backgroundColor") encoder.encode(foregroundLayer, forKey: "foregroundLayer") diff --git a/Sources/Paths/HeadGenreImageByIndexAPI.swift b/Sources/Paths/HeadGenreImageByIndexAPI.swift index 94b1e1c0..bcebdba4 100644 --- a/Sources/Paths/HeadGenreImageByIndexAPI.swift +++ b/Sources/Paths/HeadGenreImageByIndexAPI.swift @@ -38,8 +38,6 @@ public extension Paths { public var quality: Int? public var fillWidth: Int? public var fillHeight: Int? - public var isCropWhitespace: Bool? - public var isAddPlayedIndicator: Bool? public var blur: Int? public var backgroundColor: String? public var foregroundLayer: String? @@ -58,8 +56,6 @@ public extension Paths { quality: Int? = nil, fillWidth: Int? = nil, fillHeight: Int? = nil, - isCropWhitespace: Bool? = nil, - isAddPlayedIndicator: Bool? = nil, blur: Int? = nil, backgroundColor: String? = nil, foregroundLayer: String? = nil @@ -75,8 +71,6 @@ public extension Paths { self.quality = quality self.fillWidth = fillWidth self.fillHeight = fillHeight - self.isCropWhitespace = isCropWhitespace - self.isAddPlayedIndicator = isAddPlayedIndicator self.blur = blur self.backgroundColor = backgroundColor self.foregroundLayer = foregroundLayer @@ -95,8 +89,6 @@ public extension Paths { encoder.encode(quality, forKey: "quality") encoder.encode(fillWidth, forKey: "fillWidth") encoder.encode(fillHeight, forKey: "fillHeight") - encoder.encode(isCropWhitespace, forKey: "cropWhitespace") - encoder.encode(isAddPlayedIndicator, forKey: "addPlayedIndicator") encoder.encode(blur, forKey: "blur") encoder.encode(backgroundColor, forKey: "backgroundColor") encoder.encode(foregroundLayer, forKey: "foregroundLayer") diff --git a/Sources/Paths/HeadItemImage2API.swift b/Sources/Paths/HeadItemImage2API.swift index 42881153..41adf76f 100644 --- a/Sources/Paths/HeadItemImage2API.swift +++ b/Sources/Paths/HeadItemImage2API.swift @@ -38,8 +38,6 @@ public extension Paths { public var quality: Int? public var fillWidth: Int? public var fillHeight: Int? - public var isCropWhitespace: Bool? - public var isAddPlayedIndicator: Bool? public var blur: Int? public var backgroundColor: String? public var foregroundLayer: String? @@ -50,8 +48,6 @@ public extension Paths { quality: Int? = nil, fillWidth: Int? = nil, fillHeight: Int? = nil, - isCropWhitespace: Bool? = nil, - isAddPlayedIndicator: Bool? = nil, blur: Int? = nil, backgroundColor: String? = nil, foregroundLayer: String? = nil @@ -61,8 +57,6 @@ public extension Paths { self.quality = quality self.fillWidth = fillWidth self.fillHeight = fillHeight - self.isCropWhitespace = isCropWhitespace - self.isAddPlayedIndicator = isAddPlayedIndicator self.blur = blur self.backgroundColor = backgroundColor self.foregroundLayer = foregroundLayer @@ -75,8 +69,6 @@ public extension Paths { encoder.encode(quality, forKey: "quality") encoder.encode(fillWidth, forKey: "fillWidth") encoder.encode(fillHeight, forKey: "fillHeight") - encoder.encode(isCropWhitespace, forKey: "cropWhitespace") - encoder.encode(isAddPlayedIndicator, forKey: "addPlayedIndicator") encoder.encode(blur, forKey: "blur") encoder.encode(backgroundColor, forKey: "backgroundColor") encoder.encode(foregroundLayer, forKey: "foregroundLayer") diff --git a/Sources/Paths/HeadItemImageAPI.swift b/Sources/Paths/HeadItemImageAPI.swift index 02faee60..ce3cf4f6 100644 --- a/Sources/Paths/HeadItemImageAPI.swift +++ b/Sources/Paths/HeadItemImageAPI.swift @@ -25,9 +25,7 @@ public extension Paths { public var fillWidth: Int? public var fillHeight: Int? public var tag: String? - public var isCropWhitespace: Bool? public var format: Format? - public var isAddPlayedIndicator: Bool? public var percentPlayed: Double? public var unplayedCount: Int? public var blur: Int? @@ -46,9 +44,7 @@ public extension Paths { fillWidth: Int? = nil, fillHeight: Int? = nil, tag: String? = nil, - isCropWhitespace: Bool? = nil, format: Format? = nil, - isAddPlayedIndicator: Bool? = nil, percentPlayed: Double? = nil, unplayedCount: Int? = nil, blur: Int? = nil, @@ -64,9 +60,7 @@ public extension Paths { self.fillWidth = fillWidth self.fillHeight = fillHeight self.tag = tag - self.isCropWhitespace = isCropWhitespace self.format = format - self.isAddPlayedIndicator = isAddPlayedIndicator self.percentPlayed = percentPlayed self.unplayedCount = unplayedCount self.blur = blur @@ -85,9 +79,7 @@ public extension Paths { encoder.encode(fillWidth, forKey: "fillWidth") encoder.encode(fillHeight, forKey: "fillHeight") encoder.encode(tag, forKey: "tag") - encoder.encode(isCropWhitespace, forKey: "cropWhitespace") encoder.encode(format, forKey: "format") - encoder.encode(isAddPlayedIndicator, forKey: "addPlayedIndicator") encoder.encode(percentPlayed, forKey: "percentPlayed") encoder.encode(unplayedCount, forKey: "unplayedCount") encoder.encode(blur, forKey: "blur") diff --git a/Sources/Paths/HeadItemImageByIndexAPI.swift b/Sources/Paths/HeadItemImageByIndexAPI.swift index d63ffc80..7976babe 100644 --- a/Sources/Paths/HeadItemImageByIndexAPI.swift +++ b/Sources/Paths/HeadItemImageByIndexAPI.swift @@ -35,9 +35,7 @@ public extension Paths { public var fillWidth: Int? public var fillHeight: Int? public var tag: String? - public var isCropWhitespace: Bool? public var format: Format? - public var isAddPlayedIndicator: Bool? public var percentPlayed: Double? public var unplayedCount: Int? public var blur: Int? @@ -55,9 +53,7 @@ public extension Paths { fillWidth: Int? = nil, fillHeight: Int? = nil, tag: String? = nil, - isCropWhitespace: Bool? = nil, format: Format? = nil, - isAddPlayedIndicator: Bool? = nil, percentPlayed: Double? = nil, unplayedCount: Int? = nil, blur: Int? = nil, @@ -72,9 +68,7 @@ public extension Paths { self.fillWidth = fillWidth self.fillHeight = fillHeight self.tag = tag - self.isCropWhitespace = isCropWhitespace self.format = format - self.isAddPlayedIndicator = isAddPlayedIndicator self.percentPlayed = percentPlayed self.unplayedCount = unplayedCount self.blur = blur @@ -92,9 +86,7 @@ public extension Paths { encoder.encode(fillWidth, forKey: "fillWidth") encoder.encode(fillHeight, forKey: "fillHeight") encoder.encode(tag, forKey: "tag") - encoder.encode(isCropWhitespace, forKey: "cropWhitespace") encoder.encode(format, forKey: "format") - encoder.encode(isAddPlayedIndicator, forKey: "addPlayedIndicator") encoder.encode(percentPlayed, forKey: "percentPlayed") encoder.encode(unplayedCount, forKey: "unplayedCount") encoder.encode(blur, forKey: "blur") diff --git a/Sources/Paths/HeadMasterHlsVideoPlaylistAPI.swift b/Sources/Paths/HeadMasterHlsVideoPlaylistAPI.swift index 856b59ff..e79b89fd 100644 --- a/Sources/Paths/HeadMasterHlsVideoPlaylistAPI.swift +++ b/Sources/Paths/HeadMasterHlsVideoPlaylistAPI.swift @@ -67,6 +67,7 @@ public extension Paths { public var context: Context? public var streamOptions: StreamOptions? public var enableAdaptiveBitrateStreaming: Bool? + public var enableTrickplay: Bool? public typealias SubtitleMethod = JellyfinAPI.SubtitleDeliveryMethod @@ -124,7 +125,8 @@ public extension Paths { videoStreamIndex: Int? = nil, context: Context? = nil, streamOptions: StreamOptions? = nil, - enableAdaptiveBitrateStreaming: Bool? = nil + enableAdaptiveBitrateStreaming: Bool? = nil, + enableTrickplay: Bool? = nil ) { self.isStatic = isStatic self.params = params @@ -176,6 +178,7 @@ public extension Paths { self.context = context self.streamOptions = streamOptions self.enableAdaptiveBitrateStreaming = enableAdaptiveBitrateStreaming + self.enableTrickplay = enableTrickplay } public var asQuery: [(String, String?)] { @@ -230,6 +233,7 @@ public extension Paths { encoder.encode(context, forKey: "context") encoder.encode(streamOptions, forKey: "streamOptions") encoder.encode(enableAdaptiveBitrateStreaming, forKey: "enableAdaptiveBitrateStreaming") + encoder.encode(enableTrickplay, forKey: "enableTrickplay") return encoder.items } } diff --git a/Sources/Paths/HeadMusicGenreImageAPI.swift b/Sources/Paths/HeadMusicGenreImageAPI.swift index 757fe673..a729e639 100644 --- a/Sources/Paths/HeadMusicGenreImageAPI.swift +++ b/Sources/Paths/HeadMusicGenreImageAPI.swift @@ -28,8 +28,6 @@ public extension Paths { public var quality: Int? public var fillWidth: Int? public var fillHeight: Int? - public var isCropWhitespace: Bool? - public var isAddPlayedIndicator: Bool? public var blur: Int? public var backgroundColor: String? public var foregroundLayer: String? @@ -49,8 +47,6 @@ public extension Paths { quality: Int? = nil, fillWidth: Int? = nil, fillHeight: Int? = nil, - isCropWhitespace: Bool? = nil, - isAddPlayedIndicator: Bool? = nil, blur: Int? = nil, backgroundColor: String? = nil, foregroundLayer: String? = nil, @@ -67,8 +63,6 @@ public extension Paths { self.quality = quality self.fillWidth = fillWidth self.fillHeight = fillHeight - self.isCropWhitespace = isCropWhitespace - self.isAddPlayedIndicator = isAddPlayedIndicator self.blur = blur self.backgroundColor = backgroundColor self.foregroundLayer = foregroundLayer @@ -88,8 +82,6 @@ public extension Paths { encoder.encode(quality, forKey: "quality") encoder.encode(fillWidth, forKey: "fillWidth") encoder.encode(fillHeight, forKey: "fillHeight") - encoder.encode(isCropWhitespace, forKey: "cropWhitespace") - encoder.encode(isAddPlayedIndicator, forKey: "addPlayedIndicator") encoder.encode(blur, forKey: "blur") encoder.encode(backgroundColor, forKey: "backgroundColor") encoder.encode(foregroundLayer, forKey: "foregroundLayer") diff --git a/Sources/Paths/HeadMusicGenreImageByIndexAPI.swift b/Sources/Paths/HeadMusicGenreImageByIndexAPI.swift index 13cbee92..01948570 100644 --- a/Sources/Paths/HeadMusicGenreImageByIndexAPI.swift +++ b/Sources/Paths/HeadMusicGenreImageByIndexAPI.swift @@ -38,8 +38,6 @@ public extension Paths { public var quality: Int? public var fillWidth: Int? public var fillHeight: Int? - public var isCropWhitespace: Bool? - public var isAddPlayedIndicator: Bool? public var blur: Int? public var backgroundColor: String? public var foregroundLayer: String? @@ -58,8 +56,6 @@ public extension Paths { quality: Int? = nil, fillWidth: Int? = nil, fillHeight: Int? = nil, - isCropWhitespace: Bool? = nil, - isAddPlayedIndicator: Bool? = nil, blur: Int? = nil, backgroundColor: String? = nil, foregroundLayer: String? = nil @@ -75,8 +71,6 @@ public extension Paths { self.quality = quality self.fillWidth = fillWidth self.fillHeight = fillHeight - self.isCropWhitespace = isCropWhitespace - self.isAddPlayedIndicator = isAddPlayedIndicator self.blur = blur self.backgroundColor = backgroundColor self.foregroundLayer = foregroundLayer @@ -95,8 +89,6 @@ public extension Paths { encoder.encode(quality, forKey: "quality") encoder.encode(fillWidth, forKey: "fillWidth") encoder.encode(fillHeight, forKey: "fillHeight") - encoder.encode(isCropWhitespace, forKey: "cropWhitespace") - encoder.encode(isAddPlayedIndicator, forKey: "addPlayedIndicator") encoder.encode(blur, forKey: "blur") encoder.encode(backgroundColor, forKey: "backgroundColor") encoder.encode(foregroundLayer, forKey: "foregroundLayer") diff --git a/Sources/Paths/HeadPersonImageAPI.swift b/Sources/Paths/HeadPersonImageAPI.swift index 75fb7eeb..82b0c610 100644 --- a/Sources/Paths/HeadPersonImageAPI.swift +++ b/Sources/Paths/HeadPersonImageAPI.swift @@ -28,8 +28,6 @@ public extension Paths { public var quality: Int? public var fillWidth: Int? public var fillHeight: Int? - public var isCropWhitespace: Bool? - public var isAddPlayedIndicator: Bool? public var blur: Int? public var backgroundColor: String? public var foregroundLayer: String? @@ -49,8 +47,6 @@ public extension Paths { quality: Int? = nil, fillWidth: Int? = nil, fillHeight: Int? = nil, - isCropWhitespace: Bool? = nil, - isAddPlayedIndicator: Bool? = nil, blur: Int? = nil, backgroundColor: String? = nil, foregroundLayer: String? = nil, @@ -67,8 +63,6 @@ public extension Paths { self.quality = quality self.fillWidth = fillWidth self.fillHeight = fillHeight - self.isCropWhitespace = isCropWhitespace - self.isAddPlayedIndicator = isAddPlayedIndicator self.blur = blur self.backgroundColor = backgroundColor self.foregroundLayer = foregroundLayer @@ -88,8 +82,6 @@ public extension Paths { encoder.encode(quality, forKey: "quality") encoder.encode(fillWidth, forKey: "fillWidth") encoder.encode(fillHeight, forKey: "fillHeight") - encoder.encode(isCropWhitespace, forKey: "cropWhitespace") - encoder.encode(isAddPlayedIndicator, forKey: "addPlayedIndicator") encoder.encode(blur, forKey: "blur") encoder.encode(backgroundColor, forKey: "backgroundColor") encoder.encode(foregroundLayer, forKey: "foregroundLayer") diff --git a/Sources/Paths/HeadPersonImageByIndexAPI.swift b/Sources/Paths/HeadPersonImageByIndexAPI.swift index 3f868a31..386761bf 100644 --- a/Sources/Paths/HeadPersonImageByIndexAPI.swift +++ b/Sources/Paths/HeadPersonImageByIndexAPI.swift @@ -38,8 +38,6 @@ public extension Paths { public var quality: Int? public var fillWidth: Int? public var fillHeight: Int? - public var isCropWhitespace: Bool? - public var isAddPlayedIndicator: Bool? public var blur: Int? public var backgroundColor: String? public var foregroundLayer: String? @@ -58,8 +56,6 @@ public extension Paths { quality: Int? = nil, fillWidth: Int? = nil, fillHeight: Int? = nil, - isCropWhitespace: Bool? = nil, - isAddPlayedIndicator: Bool? = nil, blur: Int? = nil, backgroundColor: String? = nil, foregroundLayer: String? = nil @@ -75,8 +71,6 @@ public extension Paths { self.quality = quality self.fillWidth = fillWidth self.fillHeight = fillHeight - self.isCropWhitespace = isCropWhitespace - self.isAddPlayedIndicator = isAddPlayedIndicator self.blur = blur self.backgroundColor = backgroundColor self.foregroundLayer = foregroundLayer @@ -95,8 +89,6 @@ public extension Paths { encoder.encode(quality, forKey: "quality") encoder.encode(fillWidth, forKey: "fillWidth") encoder.encode(fillHeight, forKey: "fillHeight") - encoder.encode(isCropWhitespace, forKey: "cropWhitespace") - encoder.encode(isAddPlayedIndicator, forKey: "addPlayedIndicator") encoder.encode(blur, forKey: "blur") encoder.encode(backgroundColor, forKey: "backgroundColor") encoder.encode(foregroundLayer, forKey: "foregroundLayer") diff --git a/Sources/Paths/HeadStudioImageAPI.swift b/Sources/Paths/HeadStudioImageAPI.swift index b52cd471..a619a656 100644 --- a/Sources/Paths/HeadStudioImageAPI.swift +++ b/Sources/Paths/HeadStudioImageAPI.swift @@ -28,8 +28,6 @@ public extension Paths { public var quality: Int? public var fillWidth: Int? public var fillHeight: Int? - public var isCropWhitespace: Bool? - public var isAddPlayedIndicator: Bool? public var blur: Int? public var backgroundColor: String? public var foregroundLayer: String? @@ -49,8 +47,6 @@ public extension Paths { quality: Int? = nil, fillWidth: Int? = nil, fillHeight: Int? = nil, - isCropWhitespace: Bool? = nil, - isAddPlayedIndicator: Bool? = nil, blur: Int? = nil, backgroundColor: String? = nil, foregroundLayer: String? = nil, @@ -67,8 +63,6 @@ public extension Paths { self.quality = quality self.fillWidth = fillWidth self.fillHeight = fillHeight - self.isCropWhitespace = isCropWhitespace - self.isAddPlayedIndicator = isAddPlayedIndicator self.blur = blur self.backgroundColor = backgroundColor self.foregroundLayer = foregroundLayer @@ -88,8 +82,6 @@ public extension Paths { encoder.encode(quality, forKey: "quality") encoder.encode(fillWidth, forKey: "fillWidth") encoder.encode(fillHeight, forKey: "fillHeight") - encoder.encode(isCropWhitespace, forKey: "cropWhitespace") - encoder.encode(isAddPlayedIndicator, forKey: "addPlayedIndicator") encoder.encode(blur, forKey: "blur") encoder.encode(backgroundColor, forKey: "backgroundColor") encoder.encode(foregroundLayer, forKey: "foregroundLayer") diff --git a/Sources/Paths/HeadStudioImageByIndexAPI.swift b/Sources/Paths/HeadStudioImageByIndexAPI.swift index fc3eb14e..c9f65d74 100644 --- a/Sources/Paths/HeadStudioImageByIndexAPI.swift +++ b/Sources/Paths/HeadStudioImageByIndexAPI.swift @@ -38,8 +38,6 @@ public extension Paths { public var quality: Int? public var fillWidth: Int? public var fillHeight: Int? - public var isCropWhitespace: Bool? - public var isAddPlayedIndicator: Bool? public var blur: Int? public var backgroundColor: String? public var foregroundLayer: String? @@ -58,8 +56,6 @@ public extension Paths { quality: Int? = nil, fillWidth: Int? = nil, fillHeight: Int? = nil, - isCropWhitespace: Bool? = nil, - isAddPlayedIndicator: Bool? = nil, blur: Int? = nil, backgroundColor: String? = nil, foregroundLayer: String? = nil @@ -75,8 +71,6 @@ public extension Paths { self.quality = quality self.fillWidth = fillWidth self.fillHeight = fillHeight - self.isCropWhitespace = isCropWhitespace - self.isAddPlayedIndicator = isAddPlayedIndicator self.blur = blur self.backgroundColor = backgroundColor self.foregroundLayer = foregroundLayer @@ -95,8 +89,6 @@ public extension Paths { encoder.encode(quality, forKey: "quality") encoder.encode(fillWidth, forKey: "fillWidth") encoder.encode(fillHeight, forKey: "fillHeight") - encoder.encode(isCropWhitespace, forKey: "cropWhitespace") - encoder.encode(isAddPlayedIndicator, forKey: "addPlayedIndicator") encoder.encode(blur, forKey: "blur") encoder.encode(backgroundColor, forKey: "backgroundColor") encoder.encode(foregroundLayer, forKey: "foregroundLayer") diff --git a/Sources/Paths/HeadUniversalAudioStreamAPI.swift b/Sources/Paths/HeadUniversalAudioStreamAPI.swift index 9336981b..8d0a1756 100644 --- a/Sources/Paths/HeadUniversalAudioStreamAPI.swift +++ b/Sources/Paths/HeadUniversalAudioStreamAPI.swift @@ -28,13 +28,15 @@ public extension Paths { public var audioBitRate: Int? public var startTimeTicks: Int? public var transcodingContainer: String? - public var transcodingProtocol: String? + public var transcodingProtocol: TranscodingProtocol? public var maxAudioSampleRate: Int? public var maxAudioBitDepth: Int? public var enableRemoteMedia: Bool? public var isBreakOnNonKeyFrames: Bool? public var enableRedirection: Bool? + public typealias TranscodingProtocol = JellyfinAPI.MediaStreamProtocol + public init( container: [String]? = nil, mediaSourceID: String? = nil, @@ -47,7 +49,7 @@ public extension Paths { audioBitRate: Int? = nil, startTimeTicks: Int? = nil, transcodingContainer: String? = nil, - transcodingProtocol: String? = nil, + transcodingProtocol: TranscodingProtocol? = nil, maxAudioSampleRate: Int? = nil, maxAudioBitDepth: Int? = nil, enableRemoteMedia: Bool? = nil, diff --git a/Sources/Paths/HeadUserImageAPI.swift b/Sources/Paths/HeadUserImageAPI.swift index faea00f4..0f9cef95 100644 --- a/Sources/Paths/HeadUserImageAPI.swift +++ b/Sources/Paths/HeadUserImageAPI.swift @@ -12,11 +12,12 @@ import URLQueryEncoder public extension Paths { /// Get user profile image. - static func headUserImage(userID: String, imageType: String, parameters: HeadUserImageParameters? = nil) -> Request { - Request(path: "/Users/\(userID)/Images/\(imageType)", method: "HEAD", query: parameters?.asQuery, id: "HeadUserImage") + static func headUserImage(parameters: HeadUserImageParameters? = nil) -> Request { + Request(path: "/UserImage", method: "HEAD", query: parameters?.asQuery, id: "HeadUserImage") } struct HeadUserImageParameters { + public var userID: String? public var tag: String? public var format: Format? public var maxWidth: Int? @@ -28,8 +29,6 @@ public extension Paths { public var quality: Int? public var fillWidth: Int? public var fillHeight: Int? - public var isCropWhitespace: Bool? - public var isAddPlayedIndicator: Bool? public var blur: Int? public var backgroundColor: String? public var foregroundLayer: String? @@ -38,6 +37,7 @@ public extension Paths { public typealias Format = JellyfinAPI.ImageFormat public init( + userID: String? = nil, tag: String? = nil, format: Format? = nil, maxWidth: Int? = nil, @@ -49,13 +49,12 @@ public extension Paths { quality: Int? = nil, fillWidth: Int? = nil, fillHeight: Int? = nil, - isCropWhitespace: Bool? = nil, - isAddPlayedIndicator: Bool? = nil, blur: Int? = nil, backgroundColor: String? = nil, foregroundLayer: String? = nil, imageIndex: Int? = nil ) { + self.userID = userID self.tag = tag self.format = format self.maxWidth = maxWidth @@ -67,8 +66,6 @@ public extension Paths { self.quality = quality self.fillWidth = fillWidth self.fillHeight = fillHeight - self.isCropWhitespace = isCropWhitespace - self.isAddPlayedIndicator = isAddPlayedIndicator self.blur = blur self.backgroundColor = backgroundColor self.foregroundLayer = foregroundLayer @@ -77,6 +74,7 @@ public extension Paths { public var asQuery: [(String, String?)] { let encoder = URLQueryEncoder() + encoder.encode(userID, forKey: "userId") encoder.encode(tag, forKey: "tag") encoder.encode(format, forKey: "format") encoder.encode(maxWidth, forKey: "maxWidth") @@ -88,8 +86,6 @@ public extension Paths { encoder.encode(quality, forKey: "quality") encoder.encode(fillWidth, forKey: "fillWidth") encoder.encode(fillHeight, forKey: "fillHeight") - encoder.encode(isCropWhitespace, forKey: "cropWhitespace") - encoder.encode(isAddPlayedIndicator, forKey: "addPlayedIndicator") encoder.encode(blur, forKey: "blur") encoder.encode(backgroundColor, forKey: "backgroundColor") encoder.encode(foregroundLayer, forKey: "foregroundLayer") diff --git a/Sources/Paths/InitiateQuickConnectAPI.swift b/Sources/Paths/InitiateQuickConnectAPI.swift new file mode 100644 index 00000000..ccc70f40 --- /dev/null +++ b/Sources/Paths/InitiateQuickConnectAPI.swift @@ -0,0 +1,18 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation +import Get +import URLQueryEncoder + +public extension Paths { + /// Initiate a new quick connect request. + static var initiateQuickConnect: Request { + Request(path: "/QuickConnect/Initiate", method: "POST", id: "InitiateQuickConnect") + } +} diff --git a/Sources/Paths/MarkFavoriteItemAPI.swift b/Sources/Paths/MarkFavoriteItemAPI.swift index 20ba4eb7..b2a057c1 100644 --- a/Sources/Paths/MarkFavoriteItemAPI.swift +++ b/Sources/Paths/MarkFavoriteItemAPI.swift @@ -10,9 +10,15 @@ import Foundation import Get import URLQueryEncoder -public extension Paths { +extension Paths { /// Marks an item as a favorite. - static func markFavoriteItem(userID: String, itemID: String) -> Request { - Request(path: "/Users/\(userID)/FavoriteItems/\(itemID)", method: "POST", id: "MarkFavoriteItem") + public static func markFavoriteItem(itemID: String, userID: String? = nil) -> Request { + Request(path: "/UserFavoriteItems/\(itemID)", method: "POST", query: makeMarkFavoriteItemQuery(userID), id: "MarkFavoriteItem") + } + + private static func makeMarkFavoriteItemQuery(_ userID: String?) -> [(String, String?)] { + let encoder = URLQueryEncoder() + encoder.encode(userID, forKey: "userId") + return encoder.items } } diff --git a/Sources/Paths/MarkPlayedItemAPI.swift b/Sources/Paths/MarkPlayedItemAPI.swift index 5aee2671..cd123e8c 100644 --- a/Sources/Paths/MarkPlayedItemAPI.swift +++ b/Sources/Paths/MarkPlayedItemAPI.swift @@ -12,17 +12,22 @@ import URLQueryEncoder extension Paths { /// Marks an item as played for user. - public static func markPlayedItem(userID: String, itemID: String, datePlayed: Date? = nil) -> Request { + public static func markPlayedItem( + itemID: String, + userID: String? = nil, + datePlayed: Date? = nil + ) -> Request { Request( - path: "/Users/\(userID)/PlayedItems/\(itemID)", + path: "/UserPlayedItems/\(itemID)", method: "POST", - query: makeMarkPlayedItemQuery(datePlayed), + query: makeMarkPlayedItemQuery(userID, datePlayed), id: "MarkPlayedItem" ) } - private static func makeMarkPlayedItemQuery(_ datePlayed: Date?) -> [(String, String?)] { + private static func makeMarkPlayedItemQuery(_ userID: String?, _ datePlayed: Date?) -> [(String, String?)] { let encoder = URLQueryEncoder() + encoder.encode(userID, forKey: "userId") encoder.encode(datePlayed, forKey: "datePlayed") return encoder.items } diff --git a/Sources/Paths/MarkUnplayedItemAPI.swift b/Sources/Paths/MarkUnplayedItemAPI.swift index cd006af0..3f5e3fa2 100644 --- a/Sources/Paths/MarkUnplayedItemAPI.swift +++ b/Sources/Paths/MarkUnplayedItemAPI.swift @@ -10,9 +10,15 @@ import Foundation import Get import URLQueryEncoder -public extension Paths { +extension Paths { /// Marks an item as unplayed for user. - static func markUnplayedItem(userID: String, itemID: String) -> Request { - Request(path: "/Users/\(userID)/PlayedItems/\(itemID)", method: "DELETE", id: "MarkUnplayedItem") + public static func markUnplayedItem(itemID: String, userID: String? = nil) -> Request { + Request(path: "/UserPlayedItems/\(itemID)", method: "DELETE", query: makeMarkUnplayedItemQuery(userID), id: "MarkUnplayedItem") + } + + private static func makeMarkUnplayedItemQuery(_ userID: String?) -> [(String, String?)] { + let encoder = URLQueryEncoder() + encoder.encode(userID, forKey: "userId") + return encoder.items } } diff --git a/Sources/Paths/OnPlaybackProgressAPI.swift b/Sources/Paths/OnPlaybackProgressAPI.swift index 270855a8..09dd05d3 100644 --- a/Sources/Paths/OnPlaybackProgressAPI.swift +++ b/Sources/Paths/OnPlaybackProgressAPI.swift @@ -11,14 +11,9 @@ import Get import URLQueryEncoder public extension Paths { - /// Reports a user's playback progress. - static func onPlaybackProgress(userID: String, itemID: String, parameters: OnPlaybackProgressParameters? = nil) -> Request { - Request( - path: "/Users/\(userID)/PlayingItems/\(itemID)/Progress", - method: "POST", - query: parameters?.asQuery, - id: "OnPlaybackProgress" - ) + /// Reports a session's playback progress. + static func onPlaybackProgress(itemID: String, parameters: OnPlaybackProgressParameters? = nil) -> Request { + Request(path: "/PlayingItems/\(itemID)/Progress", method: "POST", query: parameters?.asQuery, id: "OnPlaybackProgress") } struct OnPlaybackProgressParameters { diff --git a/Sources/Paths/OnPlaybackStartAPI.swift b/Sources/Paths/OnPlaybackStartAPI.swift index 597eb468..db93bd13 100644 --- a/Sources/Paths/OnPlaybackStartAPI.swift +++ b/Sources/Paths/OnPlaybackStartAPI.swift @@ -11,9 +11,9 @@ import Get import URLQueryEncoder public extension Paths { - /// Reports that a user has begun playing an item. - static func onPlaybackStart(userID: String, itemID: String, parameters: OnPlaybackStartParameters? = nil) -> Request { - Request(path: "/Users/\(userID)/PlayingItems/\(itemID)", method: "POST", query: parameters?.asQuery, id: "OnPlaybackStart") + /// Reports that a session has begun playing an item. + static func onPlaybackStart(itemID: String, parameters: OnPlaybackStartParameters? = nil) -> Request { + Request(path: "/PlayingItems/\(itemID)", method: "POST", query: parameters?.asQuery, id: "OnPlaybackStart") } struct OnPlaybackStartParameters { diff --git a/Sources/Paths/OnPlaybackStoppedAPI.swift b/Sources/Paths/OnPlaybackStoppedAPI.swift index fbb3a826..f7587ea6 100644 --- a/Sources/Paths/OnPlaybackStoppedAPI.swift +++ b/Sources/Paths/OnPlaybackStoppedAPI.swift @@ -11,9 +11,9 @@ import Get import URLQueryEncoder public extension Paths { - /// Reports that a user has stopped playing an item. - static func onPlaybackStopped(userID: String, itemID: String, parameters: OnPlaybackStoppedParameters? = nil) -> Request { - Request(path: "/Users/\(userID)/PlayingItems/\(itemID)", method: "DELETE", query: parameters?.asQuery, id: "OnPlaybackStopped") + /// Reports that a session has stopped playing an item. + static func onPlaybackStopped(itemID: String, parameters: OnPlaybackStoppedParameters? = nil) -> Request { + Request(path: "/PlayingItems/\(itemID)", method: "DELETE", query: parameters?.asQuery, id: "OnPlaybackStopped") } struct OnPlaybackStoppedParameters { diff --git a/Sources/Paths/PostCapabilitiesAPI.swift b/Sources/Paths/PostCapabilitiesAPI.swift index 89e3952c..35c63a4e 100644 --- a/Sources/Paths/PostCapabilitiesAPI.swift +++ b/Sources/Paths/PostCapabilitiesAPI.swift @@ -18,25 +18,22 @@ public extension Paths { struct PostCapabilitiesParameters { public var id: String? - public var playableMediaTypes: [String]? + public var playableMediaTypes: [JellyfinAPI.MediaType]? public var supportedCommands: [JellyfinAPI.GeneralCommandType]? public var isSupportsMediaControl: Bool? - public var isSupportsSync: Bool? public var isSupportsPersistentIdentifier: Bool? public init( id: String? = nil, - playableMediaTypes: [String]? = nil, + playableMediaTypes: [JellyfinAPI.MediaType]? = nil, supportedCommands: [JellyfinAPI.GeneralCommandType]? = nil, isSupportsMediaControl: Bool? = nil, - isSupportsSync: Bool? = nil, isSupportsPersistentIdentifier: Bool? = nil ) { self.id = id self.playableMediaTypes = playableMediaTypes self.supportedCommands = supportedCommands self.isSupportsMediaControl = isSupportsMediaControl - self.isSupportsSync = isSupportsSync self.isSupportsPersistentIdentifier = isSupportsPersistentIdentifier } @@ -46,7 +43,6 @@ public extension Paths { encoder.encode(playableMediaTypes, forKey: "playableMediaTypes") encoder.encode(supportedCommands, forKey: "supportedCommands") encoder.encode(isSupportsMediaControl, forKey: "supportsMediaControl") - encoder.encode(isSupportsSync, forKey: "supportsSync") encoder.encode(isSupportsPersistentIdentifier, forKey: "supportsPersistentIdentifier") return encoder.items } diff --git a/Sources/Paths/PostUserImageAPI.swift b/Sources/Paths/PostUserImageAPI.swift index 30a1d385..21835ea3 100644 --- a/Sources/Paths/PostUserImageAPI.swift +++ b/Sources/Paths/PostUserImageAPI.swift @@ -12,19 +12,13 @@ import URLQueryEncoder extension Paths { /// Sets the user image. - public static func postUserImage(userID: String, imageType: String, index: Int? = nil, _ body: Data? = nil) -> Request { - Request( - path: "/Users/\(userID)/Images/\(imageType)", - method: "POST", - query: makePostUserImageQuery(index), - body: body, - id: "PostUserImage" - ) + public static func postUserImage(userID: String? = nil, _ body: Data? = nil) -> Request { + Request(path: "/UserImage", method: "POST", query: makePostUserImageQuery(userID), body: body, id: "PostUserImage") } - private static func makePostUserImageQuery(_ index: Int?) -> [(String, String?)] { + private static func makePostUserImageQuery(_ userID: String?) -> [(String, String?)] { let encoder = URLQueryEncoder() - encoder.encode(index, forKey: "index") + encoder.encode(userID, forKey: "userId") return encoder.items } } diff --git a/Sources/Paths/RemoveItemFromPlaylistAPI.swift b/Sources/Paths/RemoveItemFromPlaylistAPI.swift new file mode 100644 index 00000000..30ccd57b --- /dev/null +++ b/Sources/Paths/RemoveItemFromPlaylistAPI.swift @@ -0,0 +1,29 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation +import Get +import URLQueryEncoder + +extension Paths { + /// Removes items from a playlist. + public static func removeItemFromPlaylist(playlistID: String, entryIDs: [String]? = nil) -> Request { + Request( + path: "/Playlists/\(playlistID)/Items", + method: "DELETE", + query: makeRemoveItemFromPlaylistQuery(entryIDs), + id: "RemoveItemFromPlaylist" + ) + } + + private static func makeRemoveItemFromPlaylistQuery(_ entryIDs: [String]?) -> [(String, String?)] { + let encoder = URLQueryEncoder() + encoder.encode(entryIDs, forKey: "entryIds") + return encoder.items + } +} diff --git a/Sources/Paths/RemoveUserFromPlaylistAPI.swift b/Sources/Paths/RemoveUserFromPlaylistAPI.swift new file mode 100644 index 00000000..8cba1029 --- /dev/null +++ b/Sources/Paths/RemoveUserFromPlaylistAPI.swift @@ -0,0 +1,18 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation +import Get +import URLQueryEncoder + +public extension Paths { + /// Remove a user from a playlist's users. + static func removeUserFromPlaylist(playlistID: String, userID: String) -> Request { + Request(path: "/Playlists/\(playlistID)/Users/\(userID)", method: "DELETE", id: "RemoveUserFromPlaylist") + } +} diff --git a/Sources/Paths/SearchRemoteLyricsAPI.swift b/Sources/Paths/SearchRemoteLyricsAPI.swift new file mode 100644 index 00000000..d1252c4a --- /dev/null +++ b/Sources/Paths/SearchRemoteLyricsAPI.swift @@ -0,0 +1,18 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation +import Get +import URLQueryEncoder + +public extension Paths { + /// Search remote lyrics. + static func searchRemoteLyrics(itemID: String) -> Request<[JellyfinAPI.RemoteLyricInfoDto]> { + Request(path: "/Audio/\(itemID)/RemoteSearch/Lyrics", method: "GET", id: "SearchRemoteLyrics") + } +} diff --git a/Sources/Paths/UnmarkFavoriteItemAPI.swift b/Sources/Paths/UnmarkFavoriteItemAPI.swift index 14584aba..172c8183 100644 --- a/Sources/Paths/UnmarkFavoriteItemAPI.swift +++ b/Sources/Paths/UnmarkFavoriteItemAPI.swift @@ -10,9 +10,20 @@ import Foundation import Get import URLQueryEncoder -public extension Paths { +extension Paths { /// Unmarks item as a favorite. - static func unmarkFavoriteItem(userID: String, itemID: String) -> Request { - Request(path: "/Users/\(userID)/FavoriteItems/\(itemID)", method: "DELETE", id: "UnmarkFavoriteItem") + public static func unmarkFavoriteItem(itemID: String, userID: String? = nil) -> Request { + Request( + path: "/UserFavoriteItems/\(itemID)", + method: "DELETE", + query: makeUnmarkFavoriteItemQuery(userID), + id: "UnmarkFavoriteItem" + ) + } + + private static func makeUnmarkFavoriteItemQuery(_ userID: String?) -> [(String, String?)] { + let encoder = URLQueryEncoder() + encoder.encode(userID, forKey: "userId") + return encoder.items } } diff --git a/Sources/Paths/UpdateDisplayPreferencesAPI.swift b/Sources/Paths/UpdateDisplayPreferencesAPI.swift index f8be2678..ab0361c3 100644 --- a/Sources/Paths/UpdateDisplayPreferencesAPI.swift +++ b/Sources/Paths/UpdateDisplayPreferencesAPI.swift @@ -10,20 +10,27 @@ import Foundation import Get import URLQueryEncoder -public extension Paths { +extension Paths { /// Update Display Preferences. - static func updateDisplayPreferences( + public static func updateDisplayPreferences( displayPreferencesID: String, - userID: String, + userID: String? = nil, client: String, _ body: JellyfinAPI.DisplayPreferencesDto ) -> Request { Request( path: "/DisplayPreferences/\(displayPreferencesID)", method: "POST", - query: [("userId", userID), ("client", client)], + query: makeUpdateDisplayPreferencesQuery(userID, client), body: body, id: "UpdateDisplayPreferences" ) } + + private static func makeUpdateDisplayPreferencesQuery(_ userID: String?, _ client: String) -> [(String, String?)] { + let encoder = URLQueryEncoder() + encoder.encode(userID, forKey: "userId") + encoder.encode(client, forKey: "client") + return encoder.items + } } diff --git a/Sources/Paths/UpdateItemUserDataAPI.swift b/Sources/Paths/UpdateItemUserDataAPI.swift new file mode 100644 index 00000000..4339dc48 --- /dev/null +++ b/Sources/Paths/UpdateItemUserDataAPI.swift @@ -0,0 +1,34 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation +import Get +import URLQueryEncoder + +extension Paths { + /// Update Item User Data. + public static func updateItemUserData( + itemID: String, + userID: String? = nil, + _ body: JellyfinAPI.UpdateUserItemDataDto + ) -> Request { + Request( + path: "/UserItems/\(itemID)/UserData", + method: "POST", + query: makeUpdateItemUserDataQuery(userID), + body: body, + id: "UpdateItemUserData" + ) + } + + private static func makeUpdateItemUserDataQuery(_ userID: String?) -> [(String, String?)] { + let encoder = URLQueryEncoder() + encoder.encode(userID, forKey: "userId") + return encoder.items + } +} diff --git a/Sources/Paths/UpdatePlaylistAPI.swift b/Sources/Paths/UpdatePlaylistAPI.swift new file mode 100644 index 00000000..b70746fc --- /dev/null +++ b/Sources/Paths/UpdatePlaylistAPI.swift @@ -0,0 +1,18 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation +import Get +import URLQueryEncoder + +public extension Paths { + /// Updates a playlist. + static func updatePlaylist(playlistID: String, _ body: JellyfinAPI.UpdatePlaylistDto) -> Request { + Request(path: "/Playlists/\(playlistID)", method: "POST", body: body, id: "UpdatePlaylist") + } +} diff --git a/Sources/Paths/UpdatePlaylistUserAPI.swift b/Sources/Paths/UpdatePlaylistUserAPI.swift new file mode 100644 index 00000000..808dc56c --- /dev/null +++ b/Sources/Paths/UpdatePlaylistUserAPI.swift @@ -0,0 +1,18 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation +import Get +import URLQueryEncoder + +public extension Paths { + /// Modify a user of a playlist's users. + static func updatePlaylistUser(playlistID: String, userID: String, _ body: JellyfinAPI.UpdatePlaylistUserDto) -> Request { + Request(path: "/Playlists/\(playlistID)/Users/\(userID)", method: "POST", body: body, id: "UpdatePlaylistUser") + } +} diff --git a/Sources/Paths/UpdateUserAPI.swift b/Sources/Paths/UpdateUserAPI.swift index fc665c08..cfefbfb3 100644 --- a/Sources/Paths/UpdateUserAPI.swift +++ b/Sources/Paths/UpdateUserAPI.swift @@ -10,9 +10,15 @@ import Foundation import Get import URLQueryEncoder -public extension Paths { +extension Paths { /// Updates a user. - static func updateUser(userID: String, _ body: JellyfinAPI.UserDto) -> Request { - Request(path: "/Users/\(userID)", method: "POST", body: body, id: "UpdateUser") + public static func updateUser(userID: String? = nil, _ body: JellyfinAPI.UserDto) -> Request { + Request(path: "/Users", method: "POST", query: makeUpdateUserQuery(userID), body: body, id: "UpdateUser") + } + + private static func makeUpdateUserQuery(_ userID: String?) -> [(String, String?)] { + let encoder = URLQueryEncoder() + encoder.encode(userID, forKey: "userId") + return encoder.items } } diff --git a/Sources/Paths/UpdateUserConfigurationAPI.swift b/Sources/Paths/UpdateUserConfigurationAPI.swift index 471ca3ba..71dab7eb 100644 --- a/Sources/Paths/UpdateUserConfigurationAPI.swift +++ b/Sources/Paths/UpdateUserConfigurationAPI.swift @@ -10,9 +10,21 @@ import Foundation import Get import URLQueryEncoder -public extension Paths { +extension Paths { /// Updates a user configuration. - static func updateUserConfiguration(userID: String, _ body: JellyfinAPI.UserConfiguration) -> Request { - Request(path: "/Users/\(userID)/Configuration", method: "POST", body: body, id: "UpdateUserConfiguration") + public static func updateUserConfiguration(userID: String? = nil, _ body: JellyfinAPI.UserConfiguration) -> Request { + Request( + path: "/Users/Configuration", + method: "POST", + query: makeUpdateUserConfigurationQuery(userID), + body: body, + id: "UpdateUserConfiguration" + ) + } + + private static func makeUpdateUserConfigurationQuery(_ userID: String?) -> [(String, String?)] { + let encoder = URLQueryEncoder() + encoder.encode(userID, forKey: "userId") + return encoder.items } } diff --git a/Sources/Paths/UpdateUserItemRatingAPI.swift b/Sources/Paths/UpdateUserItemRatingAPI.swift index f562afb3..df6e3134 100644 --- a/Sources/Paths/UpdateUserItemRatingAPI.swift +++ b/Sources/Paths/UpdateUserItemRatingAPI.swift @@ -12,17 +12,22 @@ import URLQueryEncoder extension Paths { /// Updates a user's rating for an item. - public static func updateUserItemRating(userID: String, itemID: String, isLikes: Bool? = nil) -> Request { + public static func updateUserItemRating( + itemID: String, + userID: String? = nil, + isLikes: Bool? = nil + ) -> Request { Request( - path: "/Users/\(userID)/Items/\(itemID)/Rating", + path: "/UserItems/\(itemID)/Rating", method: "POST", - query: makeUpdateUserItemRatingQuery(isLikes), + query: makeUpdateUserItemRatingQuery(userID, isLikes), id: "UpdateUserItemRating" ) } - private static func makeUpdateUserItemRatingQuery(_ isLikes: Bool?) -> [(String, String?)] { + private static func makeUpdateUserItemRatingQuery(_ userID: String?, _ isLikes: Bool?) -> [(String, String?)] { let encoder = URLQueryEncoder() + encoder.encode(userID, forKey: "userId") encoder.encode(isLikes, forKey: "likes") return encoder.items } diff --git a/Sources/Paths/UpdateUserPasswordAPI.swift b/Sources/Paths/UpdateUserPasswordAPI.swift index 95e0515e..3e7b82b4 100644 --- a/Sources/Paths/UpdateUserPasswordAPI.swift +++ b/Sources/Paths/UpdateUserPasswordAPI.swift @@ -10,9 +10,15 @@ import Foundation import Get import URLQueryEncoder -public extension Paths { +extension Paths { /// Updates a user's password. - static func updateUserPassword(userID: String, _ body: JellyfinAPI.UpdateUserPassword) -> Request { - Request(path: "/Users/\(userID)/Password", method: "POST", body: body, id: "UpdateUserPassword") + public static func updateUserPassword(userID: String? = nil, _ body: JellyfinAPI.UpdateUserPassword) -> Request { + Request(path: "/Users/Password", method: "POST", query: makeUpdateUserPasswordQuery(userID), body: body, id: "UpdateUserPassword") + } + + private static func makeUpdateUserPasswordQuery(_ userID: String?) -> [(String, String?)] { + let encoder = URLQueryEncoder() + encoder.encode(userID, forKey: "userId") + return encoder.items } } diff --git a/Sources/Paths/UploadLyricsAPI.swift b/Sources/Paths/UploadLyricsAPI.swift new file mode 100644 index 00000000..ff1c8c5c --- /dev/null +++ b/Sources/Paths/UploadLyricsAPI.swift @@ -0,0 +1,18 @@ +// +// jellyfin-sdk-swift is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation +import Get +import URLQueryEncoder + +public extension Paths { + /// Upload an external lyric file. + static func uploadLyrics(itemID: String, fileName: String, _ body: String? = nil) -> Request { + Request(path: "/Audio/\(itemID)/Lyrics", method: "POST", query: [("fileName", fileName)], body: body, id: "UploadLyrics") + } +} diff --git a/Sources/QuickConnect.swift b/Sources/QuickConnect.swift index 41882b04..e7c11537 100644 --- a/Sources/QuickConnect.swift +++ b/Sources/QuickConnect.swift @@ -9,11 +9,11 @@ import Foundation /// A provider for the Quick Connect authorization flow. -/// +/// /// To start the authorization flow, call `start()`. The `state` variable /// will be updated to the current flow state and can be subscribed to with /// async/await or Combine. See `QuickConnect.State` for all possible states. -/// +/// /// To stop the authorization flow, typically for user cancellation, call `stop()`. public final class QuickConnect: ObservableObject { diff --git a/Sources/jellyfin-openapi-stable.json b/Sources/jellyfin-openapi-stable.json index 9b60e838..ae470481 100644 --- a/Sources/jellyfin-openapi-stable.json +++ b/Sources/jellyfin-openapi-stable.json @@ -2,8 +2,8 @@ "openapi": "3.0.1", "info": { "title": "Jellyfin API", - "version": "10.8.13", - "x-jellyfin-version": "10.8.13" + "version": "10.9.2", + "x-jellyfin-version": "10.9.2" }, "servers": [ { @@ -321,7 +321,7 @@ "schema": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/MediaType" } } }, @@ -504,7 +504,7 @@ "schema": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/ItemSortBy" } } }, @@ -750,7 +750,7 @@ "schema": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/MediaType" } } }, @@ -933,7 +933,7 @@ "schema": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/ItemSortBy" } } }, @@ -1059,6 +1059,7 @@ "name": "deviceProfileId", "in": "query", "description": "Optional. The dlna device profile id to utilize.", + "deprecated": true, "schema": { "type": "string" } @@ -1292,6 +1293,13 @@ "in": "query", "description": "Optional. Specify the subtitle delivery method.", "schema": { + "enum": [ + "Encode", + "Embed", + "External", + "Hls", + "Drop" + ], "allOf": [ { "$ref": "#/components/schemas/SubtitleDeliveryMethod" @@ -1424,6 +1432,10 @@ "in": "query", "description": "Optional. The MediaBrowser.Model.Dlna.EncodingContext.", "schema": { + "enum": [ + "Streaming", + "Static" + ], "allOf": [ { "$ref": "#/components/schemas/EncodingContext" @@ -1512,6 +1524,7 @@ "name": "deviceProfileId", "in": "query", "description": "Optional. The dlna device profile id to utilize.", + "deprecated": true, "schema": { "type": "string" } @@ -1745,6 +1758,13 @@ "in": "query", "description": "Optional. Specify the subtitle delivery method.", "schema": { + "enum": [ + "Encode", + "Embed", + "External", + "Hls", + "Drop" + ], "allOf": [ { "$ref": "#/components/schemas/SubtitleDeliveryMethod" @@ -1877,6 +1897,10 @@ "in": "query", "description": "Optional. The MediaBrowser.Model.Dlna.EncodingContext.", "schema": { + "enum": [ + "Streaming", + "Static" + ], "allOf": [ { "$ref": "#/components/schemas/EncodingContext" @@ -1967,6 +1991,7 @@ "name": "deviceProfileId", "in": "query", "description": "Optional. The dlna device profile id to utilize.", + "deprecated": true, "schema": { "type": "string" } @@ -1991,7 +2016,7 @@ { "name": "segmentLength", "in": "query", - "description": "The segment lenght.", + "description": "The segment length.", "schema": { "type": "integer", "format": "int32" @@ -2200,6 +2225,13 @@ "in": "query", "description": "Optional. Specify the subtitle delivery method.", "schema": { + "enum": [ + "Encode", + "Embed", + "External", + "Hls", + "Drop" + ], "allOf": [ { "$ref": "#/components/schemas/SubtitleDeliveryMethod" @@ -2332,6 +2364,10 @@ "in": "query", "description": "Optional. The MediaBrowser.Model.Dlna.EncodingContext.", "schema": { + "enum": [ + "Streaming", + "Static" + ], "allOf": [ { "$ref": "#/components/schemas/EncodingContext" @@ -2420,6 +2456,7 @@ "name": "deviceProfileId", "in": "query", "description": "Optional. The dlna device profile id to utilize.", + "deprecated": true, "schema": { "type": "string" } @@ -2444,7 +2481,7 @@ { "name": "segmentLength", "in": "query", - "description": "The segment lenght.", + "description": "The segment length.", "schema": { "type": "integer", "format": "int32" @@ -2653,6 +2690,13 @@ "in": "query", "description": "Optional. Specify the subtitle delivery method.", "schema": { + "enum": [ + "Encode", + "Embed", + "External", + "Hls", + "Drop" + ], "allOf": [ { "$ref": "#/components/schemas/SubtitleDeliveryMethod" @@ -2785,6 +2829,10 @@ "in": "query", "description": "Optional. The MediaBrowser.Model.Dlna.EncodingContext.", "schema": { + "enum": [ + "Streaming", + "Static" + ], "allOf": [ { "$ref": "#/components/schemas/EncodingContext" @@ -3165,7 +3213,7 @@ "schema": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/ItemSortBy" } } }, @@ -3546,6 +3594,7 @@ "security": [ { "CustomAuthentication": [ + "CollectionManagement", "DefaultAuthorization" ] } @@ -3598,6 +3647,7 @@ "security": [ { "CustomAuthentication": [ + "CollectionManagement", "DefaultAuthorization" ] } @@ -3648,6 +3698,7 @@ "security": [ { "CustomAuthentication": [ + "CollectionManagement", "DefaultAuthorization" ] } @@ -3903,71 +3954,6 @@ ] } }, - "/System/MediaEncoder/Path": { - "post": { - "tags": [ - "Configuration" - ], - "summary": "Updates the path to the media encoder.", - "operationId": "UpdateMediaEncoderPath", - "requestBody": { - "description": "Media encoder path form body.", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/MediaEncoderPathDto" - } - ], - "description": "Media Encoder Path Dto." - } - }, - "text/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/MediaEncoderPathDto" - } - ], - "description": "Media Encoder Path Dto." - } - }, - "application/*+json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/MediaEncoderPathDto" - } - ], - "description": "Media Encoder Path Dto." - } - } - }, - "required": true - }, - "responses": { - "204": { - "description": "Media encoder path updated." - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - }, - "deprecated": true, - "security": [ - { - "CustomAuthentication": [ - "FirstTimeSetupOrElevated", - "DefaultAuthorization" - ] - } - ] - } - }, "/web/ConfigurationPage": { "get": { "tags": [ @@ -4103,7 +4089,7 @@ "security": [ { "CustomAuthentication": [ - "DefaultAuthorization" + "RequiresElevation" ] } ] @@ -4117,14 +4103,6 @@ "summary": "Get Devices.", "operationId": "GetDevices", "parameters": [ - { - "name": "supportsSync", - "in": "query", - "description": "Gets or sets a value indicating whether [supports synchronize].", - "schema": { - "type": "boolean" - } - }, { "name": "userId", "in": "query", @@ -4471,7 +4449,6 @@ "name": "userId", "in": "query", "description": "User id.", - "required": true, "schema": { "type": "string", "format": "uuid" @@ -4543,7 +4520,6 @@ "name": "userId", "in": "query", "description": "User Id.", - "required": true, "schema": { "type": "string", "format": "uuid" @@ -4615,1220 +4591,6 @@ ] } }, - "/Dlna/ProfileInfos": { - "get": { - "tags": [ - "Dlna" - ], - "summary": "Get profile infos.", - "operationId": "GetProfileInfos", - "responses": { - "200": { - "description": "Device profile infos returned.", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/DeviceProfileInfo" - } - } - }, - "application/json; profile=\"CamelCase\"": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/DeviceProfileInfo" - } - } - }, - "application/json; profile=\"PascalCase\"": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/DeviceProfileInfo" - } - } - } - } - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - }, - "security": [ - { - "CustomAuthentication": [ - "RequiresElevation" - ] - } - ] - } - }, - "/Dlna/Profiles": { - "post": { - "tags": [ - "Dlna" - ], - "summary": "Creates a profile.", - "operationId": "CreateProfile", - "requestBody": { - "description": "Device profile.", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/DeviceProfile" - } - ], - "description": "A MediaBrowser.Model.Dlna.DeviceProfile represents a set of metadata which determines which content a certain device is able to play.\r\n
\r\nSpecifically, it defines the supported containers and\r\ncodecs (video and/or audio, including codec profiles and levels)\r\nthe device is able to direct play (without transcoding or remuxing),\r\nas well as which containers/codecs to transcode to in case it isn't." - } - }, - "text/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/DeviceProfile" - } - ], - "description": "A MediaBrowser.Model.Dlna.DeviceProfile represents a set of metadata which determines which content a certain device is able to play.\r\n
\r\nSpecifically, it defines the supported containers and\r\ncodecs (video and/or audio, including codec profiles and levels)\r\nthe device is able to direct play (without transcoding or remuxing),\r\nas well as which containers/codecs to transcode to in case it isn't." - } - }, - "application/*+json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/DeviceProfile" - } - ], - "description": "A MediaBrowser.Model.Dlna.DeviceProfile represents a set of metadata which determines which content a certain device is able to play.\r\n
\r\nSpecifically, it defines the supported containers and\r\ncodecs (video and/or audio, including codec profiles and levels)\r\nthe device is able to direct play (without transcoding or remuxing),\r\nas well as which containers/codecs to transcode to in case it isn't." - } - } - } - }, - "responses": { - "204": { - "description": "Device profile created." - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - }, - "security": [ - { - "CustomAuthentication": [ - "RequiresElevation" - ] - } - ] - } - }, - "/Dlna/Profiles/{profileId}": { - "get": { - "tags": [ - "Dlna" - ], - "summary": "Gets a single profile.", - "operationId": "GetProfile", - "parameters": [ - { - "name": "profileId", - "in": "path", - "description": "Profile Id.", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Device profile returned.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceProfile" - } - }, - "application/json; profile=\"CamelCase\"": { - "schema": { - "$ref": "#/components/schemas/DeviceProfile" - } - }, - "application/json; profile=\"PascalCase\"": { - "schema": { - "$ref": "#/components/schemas/DeviceProfile" - } - } - } - }, - "404": { - "description": "Device profile not found.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - }, - "application/json; profile=\"CamelCase\"": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - }, - "application/json; profile=\"PascalCase\"": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - }, - "security": [ - { - "CustomAuthentication": [ - "RequiresElevation" - ] - } - ] - }, - "delete": { - "tags": [ - "Dlna" - ], - "summary": "Deletes a profile.", - "operationId": "DeleteProfile", - "parameters": [ - { - "name": "profileId", - "in": "path", - "description": "Profile id.", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "204": { - "description": "Device profile deleted." - }, - "404": { - "description": "Device profile not found.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - }, - "application/json; profile=\"CamelCase\"": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - }, - "application/json; profile=\"PascalCase\"": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - }, - "security": [ - { - "CustomAuthentication": [ - "RequiresElevation" - ] - } - ] - }, - "post": { - "tags": [ - "Dlna" - ], - "summary": "Updates a profile.", - "operationId": "UpdateProfile", - "parameters": [ - { - "name": "profileId", - "in": "path", - "description": "Profile id.", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "description": "Device profile.", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/DeviceProfile" - } - ], - "description": "A MediaBrowser.Model.Dlna.DeviceProfile represents a set of metadata which determines which content a certain device is able to play.\r\n
\r\nSpecifically, it defines the supported containers and\r\ncodecs (video and/or audio, including codec profiles and levels)\r\nthe device is able to direct play (without transcoding or remuxing),\r\nas well as which containers/codecs to transcode to in case it isn't." - } - }, - "text/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/DeviceProfile" - } - ], - "description": "A MediaBrowser.Model.Dlna.DeviceProfile represents a set of metadata which determines which content a certain device is able to play.\r\n
\r\nSpecifically, it defines the supported containers and\r\ncodecs (video and/or audio, including codec profiles and levels)\r\nthe device is able to direct play (without transcoding or remuxing),\r\nas well as which containers/codecs to transcode to in case it isn't." - } - }, - "application/*+json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/DeviceProfile" - } - ], - "description": "A MediaBrowser.Model.Dlna.DeviceProfile represents a set of metadata which determines which content a certain device is able to play.\r\n
\r\nSpecifically, it defines the supported containers and\r\ncodecs (video and/or audio, including codec profiles and levels)\r\nthe device is able to direct play (without transcoding or remuxing),\r\nas well as which containers/codecs to transcode to in case it isn't." - } - } - } - }, - "responses": { - "204": { - "description": "Device profile updated." - }, - "404": { - "description": "Device profile not found.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - }, - "application/json; profile=\"CamelCase\"": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - }, - "application/json; profile=\"PascalCase\"": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - }, - "security": [ - { - "CustomAuthentication": [ - "RequiresElevation" - ] - } - ] - } - }, - "/Dlna/Profiles/Default": { - "get": { - "tags": [ - "Dlna" - ], - "summary": "Gets the default profile.", - "operationId": "GetDefaultProfile", - "responses": { - "200": { - "description": "Default device profile returned.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceProfile" - } - }, - "application/json; profile=\"CamelCase\"": { - "schema": { - "$ref": "#/components/schemas/DeviceProfile" - } - }, - "application/json; profile=\"PascalCase\"": { - "schema": { - "$ref": "#/components/schemas/DeviceProfile" - } - } - } - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - }, - "security": [ - { - "CustomAuthentication": [ - "RequiresElevation" - ] - } - ] - } - }, - "/Dlna/{serverId}/ConnectionManager": { - "get": { - "tags": [ - "DlnaServer" - ], - "summary": "Gets Dlna media receiver registrar xml.", - "operationId": "GetConnectionManager", - "parameters": [ - { - "name": "serverId", - "in": "path", - "description": "Server UUID.", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Dlna media receiver registrar xml returned.", - "content": { - "text/xml": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - }, - "503": { - "description": "DLNA is disabled." - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - }, - "security": [ - { - "CustomAuthentication": [ - "AnonymousLanAccessPolicy" - ] - } - ] - } - }, - "/Dlna/{serverId}/ConnectionManager/ConnectionManager": { - "get": { - "tags": [ - "DlnaServer" - ], - "summary": "Gets Dlna media receiver registrar xml.", - "operationId": "GetConnectionManager_2", - "parameters": [ - { - "name": "serverId", - "in": "path", - "description": "Server UUID.", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Dlna media receiver registrar xml returned.", - "content": { - "text/xml": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - }, - "503": { - "description": "DLNA is disabled." - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - }, - "security": [ - { - "CustomAuthentication": [ - "AnonymousLanAccessPolicy" - ] - } - ] - } - }, - "/Dlna/{serverId}/ConnectionManager/ConnectionManager.xml": { - "get": { - "tags": [ - "DlnaServer" - ], - "summary": "Gets Dlna media receiver registrar xml.", - "operationId": "GetConnectionManager_3", - "parameters": [ - { - "name": "serverId", - "in": "path", - "description": "Server UUID.", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Dlna media receiver registrar xml returned.", - "content": { - "text/xml": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - }, - "503": { - "description": "DLNA is disabled." - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - }, - "security": [ - { - "CustomAuthentication": [ - "AnonymousLanAccessPolicy" - ] - } - ] - } - }, - "/Dlna/{serverId}/ConnectionManager/Control": { - "post": { - "tags": [ - "DlnaServer" - ], - "summary": "Process a connection manager control request.", - "operationId": "ProcessConnectionManagerControlRequest", - "parameters": [ - { - "name": "serverId", - "in": "path", - "description": "Server UUID.", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Request processed.", - "content": { - "text/xml": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - }, - "503": { - "description": "DLNA is disabled." - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - }, - "security": [ - { - "CustomAuthentication": [ - "AnonymousLanAccessPolicy" - ] - } - ] - } - }, - "/Dlna/{serverId}/ContentDirectory": { - "get": { - "tags": [ - "DlnaServer" - ], - "summary": "Gets Dlna content directory xml.", - "operationId": "GetContentDirectory", - "parameters": [ - { - "name": "serverId", - "in": "path", - "description": "Server UUID.", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Dlna content directory returned.", - "content": { - "text/xml": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - }, - "503": { - "description": "DLNA is disabled." - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - }, - "security": [ - { - "CustomAuthentication": [ - "AnonymousLanAccessPolicy" - ] - } - ] - } - }, - "/Dlna/{serverId}/ContentDirectory/ContentDirectory": { - "get": { - "tags": [ - "DlnaServer" - ], - "summary": "Gets Dlna content directory xml.", - "operationId": "GetContentDirectory_2", - "parameters": [ - { - "name": "serverId", - "in": "path", - "description": "Server UUID.", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Dlna content directory returned.", - "content": { - "text/xml": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - }, - "503": { - "description": "DLNA is disabled." - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - }, - "security": [ - { - "CustomAuthentication": [ - "AnonymousLanAccessPolicy" - ] - } - ] - } - }, - "/Dlna/{serverId}/ContentDirectory/ContentDirectory.xml": { - "get": { - "tags": [ - "DlnaServer" - ], - "summary": "Gets Dlna content directory xml.", - "operationId": "GetContentDirectory_3", - "parameters": [ - { - "name": "serverId", - "in": "path", - "description": "Server UUID.", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Dlna content directory returned.", - "content": { - "text/xml": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - }, - "503": { - "description": "DLNA is disabled." - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - }, - "security": [ - { - "CustomAuthentication": [ - "AnonymousLanAccessPolicy" - ] - } - ] - } - }, - "/Dlna/{serverId}/ContentDirectory/Control": { - "post": { - "tags": [ - "DlnaServer" - ], - "summary": "Process a content directory control request.", - "operationId": "ProcessContentDirectoryControlRequest", - "parameters": [ - { - "name": "serverId", - "in": "path", - "description": "Server UUID.", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Request processed.", - "content": { - "text/xml": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - }, - "503": { - "description": "DLNA is disabled." - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - }, - "security": [ - { - "CustomAuthentication": [ - "AnonymousLanAccessPolicy" - ] - } - ] - } - }, - "/Dlna/{serverId}/description": { - "get": { - "tags": [ - "DlnaServer" - ], - "summary": "Get Description Xml.", - "operationId": "GetDescriptionXml", - "parameters": [ - { - "name": "serverId", - "in": "path", - "description": "Server UUID.", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Description xml returned.", - "content": { - "text/xml": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - }, - "503": { - "description": "DLNA is disabled." - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - }, - "security": [ - { - "CustomAuthentication": [ - "AnonymousLanAccessPolicy" - ] - } - ] - } - }, - "/Dlna/{serverId}/description.xml": { - "get": { - "tags": [ - "DlnaServer" - ], - "summary": "Get Description Xml.", - "operationId": "GetDescriptionXml_2", - "parameters": [ - { - "name": "serverId", - "in": "path", - "description": "Server UUID.", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Description xml returned.", - "content": { - "text/xml": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - }, - "503": { - "description": "DLNA is disabled." - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - }, - "security": [ - { - "CustomAuthentication": [ - "AnonymousLanAccessPolicy" - ] - } - ] - } - }, - "/Dlna/{serverId}/icons/{fileName}": { - "get": { - "tags": [ - "DlnaServer" - ], - "summary": "Gets a server icon.", - "operationId": "GetIconId", - "parameters": [ - { - "name": "serverId", - "in": "path", - "description": "Server UUID.", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "fileName", - "in": "path", - "description": "The icon filename.", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Request processed.", - "content": { - "image/*": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - }, - "404": { - "description": "Not Found.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - }, - "application/json; profile=\"CamelCase\"": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - }, - "application/json; profile=\"PascalCase\"": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "503": { - "description": "DLNA is disabled." - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - }, - "security": [ - { - "CustomAuthentication": [ - "AnonymousLanAccessPolicy" - ] - } - ] - } - }, - "/Dlna/{serverId}/MediaReceiverRegistrar": { - "get": { - "tags": [ - "DlnaServer" - ], - "summary": "Gets Dlna media receiver registrar xml.", - "operationId": "GetMediaReceiverRegistrar", - "parameters": [ - { - "name": "serverId", - "in": "path", - "description": "Server UUID.", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Dlna media receiver registrar xml returned.", - "content": { - "text/xml": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - }, - "503": { - "description": "DLNA is disabled." - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - }, - "security": [ - { - "CustomAuthentication": [ - "AnonymousLanAccessPolicy" - ] - } - ] - } - }, - "/Dlna/{serverId}/MediaReceiverRegistrar/Control": { - "post": { - "tags": [ - "DlnaServer" - ], - "summary": "Process a media receiver registrar control request.", - "operationId": "ProcessMediaReceiverRegistrarControlRequest", - "parameters": [ - { - "name": "serverId", - "in": "path", - "description": "Server UUID.", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Request processed.", - "content": { - "text/xml": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - }, - "503": { - "description": "DLNA is disabled." - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - }, - "security": [ - { - "CustomAuthentication": [ - "AnonymousLanAccessPolicy" - ] - } - ] - } - }, - "/Dlna/{serverId}/MediaReceiverRegistrar/MediaReceiverRegistrar": { - "get": { - "tags": [ - "DlnaServer" - ], - "summary": "Gets Dlna media receiver registrar xml.", - "operationId": "GetMediaReceiverRegistrar_2", - "parameters": [ - { - "name": "serverId", - "in": "path", - "description": "Server UUID.", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Dlna media receiver registrar xml returned.", - "content": { - "text/xml": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - }, - "503": { - "description": "DLNA is disabled." - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - }, - "security": [ - { - "CustomAuthentication": [ - "AnonymousLanAccessPolicy" - ] - } - ] - } - }, - "/Dlna/{serverId}/MediaReceiverRegistrar/MediaReceiverRegistrar.xml": { - "get": { - "tags": [ - "DlnaServer" - ], - "summary": "Gets Dlna media receiver registrar xml.", - "operationId": "GetMediaReceiverRegistrar_3", - "parameters": [ - { - "name": "serverId", - "in": "path", - "description": "Server UUID.", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Dlna media receiver registrar xml returned.", - "content": { - "text/xml": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - }, - "503": { - "description": "DLNA is disabled." - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - }, - "security": [ - { - "CustomAuthentication": [ - "AnonymousLanAccessPolicy" - ] - } - ] - } - }, - "/Dlna/icons/{fileName}": { - "get": { - "tags": [ - "DlnaServer" - ], - "summary": "Gets a server icon.", - "operationId": "GetIcon", - "parameters": [ - { - "name": "fileName", - "in": "path", - "description": "The icon filename.", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Request processed.", - "content": { - "image/*": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - }, - "404": { - "description": "Not Found.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - }, - "application/json; profile=\"CamelCase\"": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - }, - "application/json; profile=\"PascalCase\"": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "503": { - "description": "DLNA is disabled." - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - }, - "security": [ - { - "CustomAuthentication": [ - "AnonymousLanAccessPolicy" - ] - } - ] - } - }, "/Audio/{itemId}/hls1/{playlistId}/{segmentId}.{container}": { "get": { "tags": [ @@ -5923,6 +4685,7 @@ "name": "deviceProfileId", "in": "query", "description": "Optional. The dlna device profile id to utilize.", + "deprecated": true, "schema": { "type": "string" } @@ -6165,6 +4928,13 @@ "in": "query", "description": "Optional. Specify the subtitle delivery method.", "schema": { + "enum": [ + "Encode", + "Embed", + "External", + "Hls", + "Drop" + ], "allOf": [ { "$ref": "#/components/schemas/SubtitleDeliveryMethod" @@ -6297,6 +5067,10 @@ "in": "query", "description": "Optional. The MediaBrowser.Model.Dlna.EncodingContext.", "schema": { + "enum": [ + "Streaming", + "Static" + ], "allOf": [ { "$ref": "#/components/schemas/EncodingContext" @@ -6391,6 +5165,7 @@ "name": "deviceProfileId", "in": "query", "description": "Optional. The dlna device profile id to utilize.", + "deprecated": true, "schema": { "type": "string" } @@ -6633,6 +5408,13 @@ "in": "query", "description": "Optional. Specify the subtitle delivery method.", "schema": { + "enum": [ + "Encode", + "Embed", + "External", + "Hls", + "Drop" + ], "allOf": [ { "$ref": "#/components/schemas/SubtitleDeliveryMethod" @@ -6765,6 +5547,10 @@ "in": "query", "description": "Optional. The MediaBrowser.Model.Dlna.EncodingContext.", "schema": { + "enum": [ + "Streaming", + "Static" + ], "allOf": [ { "$ref": "#/components/schemas/EncodingContext" @@ -6859,6 +5645,7 @@ "name": "deviceProfileId", "in": "query", "description": "Optional. The dlna device profile id to utilize.", + "deprecated": true, "schema": { "type": "string" } @@ -7102,6 +5889,13 @@ "in": "query", "description": "Optional. Specify the subtitle delivery method.", "schema": { + "enum": [ + "Encode", + "Embed", + "External", + "Hls", + "Drop" + ], "allOf": [ { "$ref": "#/components/schemas/SubtitleDeliveryMethod" @@ -7234,6 +6028,10 @@ "in": "query", "description": "Optional. The MediaBrowser.Model.Dlna.EncodingContext.", "schema": { + "enum": [ + "Streaming", + "Static" + ], "allOf": [ { "$ref": "#/components/schemas/EncodingContext" @@ -7335,6 +6133,7 @@ "name": "deviceProfileId", "in": "query", "description": "Optional. The dlna device profile id to utilize.", + "deprecated": true, "schema": { "type": "string" } @@ -7578,6 +6377,13 @@ "in": "query", "description": "Optional. Specify the subtitle delivery method.", "schema": { + "enum": [ + "Encode", + "Embed", + "External", + "Hls", + "Drop" + ], "allOf": [ { "$ref": "#/components/schemas/SubtitleDeliveryMethod" @@ -7710,6 +6516,10 @@ "in": "query", "description": "Optional. The MediaBrowser.Model.Dlna.EncodingContext.", "schema": { + "enum": [ + "Streaming", + "Static" + ], "allOf": [ { "$ref": "#/components/schemas/EncodingContext" @@ -7861,6 +6671,7 @@ "name": "deviceProfileId", "in": "query", "description": "Optional. The dlna device profile id to utilize.", + "deprecated": true, "schema": { "type": "string" } @@ -8112,6 +6923,13 @@ "in": "query", "description": "Optional. Specify the subtitle delivery method.", "schema": { + "enum": [ + "Encode", + "Embed", + "External", + "Hls", + "Drop" + ], "allOf": [ { "$ref": "#/components/schemas/SubtitleDeliveryMethod" @@ -8244,6 +7062,10 @@ "in": "query", "description": "Optional. The MediaBrowser.Model.Dlna.EncodingContext.", "schema": { + "enum": [ + "Streaming", + "Static" + ], "allOf": [ { "$ref": "#/components/schemas/EncodingContext" @@ -8347,6 +7169,7 @@ "name": "deviceProfileId", "in": "query", "description": "Optional. The dlna device profile id to utilize.", + "deprecated": true, "schema": { "type": "string" } @@ -8371,7 +7194,7 @@ { "name": "segmentLength", "in": "query", - "description": "The segment lenght.", + "description": "The segment length.", "schema": { "type": "integer", "format": "int32" @@ -8580,6 +7403,13 @@ "in": "query", "description": "Optional. Specify the subtitle delivery method.", "schema": { + "enum": [ + "Encode", + "Embed", + "External", + "Hls", + "Drop" + ], "allOf": [ { "$ref": "#/components/schemas/SubtitleDeliveryMethod" @@ -8712,6 +7542,10 @@ "in": "query", "description": "Optional. The MediaBrowser.Model.Dlna.EncodingContext.", "schema": { + "enum": [ + "Streaming", + "Static" + ], "allOf": [ { "$ref": "#/components/schemas/EncodingContext" @@ -8832,6 +7666,7 @@ "name": "deviceProfileId", "in": "query", "description": "Optional. The dlna device profile id to utilize.", + "deprecated": true, "schema": { "type": "string" } @@ -9083,6 +7918,13 @@ "in": "query", "description": "Optional. Specify the subtitle delivery method.", "schema": { + "enum": [ + "Encode", + "Embed", + "External", + "Hls", + "Drop" + ], "allOf": [ { "$ref": "#/components/schemas/SubtitleDeliveryMethod" @@ -9215,6 +8057,10 @@ "in": "query", "description": "Optional. The MediaBrowser.Model.Dlna.EncodingContext.", "schema": { + "enum": [ + "Streaming", + "Static" + ], "allOf": [ { "$ref": "#/components/schemas/EncodingContext" @@ -9309,6 +8155,7 @@ "name": "deviceProfileId", "in": "query", "description": "Optional. The dlna device profile id to utilize.", + "deprecated": true, "schema": { "type": "string" } @@ -9561,6 +8408,13 @@ "in": "query", "description": "Optional. Specify the subtitle delivery method.", "schema": { + "enum": [ + "Encode", + "Embed", + "External", + "Hls", + "Drop" + ], "allOf": [ { "$ref": "#/components/schemas/SubtitleDeliveryMethod" @@ -9693,6 +8547,10 @@ "in": "query", "description": "Optional. The MediaBrowser.Model.Dlna.EncodingContext.", "schema": { + "enum": [ + "Streaming", + "Static" + ], "allOf": [ { "$ref": "#/components/schemas/EncodingContext" @@ -9720,6 +8578,15 @@ "type": "boolean", "default": true } + }, + { + "name": "enableTrickplay", + "in": "query", + "description": "Enable trickplay image playlists being added to master playlist.", + "schema": { + "type": "boolean", + "default": true + } } ], "responses": { @@ -9794,6 +8661,7 @@ "name": "deviceProfileId", "in": "query", "description": "Optional. The dlna device profile id to utilize.", + "deprecated": true, "schema": { "type": "string" } @@ -10046,6 +8914,13 @@ "in": "query", "description": "Optional. Specify the subtitle delivery method.", "schema": { + "enum": [ + "Encode", + "Embed", + "External", + "Hls", + "Drop" + ], "allOf": [ { "$ref": "#/components/schemas/SubtitleDeliveryMethod" @@ -10178,6 +9053,10 @@ "in": "query", "description": "Optional. The MediaBrowser.Model.Dlna.EncodingContext.", "schema": { + "enum": [ + "Streaming", + "Static" + ], "allOf": [ { "$ref": "#/components/schemas/EncodingContext" @@ -10205,6 +9084,15 @@ "type": "boolean", "default": true } + }, + { + "name": "enableTrickplay", + "in": "query", + "description": "Enable trickplay image playlists being added to master playlist.", + "schema": { + "type": "boolean", + "default": true + } } ], "responses": { @@ -10273,7 +9161,8 @@ "security": [ { "CustomAuthentication": [ - "FirstTimeSetupOrElevated" + "FirstTimeSetupOrElevated", + "DefaultAuthorization" ] } ] @@ -10355,7 +9244,8 @@ "security": [ { "CustomAuthentication": [ - "FirstTimeSetupOrElevated" + "FirstTimeSetupOrElevated", + "DefaultAuthorization" ] } ] @@ -10408,7 +9298,8 @@ "security": [ { "CustomAuthentication": [ - "FirstTimeSetupOrElevated" + "FirstTimeSetupOrElevated", + "DefaultAuthorization" ] } ] @@ -10462,7 +9353,8 @@ "security": [ { "CustomAuthentication": [ - "FirstTimeSetupOrElevated" + "FirstTimeSetupOrElevated", + "DefaultAuthorization" ] } ] @@ -10517,7 +9409,8 @@ "security": [ { "CustomAuthentication": [ - "FirstTimeSetupOrElevated" + "FirstTimeSetupOrElevated", + "DefaultAuthorization" ] } ] @@ -10600,7 +9493,8 @@ "security": [ { "CustomAuthentication": [ - "FirstTimeSetupOrElevated" + "FirstTimeSetupOrElevated", + "DefaultAuthorization" ] } ] @@ -10650,7 +9544,7 @@ "schema": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/MediaType" } } } @@ -10967,7 +9861,7 @@ "schema": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/ItemSortBy" } } }, @@ -11391,6 +10285,21 @@ "description": "Image type.", "required": true, "schema": { + "enum": [ + "Primary", + "Art", + "Backdrop", + "Banner", + "Logo", + "Thumb", + "Disc", + "Box", + "Screenshot", + "Menu", + "Chapter", + "BoxRear", + "Profile" + ], "allOf": [ { "$ref": "#/components/schemas/ImageType" @@ -11412,6 +10321,14 @@ "in": "query", "description": "Determines the output format of the image - original,gif,jpg,png.", "schema": { + "enum": [ + "Bmp", + "Gif", + "Jpg", + "Png", + "Webp", + "Svg" + ], "allOf": [ { "$ref": "#/components/schemas/ImageFormat" @@ -11500,23 +10417,6 @@ "format": "int32" } }, - { - "name": "cropWhitespace", - "in": "query", - "description": "Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.", - "deprecated": true, - "schema": { - "type": "boolean" - } - }, - { - "name": "addPlayedIndicator", - "in": "query", - "description": "Optional. Add a played indicator.", - "schema": { - "type": "boolean" - } - }, { "name": "blur", "in": "query", @@ -11609,6 +10509,21 @@ "description": "Image type.", "required": true, "schema": { + "enum": [ + "Primary", + "Art", + "Backdrop", + "Banner", + "Logo", + "Thumb", + "Disc", + "Box", + "Screenshot", + "Menu", + "Chapter", + "BoxRear", + "Profile" + ], "allOf": [ { "$ref": "#/components/schemas/ImageType" @@ -11630,6 +10545,14 @@ "in": "query", "description": "Determines the output format of the image - original,gif,jpg,png.", "schema": { + "enum": [ + "Bmp", + "Gif", + "Jpg", + "Png", + "Webp", + "Svg" + ], "allOf": [ { "$ref": "#/components/schemas/ImageFormat" @@ -11718,23 +10641,6 @@ "format": "int32" } }, - { - "name": "cropWhitespace", - "in": "query", - "description": "Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.", - "deprecated": true, - "schema": { - "type": "boolean" - } - }, - { - "name": "addPlayedIndicator", - "in": "query", - "description": "Optional. Add a played indicator.", - "schema": { - "type": "boolean" - } - }, { "name": "blur", "in": "query", @@ -11827,6 +10733,14 @@ "in": "query", "description": "Determines the output format of the image - original,gif,jpg,png.", "schema": { + "enum": [ + "Bmp", + "Gif", + "Jpg", + "Png", + "Webp", + "Svg" + ], "allOf": [ { "$ref": "#/components/schemas/ImageFormat" @@ -12061,6 +10975,21 @@ "description": "Image type.", "required": true, "schema": { + "enum": [ + "Primary", + "Art", + "Backdrop", + "Banner", + "Logo", + "Thumb", + "Disc", + "Box", + "Screenshot", + "Menu", + "Chapter", + "BoxRear", + "Profile" + ], "allOf": [ { "$ref": "#/components/schemas/ImageType" @@ -12082,6 +11011,14 @@ "in": "query", "description": "Determines the output format of the image - original,gif,jpg,png.", "schema": { + "enum": [ + "Bmp", + "Gif", + "Jpg", + "Png", + "Webp", + "Svg" + ], "allOf": [ { "$ref": "#/components/schemas/ImageFormat" @@ -12170,23 +11107,6 @@ "format": "int32" } }, - { - "name": "cropWhitespace", - "in": "query", - "description": "Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.", - "deprecated": true, - "schema": { - "type": "boolean" - } - }, - { - "name": "addPlayedIndicator", - "in": "query", - "description": "Optional. Add a played indicator.", - "schema": { - "type": "boolean" - } - }, { "name": "blur", "in": "query", @@ -12278,6 +11198,21 @@ "description": "Image type.", "required": true, "schema": { + "enum": [ + "Primary", + "Art", + "Backdrop", + "Banner", + "Logo", + "Thumb", + "Disc", + "Box", + "Screenshot", + "Menu", + "Chapter", + "BoxRear", + "Profile" + ], "allOf": [ { "$ref": "#/components/schemas/ImageType" @@ -12299,6 +11234,14 @@ "in": "query", "description": "Determines the output format of the image - original,gif,jpg,png.", "schema": { + "enum": [ + "Bmp", + "Gif", + "Jpg", + "Png", + "Webp", + "Svg" + ], "allOf": [ { "$ref": "#/components/schemas/ImageFormat" @@ -12387,23 +11330,6 @@ "format": "int32" } }, - { - "name": "cropWhitespace", - "in": "query", - "description": "Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.", - "deprecated": true, - "schema": { - "type": "boolean" - } - }, - { - "name": "addPlayedIndicator", - "in": "query", - "description": "Optional. Add a played indicator.", - "schema": { - "type": "boolean" - } - }, { "name": "blur", "in": "query", @@ -12497,6 +11423,21 @@ "description": "Image type.", "required": true, "schema": { + "enum": [ + "Primary", + "Art", + "Backdrop", + "Banner", + "Logo", + "Thumb", + "Disc", + "Box", + "Screenshot", + "Menu", + "Chapter", + "BoxRear", + "Profile" + ], "allOf": [ { "$ref": "#/components/schemas/ImageType" @@ -12528,6 +11469,14 @@ "in": "query", "description": "Determines the output format of the image - original,gif,jpg,png.", "schema": { + "enum": [ + "Bmp", + "Gif", + "Jpg", + "Png", + "Webp", + "Svg" + ], "allOf": [ { "$ref": "#/components/schemas/ImageFormat" @@ -12616,23 +11565,6 @@ "format": "int32" } }, - { - "name": "cropWhitespace", - "in": "query", - "description": "Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.", - "deprecated": true, - "schema": { - "type": "boolean" - } - }, - { - "name": "addPlayedIndicator", - "in": "query", - "description": "Optional. Add a played indicator.", - "schema": { - "type": "boolean" - } - }, { "name": "blur", "in": "query", @@ -12715,6 +11647,21 @@ "description": "Image type.", "required": true, "schema": { + "enum": [ + "Primary", + "Art", + "Backdrop", + "Banner", + "Logo", + "Thumb", + "Disc", + "Box", + "Screenshot", + "Menu", + "Chapter", + "BoxRear", + "Profile" + ], "allOf": [ { "$ref": "#/components/schemas/ImageType" @@ -12746,6 +11693,14 @@ "in": "query", "description": "Determines the output format of the image - original,gif,jpg,png.", "schema": { + "enum": [ + "Bmp", + "Gif", + "Jpg", + "Png", + "Webp", + "Svg" + ], "allOf": [ { "$ref": "#/components/schemas/ImageFormat" @@ -12834,23 +11789,6 @@ "format": "int32" } }, - { - "name": "cropWhitespace", - "in": "query", - "description": "Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.", - "deprecated": true, - "schema": { - "type": "boolean" - } - }, - { - "name": "addPlayedIndicator", - "in": "query", - "description": "Optional. Add a played indicator.", - "schema": { - "type": "boolean" - } - }, { "name": "blur", "in": "query", @@ -13021,6 +11959,21 @@ "description": "Image type.", "required": true, "schema": { + "enum": [ + "Primary", + "Art", + "Backdrop", + "Banner", + "Logo", + "Thumb", + "Disc", + "Box", + "Screenshot", + "Menu", + "Chapter", + "BoxRear", + "Profile" + ], "allOf": [ { "$ref": "#/components/schemas/ImageType" @@ -13101,6 +12054,21 @@ "description": "Image type.", "required": true, "schema": { + "enum": [ + "Primary", + "Art", + "Backdrop", + "Banner", + "Logo", + "Thumb", + "Disc", + "Box", + "Screenshot", + "Menu", + "Chapter", + "BoxRear", + "Profile" + ], "allOf": [ { "$ref": "#/components/schemas/ImageType" @@ -13124,6 +12092,26 @@ "204": { "description": "Image saved." }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, "404": { "description": "Item not found.", "content": { @@ -13182,6 +12170,21 @@ "description": "Image type.", "required": true, "schema": { + "enum": [ + "Primary", + "Art", + "Backdrop", + "Banner", + "Logo", + "Thumb", + "Disc", + "Box", + "Screenshot", + "Menu", + "Chapter", + "BoxRear", + "Profile" + ], "allOf": [ { "$ref": "#/components/schemas/ImageType" @@ -13261,20 +12264,19 @@ "type": "string" } }, - { - "name": "cropWhitespace", - "in": "query", - "description": "Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.", - "deprecated": true, - "schema": { - "type": "boolean" - } - }, { "name": "format", "in": "query", "description": "Optional. The MediaBrowser.Model.Drawing.ImageFormat of the returned image.", "schema": { + "enum": [ + "Bmp", + "Gif", + "Jpg", + "Png", + "Webp", + "Svg" + ], "allOf": [ { "$ref": "#/components/schemas/ImageFormat" @@ -13282,14 +12284,6 @@ ] } }, - { - "name": "addPlayedIndicator", - "in": "query", - "description": "Optional. Add a played indicator.", - "schema": { - "type": "boolean" - } - }, { "name": "percentPlayed", "in": "query", @@ -13400,6 +12394,21 @@ "description": "Image type.", "required": true, "schema": { + "enum": [ + "Primary", + "Art", + "Backdrop", + "Banner", + "Logo", + "Thumb", + "Disc", + "Box", + "Screenshot", + "Menu", + "Chapter", + "BoxRear", + "Profile" + ], "allOf": [ { "$ref": "#/components/schemas/ImageType" @@ -13479,20 +12488,19 @@ "type": "string" } }, - { - "name": "cropWhitespace", - "in": "query", - "description": "Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.", - "deprecated": true, - "schema": { - "type": "boolean" - } - }, { "name": "format", "in": "query", "description": "Optional. The MediaBrowser.Model.Drawing.ImageFormat of the returned image.", "schema": { + "enum": [ + "Bmp", + "Gif", + "Jpg", + "Png", + "Webp", + "Svg" + ], "allOf": [ { "$ref": "#/components/schemas/ImageFormat" @@ -13500,14 +12508,6 @@ ] } }, - { - "name": "addPlayedIndicator", - "in": "query", - "description": "Optional. Add a played indicator.", - "schema": { - "type": "boolean" - } - }, { "name": "percentPlayed", "in": "query", @@ -13620,6 +12620,21 @@ "description": "Image type.", "required": true, "schema": { + "enum": [ + "Primary", + "Art", + "Backdrop", + "Banner", + "Logo", + "Thumb", + "Disc", + "Box", + "Screenshot", + "Menu", + "Chapter", + "BoxRear", + "Profile" + ], "allOf": [ { "$ref": "#/components/schemas/ImageType" @@ -13701,6 +12716,21 @@ "description": "Image type.", "required": true, "schema": { + "enum": [ + "Primary", + "Art", + "Backdrop", + "Banner", + "Logo", + "Thumb", + "Disc", + "Box", + "Screenshot", + "Menu", + "Chapter", + "BoxRear", + "Profile" + ], "allOf": [ { "$ref": "#/components/schemas/ImageType" @@ -13734,6 +12764,26 @@ "204": { "description": "Image saved." }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, "404": { "description": "Item not found.", "content": { @@ -13792,6 +12842,21 @@ "description": "Image type.", "required": true, "schema": { + "enum": [ + "Primary", + "Art", + "Backdrop", + "Banner", + "Logo", + "Thumb", + "Disc", + "Box", + "Screenshot", + "Menu", + "Chapter", + "BoxRear", + "Profile" + ], "allOf": [ { "$ref": "#/components/schemas/ImageType" @@ -13881,20 +12946,19 @@ "type": "string" } }, - { - "name": "cropWhitespace", - "in": "query", - "description": "Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.", - "deprecated": true, - "schema": { - "type": "boolean" - } - }, { "name": "format", "in": "query", "description": "Optional. The MediaBrowser.Model.Drawing.ImageFormat of the returned image.", "schema": { + "enum": [ + "Bmp", + "Gif", + "Jpg", + "Png", + "Webp", + "Svg" + ], "allOf": [ { "$ref": "#/components/schemas/ImageFormat" @@ -13902,14 +12966,6 @@ ] } }, - { - "name": "addPlayedIndicator", - "in": "query", - "description": "Optional. Add a played indicator.", - "schema": { - "type": "boolean" - } - }, { "name": "percentPlayed", "in": "query", @@ -14011,6 +13067,21 @@ "description": "Image type.", "required": true, "schema": { + "enum": [ + "Primary", + "Art", + "Backdrop", + "Banner", + "Logo", + "Thumb", + "Disc", + "Box", + "Screenshot", + "Menu", + "Chapter", + "BoxRear", + "Profile" + ], "allOf": [ { "$ref": "#/components/schemas/ImageType" @@ -14100,20 +13171,19 @@ "type": "string" } }, - { - "name": "cropWhitespace", - "in": "query", - "description": "Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.", - "deprecated": true, - "schema": { - "type": "boolean" - } - }, { "name": "format", "in": "query", "description": "Optional. The MediaBrowser.Model.Drawing.ImageFormat of the returned image.", "schema": { + "enum": [ + "Bmp", + "Gif", + "Jpg", + "Png", + "Webp", + "Svg" + ], "allOf": [ { "$ref": "#/components/schemas/ImageFormat" @@ -14121,14 +13191,6 @@ ] } }, - { - "name": "addPlayedIndicator", - "in": "query", - "description": "Optional. Add a played indicator.", - "schema": { - "type": "boolean" - } - }, { "name": "percentPlayed", "in": "query", @@ -14232,6 +13294,21 @@ "description": "Image type.", "required": true, "schema": { + "enum": [ + "Primary", + "Art", + "Backdrop", + "Banner", + "Logo", + "Thumb", + "Disc", + "Box", + "Screenshot", + "Menu", + "Chapter", + "BoxRear", + "Profile" + ], "allOf": [ { "$ref": "#/components/schemas/ImageType" @@ -14314,21 +13391,20 @@ "type": "string" } }, - { - "name": "cropWhitespace", - "in": "query", - "description": "Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.", - "deprecated": true, - "schema": { - "type": "boolean" - } - }, { "name": "format", "in": "path", "description": "Determines the output format of the image - original,gif,jpg,png.", "required": true, "schema": { + "enum": [ + "Bmp", + "Gif", + "Jpg", + "Png", + "Webp", + "Svg" + ], "allOf": [ { "$ref": "#/components/schemas/ImageFormat" @@ -14337,14 +13413,6 @@ "description": "Enum ImageOutputFormat." } }, - { - "name": "addPlayedIndicator", - "in": "query", - "description": "Optional. Add a played indicator.", - "schema": { - "type": "boolean" - } - }, { "name": "percentPlayed", "in": "path", @@ -14458,6 +13526,21 @@ "description": "Image type.", "required": true, "schema": { + "enum": [ + "Primary", + "Art", + "Backdrop", + "Banner", + "Logo", + "Thumb", + "Disc", + "Box", + "Screenshot", + "Menu", + "Chapter", + "BoxRear", + "Profile" + ], "allOf": [ { "$ref": "#/components/schemas/ImageType" @@ -14540,21 +13623,20 @@ "type": "string" } }, - { - "name": "cropWhitespace", - "in": "query", - "description": "Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.", - "deprecated": true, - "schema": { - "type": "boolean" - } - }, { "name": "format", "in": "path", "description": "Determines the output format of the image - original,gif,jpg,png.", "required": true, "schema": { + "enum": [ + "Bmp", + "Gif", + "Jpg", + "Png", + "Webp", + "Svg" + ], "allOf": [ { "$ref": "#/components/schemas/ImageFormat" @@ -14563,14 +13645,6 @@ "description": "Enum ImageOutputFormat." } }, - { - "name": "addPlayedIndicator", - "in": "query", - "description": "Optional. Add a played indicator.", - "schema": { - "type": "boolean" - } - }, { "name": "percentPlayed", "in": "path", @@ -14686,6 +13760,21 @@ "description": "Image type.", "required": true, "schema": { + "enum": [ + "Primary", + "Art", + "Backdrop", + "Banner", + "Logo", + "Thumb", + "Disc", + "Box", + "Screenshot", + "Menu", + "Chapter", + "BoxRear", + "Profile" + ], "allOf": [ { "$ref": "#/components/schemas/ImageType" @@ -14778,6 +13867,21 @@ "description": "Image type.", "required": true, "schema": { + "enum": [ + "Primary", + "Art", + "Backdrop", + "Banner", + "Logo", + "Thumb", + "Disc", + "Box", + "Screenshot", + "Menu", + "Chapter", + "BoxRear", + "Profile" + ], "allOf": [ { "$ref": "#/components/schemas/ImageType" @@ -14799,6 +13903,14 @@ "in": "query", "description": "Determines the output format of the image - original,gif,jpg,png.", "schema": { + "enum": [ + "Bmp", + "Gif", + "Jpg", + "Png", + "Webp", + "Svg" + ], "allOf": [ { "$ref": "#/components/schemas/ImageFormat" @@ -14887,23 +13999,6 @@ "format": "int32" } }, - { - "name": "cropWhitespace", - "in": "query", - "description": "Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.", - "deprecated": true, - "schema": { - "type": "boolean" - } - }, - { - "name": "addPlayedIndicator", - "in": "query", - "description": "Optional. Add a played indicator.", - "schema": { - "type": "boolean" - } - }, { "name": "blur", "in": "query", @@ -14995,6 +14090,21 @@ "description": "Image type.", "required": true, "schema": { + "enum": [ + "Primary", + "Art", + "Backdrop", + "Banner", + "Logo", + "Thumb", + "Disc", + "Box", + "Screenshot", + "Menu", + "Chapter", + "BoxRear", + "Profile" + ], "allOf": [ { "$ref": "#/components/schemas/ImageType" @@ -15016,6 +14126,14 @@ "in": "query", "description": "Determines the output format of the image - original,gif,jpg,png.", "schema": { + "enum": [ + "Bmp", + "Gif", + "Jpg", + "Png", + "Webp", + "Svg" + ], "allOf": [ { "$ref": "#/components/schemas/ImageFormat" @@ -15104,23 +14222,6 @@ "format": "int32" } }, - { - "name": "cropWhitespace", - "in": "query", - "description": "Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.", - "deprecated": true, - "schema": { - "type": "boolean" - } - }, - { - "name": "addPlayedIndicator", - "in": "query", - "description": "Optional. Add a played indicator.", - "schema": { - "type": "boolean" - } - }, { "name": "blur", "in": "query", @@ -15214,6 +14315,21 @@ "description": "Image type.", "required": true, "schema": { + "enum": [ + "Primary", + "Art", + "Backdrop", + "Banner", + "Logo", + "Thumb", + "Disc", + "Box", + "Screenshot", + "Menu", + "Chapter", + "BoxRear", + "Profile" + ], "allOf": [ { "$ref": "#/components/schemas/ImageType" @@ -15245,6 +14361,14 @@ "in": "query", "description": "Determines the output format of the image - original,gif,jpg,png.", "schema": { + "enum": [ + "Bmp", + "Gif", + "Jpg", + "Png", + "Webp", + "Svg" + ], "allOf": [ { "$ref": "#/components/schemas/ImageFormat" @@ -15333,23 +14457,6 @@ "format": "int32" } }, - { - "name": "cropWhitespace", - "in": "query", - "description": "Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.", - "deprecated": true, - "schema": { - "type": "boolean" - } - }, - { - "name": "addPlayedIndicator", - "in": "query", - "description": "Optional. Add a played indicator.", - "schema": { - "type": "boolean" - } - }, { "name": "blur", "in": "query", @@ -15432,6 +14539,21 @@ "description": "Image type.", "required": true, "schema": { + "enum": [ + "Primary", + "Art", + "Backdrop", + "Banner", + "Logo", + "Thumb", + "Disc", + "Box", + "Screenshot", + "Menu", + "Chapter", + "BoxRear", + "Profile" + ], "allOf": [ { "$ref": "#/components/schemas/ImageType" @@ -15463,6 +14585,14 @@ "in": "query", "description": "Determines the output format of the image - original,gif,jpg,png.", "schema": { + "enum": [ + "Bmp", + "Gif", + "Jpg", + "Png", + "Webp", + "Svg" + ], "allOf": [ { "$ref": "#/components/schemas/ImageFormat" @@ -15551,23 +14681,6 @@ "format": "int32" } }, - { - "name": "cropWhitespace", - "in": "query", - "description": "Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.", - "deprecated": true, - "schema": { - "type": "boolean" - } - }, - { - "name": "addPlayedIndicator", - "in": "query", - "description": "Optional. Add a played indicator.", - "schema": { - "type": "boolean" - } - }, { "name": "blur", "in": "query", @@ -15652,6 +14765,21 @@ "description": "Image type.", "required": true, "schema": { + "enum": [ + "Primary", + "Art", + "Backdrop", + "Banner", + "Logo", + "Thumb", + "Disc", + "Box", + "Screenshot", + "Menu", + "Chapter", + "BoxRear", + "Profile" + ], "allOf": [ { "$ref": "#/components/schemas/ImageType" @@ -15673,6 +14801,14 @@ "in": "query", "description": "Determines the output format of the image - original,gif,jpg,png.", "schema": { + "enum": [ + "Bmp", + "Gif", + "Jpg", + "Png", + "Webp", + "Svg" + ], "allOf": [ { "$ref": "#/components/schemas/ImageFormat" @@ -15761,23 +14897,6 @@ "format": "int32" } }, - { - "name": "cropWhitespace", - "in": "query", - "description": "Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.", - "deprecated": true, - "schema": { - "type": "boolean" - } - }, - { - "name": "addPlayedIndicator", - "in": "query", - "description": "Optional. Add a played indicator.", - "schema": { - "type": "boolean" - } - }, { "name": "blur", "in": "query", @@ -15869,6 +14988,21 @@ "description": "Image type.", "required": true, "schema": { + "enum": [ + "Primary", + "Art", + "Backdrop", + "Banner", + "Logo", + "Thumb", + "Disc", + "Box", + "Screenshot", + "Menu", + "Chapter", + "BoxRear", + "Profile" + ], "allOf": [ { "$ref": "#/components/schemas/ImageType" @@ -15890,6 +15024,14 @@ "in": "query", "description": "Determines the output format of the image - original,gif,jpg,png.", "schema": { + "enum": [ + "Bmp", + "Gif", + "Jpg", + "Png", + "Webp", + "Svg" + ], "allOf": [ { "$ref": "#/components/schemas/ImageFormat" @@ -15978,23 +15120,6 @@ "format": "int32" } }, - { - "name": "cropWhitespace", - "in": "query", - "description": "Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.", - "deprecated": true, - "schema": { - "type": "boolean" - } - }, - { - "name": "addPlayedIndicator", - "in": "query", - "description": "Optional. Add a played indicator.", - "schema": { - "type": "boolean" - } - }, { "name": "blur", "in": "query", @@ -16088,6 +15213,21 @@ "description": "Image type.", "required": true, "schema": { + "enum": [ + "Primary", + "Art", + "Backdrop", + "Banner", + "Logo", + "Thumb", + "Disc", + "Box", + "Screenshot", + "Menu", + "Chapter", + "BoxRear", + "Profile" + ], "allOf": [ { "$ref": "#/components/schemas/ImageType" @@ -16119,6 +15259,14 @@ "in": "query", "description": "Determines the output format of the image - original,gif,jpg,png.", "schema": { + "enum": [ + "Bmp", + "Gif", + "Jpg", + "Png", + "Webp", + "Svg" + ], "allOf": [ { "$ref": "#/components/schemas/ImageFormat" @@ -16207,23 +15355,6 @@ "format": "int32" } }, - { - "name": "cropWhitespace", - "in": "query", - "description": "Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.", - "deprecated": true, - "schema": { - "type": "boolean" - } - }, - { - "name": "addPlayedIndicator", - "in": "query", - "description": "Optional. Add a played indicator.", - "schema": { - "type": "boolean" - } - }, { "name": "blur", "in": "query", @@ -16306,6 +15437,21 @@ "description": "Image type.", "required": true, "schema": { + "enum": [ + "Primary", + "Art", + "Backdrop", + "Banner", + "Logo", + "Thumb", + "Disc", + "Box", + "Screenshot", + "Menu", + "Chapter", + "BoxRear", + "Profile" + ], "allOf": [ { "$ref": "#/components/schemas/ImageType" @@ -16337,6 +15483,14 @@ "in": "query", "description": "Determines the output format of the image - original,gif,jpg,png.", "schema": { + "enum": [ + "Bmp", + "Gif", + "Jpg", + "Png", + "Webp", + "Svg" + ], "allOf": [ { "$ref": "#/components/schemas/ImageFormat" @@ -16425,23 +15579,6 @@ "format": "int32" } }, - { - "name": "cropWhitespace", - "in": "query", - "description": "Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.", - "deprecated": true, - "schema": { - "type": "boolean" - } - }, - { - "name": "addPlayedIndicator", - "in": "query", - "description": "Optional. Add a played indicator.", - "schema": { - "type": "boolean" - } - }, { "name": "blur", "in": "query", @@ -16526,6 +15663,21 @@ "description": "Image type.", "required": true, "schema": { + "enum": [ + "Primary", + "Art", + "Backdrop", + "Banner", + "Logo", + "Thumb", + "Disc", + "Box", + "Screenshot", + "Menu", + "Chapter", + "BoxRear", + "Profile" + ], "allOf": [ { "$ref": "#/components/schemas/ImageType" @@ -16547,6 +15699,14 @@ "in": "query", "description": "Determines the output format of the image - original,gif,jpg,png.", "schema": { + "enum": [ + "Bmp", + "Gif", + "Jpg", + "Png", + "Webp", + "Svg" + ], "allOf": [ { "$ref": "#/components/schemas/ImageFormat" @@ -16635,23 +15795,6 @@ "format": "int32" } }, - { - "name": "cropWhitespace", - "in": "query", - "description": "Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.", - "deprecated": true, - "schema": { - "type": "boolean" - } - }, - { - "name": "addPlayedIndicator", - "in": "query", - "description": "Optional. Add a played indicator.", - "schema": { - "type": "boolean" - } - }, { "name": "blur", "in": "query", @@ -16743,6 +15886,21 @@ "description": "Image type.", "required": true, "schema": { + "enum": [ + "Primary", + "Art", + "Backdrop", + "Banner", + "Logo", + "Thumb", + "Disc", + "Box", + "Screenshot", + "Menu", + "Chapter", + "BoxRear", + "Profile" + ], "allOf": [ { "$ref": "#/components/schemas/ImageType" @@ -16764,6 +15922,14 @@ "in": "query", "description": "Determines the output format of the image - original,gif,jpg,png.", "schema": { + "enum": [ + "Bmp", + "Gif", + "Jpg", + "Png", + "Webp", + "Svg" + ], "allOf": [ { "$ref": "#/components/schemas/ImageFormat" @@ -16852,23 +16018,6 @@ "format": "int32" } }, - { - "name": "cropWhitespace", - "in": "query", - "description": "Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.", - "deprecated": true, - "schema": { - "type": "boolean" - } - }, - { - "name": "addPlayedIndicator", - "in": "query", - "description": "Optional. Add a played indicator.", - "schema": { - "type": "boolean" - } - }, { "name": "blur", "in": "query", @@ -16962,6 +16111,21 @@ "description": "Image type.", "required": true, "schema": { + "enum": [ + "Primary", + "Art", + "Backdrop", + "Banner", + "Logo", + "Thumb", + "Disc", + "Box", + "Screenshot", + "Menu", + "Chapter", + "BoxRear", + "Profile" + ], "allOf": [ { "$ref": "#/components/schemas/ImageType" @@ -16993,6 +16157,14 @@ "in": "query", "description": "Determines the output format of the image - original,gif,jpg,png.", "schema": { + "enum": [ + "Bmp", + "Gif", + "Jpg", + "Png", + "Webp", + "Svg" + ], "allOf": [ { "$ref": "#/components/schemas/ImageFormat" @@ -17081,23 +16253,6 @@ "format": "int32" } }, - { - "name": "cropWhitespace", - "in": "query", - "description": "Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.", - "deprecated": true, - "schema": { - "type": "boolean" - } - }, - { - "name": "addPlayedIndicator", - "in": "query", - "description": "Optional. Add a played indicator.", - "schema": { - "type": "boolean" - } - }, { "name": "blur", "in": "query", @@ -17180,6 +16335,21 @@ "description": "Image type.", "required": true, "schema": { + "enum": [ + "Primary", + "Art", + "Backdrop", + "Banner", + "Logo", + "Thumb", + "Disc", + "Box", + "Screenshot", + "Menu", + "Chapter", + "BoxRear", + "Profile" + ], "allOf": [ { "$ref": "#/components/schemas/ImageType" @@ -17211,6 +16381,14 @@ "in": "query", "description": "Determines the output format of the image - original,gif,jpg,png.", "schema": { + "enum": [ + "Bmp", + "Gif", + "Jpg", + "Png", + "Webp", + "Svg" + ], "allOf": [ { "$ref": "#/components/schemas/ImageFormat" @@ -17299,23 +16477,6 @@ "format": "int32" } }, - { - "name": "cropWhitespace", - "in": "query", - "description": "Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.", - "deprecated": true, - "schema": { - "type": "boolean" - } - }, - { - "name": "addPlayedIndicator", - "in": "query", - "description": "Optional. Add a played indicator.", - "schema": { - "type": "boolean" - } - }, { "name": "blur", "in": "query", @@ -17377,7 +16538,7 @@ } } }, - "/Users/{userId}/Images/{imageType}": { + "/UserImage": { "post": { "tags": [ "Image" @@ -17387,36 +16548,12 @@ "parameters": [ { "name": "userId", - "in": "path", + "in": "query", "description": "User Id.", - "required": true, "schema": { "type": "string", "format": "uuid" } - }, - { - "name": "imageType", - "in": "path", - "description": "(Unused) Image type.", - "required": true, - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ImageType" - } - ], - "description": "Enum ImageType." - } - }, - { - "name": "index", - "in": "query", - "description": "(Unused) Image index.", - "schema": { - "type": "integer", - "format": "int32" - } } ], "requestBody": { @@ -17433,6 +16570,26 @@ "204": { "description": "Image updated." }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, "403": { "description": "User does not have permission to delete the image.", "content": { @@ -17453,6 +16610,26 @@ } } }, + "404": { + "description": "Item not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, "401": { "description": "Unauthorized" } @@ -17474,36 +16651,12 @@ "parameters": [ { "name": "userId", - "in": "path", + "in": "query", "description": "User Id.", - "required": true, "schema": { "type": "string", "format": "uuid" } - }, - { - "name": "imageType", - "in": "path", - "description": "(Unused) Image type.", - "required": true, - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ImageType" - } - ], - "description": "Enum ImageType." - } - }, - { - "name": "index", - "in": "query", - "description": "(Unused) Image index.", - "schema": { - "type": "integer", - "format": "int32" - } } ], "responses": { @@ -17551,28 +16704,13 @@ "parameters": [ { "name": "userId", - "in": "path", + "in": "query", "description": "User id.", - "required": true, "schema": { "type": "string", "format": "uuid" } }, - { - "name": "imageType", - "in": "path", - "description": "Image type.", - "required": true, - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ImageType" - } - ], - "description": "Enum ImageType." - } - }, { "name": "tag", "in": "query", @@ -17586,6 +16724,14 @@ "in": "query", "description": "Determines the output format of the image - original,gif,jpg,png.", "schema": { + "enum": [ + "Bmp", + "Gif", + "Jpg", + "Png", + "Webp", + "Svg" + ], "allOf": [ { "$ref": "#/components/schemas/ImageFormat" @@ -17674,23 +16820,6 @@ "format": "int32" } }, - { - "name": "cropWhitespace", - "in": "query", - "description": "Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.", - "deprecated": true, - "schema": { - "type": "boolean" - } - }, - { - "name": "addPlayedIndicator", - "in": "query", - "description": "Optional. Add a played indicator.", - "schema": { - "type": "boolean" - } - }, { "name": "blur", "in": "query", @@ -17738,6 +16867,26 @@ } } }, + "400": { + "description": "User id not provided.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, "404": { "description": "Item not found.", "content": { @@ -17769,28 +16918,13 @@ "parameters": [ { "name": "userId", - "in": "path", + "in": "query", "description": "User id.", - "required": true, "schema": { "type": "string", "format": "uuid" } }, - { - "name": "imageType", - "in": "path", - "description": "Image type.", - "required": true, - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ImageType" - } - ], - "description": "Enum ImageType." - } - }, { "name": "tag", "in": "query", @@ -17804,6 +16938,14 @@ "in": "query", "description": "Determines the output format of the image - original,gif,jpg,png.", "schema": { + "enum": [ + "Bmp", + "Gif", + "Jpg", + "Png", + "Webp", + "Svg" + ], "allOf": [ { "$ref": "#/components/schemas/ImageFormat" @@ -17892,23 +17034,6 @@ "format": "int32" } }, - { - "name": "cropWhitespace", - "in": "query", - "description": "Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.", - "deprecated": true, - "schema": { - "type": "boolean" - } - }, - { - "name": "addPlayedIndicator", - "in": "query", - "description": "Optional. Add a played indicator.", - "schema": { - "type": "boolean" - } - }, { "name": "blur", "in": "query", @@ -17956,8 +17081,8 @@ } } }, - "404": { - "description": "Item not found.", + "400": { + "description": "User id not provided.", "content": { "application/json": { "schema": { @@ -17975,426 +17100,6 @@ } } } - } - } - } - }, - "/Users/{userId}/Images/{imageType}/{imageIndex}": { - "get": { - "tags": [ - "Image" - ], - "summary": "Get user profile image.", - "operationId": "GetUserImageByIndex", - "parameters": [ - { - "name": "userId", - "in": "path", - "description": "User id.", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - }, - { - "name": "imageType", - "in": "path", - "description": "Image type.", - "required": true, - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ImageType" - } - ], - "description": "Enum ImageType." - } - }, - { - "name": "imageIndex", - "in": "path", - "description": "Image index.", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - }, - { - "name": "tag", - "in": "query", - "description": "Optional. Supply the cache tag from the item object to receive strong caching headers.", - "schema": { - "type": "string" - } - }, - { - "name": "format", - "in": "query", - "description": "Determines the output format of the image - original,gif,jpg,png.", - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ImageFormat" - } - ] - } - }, - { - "name": "maxWidth", - "in": "query", - "description": "The maximum image width to return.", - "schema": { - "type": "integer", - "format": "int32" - } - }, - { - "name": "maxHeight", - "in": "query", - "description": "The maximum image height to return.", - "schema": { - "type": "integer", - "format": "int32" - } - }, - { - "name": "percentPlayed", - "in": "query", - "description": "Optional. Percent to render for the percent played overlay.", - "schema": { - "type": "number", - "format": "double" - } - }, - { - "name": "unplayedCount", - "in": "query", - "description": "Optional. Unplayed count overlay to render.", - "schema": { - "type": "integer", - "format": "int32" - } - }, - { - "name": "width", - "in": "query", - "description": "The fixed image width to return.", - "schema": { - "type": "integer", - "format": "int32" - } - }, - { - "name": "height", - "in": "query", - "description": "The fixed image height to return.", - "schema": { - "type": "integer", - "format": "int32" - } - }, - { - "name": "quality", - "in": "query", - "description": "Optional. Quality setting, from 0-100. Defaults to 90 and should suffice in most cases.", - "schema": { - "type": "integer", - "format": "int32" - } - }, - { - "name": "fillWidth", - "in": "query", - "description": "Width of box to fill.", - "schema": { - "type": "integer", - "format": "int32" - } - }, - { - "name": "fillHeight", - "in": "query", - "description": "Height of box to fill.", - "schema": { - "type": "integer", - "format": "int32" - } - }, - { - "name": "cropWhitespace", - "in": "query", - "description": "Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.", - "deprecated": true, - "schema": { - "type": "boolean" - } - }, - { - "name": "addPlayedIndicator", - "in": "query", - "description": "Optional. Add a played indicator.", - "schema": { - "type": "boolean" - } - }, - { - "name": "blur", - "in": "query", - "description": "Optional. Blur image.", - "schema": { - "type": "integer", - "format": "int32" - } - }, - { - "name": "backgroundColor", - "in": "query", - "description": "Optional. Apply a background color for transparent images.", - "schema": { - "type": "string" - } - }, - { - "name": "foregroundLayer", - "in": "query", - "description": "Optional. Apply a foreground layer on top of the image.", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Image stream returned.", - "content": { - "image/*": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - }, - "404": { - "description": "Item not found.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - }, - "application/json; profile=\"CamelCase\"": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - }, - "application/json; profile=\"PascalCase\"": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - } - } - }, - "head": { - "tags": [ - "Image" - ], - "summary": "Get user profile image.", - "operationId": "HeadUserImageByIndex", - "parameters": [ - { - "name": "userId", - "in": "path", - "description": "User id.", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - }, - { - "name": "imageType", - "in": "path", - "description": "Image type.", - "required": true, - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ImageType" - } - ], - "description": "Enum ImageType." - } - }, - { - "name": "imageIndex", - "in": "path", - "description": "Image index.", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - }, - { - "name": "tag", - "in": "query", - "description": "Optional. Supply the cache tag from the item object to receive strong caching headers.", - "schema": { - "type": "string" - } - }, - { - "name": "format", - "in": "query", - "description": "Determines the output format of the image - original,gif,jpg,png.", - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ImageFormat" - } - ] - } - }, - { - "name": "maxWidth", - "in": "query", - "description": "The maximum image width to return.", - "schema": { - "type": "integer", - "format": "int32" - } - }, - { - "name": "maxHeight", - "in": "query", - "description": "The maximum image height to return.", - "schema": { - "type": "integer", - "format": "int32" - } - }, - { - "name": "percentPlayed", - "in": "query", - "description": "Optional. Percent to render for the percent played overlay.", - "schema": { - "type": "number", - "format": "double" - } - }, - { - "name": "unplayedCount", - "in": "query", - "description": "Optional. Unplayed count overlay to render.", - "schema": { - "type": "integer", - "format": "int32" - } - }, - { - "name": "width", - "in": "query", - "description": "The fixed image width to return.", - "schema": { - "type": "integer", - "format": "int32" - } - }, - { - "name": "height", - "in": "query", - "description": "The fixed image height to return.", - "schema": { - "type": "integer", - "format": "int32" - } - }, - { - "name": "quality", - "in": "query", - "description": "Optional. Quality setting, from 0-100. Defaults to 90 and should suffice in most cases.", - "schema": { - "type": "integer", - "format": "int32" - } - }, - { - "name": "fillWidth", - "in": "query", - "description": "Width of box to fill.", - "schema": { - "type": "integer", - "format": "int32" - } - }, - { - "name": "fillHeight", - "in": "query", - "description": "Height of box to fill.", - "schema": { - "type": "integer", - "format": "int32" - } - }, - { - "name": "cropWhitespace", - "in": "query", - "description": "Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.", - "deprecated": true, - "schema": { - "type": "boolean" - } - }, - { - "name": "addPlayedIndicator", - "in": "query", - "description": "Optional. Add a played indicator.", - "schema": { - "type": "boolean" - } - }, - { - "name": "blur", - "in": "query", - "description": "Optional. Blur image.", - "schema": { - "type": "integer", - "format": "int32" - } - }, - { - "name": "backgroundColor", - "in": "query", - "description": "Optional. Apply a background color for transparent images.", - "schema": { - "type": "string" - } - }, - { - "name": "foregroundLayer", - "in": "query", - "description": "Optional. Apply a foreground layer on top of the image.", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Image stream returned.", - "content": { - "image/*": { - "schema": { - "type": "string", - "format": "binary" - } - } - } }, "404": { "description": "Item not found.", @@ -18419,535 +17124,7 @@ } } }, - "/Users/{userId}/Images/{imageType}/{index}": { - "post": { - "tags": [ - "Image" - ], - "summary": "Sets the user image.", - "operationId": "PostUserImageByIndex", - "parameters": [ - { - "name": "userId", - "in": "path", - "description": "User Id.", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - }, - { - "name": "imageType", - "in": "path", - "description": "(Unused) Image type.", - "required": true, - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ImageType" - } - ], - "description": "Enum ImageType." - } - }, - { - "name": "index", - "in": "path", - "description": "(Unused) Image index.", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "requestBody": { - "content": { - "image/*": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - }, - "responses": { - "204": { - "description": "Image updated." - }, - "403": { - "description": "User does not have permission to delete the image.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - }, - "application/json; profile=\"CamelCase\"": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - }, - "application/json; profile=\"PascalCase\"": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "401": { - "description": "Unauthorized" - } - }, - "security": [ - { - "CustomAuthentication": [ - "DefaultAuthorization" - ] - } - ] - }, - "delete": { - "tags": [ - "Image" - ], - "summary": "Delete the user's image.", - "operationId": "DeleteUserImageByIndex", - "parameters": [ - { - "name": "userId", - "in": "path", - "description": "User Id.", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - }, - { - "name": "imageType", - "in": "path", - "description": "(Unused) Image type.", - "required": true, - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ImageType" - } - ], - "description": "Enum ImageType." - } - }, - { - "name": "index", - "in": "path", - "description": "(Unused) Image index.", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "204": { - "description": "Image deleted." - }, - "403": { - "description": "User does not have permission to delete the image.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - }, - "application/json; profile=\"CamelCase\"": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - }, - "application/json; profile=\"PascalCase\"": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "401": { - "description": "Unauthorized" - } - }, - "security": [ - { - "CustomAuthentication": [ - "DefaultAuthorization" - ] - } - ] - } - }, - "/Images/General": { - "get": { - "tags": [ - "ImageByName" - ], - "summary": "Get all general images.", - "operationId": "GetGeneralImages", - "responses": { - "200": { - "description": "Retrieved list of images.", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ImageByNameInfo" - } - } - }, - "application/json; profile=\"CamelCase\"": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ImageByNameInfo" - } - } - }, - "application/json; profile=\"PascalCase\"": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ImageByNameInfo" - } - } - } - } - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - }, - "security": [ - { - "CustomAuthentication": [ - "DefaultAuthorization" - ] - } - ] - } - }, - "/Images/General/{name}/{type}": { - "get": { - "tags": [ - "ImageByName" - ], - "summary": "Get General Image.", - "operationId": "GetGeneralImage", - "parameters": [ - { - "name": "name", - "in": "path", - "description": "The name of the image.", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "type", - "in": "path", - "description": "Image Type (primary, backdrop, logo, etc).", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Image stream retrieved.", - "content": { - "image/*": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - }, - "404": { - "description": "Image not found.", - "content": { - "application/octet-stream": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - }, - "application/json; profile=\"CamelCase\"": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - }, - "application/json; profile=\"PascalCase\"": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - } - } - } - }, - "/Images/MediaInfo": { - "get": { - "tags": [ - "ImageByName" - ], - "summary": "Get all media info images.", - "operationId": "GetMediaInfoImages", - "responses": { - "200": { - "description": "Image list retrieved.", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ImageByNameInfo" - } - } - }, - "application/json; profile=\"CamelCase\"": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ImageByNameInfo" - } - } - }, - "application/json; profile=\"PascalCase\"": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ImageByNameInfo" - } - } - } - } - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - }, - "security": [ - { - "CustomAuthentication": [ - "DefaultAuthorization" - ] - } - ] - } - }, - "/Images/MediaInfo/{theme}/{name}": { - "get": { - "tags": [ - "ImageByName" - ], - "summary": "Get media info image.", - "operationId": "GetMediaInfoImage", - "parameters": [ - { - "name": "theme", - "in": "path", - "description": "The theme to get the image from.", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "name", - "in": "path", - "description": "The name of the image.", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Image stream retrieved.", - "content": { - "image/*": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - }, - "404": { - "description": "Image not found.", - "content": { - "application/octet-stream": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - }, - "application/json; profile=\"CamelCase\"": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - }, - "application/json; profile=\"PascalCase\"": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - } - } - } - }, - "/Images/Ratings": { - "get": { - "tags": [ - "ImageByName" - ], - "summary": "Get all general images.", - "operationId": "GetRatingImages", - "responses": { - "200": { - "description": "Retrieved list of images.", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ImageByNameInfo" - } - } - }, - "application/json; profile=\"CamelCase\"": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ImageByNameInfo" - } - } - }, - "application/json; profile=\"PascalCase\"": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ImageByNameInfo" - } - } - } - } - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - }, - "security": [ - { - "CustomAuthentication": [ - "DefaultAuthorization" - ] - } - ] - } - }, - "/Images/Ratings/{theme}/{name}": { - "get": { - "tags": [ - "ImageByName" - ], - "summary": "Get rating image.", - "operationId": "GetRatingImage", - "parameters": [ - { - "name": "theme", - "in": "path", - "description": "The theme to get the image from.", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "name", - "in": "path", - "description": "The name of the image.", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Image stream retrieved.", - "content": { - "image/*": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - }, - "404": { - "description": "Image not found.", - "content": { - "application/octet-stream": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - }, - "application/json; profile=\"CamelCase\"": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - }, - "application/json; profile=\"PascalCase\"": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - } - } - } - }, - "/Albums/{id}/InstantMix": { + "/Albums/{itemId}/InstantMix": { "get": { "tags": [ "InstantMix" @@ -18956,7 +17133,7 @@ "operationId": "GetInstantMixFromAlbum", "parameters": [ { - "name": "id", + "name": "itemId", "in": "path", "description": "The item id.", "required": true, @@ -19052,6 +17229,26 @@ } } }, + "404": { + "description": "Item not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, "401": { "description": "Unauthorized" }, @@ -19068,7 +17265,7 @@ ] } }, - "/Artists/{id}/InstantMix": { + "/Artists/{itemId}/InstantMix": { "get": { "tags": [ "InstantMix" @@ -19077,7 +17274,7 @@ "operationId": "GetInstantMixFromArtists", "parameters": [ { - "name": "id", + "name": "itemId", "in": "path", "description": "The item id.", "required": true, @@ -19173,6 +17370,26 @@ } } }, + "404": { + "description": "Item not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, "401": { "description": "Unauthorized" }, @@ -19294,6 +17511,26 @@ } } }, + "404": { + "description": "Item not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, "401": { "description": "Unauthorized" }, @@ -19311,7 +17548,7 @@ ] } }, - "/Items/{id}/InstantMix": { + "/Items/{itemId}/InstantMix": { "get": { "tags": [ "InstantMix" @@ -19320,7 +17557,7 @@ "operationId": "GetInstantMixFromItem", "parameters": [ { - "name": "id", + "name": "itemId", "in": "path", "description": "The item id.", "required": true, @@ -19416,6 +17653,26 @@ } } }, + "404": { + "description": "Item not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, "401": { "description": "Unauthorized" }, @@ -19657,6 +17914,26 @@ } } }, + "404": { + "description": "Item not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, "401": { "description": "Unauthorized" }, @@ -19673,7 +17950,7 @@ ] } }, - "/Playlists/{id}/InstantMix": { + "/Playlists/{itemId}/InstantMix": { "get": { "tags": [ "InstantMix" @@ -19682,7 +17959,7 @@ "operationId": "GetInstantMixFromPlaylist", "parameters": [ { - "name": "id", + "name": "itemId", "in": "path", "description": "The item id.", "required": true, @@ -19778,6 +18055,26 @@ } } }, + "404": { + "description": "Item not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, "401": { "description": "Unauthorized" }, @@ -19794,7 +18091,7 @@ ] } }, - "/Songs/{id}/InstantMix": { + "/Songs/{itemId}/InstantMix": { "get": { "tags": [ "InstantMix" @@ -19803,7 +18100,7 @@ "operationId": "GetInstantMixFromSong", "parameters": [ { - "name": "id", + "name": "itemId", "in": "path", "description": "The item id.", "required": true, @@ -19899,6 +18196,26 @@ } } }, + "404": { + "description": "Item not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, "401": { "description": "Unauthorized" }, @@ -20066,6 +18383,26 @@ "204": { "description": "Item metadata refreshed." }, + "404": { + "description": "Item not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, "401": { "description": "Unauthorized" }, @@ -20881,6 +19218,12 @@ "in": "query", "description": "(Optional) Specifies the metadata refresh mode.", "schema": { + "enum": [ + "None", + "ValidationOnly", + "Default", + "FullRefresh" + ], "allOf": [ { "$ref": "#/components/schemas/MetadataRefreshMode" @@ -20894,6 +19237,12 @@ "in": "query", "description": "(Optional) Specifies the image refresh mode.", "schema": { + "enum": [ + "None", + "ValidationOnly", + "Default", + "FullRefresh" + ], "allOf": [ { "$ref": "#/components/schemas/MetadataRefreshMode" @@ -20972,7 +19321,7 @@ { "name": "userId", "in": "query", - "description": "The user id supplied as query parameter.", + "description": "The user id supplied as query parameter; this is required when not using an API key.", "schema": { "type": "string", "format": "uuid" @@ -21031,7 +19380,8 @@ "in": "query", "description": "Optional. Return items that are siblings of a supplied item.", "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -21170,7 +19520,7 @@ { "name": "hasImdbId", "in": "query", - "description": "Optional filter by items that have an imdb id or not.", + "description": "Optional filter by items that have an IMDb id or not.", "schema": { "type": "boolean" } @@ -21178,7 +19528,7 @@ { "name": "hasTmdbId", "in": "query", - "description": "Optional filter by items that have a tmdb id or not.", + "description": "Optional filter by items that have a TMDb id or not.", "schema": { "type": "boolean" } @@ -21186,7 +19536,7 @@ { "name": "hasTvdbId", "in": "query", - "description": "Optional filter by items that have a tvdb id or not.", + "description": "Optional filter by items that have a TVDb id or not.", "schema": { "type": "boolean" } @@ -21280,7 +19630,7 @@ { "name": "sortOrder", "in": "query", - "description": "Sort Order - Ascending,Descending.", + "description": "Sort Order - Ascending, Descending.", "schema": { "type": "array", "items": { @@ -21356,7 +19706,7 @@ "schema": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/MediaType" } } }, @@ -21378,7 +19728,7 @@ "schema": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/ItemSortBy" } } }, @@ -21852,6 +20202,26 @@ } } }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, "403": { "description": "Forbidden" } @@ -21865,835 +20235,190 @@ ] } }, - "/Users/{userId}/Items": { + "/UserItems/{itemId}/UserData": { "get": { "tags": [ "Items" ], - "summary": "Gets items based on a query.", - "operationId": "GetItemsByUserId", + "summary": "Get Item User Data.", + "operationId": "GetItemUserData", "parameters": [ { "name": "userId", + "in": "query", + "description": "The user id.", + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "itemId", "in": "path", - "description": "The user id supplied as query parameter.", + "description": "The item id.", "required": true, "schema": { "type": "string", "format": "uuid" } - }, - { - "name": "maxOfficialRating", - "in": "query", - "description": "Optional filter by maximum official rating (PG, PG-13, TV-MA, etc).", - "schema": { - "type": "string" - } - }, - { - "name": "hasThemeSong", - "in": "query", - "description": "Optional filter by items with theme songs.", - "schema": { - "type": "boolean" - } - }, - { - "name": "hasThemeVideo", - "in": "query", - "description": "Optional filter by items with theme videos.", - "schema": { - "type": "boolean" - } - }, - { - "name": "hasSubtitles", - "in": "query", - "description": "Optional filter by items with subtitles.", - "schema": { - "type": "boolean" - } - }, - { - "name": "hasSpecialFeature", - "in": "query", - "description": "Optional filter by items with special features.", - "schema": { - "type": "boolean" - } - }, - { - "name": "hasTrailer", - "in": "query", - "description": "Optional filter by items with trailers.", - "schema": { - "type": "boolean" - } - }, - { - "name": "adjacentTo", - "in": "query", - "description": "Optional. Return items that are siblings of a supplied item.", - "schema": { - "type": "string" - } - }, - { - "name": "parentIndexNumber", - "in": "query", - "description": "Optional filter by parent index number.", - "schema": { - "type": "integer", - "format": "int32" - } - }, - { - "name": "hasParentalRating", - "in": "query", - "description": "Optional filter by items that have or do not have a parental rating.", - "schema": { - "type": "boolean" - } - }, - { - "name": "isHd", - "in": "query", - "description": "Optional filter by items that are HD or not.", - "schema": { - "type": "boolean" - } - }, - { - "name": "is4K", - "in": "query", - "description": "Optional filter by items that are 4K or not.", - "schema": { - "type": "boolean" - } - }, - { - "name": "locationTypes", - "in": "query", - "description": "Optional. If specified, results will be filtered based on LocationType. This allows multiple, comma delimited.", - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/LocationType" + } + ], + "responses": { + "200": { + "description": "return item user data.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserItemDataDto" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/UserItemDataDto" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/UserItemDataDto" + } } } }, - { - "name": "excludeLocationTypes", - "in": "query", - "description": "Optional. If specified, results will be filtered based on the LocationType. This allows multiple, comma delimited.", - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/LocationType" + "404": { + "description": "Item is not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } } } }, - { - "name": "isMissing", - "in": "query", - "description": "Optional filter by items that are missing episodes or not.", - "schema": { - "type": "boolean" - } + "401": { + "description": "Unauthorized" }, + "403": { + "description": "Forbidden" + } + }, + "security": [ { - "name": "isUnaired", - "in": "query", - "description": "Optional filter by items that are unaired episodes or not.", - "schema": { - "type": "boolean" - } - }, + "CustomAuthentication": [ + "DefaultAuthorization" + ] + } + ] + }, + "post": { + "tags": [ + "Items" + ], + "summary": "Update Item User Data.", + "operationId": "UpdateItemUserData", + "parameters": [ { - "name": "minCommunityRating", + "name": "userId", "in": "query", - "description": "Optional filter by minimum community rating.", - "schema": { - "type": "number", - "format": "double" - } - }, - { - "name": "minCriticRating", - "in": "query", - "description": "Optional filter by minimum critic rating.", - "schema": { - "type": "number", - "format": "double" - } - }, - { - "name": "minPremiereDate", - "in": "query", - "description": "Optional. The minimum premiere date. Format = ISO.", - "schema": { - "type": "string", - "format": "date-time" - } - }, - { - "name": "minDateLastSaved", - "in": "query", - "description": "Optional. The minimum last saved date. Format = ISO.", - "schema": { - "type": "string", - "format": "date-time" - } - }, - { - "name": "minDateLastSavedForUser", - "in": "query", - "description": "Optional. The minimum last saved date for the current user. Format = ISO.", - "schema": { - "type": "string", - "format": "date-time" - } - }, - { - "name": "maxPremiereDate", - "in": "query", - "description": "Optional. The maximum premiere date. Format = ISO.", - "schema": { - "type": "string", - "format": "date-time" - } - }, - { - "name": "hasOverview", - "in": "query", - "description": "Optional filter by items that have an overview or not.", - "schema": { - "type": "boolean" - } - }, - { - "name": "hasImdbId", - "in": "query", - "description": "Optional filter by items that have an imdb id or not.", - "schema": { - "type": "boolean" - } - }, - { - "name": "hasTmdbId", - "in": "query", - "description": "Optional filter by items that have a tmdb id or not.", - "schema": { - "type": "boolean" - } - }, - { - "name": "hasTvdbId", - "in": "query", - "description": "Optional filter by items that have a tvdb id or not.", - "schema": { - "type": "boolean" - } - }, - { - "name": "isMovie", - "in": "query", - "description": "Optional filter for live tv movies.", - "schema": { - "type": "boolean" - } - }, - { - "name": "isSeries", - "in": "query", - "description": "Optional filter for live tv series.", - "schema": { - "type": "boolean" - } - }, - { - "name": "isNews", - "in": "query", - "description": "Optional filter for live tv news.", - "schema": { - "type": "boolean" - } - }, - { - "name": "isKids", - "in": "query", - "description": "Optional filter for live tv kids.", - "schema": { - "type": "boolean" - } - }, - { - "name": "isSports", - "in": "query", - "description": "Optional filter for live tv sports.", - "schema": { - "type": "boolean" - } - }, - { - "name": "excludeItemIds", - "in": "query", - "description": "Optional. If specified, results will be filtered by excluding item ids. This allows multiple, comma delimited.", - "schema": { - "type": "array", - "items": { - "type": "string", - "format": "uuid" - } - } - }, - { - "name": "startIndex", - "in": "query", - "description": "Optional. The record index to start at. All items with a lower index will be dropped from the results.", - "schema": { - "type": "integer", - "format": "int32" - } - }, - { - "name": "limit", - "in": "query", - "description": "Optional. The maximum number of records to return.", - "schema": { - "type": "integer", - "format": "int32" - } - }, - { - "name": "recursive", - "in": "query", - "description": "When searching within folders, this determines whether or not the search will be recursive. true/false.", - "schema": { - "type": "boolean" - } - }, - { - "name": "searchTerm", - "in": "query", - "description": "Optional. Filter based on a search term.", - "schema": { - "type": "string" - } - }, - { - "name": "sortOrder", - "in": "query", - "description": "Sort Order - Ascending,Descending.", - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/SortOrder" - } - } - }, - { - "name": "parentId", - "in": "query", - "description": "Specify this to localize the search to a specific item or folder. Omit to use the root.", + "description": "The user id.", "schema": { "type": "string", "format": "uuid" } }, { - "name": "fields", - "in": "query", - "description": "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimited. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines.", + "name": "itemId", + "in": "path", + "description": "The item id.", + "required": true, "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ItemFields" - } - } - }, - { - "name": "excludeItemTypes", - "in": "query", - "description": "Optional. If specified, results will be filtered based on item type. This allows multiple, comma delimited.", - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/BaseItemKind" - } - } - }, - { - "name": "includeItemTypes", - "in": "query", - "description": "Optional. If specified, results will be filtered based on the item type. This allows multiple, comma delimited.", - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/BaseItemKind" - } - } - }, - { - "name": "filters", - "in": "query", - "description": "Optional. Specify additional filters to apply. This allows multiple, comma delimited. Options: IsFolder, IsNotFolder, IsUnplayed, IsPlayed, IsFavorite, IsResumable, Likes, Dislikes.", - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ItemFilter" - } - } - }, - { - "name": "isFavorite", - "in": "query", - "description": "Optional filter by items that are marked as favorite, or not.", - "schema": { - "type": "boolean" - } - }, - { - "name": "mediaTypes", - "in": "query", - "description": "Optional filter by MediaType. Allows multiple, comma delimited.", - "schema": { - "type": "array", - "items": { - "type": "string" - } - } - }, - { - "name": "imageTypes", - "in": "query", - "description": "Optional. If specified, results will be filtered based on those containing image types. This allows multiple, comma delimited.", - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ImageType" - } - } - }, - { - "name": "sortBy", - "in": "query", - "description": "Optional. Specify one or more sort orders, comma delimited. Options: Album, AlbumArtist, Artist, Budget, CommunityRating, CriticRating, DateCreated, DatePlayed, PlayCount, PremiereDate, ProductionYear, SortName, Random, Revenue, Runtime.", - "schema": { - "type": "array", - "items": { - "type": "string" - } - } - }, - { - "name": "isPlayed", - "in": "query", - "description": "Optional filter by items that are played, or not.", - "schema": { - "type": "boolean" - } - }, - { - "name": "genres", - "in": "query", - "description": "Optional. If specified, results will be filtered based on genre. This allows multiple, pipe delimited.", - "schema": { - "type": "array", - "items": { - "type": "string" - } - } - }, - { - "name": "officialRatings", - "in": "query", - "description": "Optional. If specified, results will be filtered based on OfficialRating. This allows multiple, pipe delimited.", - "schema": { - "type": "array", - "items": { - "type": "string" - } - } - }, - { - "name": "tags", - "in": "query", - "description": "Optional. If specified, results will be filtered based on tag. This allows multiple, pipe delimited.", - "schema": { - "type": "array", - "items": { - "type": "string" - } - } - }, - { - "name": "years", - "in": "query", - "description": "Optional. If specified, results will be filtered based on production year. This allows multiple, comma delimited.", - "schema": { - "type": "array", - "items": { - "type": "integer", - "format": "int32" - } - } - }, - { - "name": "enableUserData", - "in": "query", - "description": "Optional, include user data.", - "schema": { - "type": "boolean" - } - }, - { - "name": "imageTypeLimit", - "in": "query", - "description": "Optional, the max number of images to return, per image type.", - "schema": { - "type": "integer", - "format": "int32" - } - }, - { - "name": "enableImageTypes", - "in": "query", - "description": "Optional. The image types to include in the output.", - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ImageType" - } - } - }, - { - "name": "person", - "in": "query", - "description": "Optional. If specified, results will be filtered to include only those containing the specified person.", - "schema": { - "type": "string" - } - }, - { - "name": "personIds", - "in": "query", - "description": "Optional. If specified, results will be filtered to include only those containing the specified person id.", - "schema": { - "type": "array", - "items": { - "type": "string", - "format": "uuid" - } - } - }, - { - "name": "personTypes", - "in": "query", - "description": "Optional. If specified, along with Person, results will be filtered to include only those containing the specified person and PersonType. Allows multiple, comma-delimited.", - "schema": { - "type": "array", - "items": { - "type": "string" - } - } - }, - { - "name": "studios", - "in": "query", - "description": "Optional. If specified, results will be filtered based on studio. This allows multiple, pipe delimited.", - "schema": { - "type": "array", - "items": { - "type": "string" - } - } - }, - { - "name": "artists", - "in": "query", - "description": "Optional. If specified, results will be filtered based on artists. This allows multiple, pipe delimited.", - "schema": { - "type": "array", - "items": { - "type": "string" - } - } - }, - { - "name": "excludeArtistIds", - "in": "query", - "description": "Optional. If specified, results will be filtered based on artist id. This allows multiple, pipe delimited.", - "schema": { - "type": "array", - "items": { - "type": "string", - "format": "uuid" - } - } - }, - { - "name": "artistIds", - "in": "query", - "description": "Optional. If specified, results will be filtered to include only those containing the specified artist id.", - "schema": { - "type": "array", - "items": { - "type": "string", - "format": "uuid" - } - } - }, - { - "name": "albumArtistIds", - "in": "query", - "description": "Optional. If specified, results will be filtered to include only those containing the specified album artist id.", - "schema": { - "type": "array", - "items": { - "type": "string", - "format": "uuid" - } - } - }, - { - "name": "contributingArtistIds", - "in": "query", - "description": "Optional. If specified, results will be filtered to include only those containing the specified contributing artist id.", - "schema": { - "type": "array", - "items": { - "type": "string", - "format": "uuid" - } - } - }, - { - "name": "albums", - "in": "query", - "description": "Optional. If specified, results will be filtered based on album. This allows multiple, pipe delimited.", - "schema": { - "type": "array", - "items": { - "type": "string" - } - } - }, - { - "name": "albumIds", - "in": "query", - "description": "Optional. If specified, results will be filtered based on album id. This allows multiple, pipe delimited.", - "schema": { - "type": "array", - "items": { - "type": "string", - "format": "uuid" - } - } - }, - { - "name": "ids", - "in": "query", - "description": "Optional. If specific items are needed, specify a list of item id's to retrieve. This allows multiple, comma delimited.", - "schema": { - "type": "array", - "items": { - "type": "string", - "format": "uuid" - } - } - }, - { - "name": "videoTypes", - "in": "query", - "description": "Optional filter by VideoType (videofile, dvd, bluray, iso). Allows multiple, comma delimited.", - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/VideoType" - } - } - }, - { - "name": "minOfficialRating", - "in": "query", - "description": "Optional filter by minimum official rating (PG, PG-13, TV-MA, etc).", - "schema": { - "type": "string" - } - }, - { - "name": "isLocked", - "in": "query", - "description": "Optional filter by items that are locked.", - "schema": { - "type": "boolean" - } - }, - { - "name": "isPlaceHolder", - "in": "query", - "description": "Optional filter by items that are placeholders.", - "schema": { - "type": "boolean" - } - }, - { - "name": "hasOfficialRating", - "in": "query", - "description": "Optional filter by items that have official ratings.", - "schema": { - "type": "boolean" - } - }, - { - "name": "collapseBoxSetItems", - "in": "query", - "description": "Whether or not to hide items behind their boxsets.", - "schema": { - "type": "boolean" - } - }, - { - "name": "minWidth", - "in": "query", - "description": "Optional. Filter by the minimum width of the item.", - "schema": { - "type": "integer", - "format": "int32" - } - }, - { - "name": "minHeight", - "in": "query", - "description": "Optional. Filter by the minimum height of the item.", - "schema": { - "type": "integer", - "format": "int32" - } - }, - { - "name": "maxWidth", - "in": "query", - "description": "Optional. Filter by the maximum width of the item.", - "schema": { - "type": "integer", - "format": "int32" - } - }, - { - "name": "maxHeight", - "in": "query", - "description": "Optional. Filter by the maximum height of the item.", - "schema": { - "type": "integer", - "format": "int32" - } - }, - { - "name": "is3D", - "in": "query", - "description": "Optional filter by items that are 3D, or not.", - "schema": { - "type": "boolean" - } - }, - { - "name": "seriesStatus", - "in": "query", - "description": "Optional filter by Series Status. Allows multiple, comma delimited.", - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/SeriesStatus" - } - } - }, - { - "name": "nameStartsWithOrGreater", - "in": "query", - "description": "Optional filter by items whose name is sorted equally or greater than a given input string.", - "schema": { - "type": "string" - } - }, - { - "name": "nameStartsWith", - "in": "query", - "description": "Optional filter by items whose name is sorted equally than a given input string.", - "schema": { - "type": "string" - } - }, - { - "name": "nameLessThan", - "in": "query", - "description": "Optional filter by items whose name is equally or lesser than a given input string.", - "schema": { - "type": "string" - } - }, - { - "name": "studioIds", - "in": "query", - "description": "Optional. If specified, results will be filtered based on studio id. This allows multiple, pipe delimited.", - "schema": { - "type": "array", - "items": { - "type": "string", - "format": "uuid" - } - } - }, - { - "name": "genreIds", - "in": "query", - "description": "Optional. If specified, results will be filtered based on genre id. This allows multiple, pipe delimited.", - "schema": { - "type": "array", - "items": { - "type": "string", - "format": "uuid" - } - } - }, - { - "name": "enableTotalRecordCount", - "in": "query", - "description": "Optional. Enable the total record count.", - "schema": { - "type": "boolean", - "default": true - } - }, - { - "name": "enableImages", - "in": "query", - "description": "Optional, include image information in output.", - "schema": { - "type": "boolean", - "default": true + "type": "string", + "format": "uuid" } } ], + "requestBody": { + "description": "New user data object.", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/UpdateUserItemDataDto" + } + ], + "description": "This is used by the api to get information about a item user data." + } + }, + "text/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/UpdateUserItemDataDto" + } + ], + "description": "This is used by the api to get information about a item user data." + } + }, + "application/*+json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/UpdateUserItemDataDto" + } + ], + "description": "This is used by the api to get information about a item user data." + } + } + }, + "required": true + }, "responses": { "200": { - "description": "Success", + "description": "return updated user item data.", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BaseItemDtoQueryResult" + "$ref": "#/components/schemas/UserItemDataDto" } }, "application/json; profile=\"CamelCase\"": { "schema": { - "$ref": "#/components/schemas/BaseItemDtoQueryResult" + "$ref": "#/components/schemas/UserItemDataDto" } }, "application/json; profile=\"PascalCase\"": { "schema": { - "$ref": "#/components/schemas/BaseItemDtoQueryResult" + "$ref": "#/components/schemas/UserItemDataDto" + } + } + } + }, + "404": { + "description": "Item is not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" } } } @@ -22714,7 +20439,7 @@ ] } }, - "/Users/{userId}/Items/Resume": { + "/UserItems/Resume": { "get": { "tags": [ "Items" @@ -22724,9 +20449,8 @@ "parameters": [ { "name": "userId", - "in": "path", + "in": "query", "description": "The user id.", - "required": true, "schema": { "type": "string", "format": "uuid" @@ -22785,7 +20509,7 @@ "schema": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/MediaType" } } }, @@ -23040,6 +20764,89 @@ } } }, + "404": { + "description": "Item not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "Forbidden" + } + }, + "security": [ + { + "CustomAuthentication": [ + "DefaultAuthorization" + ] + } + ] + }, + "get": { + "tags": [ + "UserLibrary" + ], + "summary": "Gets an item from a user's library.", + "operationId": "GetItem", + "parameters": [ + { + "name": "userId", + "in": "query", + "description": "User id.", + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "itemId", + "in": "path", + "description": "Item id.", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "Item returned.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BaseItemDto" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/BaseItemDto" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/BaseItemDto" + } + } + } + }, + "401": { + "description": "Unauthorized" + }, "403": { "description": "Forbidden" } @@ -23606,7 +21413,8 @@ "security": [ { "CustomAuthentication": [ - "Download" + "Download", + "DefaultAuthorization" ] } ] @@ -24123,7 +21931,26 @@ "in": "query", "description": "Library content type.", "schema": { - "type": "string" + "enum": [ + "unknown", + "movies", + "tvshows", + "music", + "musicvideos", + "trailers", + "homevideos", + "boxsets", + "books", + "photos", + "livetv", + "playlists", + "folders" + ], + "allOf": [ + { + "$ref": "#/components/schemas/CollectionType" + } + ] } }, { @@ -24167,7 +21994,8 @@ "security": [ { "CustomAuthentication": [ - "FirstTimeSetupOrDefault" + "FirstTimeSetupOrDefault", + "DefaultAuthorization" ] } ] @@ -24872,7 +22700,8 @@ "security": [ { "CustomAuthentication": [ - "FirstTimeSetupOrElevated" + "FirstTimeSetupOrElevated", + "DefaultAuthorization" ] } ] @@ -24897,6 +22726,16 @@ "in": "query", "description": "The type of the collection.", "schema": { + "enum": [ + "movies", + "tvshows", + "music", + "musicvideos", + "homevideos", + "boxsets", + "books", + "mixed" + ], "allOf": [ { "$ref": "#/components/schemas/CollectionTypeOptions" @@ -24974,7 +22813,8 @@ "security": [ { "CustomAuthentication": [ - "FirstTimeSetupOrElevated" + "FirstTimeSetupOrElevated", + "DefaultAuthorization" ] } ] @@ -25018,7 +22858,8 @@ "security": [ { "CustomAuthentication": [ - "FirstTimeSetupOrElevated" + "FirstTimeSetupOrElevated", + "DefaultAuthorization" ] } ] @@ -25070,6 +22911,26 @@ "204": { "description": "Library updated." }, + "404": { + "description": "Item not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, "401": { "description": "Unauthorized" }, @@ -25080,7 +22941,8 @@ "security": [ { "CustomAuthentication": [ - "FirstTimeSetupOrElevated" + "FirstTimeSetupOrElevated", + "DefaultAuthorization" ] } ] @@ -25174,7 +23036,8 @@ "security": [ { "CustomAuthentication": [ - "FirstTimeSetupOrElevated" + "FirstTimeSetupOrElevated", + "DefaultAuthorization" ] } ] @@ -25248,7 +23111,8 @@ "security": [ { "CustomAuthentication": [ - "FirstTimeSetupOrElevated" + "FirstTimeSetupOrElevated", + "DefaultAuthorization" ] } ] @@ -25300,7 +23164,8 @@ "security": [ { "CustomAuthentication": [ - "FirstTimeSetupOrElevated" + "FirstTimeSetupOrElevated", + "DefaultAuthorization" ] } ] @@ -25363,7 +23228,8 @@ "security": [ { "CustomAuthentication": [ - "FirstTimeSetupOrElevated" + "FirstTimeSetupOrElevated", + "DefaultAuthorization" ] } ] @@ -25417,7 +23283,8 @@ "security": [ { "CustomAuthentication": [ - "LiveTvAccess" + "LiveTvAccess", + "DefaultAuthorization" ] } ] @@ -25497,7 +23364,8 @@ "security": [ { "CustomAuthentication": [ - "LiveTvManagement" + "LiveTvManagement", + "DefaultAuthorization" ] } ] @@ -25516,6 +23384,10 @@ "in": "query", "description": "Optional. Filter by channel type.", "schema": { + "enum": [ + "TV", + "Radio" + ], "allOf": [ { "$ref": "#/components/schemas/ChannelType" @@ -25668,7 +23540,7 @@ "schema": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/ItemSortBy" } } }, @@ -25677,6 +23549,10 @@ "in": "query", "description": "Optional. Sort order.", "schema": { + "enum": [ + "Ascending", + "Descending" + ], "allOf": [ { "$ref": "#/components/schemas/SortOrder" @@ -25734,7 +23610,8 @@ "security": [ { "CustomAuthentication": [ - "LiveTvAccess" + "LiveTvAccess", + "DefaultAuthorization" ] } ] @@ -25789,6 +23666,26 @@ } } }, + "404": { + "description": "Item not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, "401": { "description": "Unauthorized" }, @@ -25799,7 +23696,8 @@ "security": [ { "CustomAuthentication": [ - "LiveTvAccess" + "LiveTvAccess", + "DefaultAuthorization" ] } ] @@ -25843,7 +23741,8 @@ "security": [ { "CustomAuthentication": [ - "LiveTvAccess" + "LiveTvAccess", + "DefaultAuthorization" ] } ] @@ -25887,7 +23786,8 @@ "security": [ { "CustomAuthentication": [ - "LiveTvAccess" + "LiveTvAccess", + "DefaultAuthorization" ] } ] @@ -25991,7 +23891,8 @@ "security": [ { "CustomAuthentication": [ - "LiveTvManagement" + "LiveTvManagement", + "DefaultAuthorization" ] } ] @@ -26026,7 +23927,8 @@ "security": [ { "CustomAuthentication": [ - "LiveTvManagement" + "LiveTvManagement", + "DefaultAuthorization" ] } ] @@ -26070,7 +23972,8 @@ "security": [ { "CustomAuthentication": [ - "LiveTvAccess" + "LiveTvAccess", + "DefaultAuthorization" ] } ] @@ -26157,7 +24060,8 @@ "security": [ { "CustomAuthentication": [ - "LiveTvAccess" + "LiveTvAccess", + "DefaultAuthorization" ] } ] @@ -26192,7 +24096,8 @@ "security": [ { "CustomAuthentication": [ - "LiveTvAccess" + "LiveTvAccess", + "DefaultAuthorization" ] } ] @@ -26459,7 +24364,7 @@ "schema": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/ItemSortBy" } } }, @@ -26602,7 +24507,8 @@ "security": [ { "CustomAuthentication": [ - "LiveTvAccess" + "LiveTvAccess", + "DefaultAuthorization" ] } ] @@ -26679,7 +24585,8 @@ "security": [ { "CustomAuthentication": [ - "LiveTvAccess" + "LiveTvAccess", + "DefaultAuthorization" ] } ] @@ -26743,7 +24650,8 @@ "security": [ { "CustomAuthentication": [ - "LiveTvAccess" + "LiveTvAccess", + "DefaultAuthorization" ] } ] @@ -26931,7 +24839,8 @@ "security": [ { "CustomAuthentication": [ - "LiveTvAccess" + "LiveTvAccess", + "DefaultAuthorization" ] } ] @@ -26985,6 +24894,15 @@ "in": "query", "description": "Optional. Filter by recording status.", "schema": { + "enum": [ + "New", + "InProgress", + "Completed", + "Cancelled", + "ConflictedOk", + "ConflictedNotOk", + "Error" + ], "allOf": [ { "$ref": "#/components/schemas/RecordingStatus" @@ -27144,7 +25062,8 @@ "security": [ { "CustomAuthentication": [ - "LiveTvAccess" + "LiveTvAccess", + "DefaultAuthorization" ] } ] @@ -27199,6 +25118,26 @@ } } }, + "404": { + "description": "Item not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, "401": { "description": "Unauthorized" }, @@ -27209,7 +25148,8 @@ "security": [ { "CustomAuthentication": [ - "LiveTvAccess" + "LiveTvAccess", + "DefaultAuthorization" ] } ] @@ -27266,7 +25206,8 @@ "security": [ { "CustomAuthentication": [ - "LiveTvManagement" + "LiveTvManagement", + "DefaultAuthorization" ] } ] @@ -27321,7 +25262,8 @@ "security": [ { "CustomAuthentication": [ - "LiveTvAccess" + "LiveTvAccess", + "DefaultAuthorization" ] } ] @@ -27377,7 +25319,8 @@ "security": [ { "CustomAuthentication": [ - "LiveTvAccess" + "LiveTvAccess", + "DefaultAuthorization" ] } ] @@ -27434,7 +25377,8 @@ "security": [ { "CustomAuthentication": [ - "LiveTvAccess" + "LiveTvAccess", + "DefaultAuthorization" ] } ] @@ -27496,6 +25440,15 @@ "in": "query", "description": "Optional. Filter by recording status.", "schema": { + "enum": [ + "New", + "InProgress", + "Completed", + "Cancelled", + "ConflictedOk", + "ConflictedNotOk", + "Error" + ], "allOf": [ { "$ref": "#/components/schemas/RecordingStatus" @@ -27608,7 +25561,8 @@ "security": [ { "CustomAuthentication": [ - "LiveTvAccess" + "LiveTvAccess", + "DefaultAuthorization" ] } ] @@ -27635,6 +25589,10 @@ "in": "query", "description": "Optional. Sort in Ascending or Descending order.", "schema": { + "enum": [ + "Ascending", + "Descending" + ], "allOf": [ { "$ref": "#/components/schemas/SortOrder" @@ -27674,7 +25632,8 @@ "security": [ { "CustomAuthentication": [ - "LiveTvAccess" + "LiveTvAccess", + "DefaultAuthorization" ] } ] @@ -27734,7 +25693,8 @@ "security": [ { "CustomAuthentication": [ - "LiveTvManagement" + "LiveTvManagement", + "DefaultAuthorization" ] } ] @@ -27809,7 +25769,8 @@ "security": [ { "CustomAuthentication": [ - "LiveTvAccess" + "LiveTvAccess", + "DefaultAuthorization" ] } ] @@ -27845,7 +25806,8 @@ "security": [ { "CustomAuthentication": [ - "LiveTvManagement" + "LiveTvManagement", + "DefaultAuthorization" ] } ] @@ -27916,7 +25878,8 @@ "security": [ { "CustomAuthentication": [ - "LiveTvManagement" + "LiveTvManagement", + "DefaultAuthorization" ] } ] @@ -27994,7 +25957,8 @@ "security": [ { "CustomAuthentication": [ - "LiveTvAccess" + "LiveTvAccess", + "DefaultAuthorization" ] } ] @@ -28051,7 +26015,8 @@ "security": [ { "CustomAuthentication": [ - "LiveTvManagement" + "LiveTvManagement", + "DefaultAuthorization" ] } ] @@ -28106,7 +26071,8 @@ "security": [ { "CustomAuthentication": [ - "LiveTvAccess" + "LiveTvAccess", + "DefaultAuthorization" ] } ] @@ -28142,7 +26108,8 @@ "security": [ { "CustomAuthentication": [ - "LiveTvManagement" + "LiveTvManagement", + "DefaultAuthorization" ] } ] @@ -28210,7 +26177,8 @@ "security": [ { "CustomAuthentication": [ - "LiveTvManagement" + "LiveTvManagement", + "DefaultAuthorization" ] } ] @@ -28264,7 +26232,8 @@ "security": [ { "CustomAuthentication": [ - "LiveTvAccess" + "LiveTvAccess", + "DefaultAuthorization" ] } ] @@ -28340,7 +26309,8 @@ "security": [ { "CustomAuthentication": [ - "LiveTvManagement" + "LiveTvManagement", + "DefaultAuthorization" ] } ] @@ -28375,7 +26345,8 @@ "security": [ { "CustomAuthentication": [ - "LiveTvManagement" + "LiveTvManagement", + "DefaultAuthorization" ] } ] @@ -28428,7 +26399,8 @@ "security": [ { "CustomAuthentication": [ - "LiveTvAccess" + "LiveTvAccess", + "DefaultAuthorization" ] } ] @@ -28466,7 +26438,8 @@ "security": [ { "CustomAuthentication": [ - "LiveTvManagement" + "LiveTvManagement", + "DefaultAuthorization" ] } ] @@ -28530,7 +26503,8 @@ "security": [ { "CustomAuthentication": [ - "LiveTvManagement" + "LiveTvManagement", + "DefaultAuthorization" ] } ] @@ -28594,7 +26568,8 @@ "security": [ { "CustomAuthentication": [ - "LiveTvManagement" + "LiveTvManagement", + "DefaultAuthorization" ] } ] @@ -28647,7 +26622,8 @@ "security": [ { "CustomAuthentication": [ - "FirstTimeSetupOrDefault" + "FirstTimeSetupOrDefault", + "DefaultAuthorization" ] } ] @@ -28700,7 +26676,8 @@ "security": [ { "CustomAuthentication": [ - "FirstTimeSetupOrDefault" + "FirstTimeSetupOrDefault", + "DefaultAuthorization" ] } ] @@ -28753,7 +26730,8 @@ "security": [ { "CustomAuthentication": [ - "FirstTimeSetupOrDefault" + "FirstTimeSetupOrDefault", + "DefaultAuthorization" ] } ] @@ -28806,7 +26784,504 @@ "security": [ { "CustomAuthentication": [ - "FirstTimeSetupOrDefault" + "FirstTimeSetupOrDefault", + "DefaultAuthorization" + ] + } + ] + } + }, + "/Audio/{itemId}/Lyrics": { + "get": { + "tags": [ + "Lyrics" + ], + "summary": "Gets an item's lyrics.", + "operationId": "GetLyrics", + "parameters": [ + { + "name": "itemId", + "in": "path", + "description": "Item id.", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "Lyrics returned.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LyricDto" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/LyricDto" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/LyricDto" + } + } + } + }, + "404": { + "description": "Something went wrong. No Lyrics will be returned.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + } + }, + "security": [ + { + "CustomAuthentication": [ + "DefaultAuthorization" + ] + } + ] + }, + "post": { + "tags": [ + "Lyrics" + ], + "summary": "Upload an external lyric file.", + "operationId": "UploadLyrics", + "parameters": [ + { + "name": "itemId", + "in": "path", + "description": "The item the lyric belongs to.", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "fileName", + "in": "query", + "description": "Name of the file being uploaded.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "text/plain": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + }, + "responses": { + "200": { + "description": "Lyrics uploaded.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LyricDto" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/LyricDto" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/LyricDto" + } + } + } + }, + "400": { + "description": "Error processing upload.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "404": { + "description": "Item not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + } + }, + "security": [ + { + "CustomAuthentication": [ + "LyricManagement", + "DefaultAuthorization" + ] + } + ] + }, + "delete": { + "tags": [ + "Lyrics" + ], + "summary": "Deletes an external lyric file.", + "operationId": "DeleteLyrics", + "parameters": [ + { + "name": "itemId", + "in": "path", + "description": "The item id.", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "204": { + "description": "Lyric deleted." + }, + "404": { + "description": "Item not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + } + }, + "security": [ + { + "CustomAuthentication": [ + "LyricManagement", + "DefaultAuthorization" + ] + } + ] + } + }, + "/Audio/{itemId}/RemoteSearch/Lyrics": { + "get": { + "tags": [ + "Lyrics" + ], + "summary": "Search remote lyrics.", + "operationId": "SearchRemoteLyrics", + "parameters": [ + { + "name": "itemId", + "in": "path", + "description": "The item id.", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "Lyrics retrieved.", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RemoteLyricInfoDto" + } + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RemoteLyricInfoDto" + } + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RemoteLyricInfoDto" + } + } + } + } + }, + "404": { + "description": "Item not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + } + }, + "security": [ + { + "CustomAuthentication": [ + "LyricManagement", + "DefaultAuthorization" + ] + } + ] + } + }, + "/Audio/{itemId}/RemoteSearch/Lyrics/{lyricId}": { + "post": { + "tags": [ + "Lyrics" + ], + "summary": "Downloads a remote lyric.", + "operationId": "DownloadRemoteLyrics", + "parameters": [ + { + "name": "itemId", + "in": "path", + "description": "The item id.", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "lyricId", + "in": "path", + "description": "The lyric id.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Lyric downloaded.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LyricDto" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/LyricDto" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/LyricDto" + } + } + } + }, + "404": { + "description": "Item not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + } + }, + "security": [ + { + "CustomAuthentication": [ + "LyricManagement", + "DefaultAuthorization" + ] + } + ] + } + }, + "/Providers/Lyrics/{lyricId}": { + "get": { + "tags": [ + "Lyrics" + ], + "summary": "Gets the remote lyrics.", + "operationId": "GetRemoteLyrics", + "parameters": [ + { + "name": "lyricId", + "in": "path", + "description": "The remote provider item id.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "File returned.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LyricDto" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/LyricDto" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/LyricDto" + } + } + } + }, + "404": { + "description": "Lyric not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + } + }, + "security": [ + { + "CustomAuthentication": [ + "LyricManagement", + "DefaultAuthorization" ] } ] @@ -28834,7 +27309,6 @@ "name": "userId", "in": "query", "description": "The user id.", - "required": true, "schema": { "type": "string", "format": "uuid" @@ -28862,6 +27336,26 @@ } } }, + "404": { + "description": "Item not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, "401": { "description": "Unauthorized" }, @@ -29084,6 +27578,26 @@ } } }, + "404": { + "description": "Item not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, "401": { "description": "Unauthorized" }, @@ -29611,7 +28125,7 @@ "schema": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/ItemSortBy" } } }, @@ -29747,357 +28261,6 @@ ] } }, - "/Notifications/{userId}": { - "get": { - "tags": [ - "Notifications" - ], - "summary": "Gets a user's notifications.", - "operationId": "GetNotifications", - "parameters": [ - { - "name": "userId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Notifications returned.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/NotificationResultDto" - } - }, - "application/json; profile=\"CamelCase\"": { - "schema": { - "$ref": "#/components/schemas/NotificationResultDto" - } - }, - "application/json; profile=\"PascalCase\"": { - "schema": { - "$ref": "#/components/schemas/NotificationResultDto" - } - } - } - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - }, - "security": [ - { - "CustomAuthentication": [ - "DefaultAuthorization" - ] - } - ] - } - }, - "/Notifications/{userId}/Read": { - "post": { - "tags": [ - "Notifications" - ], - "summary": "Sets notifications as read.", - "operationId": "SetRead", - "parameters": [ - { - "name": "userId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "204": { - "description": "Notifications set as read." - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - }, - "security": [ - { - "CustomAuthentication": [ - "DefaultAuthorization" - ] - } - ] - } - }, - "/Notifications/{userId}/Summary": { - "get": { - "tags": [ - "Notifications" - ], - "summary": "Gets a user's notification summary.", - "operationId": "GetNotificationsSummary", - "parameters": [ - { - "name": "userId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Summary of user's notifications returned.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/NotificationsSummaryDto" - } - }, - "application/json; profile=\"CamelCase\"": { - "schema": { - "$ref": "#/components/schemas/NotificationsSummaryDto" - } - }, - "application/json; profile=\"PascalCase\"": { - "schema": { - "$ref": "#/components/schemas/NotificationsSummaryDto" - } - } - } - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - }, - "security": [ - { - "CustomAuthentication": [ - "DefaultAuthorization" - ] - } - ] - } - }, - "/Notifications/{userId}/Unread": { - "post": { - "tags": [ - "Notifications" - ], - "summary": "Sets notifications as unread.", - "operationId": "SetUnread", - "parameters": [ - { - "name": "userId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "204": { - "description": "Notifications set as unread." - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - }, - "security": [ - { - "CustomAuthentication": [ - "DefaultAuthorization" - ] - } - ] - } - }, - "/Notifications/Admin": { - "post": { - "tags": [ - "Notifications" - ], - "summary": "Sends a notification to all admins.", - "operationId": "CreateAdminNotification", - "requestBody": { - "description": "The notification request.", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/AdminNotificationDto" - } - ], - "description": "The admin notification dto." - } - }, - "text/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/AdminNotificationDto" - } - ], - "description": "The admin notification dto." - } - }, - "application/*+json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/AdminNotificationDto" - } - ], - "description": "The admin notification dto." - } - } - }, - "required": true - }, - "responses": { - "204": { - "description": "Notification sent." - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - }, - "security": [ - { - "CustomAuthentication": [ - "DefaultAuthorization" - ] - } - ] - } - }, - "/Notifications/Services": { - "get": { - "tags": [ - "Notifications" - ], - "summary": "Gets notification services.", - "operationId": "GetNotificationServices", - "responses": { - "200": { - "description": "All notification services returned.", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/NameIdPair" - } - } - }, - "application/json; profile=\"CamelCase\"": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/NameIdPair" - } - } - }, - "application/json; profile=\"PascalCase\"": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/NameIdPair" - } - } - } - } - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - }, - "security": [ - { - "CustomAuthentication": [ - "DefaultAuthorization" - ] - } - ] - } - }, - "/Notifications/Types": { - "get": { - "tags": [ - "Notifications" - ], - "summary": "Gets notification types.", - "operationId": "GetNotificationTypes", - "responses": { - "200": { - "description": "All notification types returned.", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/NotificationTypeInfo" - } - } - }, - "application/json; profile=\"CamelCase\"": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/NotificationTypeInfo" - } - } - }, - "application/json; profile=\"PascalCase\"": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/NotificationTypeInfo" - } - } - } - } - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - }, - "security": [ - { - "CustomAuthentication": [ - "DefaultAuthorization" - ] - } - ] - } - }, "/Packages": { "get": { "tags": [ @@ -30145,7 +28308,7 @@ "security": [ { "CustomAuthentication": [ - "DefaultAuthorization" + "RequiresElevation" ] } ] @@ -30209,7 +28372,7 @@ "security": [ { "CustomAuthentication": [ - "DefaultAuthorization" + "RequiresElevation" ] } ] @@ -30292,8 +28455,7 @@ "security": [ { "CustomAuthentication": [ - "RequiresElevation", - "DefaultAuthorization" + "RequiresElevation" ] } ] @@ -30332,8 +28494,7 @@ "security": [ { "CustomAuthentication": [ - "RequiresElevation", - "DefaultAuthorization" + "RequiresElevation" ] } ] @@ -30386,7 +28547,7 @@ "security": [ { "CustomAuthentication": [ - "DefaultAuthorization" + "RequiresElevation" ] } ] @@ -30441,8 +28602,7 @@ "security": [ { "CustomAuthentication": [ - "RequiresElevation", - "DefaultAuthorization" + "RequiresElevation" ] } ] @@ -30749,7 +28909,18 @@ "description": "The media type.", "deprecated": true, "schema": { - "type": "string" + "enum": [ + "Unknown", + "Video", + "Audio", + "Photo", + "Book" + ], + "allOf": [ + { + "$ref": "#/components/schemas/MediaType" + } + ] } } ], @@ -30790,7 +28961,7 @@ }, "responses": { "200": { - "description": "Success", + "description": "Playlist created.", "content": { "application/json": { "schema": { @@ -30825,13 +28996,125 @@ ] } }, + "/Playlists/{playlistId}": { + "post": { + "tags": [ + "Playlists" + ], + "summary": "Updates a playlist.", + "operationId": "UpdatePlaylist", + "parameters": [ + { + "name": "playlistId", + "in": "path", + "description": "The playlist id.", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "description": "The Jellyfin.Api.Models.PlaylistDtos.UpdatePlaylistDto id.", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/UpdatePlaylistDto" + } + ], + "description": "Update existing playlist dto. Fields set to `null` will not be updated and keep their current values." + } + }, + "text/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/UpdatePlaylistDto" + } + ], + "description": "Update existing playlist dto. Fields set to `null` will not be updated and keep their current values." + } + }, + "application/*+json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/UpdatePlaylistDto" + } + ], + "description": "Update existing playlist dto. Fields set to `null` will not be updated and keep their current values." + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "Playlist updated." + }, + "403": { + "description": "Access forbidden.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "404": { + "description": "Playlist not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "Unauthorized" + } + }, + "security": [ + { + "CustomAuthentication": [ + "DefaultAuthorization" + ] + } + ] + } + }, "/Playlists/{playlistId}/Items": { "post": { "tags": [ "Playlists" ], "summary": "Adds items to a playlist.", - "operationId": "AddToPlaylist", + "operationId": "AddItemToPlaylist", "parameters": [ { "name": "playlistId", @@ -30869,11 +29152,48 @@ "204": { "description": "Items added to playlist." }, + "403": { + "description": "Access forbidden.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "404": { + "description": "Playlist not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, "401": { "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" } }, "security": [ @@ -30889,7 +29209,7 @@ "Playlists" ], "summary": "Removes items from a playlist.", - "operationId": "RemoveFromPlaylist", + "operationId": "RemoveItemFromPlaylist", "parameters": [ { "name": "playlistId", @@ -30916,11 +29236,48 @@ "204": { "description": "Items removed." }, + "403": { + "description": "Access forbidden.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "404": { + "description": "Playlist not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, "401": { "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" } }, "security": [ @@ -30952,7 +29309,6 @@ "name": "userId", "in": "query", "description": "User id.", - "required": true, "schema": { "type": "string", "format": "uuid" @@ -31045,14 +29401,48 @@ } } }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, "404": { - "description": "Playlist not found." + "description": "Playlist not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } }, "401": { "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" } }, "security": [ @@ -31105,6 +29495,788 @@ "204": { "description": "Item moved to new index." }, + "403": { + "description": "Access forbidden.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "404": { + "description": "Playlist not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "Unauthorized" + } + }, + "security": [ + { + "CustomAuthentication": [ + "DefaultAuthorization" + ] + } + ] + } + }, + "/Playlists/{playlistId}/Users": { + "get": { + "tags": [ + "Playlists" + ], + "summary": "Get a playlist's users.", + "operationId": "GetPlaylistUsers", + "parameters": [ + { + "name": "playlistId", + "in": "path", + "description": "The playlist id.", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "Found shares.", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PlaylistUserPermissions" + } + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PlaylistUserPermissions" + } + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PlaylistUserPermissions" + } + } + } + } + }, + "403": { + "description": "Access forbidden.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "404": { + "description": "Playlist not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "Unauthorized" + } + }, + "security": [ + { + "CustomAuthentication": [ + "DefaultAuthorization" + ] + } + ] + } + }, + "/Playlists/{playlistId}/Users/{userId}": { + "get": { + "tags": [ + "Playlists" + ], + "summary": "Get a playlist user.", + "operationId": "GetPlaylistUser", + "parameters": [ + { + "name": "playlistId", + "in": "path", + "description": "The playlist id.", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "userId", + "in": "path", + "description": "The user id.", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "User permission found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PlaylistUserPermissions" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/PlaylistUserPermissions" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/PlaylistUserPermissions" + } + } + } + }, + "403": { + "description": "Access forbidden.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "404": { + "description": "Playlist not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "Unauthorized" + } + }, + "security": [ + { + "CustomAuthentication": [ + "DefaultAuthorization" + ] + } + ] + }, + "post": { + "tags": [ + "Playlists" + ], + "summary": "Modify a user of a playlist's users.", + "operationId": "UpdatePlaylistUser", + "parameters": [ + { + "name": "playlistId", + "in": "path", + "description": "The playlist id.", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "userId", + "in": "path", + "description": "The user id.", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "description": "The Jellyfin.Api.Models.PlaylistDtos.UpdatePlaylistUserDto.", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/UpdatePlaylistUserDto" + } + ], + "description": "Update existing playlist user dto. Fields set to `null` will not be updated and keep their current values." + } + }, + "text/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/UpdatePlaylistUserDto" + } + ], + "description": "Update existing playlist user dto. Fields set to `null` will not be updated and keep their current values." + } + }, + "application/*+json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/UpdatePlaylistUserDto" + } + ], + "description": "Update existing playlist user dto. Fields set to `null` will not be updated and keep their current values." + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "User's permissions modified." + }, + "403": { + "description": "Access forbidden.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "404": { + "description": "Playlist not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "Unauthorized" + } + }, + "security": [ + { + "CustomAuthentication": [ + "DefaultAuthorization" + ] + } + ] + }, + "delete": { + "tags": [ + "Playlists" + ], + "summary": "Remove a user from a playlist's users.", + "operationId": "RemoveUserFromPlaylist", + "parameters": [ + { + "name": "playlistId", + "in": "path", + "description": "The playlist id.", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "userId", + "in": "path", + "description": "The user id.", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "204": { + "description": "User permissions removed from playlist." + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "404": { + "description": "No playlist or user permissions found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "Unauthorized access." + } + }, + "security": [ + { + "CustomAuthentication": [ + "DefaultAuthorization" + ] + } + ] + } + }, + "/PlayingItems/{itemId}": { + "post": { + "tags": [ + "Playstate" + ], + "summary": "Reports that a session has begun playing an item.", + "operationId": "OnPlaybackStart", + "parameters": [ + { + "name": "itemId", + "in": "path", + "description": "Item id.", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "mediaSourceId", + "in": "query", + "description": "The id of the MediaSource.", + "schema": { + "type": "string" + } + }, + { + "name": "audioStreamIndex", + "in": "query", + "description": "The audio stream index.", + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "subtitleStreamIndex", + "in": "query", + "description": "The subtitle stream index.", + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "playMethod", + "in": "query", + "description": "The play method.", + "schema": { + "enum": [ + "Transcode", + "DirectStream", + "DirectPlay" + ], + "allOf": [ + { + "$ref": "#/components/schemas/PlayMethod" + } + ] + } + }, + { + "name": "liveStreamId", + "in": "query", + "description": "The live stream id.", + "schema": { + "type": "string" + } + }, + { + "name": "playSessionId", + "in": "query", + "description": "The play session id.", + "schema": { + "type": "string" + } + }, + { + "name": "canSeek", + "in": "query", + "description": "Indicates if the client can seek.", + "schema": { + "type": "boolean", + "default": false + } + } + ], + "responses": { + "204": { + "description": "Play start recorded." + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + } + }, + "security": [ + { + "CustomAuthentication": [ + "DefaultAuthorization" + ] + } + ] + }, + "delete": { + "tags": [ + "Playstate" + ], + "summary": "Reports that a session has stopped playing an item.", + "operationId": "OnPlaybackStopped", + "parameters": [ + { + "name": "itemId", + "in": "path", + "description": "Item id.", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "mediaSourceId", + "in": "query", + "description": "The id of the MediaSource.", + "schema": { + "type": "string" + } + }, + { + "name": "nextMediaType", + "in": "query", + "description": "The next media type that will play.", + "schema": { + "type": "string" + } + }, + { + "name": "positionTicks", + "in": "query", + "description": "Optional. The position, in ticks, where playback stopped. 1 tick = 10000 ms.", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "liveStreamId", + "in": "query", + "description": "The live stream id.", + "schema": { + "type": "string" + } + }, + { + "name": "playSessionId", + "in": "query", + "description": "The play session id.", + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "Playback stop recorded." + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + } + }, + "security": [ + { + "CustomAuthentication": [ + "DefaultAuthorization" + ] + } + ] + } + }, + "/PlayingItems/{itemId}/Progress": { + "post": { + "tags": [ + "Playstate" + ], + "summary": "Reports a session's playback progress.", + "operationId": "OnPlaybackProgress", + "parameters": [ + { + "name": "itemId", + "in": "path", + "description": "Item id.", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "mediaSourceId", + "in": "query", + "description": "The id of the MediaSource.", + "schema": { + "type": "string" + } + }, + { + "name": "positionTicks", + "in": "query", + "description": "Optional. The current position, in ticks. 1 tick = 10000 ms.", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "audioStreamIndex", + "in": "query", + "description": "The audio stream index.", + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "subtitleStreamIndex", + "in": "query", + "description": "The subtitle stream index.", + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "volumeLevel", + "in": "query", + "description": "Scale of 0-100.", + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "playMethod", + "in": "query", + "description": "The play method.", + "schema": { + "enum": [ + "Transcode", + "DirectStream", + "DirectPlay" + ], + "allOf": [ + { + "$ref": "#/components/schemas/PlayMethod" + } + ] + } + }, + { + "name": "liveStreamId", + "in": "query", + "description": "The live stream id.", + "schema": { + "type": "string" + } + }, + { + "name": "playSessionId", + "in": "query", + "description": "The play session id.", + "schema": { + "type": "string" + } + }, + { + "name": "repeatMode", + "in": "query", + "description": "The repeat mode.", + "schema": { + "enum": [ + "RepeatNone", + "RepeatAll", + "RepeatOne" + ], + "allOf": [ + { + "$ref": "#/components/schemas/RepeatMode" + } + ] + } + }, + { + "name": "isPaused", + "in": "query", + "description": "Indicates if the player is paused.", + "schema": { + "type": "boolean", + "default": false + } + }, + { + "name": "isMuted", + "in": "query", + "description": "Indicates if the player is muted.", + "schema": { + "type": "boolean", + "default": false + } + } + ], + "responses": { + "204": { + "description": "Play progress recorded." + }, "401": { "description": "Unauthorized" }, @@ -31345,7 +30517,7 @@ ] } }, - "/Users/{userId}/PlayedItems/{itemId}": { + "/UserPlayedItems/{itemId}": { "post": { "tags": [ "Playstate" @@ -31355,9 +30527,8 @@ "parameters": [ { "name": "userId", - "in": "path", + "in": "query", "description": "User id.", - "required": true, "schema": { "type": "string", "format": "uuid" @@ -31404,6 +30575,26 @@ } } }, + "404": { + "description": "Item not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, "401": { "description": "Unauthorized" }, @@ -31428,9 +30619,8 @@ "parameters": [ { "name": "userId", - "in": "path", + "in": "query", "description": "User id.", - "required": true, "schema": { "type": "string", "format": "uuid" @@ -31468,357 +30658,26 @@ } } }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - }, - "security": [ - { - "CustomAuthentication": [ - "DefaultAuthorization" - ] - } - ] - } - }, - "/Users/{userId}/PlayingItems/{itemId}": { - "post": { - "tags": [ - "Playstate" - ], - "summary": "Reports that a user has begun playing an item.", - "operationId": "OnPlaybackStart", - "parameters": [ - { - "name": "userId", - "in": "path", - "description": "User id.", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - }, - { - "name": "itemId", - "in": "path", - "description": "Item id.", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - }, - { - "name": "mediaSourceId", - "in": "query", - "description": "The id of the MediaSource.", - "schema": { - "type": "string" - } - }, - { - "name": "audioStreamIndex", - "in": "query", - "description": "The audio stream index.", - "schema": { - "type": "integer", - "format": "int32" - } - }, - { - "name": "subtitleStreamIndex", - "in": "query", - "description": "The subtitle stream index.", - "schema": { - "type": "integer", - "format": "int32" - } - }, - { - "name": "playMethod", - "in": "query", - "description": "The play method.", - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/PlayMethod" + "404": { + "description": "Item not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" } - ] - } - }, - { - "name": "liveStreamId", - "in": "query", - "description": "The live stream id.", - "schema": { - "type": "string" - } - }, - { - "name": "playSessionId", - "in": "query", - "description": "The play session id.", - "schema": { - "type": "string" - } - }, - { - "name": "canSeek", - "in": "query", - "description": "Indicates if the client can seek.", - "schema": { - "type": "boolean", - "default": false - } - } - ], - "responses": { - "204": { - "description": "Play start recorded." - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - }, - "security": [ - { - "CustomAuthentication": [ - "DefaultAuthorization" - ] - } - ] - }, - "delete": { - "tags": [ - "Playstate" - ], - "summary": "Reports that a user has stopped playing an item.", - "operationId": "OnPlaybackStopped", - "parameters": [ - { - "name": "userId", - "in": "path", - "description": "User id.", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - }, - { - "name": "itemId", - "in": "path", - "description": "Item id.", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - }, - { - "name": "mediaSourceId", - "in": "query", - "description": "The id of the MediaSource.", - "schema": { - "type": "string" - } - }, - { - "name": "nextMediaType", - "in": "query", - "description": "The next media type that will play.", - "schema": { - "type": "string" - } - }, - { - "name": "positionTicks", - "in": "query", - "description": "Optional. The position, in ticks, where playback stopped. 1 tick = 10000 ms.", - "schema": { - "type": "integer", - "format": "int64" - } - }, - { - "name": "liveStreamId", - "in": "query", - "description": "The live stream id.", - "schema": { - "type": "string" - } - }, - { - "name": "playSessionId", - "in": "query", - "description": "The play session id.", - "schema": { - "type": "string" - } - } - ], - "responses": { - "204": { - "description": "Playback stop recorded." - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - }, - "security": [ - { - "CustomAuthentication": [ - "DefaultAuthorization" - ] - } - ] - } - }, - "/Users/{userId}/PlayingItems/{itemId}/Progress": { - "post": { - "tags": [ - "Playstate" - ], - "summary": "Reports a user's playback progress.", - "operationId": "OnPlaybackProgress", - "parameters": [ - { - "name": "userId", - "in": "path", - "description": "User id.", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - }, - { - "name": "itemId", - "in": "path", - "description": "Item id.", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - }, - { - "name": "mediaSourceId", - "in": "query", - "description": "The id of the MediaSource.", - "schema": { - "type": "string" - } - }, - { - "name": "positionTicks", - "in": "query", - "description": "Optional. The current position, in ticks. 1 tick = 10000 ms.", - "schema": { - "type": "integer", - "format": "int64" - } - }, - { - "name": "audioStreamIndex", - "in": "query", - "description": "The audio stream index.", - "schema": { - "type": "integer", - "format": "int32" - } - }, - { - "name": "subtitleStreamIndex", - "in": "query", - "description": "The subtitle stream index.", - "schema": { - "type": "integer", - "format": "int32" - } - }, - { - "name": "volumeLevel", - "in": "query", - "description": "Scale of 0-100.", - "schema": { - "type": "integer", - "format": "int32" - } - }, - { - "name": "playMethod", - "in": "query", - "description": "The play method.", - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/PlayMethod" + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" } - ] - } - }, - { - "name": "liveStreamId", - "in": "query", - "description": "The live stream id.", - "schema": { - "type": "string" - } - }, - { - "name": "playSessionId", - "in": "query", - "description": "The play session id.", - "schema": { - "type": "string" - } - }, - { - "name": "repeatMode", - "in": "query", - "description": "The repeat mode.", - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/RepeatMode" + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" } - ] + } } }, - { - "name": "isPaused", - "in": "query", - "description": "Indicates if the player is paused.", - "schema": { - "type": "boolean", - "default": false - } - }, - { - "name": "isMuted", - "in": "query", - "description": "Indicates if the player is muted.", - "schema": { - "type": "boolean", - "default": false - } - } - ], - "responses": { - "204": { - "description": "Play progress recorded." - }, "401": { "description": "Unauthorized" }, @@ -31882,7 +30741,7 @@ "security": [ { "CustomAuthentication": [ - "DefaultAuthorization" + "RequiresElevation" ] } ] @@ -31942,8 +30801,7 @@ "security": [ { "CustomAuthentication": [ - "RequiresElevation", - "DefaultAuthorization" + "RequiresElevation" ] } ] @@ -32011,8 +30869,7 @@ "security": [ { "CustomAuthentication": [ - "RequiresElevation", - "DefaultAuthorization" + "RequiresElevation" ] } ] @@ -32080,8 +30937,7 @@ "security": [ { "CustomAuthentication": [ - "RequiresElevation", - "DefaultAuthorization" + "RequiresElevation" ] } ] @@ -32149,8 +31005,7 @@ "security": [ { "CustomAuthentication": [ - "RequiresElevation", - "DefaultAuthorization" + "RequiresElevation" ] } ] @@ -32226,7 +31081,7 @@ "security": [ { "CustomAuthentication": [ - "DefaultAuthorization" + "RequiresElevation" ] } ] @@ -32302,7 +31157,7 @@ "security": [ { "CustomAuthentication": [ - "DefaultAuthorization" + "RequiresElevation" ] } ] @@ -32360,7 +31215,7 @@ "security": [ { "CustomAuthentication": [ - "DefaultAuthorization" + "RequiresElevation" ] } ] @@ -32419,7 +31274,7 @@ "security": [ { "CustomAuthentication": [ - "DefaultAuthorization" + "RequiresElevation" ] } ] @@ -32431,7 +31286,7 @@ "QuickConnect" ], "summary": "Authorizes a pending quick connect request.", - "operationId": "Authorize", + "operationId": "AuthorizeQuickConnect", "parameters": [ { "name": "code", @@ -32441,6 +31296,15 @@ "schema": { "type": "string" } + }, + { + "name": "userId", + "in": "query", + "description": "The user the authorize. Access to the requested user is required.", + "schema": { + "type": "string", + "format": "uuid" + } } ], "responses": { @@ -32503,7 +31367,7 @@ "QuickConnect" ], "summary": "Attempts to retrieve authentication information.", - "operationId": "Connect", + "operationId": "GetQuickConnectState", "parameters": [ { "name": "secret", @@ -32565,7 +31429,7 @@ "QuickConnect" ], "summary": "Gets the current quick connect state.", - "operationId": "GetEnabled", + "operationId": "GetQuickConnectEnabled", "responses": { "200": { "description": "Quick connect state returned.", @@ -32591,12 +31455,12 @@ } }, "/QuickConnect/Initiate": { - "get": { + "post": { "tags": [ "QuickConnect" ], "summary": "Initiate a new quick connect request.", - "operationId": "Initiate", + "operationId": "InitiateQuickConnect", "responses": { "200": { "description": "Quick connect request successfully created.", @@ -32647,6 +31511,21 @@ "in": "query", "description": "The image type.", "schema": { + "enum": [ + "Primary", + "Art", + "Backdrop", + "Banner", + "Logo", + "Thumb", + "Disc", + "Box", + "Screenshot", + "Menu", + "Chapter", + "BoxRear", + "Profile" + ], "allOf": [ { "$ref": "#/components/schemas/ImageType" @@ -32771,6 +31650,21 @@ "description": "The image type.", "required": true, "schema": { + "enum": [ + "Primary", + "Art", + "Backdrop", + "Banner", + "Logo", + "Thumb", + "Disc", + "Box", + "Screenshot", + "Menu", + "Chapter", + "BoxRear", + "Profile" + ], "allOf": [ { "$ref": "#/components/schemas/ImageType" @@ -33267,7 +32161,7 @@ "Search" ], "summary": "Gets the search hint result.", - "operationId": "Get", + "operationId": "GetSearchHints", "parameters": [ { "name": "startIndex", @@ -33308,7 +32202,7 @@ { "name": "includeItemTypes", "in": "query", - "description": "If specified, only results with the specified item types are returned. This allows multiple, comma delimeted.", + "description": "If specified, only results with the specified item types are returned. This allows multiple, comma delimited.", "schema": { "type": "array", "items": { @@ -33319,7 +32213,7 @@ { "name": "excludeItemTypes", "in": "query", - "description": "If specified, results with these item types are filtered out. This allows multiple, comma delimeted.", + "description": "If specified, results with these item types are filtered out. This allows multiple, comma delimited.", "schema": { "type": "array", "items": { @@ -33330,11 +32224,11 @@ { "name": "mediaTypes", "in": "query", - "description": "If specified, only results with the specified media types are returned. This allows multiple, comma delimeted.", + "description": "If specified, only results with the specified media types are returned. This allows multiple, comma delimited.", "schema": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/MediaType" } } }, @@ -33751,6 +32645,51 @@ "description": "The command to send.", "required": true, "schema": { + "enum": [ + "MoveUp", + "MoveDown", + "MoveLeft", + "MoveRight", + "PageUp", + "PageDown", + "PreviousLetter", + "NextLetter", + "ToggleOsd", + "ToggleContextMenu", + "Select", + "Back", + "TakeScreenshot", + "SendKey", + "SendString", + "GoHome", + "GoToSettings", + "VolumeUp", + "VolumeDown", + "Mute", + "Unmute", + "ToggleMute", + "SetVolume", + "SetAudioStreamIndex", + "SetSubtitleStreamIndex", + "ToggleFullscreen", + "DisplayContent", + "GoToSearch", + "DisplayMessage", + "SetRepeatMode", + "ChannelUp", + "ChannelDown", + "Guide", + "ToggleStats", + "PlayMediaSource", + "PlayTrailers", + "SetShuffleQueue", + "PlayState", + "PlayNext", + "ToggleOsdMenu", + "Play", + "SetMaxStreamingBitrate", + "SetPlaybackOrder" + ], "allOf": [ { "$ref": "#/components/schemas/GeneralCommandType" @@ -33874,6 +32813,13 @@ "description": "The type of play command to issue (PlayNow, PlayNext, PlayLast). Clients who have not yet implemented play next and play last may play now.", "required": true, "schema": { + "enum": [ + "PlayNow", + "PlayNext", + "PlayLast", + "PlayInstantMix", + "PlayShuffle" + ], "allOf": [ { "$ref": "#/components/schemas/PlayCommand" @@ -33983,6 +32929,17 @@ "description": "The MediaBrowser.Model.Session.PlaystateCommand.", "required": true, "schema": { + "enum": [ + "Stop", + "Pause", + "Unpause", + "NextTrack", + "PreviousTrack", + "Seek", + "Rewind", + "FastForward", + "PlayPause" + ], "allOf": [ { "$ref": "#/components/schemas/PlaystateCommand" @@ -34052,6 +33009,51 @@ "description": "The command to send.", "required": true, "schema": { + "enum": [ + "MoveUp", + "MoveDown", + "MoveLeft", + "MoveRight", + "PageUp", + "PageDown", + "PreviousLetter", + "NextLetter", + "ToggleOsd", + "ToggleContextMenu", + "Select", + "Back", + "TakeScreenshot", + "SendKey", + "SendString", + "GoHome", + "GoToSettings", + "VolumeUp", + "VolumeDown", + "Mute", + "Unmute", + "ToggleMute", + "SetVolume", + "SetAudioStreamIndex", + "SetSubtitleStreamIndex", + "ToggleFullscreen", + "DisplayContent", + "GoToSearch", + "DisplayMessage", + "SetRepeatMode", + "ChannelUp", + "ChannelDown", + "Guide", + "ToggleStats", + "PlayMediaSource", + "PlayTrailers", + "SetShuffleQueue", + "PlayState", + "PlayNext", + "ToggleOsdMenu", + "Play", + "SetMaxStreamingBitrate", + "SetPlaybackOrder" + ], "allOf": [ { "$ref": "#/components/schemas/GeneralCommandType" @@ -34198,6 +33200,45 @@ "description": "The type of item to browse to.", "required": true, "schema": { + "enum": [ + "AggregateFolder", + "Audio", + "AudioBook", + "BasePluginFolder", + "Book", + "BoxSet", + "Channel", + "ChannelFolderItem", + "CollectionFolder", + "Episode", + "Folder", + "Genre", + "ManualPlaylistsFolder", + "Movie", + "LiveTvChannel", + "LiveTvProgram", + "MusicAlbum", + "MusicArtist", + "MusicGenre", + "MusicVideo", + "Person", + "Photo", + "PhotoAlbum", + "Playlist", + "PlaylistsFolder", + "Program", + "Recording", + "Season", + "Series", + "Studio", + "Trailer", + "TvChannel", + "TvProgram", + "UserRootFolder", + "UserView", + "Video", + "Year" + ], "allOf": [ { "$ref": "#/components/schemas/BaseItemKind" @@ -34268,7 +33309,7 @@ "schema": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/MediaType" } } }, @@ -34292,15 +33333,6 @@ "default": false } }, - { - "name": "supportsSync", - "in": "query", - "description": "Determines whether sync is supported.", - "schema": { - "type": "boolean", - "default": false - } - }, { "name": "supportsPersistentIdentifier", "in": "query", @@ -34498,7 +33530,8 @@ "security": [ { "CustomAuthentication": [ - "FirstTimeSetupOrElevated" + "FirstTimeSetupOrElevated", + "DefaultAuthorization" ] } ] @@ -34542,7 +33575,8 @@ "security": [ { "CustomAuthentication": [ - "FirstTimeSetupOrElevated" + "FirstTimeSetupOrElevated", + "DefaultAuthorization" ] } ] @@ -34603,7 +33637,8 @@ "security": [ { "CustomAuthentication": [ - "FirstTimeSetupOrElevated" + "FirstTimeSetupOrElevated", + "DefaultAuthorization" ] } ] @@ -34647,7 +33682,8 @@ "security": [ { "CustomAuthentication": [ - "FirstTimeSetupOrElevated" + "FirstTimeSetupOrElevated", + "DefaultAuthorization" ] } ] @@ -34710,7 +33746,8 @@ "security": [ { "CustomAuthentication": [ - "FirstTimeSetupOrElevated" + "FirstTimeSetupOrElevated", + "DefaultAuthorization" ] } ] @@ -34754,7 +33791,8 @@ "security": [ { "CustomAuthentication": [ - "FirstTimeSetupOrElevated" + "FirstTimeSetupOrElevated", + "DefaultAuthorization" ] } ] @@ -34814,7 +33852,8 @@ "security": [ { "CustomAuthentication": [ - "FirstTimeSetupOrElevated" + "FirstTimeSetupOrElevated", + "DefaultAuthorization" ] } ] @@ -35250,6 +34289,26 @@ } } }, + "404": { + "description": "Item not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, "401": { "description": "Unauthorized" }, @@ -35260,6 +34319,7 @@ "security": [ { "CustomAuthentication": [ + "SubtitleManagement", "DefaultAuthorization" ] } @@ -35298,6 +34358,26 @@ "204": { "description": "Subtitle downloaded." }, + "404": { + "description": "Item not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, "401": { "description": "Unauthorized" }, @@ -35308,13 +34388,14 @@ "security": [ { "CustomAuthentication": [ + "SubtitleManagement", "DefaultAuthorization" ] } ] } }, - "/Providers/Subtitles/Subtitles/{id}": { + "/Providers/Subtitles/Subtitles/{subtitleId}": { "get": { "tags": [ "Subtitle" @@ -35323,7 +34404,7 @@ "operationId": "GetRemoteSubtitles", "parameters": [ { - "name": "id", + "name": "subtitleId", "in": "path", "description": "The item id.", "required": true, @@ -35354,6 +34435,7 @@ "security": [ { "CustomAuthentication": [ + "SubtitleManagement", "DefaultAuthorization" ] } @@ -35420,6 +34502,26 @@ } } }, + "404": { + "description": "Item not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, "401": { "description": "Unauthorized" }, @@ -35495,6 +34597,26 @@ "204": { "description": "Subtitle uploaded." }, + "404": { + "description": "Item not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, "401": { "description": "Unauthorized" }, @@ -35505,7 +34627,8 @@ "security": [ { "CustomAuthentication": [ - "RequiresElevation" + "SubtitleManagement", + "DefaultAuthorization" ] } ] @@ -35864,7 +34987,7 @@ } } }, - "/Users/{userId}/Suggestions": { + "/Items/Suggestions": { "get": { "tags": [ "Suggestions" @@ -35874,9 +34997,8 @@ "parameters": [ { "name": "userId", - "in": "path", + "in": "query", "description": "The user id.", - "required": true, "schema": { "type": "string", "format": "uuid" @@ -35889,7 +35011,7 @@ "schema": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/MediaType" } } }, @@ -36027,7 +35149,8 @@ { "CustomAuthentication": [ "SyncPlayIsInGroup", - "SyncPlayHasAccess" + "SyncPlayHasAccess", + "DefaultAuthorization" ] } ] @@ -36091,7 +35214,8 @@ { "CustomAuthentication": [ "SyncPlayJoinGroup", - "SyncPlayHasAccess" + "SyncPlayHasAccess", + "DefaultAuthorization" ] } ] @@ -36119,7 +35243,8 @@ { "CustomAuthentication": [ "SyncPlayIsInGroup", - "SyncPlayHasAccess" + "SyncPlayHasAccess", + "DefaultAuthorization" ] } ] @@ -36173,7 +35298,8 @@ { "CustomAuthentication": [ "SyncPlayJoinGroup", - "SyncPlayHasAccess" + "SyncPlayHasAccess", + "DefaultAuthorization" ] } ] @@ -36237,7 +35363,8 @@ { "CustomAuthentication": [ "SyncPlayIsInGroup", - "SyncPlayHasAccess" + "SyncPlayHasAccess", + "DefaultAuthorization" ] } ] @@ -36301,7 +35428,8 @@ { "CustomAuthentication": [ "SyncPlayCreateGroup", - "SyncPlayHasAccess" + "SyncPlayHasAccess", + "DefaultAuthorization" ] } ] @@ -36365,7 +35493,8 @@ { "CustomAuthentication": [ "SyncPlayIsInGroup", - "SyncPlayHasAccess" + "SyncPlayHasAccess", + "DefaultAuthorization" ] } ] @@ -36393,7 +35522,8 @@ { "CustomAuthentication": [ "SyncPlayIsInGroup", - "SyncPlayHasAccess" + "SyncPlayHasAccess", + "DefaultAuthorization" ] } ] @@ -36456,7 +35586,8 @@ "security": [ { "CustomAuthentication": [ - "SyncPlayHasAccess" + "SyncPlayHasAccess", + "DefaultAuthorization" ] } ] @@ -36520,7 +35651,8 @@ { "CustomAuthentication": [ "SyncPlayIsInGroup", - "SyncPlayHasAccess" + "SyncPlayHasAccess", + "DefaultAuthorization" ] } ] @@ -36584,7 +35716,8 @@ { "CustomAuthentication": [ "SyncPlayIsInGroup", - "SyncPlayHasAccess" + "SyncPlayHasAccess", + "DefaultAuthorization" ] } ] @@ -36648,7 +35781,8 @@ { "CustomAuthentication": [ "SyncPlayIsInGroup", - "SyncPlayHasAccess" + "SyncPlayHasAccess", + "DefaultAuthorization" ] } ] @@ -36712,7 +35846,8 @@ { "CustomAuthentication": [ "SyncPlayIsInGroup", - "SyncPlayHasAccess" + "SyncPlayHasAccess", + "DefaultAuthorization" ] } ] @@ -36776,7 +35911,8 @@ { "CustomAuthentication": [ "SyncPlayIsInGroup", - "SyncPlayHasAccess" + "SyncPlayHasAccess", + "DefaultAuthorization" ] } ] @@ -36840,7 +35976,8 @@ { "CustomAuthentication": [ "SyncPlayIsInGroup", - "SyncPlayHasAccess" + "SyncPlayHasAccess", + "DefaultAuthorization" ] } ] @@ -36904,7 +36041,8 @@ { "CustomAuthentication": [ "SyncPlayIsInGroup", - "SyncPlayHasAccess" + "SyncPlayHasAccess", + "DefaultAuthorization" ] } ] @@ -36968,7 +36106,8 @@ { "CustomAuthentication": [ "SyncPlayIsInGroup", - "SyncPlayHasAccess" + "SyncPlayHasAccess", + "DefaultAuthorization" ] } ] @@ -37032,7 +36171,8 @@ { "CustomAuthentication": [ "SyncPlayIsInGroup", - "SyncPlayHasAccess" + "SyncPlayHasAccess", + "DefaultAuthorization" ] } ] @@ -37096,7 +36236,8 @@ { "CustomAuthentication": [ "SyncPlayIsInGroup", - "SyncPlayHasAccess" + "SyncPlayHasAccess", + "DefaultAuthorization" ] } ] @@ -37124,7 +36265,8 @@ { "CustomAuthentication": [ "SyncPlayIsInGroup", - "SyncPlayHasAccess" + "SyncPlayHasAccess", + "DefaultAuthorization" ] } ] @@ -37152,7 +36294,8 @@ { "CustomAuthentication": [ "SyncPlayIsInGroup", - "SyncPlayHasAccess" + "SyncPlayHasAccess", + "DefaultAuthorization" ] } ] @@ -37186,11 +36329,28 @@ } } }, + "403": { + "description": "User does not have permission to get endpoint information.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, "401": { "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" } }, "security": [ @@ -37230,17 +36390,35 @@ } } }, + "403": { + "description": "User does not have permission to retrieve information.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, "401": { "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" } }, "security": [ { "CustomAuthentication": [ - "FirstTimeSetupOrIgnoreParentalControl" + "FirstTimeSetupOrIgnoreParentalControl", + "DefaultAuthorization" ] } ] @@ -37314,11 +36492,28 @@ } } }, + "403": { + "description": "User does not have permission to get server logs.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, "401": { "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" } }, "security": [ @@ -37360,11 +36555,48 @@ } } }, + "403": { + "description": "User does not have permission to get log files.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "404": { + "description": "Could not find a log file with the name.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, "401": { "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" } }, "security": [ @@ -37447,11 +36679,28 @@ "204": { "description": "Server restarted." }, + "403": { + "description": "User does not have permission to restart server.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, "401": { "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" } }, "security": [ @@ -37474,11 +36723,28 @@ "204": { "description": "Server shut down." }, + "403": { + "description": "User does not have permission to shutdown server.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, "401": { "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" } }, "security": [ @@ -37620,7 +36886,7 @@ { "name": "userId", "in": "query", - "description": "The user id.", + "description": "The user id supplied as query parameter; this is required when not using an API key.", "schema": { "type": "string", "format": "uuid" @@ -37679,7 +36945,8 @@ "in": "query", "description": "Optional. Return items that are siblings of a supplied item.", "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -37818,7 +37085,7 @@ { "name": "hasImdbId", "in": "query", - "description": "Optional filter by items that have an imdb id or not.", + "description": "Optional filter by items that have an IMDb id or not.", "schema": { "type": "boolean" } @@ -37826,7 +37093,7 @@ { "name": "hasTmdbId", "in": "query", - "description": "Optional filter by items that have a tmdb id or not.", + "description": "Optional filter by items that have a TMDb id or not.", "schema": { "type": "boolean" } @@ -37834,7 +37101,7 @@ { "name": "hasTvdbId", "in": "query", - "description": "Optional filter by items that have a tvdb id or not.", + "description": "Optional filter by items that have a TVDb id or not.", "schema": { "type": "boolean" } @@ -37928,7 +37195,7 @@ { "name": "sortOrder", "in": "query", - "description": "Sort Order - Ascending,Descending.", + "description": "Sort Order - Ascending, Descending.", "schema": { "type": "array", "items": { @@ -37993,7 +37260,7 @@ "schema": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/MediaType" } } }, @@ -38015,7 +37282,7 @@ "schema": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/ItemSortBy" } } }, @@ -38446,6 +37713,188 @@ ] } }, + "/Videos/{itemId}/Trickplay/{width}/{index}.jpg": { + "get": { + "tags": [ + "Trickplay" + ], + "summary": "Gets a trickplay tile image.", + "operationId": "GetTrickplayTileImage", + "parameters": [ + { + "name": "itemId", + "in": "path", + "description": "The item id.", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "width", + "in": "path", + "description": "The width of a single tile.", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "index", + "in": "path", + "description": "The index of the desired tile.", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "mediaSourceId", + "in": "query", + "description": "The media version id, if using an alternate version.", + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "Tile image not found at specified index.", + "content": { + "image/*": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + } + }, + "security": [ + { + "CustomAuthentication": [ + "DefaultAuthorization" + ] + } + ] + } + }, + "/Videos/{itemId}/Trickplay/{width}/tiles.m3u8": { + "get": { + "tags": [ + "Trickplay" + ], + "summary": "Gets an image tiles playlist for trickplay.", + "operationId": "GetTrickplayHlsPlaylist", + "parameters": [ + { + "name": "itemId", + "in": "path", + "description": "The item id.", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "width", + "in": "path", + "description": "The width of a single tile.", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "mediaSourceId", + "in": "query", + "description": "The media version id, if using an alternate version.", + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "Tiles playlist returned.", + "content": { + "application/x-mpegURL": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + } + }, + "security": [ + { + "CustomAuthentication": [ + "DefaultAuthorization" + ] + } + ] + } + }, "/Shows/{seriesId}/Episodes": { "get": { "tags": [ @@ -38515,7 +37964,8 @@ "in": "query", "description": "Optional. Return items that are siblings of a supplied item.", "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -38586,7 +38036,45 @@ "in": "query", "description": "Optional. Specify one or more sort orders, comma delimited. Options: Album, AlbumArtist, Artist, Budget, CommunityRating, CriticRating, DateCreated, DatePlayed, PlayCount, PremiereDate, ProductionYear, SortName, Random, Revenue, Runtime.", "schema": { - "type": "string" + "enum": [ + "Default", + "AiredEpisodeOrder", + "Album", + "AlbumArtist", + "Artist", + "DateCreated", + "OfficialRating", + "DatePlayed", + "PremiereDate", + "StartDate", + "SortName", + "Name", + "Random", + "Runtime", + "CommunityRating", + "ProductionYear", + "PlayCount", + "CriticRating", + "IsFolder", + "IsUnplayed", + "IsPlayed", + "SeriesSortName", + "VideoBitRate", + "AirTime", + "Studio", + "IsFavoriteOrLiked", + "DateLastContentAdded", + "SeriesDatePlayed", + "ParentIndexNumber", + "IndexNumber", + "SimilarityScore", + "SearchScore" + ], + "allOf": [ + { + "$ref": "#/components/schemas/ItemSortBy" + } + ] } } ], @@ -38706,7 +38194,8 @@ "in": "query", "description": "Optional. Return items that are siblings of a supplied item.", "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -38854,7 +38343,8 @@ "in": "query", "description": "Optional. Filter by series id.", "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -38929,10 +38419,19 @@ "default": false } }, + { + "name": "enableResumable", + "in": "query", + "description": "Whether to include resumable episodes in next up results.", + "schema": { + "type": "boolean", + "default": true + } + }, { "name": "enableRewatching", "in": "query", - "description": "Whether to include watched episode in next up results.", + "description": "Whether to include watched episodes in next up results.", "schema": { "type": "boolean", "default": false @@ -39227,7 +38726,15 @@ "in": "query", "description": "Optional. The transcoding protocol.", "schema": { - "type": "string" + "enum": [ + "http", + "hls" + ], + "allOf": [ + { + "$ref": "#/components/schemas/MediaStreamProtocol" + } + ] } }, { @@ -39290,6 +38797,26 @@ "302": { "description": "Redirected to remote audio stream." }, + "404": { + "description": "Item not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, "401": { "description": "Unauthorized" }, @@ -39426,7 +38953,15 @@ "in": "query", "description": "Optional. The transcoding protocol.", "schema": { - "type": "string" + "enum": [ + "http", + "hls" + ], + "allOf": [ + { + "$ref": "#/components/schemas/MediaStreamProtocol" + } + ] } }, { @@ -39489,6 +39024,26 @@ "302": { "description": "Redirected to remote audio stream." }, + "404": { + "description": "Item not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, "401": { "description": "Unauthorized" }, @@ -39574,139 +39129,6 @@ ] } ] - } - }, - "/Users/{userId}": { - "get": { - "tags": [ - "User" - ], - "summary": "Gets a user by Id.", - "operationId": "GetUserById", - "parameters": [ - { - "name": "userId", - "in": "path", - "description": "The user id.", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - } - ], - "responses": { - "200": { - "description": "User returned.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UserDto" - } - }, - "application/json; profile=\"CamelCase\"": { - "schema": { - "$ref": "#/components/schemas/UserDto" - } - }, - "application/json; profile=\"PascalCase\"": { - "schema": { - "$ref": "#/components/schemas/UserDto" - } - } - } - }, - "404": { - "description": "User not found.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - }, - "application/json; profile=\"CamelCase\"": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - }, - "application/json; profile=\"PascalCase\"": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - }, - "security": [ - { - "CustomAuthentication": [ - "IgnoreParentalControl" - ] - } - ] - }, - "delete": { - "tags": [ - "User" - ], - "summary": "Deletes a user.", - "operationId": "DeleteUser", - "parameters": [ - { - "name": "userId", - "in": "path", - "description": "The user id.", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - } - ], - "responses": { - "204": { - "description": "User deleted." - }, - "404": { - "description": "User not found.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - }, - "application/json; profile=\"CamelCase\"": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - }, - "application/json; profile=\"PascalCase\"": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - }, - "security": [ - { - "CustomAuthentication": [ - "RequiresElevation" - ] - } - ] }, "post": { "tags": [ @@ -39717,9 +39139,8 @@ "parameters": [ { "name": "userId", - "in": "path", + "in": "query", "description": "The user id.", - "required": true, "schema": { "type": "string", "format": "uuid" @@ -39819,13 +39240,13 @@ ] } }, - "/Users/{userId}/Authenticate": { - "post": { + "/Users/{userId}": { + "get": { "tags": [ "User" ], - "summary": "Authenticates a user.", - "operationId": "AuthenticateUser", + "summary": "Gets a user by Id.", + "operationId": "GetUserById", "parameters": [ { "name": "userId", @@ -39836,62 +39257,25 @@ "type": "string", "format": "uuid" } - }, - { - "name": "pw", - "in": "query", - "description": "The password as plain text.", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "password", - "in": "query", - "description": "The password sha1-hash.", - "schema": { - "type": "string" - } } ], "responses": { "200": { - "description": "User authenticated.", + "description": "User returned.", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AuthenticationResult" + "$ref": "#/components/schemas/UserDto" } }, "application/json; profile=\"CamelCase\"": { "schema": { - "$ref": "#/components/schemas/AuthenticationResult" + "$ref": "#/components/schemas/UserDto" } }, "application/json; profile=\"PascalCase\"": { "schema": { - "$ref": "#/components/schemas/AuthenticationResult" - } - } - } - }, - "403": { - "description": "Sha1-hashed password only is not allowed.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - }, - "application/json; profile=\"CamelCase\"": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - }, - "application/json; profile=\"PascalCase\"": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" + "$ref": "#/components/schemas/UserDto" } } } @@ -39915,109 +39299,29 @@ } } } - } - } - } - }, - "/Users/{userId}/Configuration": { - "post": { - "tags": [ - "User" - ], - "summary": "Updates a user configuration.", - "operationId": "UpdateUserConfiguration", - "parameters": [ - { - "name": "userId", - "in": "path", - "description": "The user id.", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - } - ], - "requestBody": { - "description": "The new user configuration.", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/UserConfiguration" - } - ], - "description": "Class UserConfiguration." - } - }, - "text/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/UserConfiguration" - } - ], - "description": "Class UserConfiguration." - } - }, - "application/*+json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/UserConfiguration" - } - ], - "description": "Class UserConfiguration." - } - } - }, - "required": true - }, - "responses": { - "204": { - "description": "User configuration updated." - }, - "403": { - "description": "User configuration update forbidden.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - }, - "application/json; profile=\"CamelCase\"": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - }, - "application/json; profile=\"PascalCase\"": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } }, "401": { "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" } }, "security": [ { "CustomAuthentication": [ + "IgnoreParentalControl", "DefaultAuthorization" ] } ] - } - }, - "/Users/{userId}/EasyPassword": { - "post": { + }, + "delete": { "tags": [ "User" ], - "summary": "Updates a user's easy password.", - "operationId": "UpdateUserEasyPassword", + "summary": "Deletes a user.", + "operationId": "DeleteUser", "parameters": [ { "name": "userId", @@ -40030,65 +39334,9 @@ } } ], - "requestBody": { - "description": "The M:Jellyfin.Api.Controllers.UserController.UpdateUserEasyPassword(System.Guid,Jellyfin.Api.Models.UserDtos.UpdateUserEasyPassword) request.", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/UpdateUserEasyPassword" - } - ], - "description": "The update user easy password request body." - } - }, - "text/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/UpdateUserEasyPassword" - } - ], - "description": "The update user easy password request body." - } - }, - "application/*+json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/UpdateUserEasyPassword" - } - ], - "description": "The update user easy password request body." - } - } - }, - "required": true - }, "responses": { "204": { - "description": "Password successfully reset." - }, - "403": { - "description": "User is not allowed to update the password.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - }, - "application/json; profile=\"CamelCase\"": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - }, - "application/json; profile=\"PascalCase\"": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } + "description": "User deleted." }, "404": { "description": "User not found.", @@ -40112,124 +39360,15 @@ }, "401": { "description": "Unauthorized" - } - }, - "security": [ - { - "CustomAuthentication": [ - "DefaultAuthorization" - ] - } - ] - } - }, - "/Users/{userId}/Password": { - "post": { - "tags": [ - "User" - ], - "summary": "Updates a user's password.", - "operationId": "UpdateUserPassword", - "parameters": [ - { - "name": "userId", - "in": "path", - "description": "The user id.", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - } - ], - "requestBody": { - "description": "The M:Jellyfin.Api.Controllers.UserController.UpdateUserPassword(System.Guid,Jellyfin.Api.Models.UserDtos.UpdateUserPassword) request.", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/UpdateUserPassword" - } - ], - "description": "The update user password request body." - } - }, - "text/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/UpdateUserPassword" - } - ], - "description": "The update user password request body." - } - }, - "application/*+json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/UpdateUserPassword" - } - ], - "description": "The update user password request body." - } - } - }, - "required": true - }, - "responses": { - "204": { - "description": "Password successfully reset." }, "403": { - "description": "User is not allowed to update the password.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - }, - "application/json; profile=\"CamelCase\"": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - }, - "application/json; profile=\"PascalCase\"": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "404": { - "description": "User not found.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - }, - "application/json; profile=\"CamelCase\"": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - }, - "application/json; profile=\"PascalCase\"": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "401": { - "description": "Unauthorized" + "description": "Forbidden" } }, "security": [ { "CustomAuthentication": [ - "DefaultAuthorization" + "RequiresElevation" ] } ] @@ -40481,6 +39620,97 @@ } } }, + "/Users/Configuration": { + "post": { + "tags": [ + "User" + ], + "summary": "Updates a user configuration.", + "operationId": "UpdateUserConfiguration", + "parameters": [ + { + "name": "userId", + "in": "query", + "description": "The user id.", + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "description": "The new user configuration.", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/UserConfiguration" + } + ], + "description": "Class UserConfiguration." + } + }, + "text/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/UserConfiguration" + } + ], + "description": "Class UserConfiguration." + } + }, + "application/*+json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/UserConfiguration" + } + ], + "description": "Class UserConfiguration." + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "User configuration updated." + }, + "403": { + "description": "User configuration update forbidden.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "Unauthorized" + } + }, + "security": [ + { + "CustomAuthentication": [ + "DefaultAuthorization" + ] + } + ] + } + }, "/Users/ForgotPassword": { "post": { "tags": [ @@ -40759,6 +39989,117 @@ ] } }, + "/Users/Password": { + "post": { + "tags": [ + "User" + ], + "summary": "Updates a user's password.", + "operationId": "UpdateUserPassword", + "parameters": [ + { + "name": "userId", + "in": "query", + "description": "The user id.", + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "description": "The M:Jellyfin.Api.Controllers.UserController.UpdateUserPassword(System.Nullable{System.Guid},Jellyfin.Api.Models.UserDtos.UpdateUserPassword) request.", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/UpdateUserPassword" + } + ], + "description": "The update user password request body." + } + }, + "text/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/UpdateUserPassword" + } + ], + "description": "The update user password request body." + } + }, + "application/*+json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/UpdateUserPassword" + } + ], + "description": "The update user password request body." + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "Password successfully reset." + }, + "403": { + "description": "User is not allowed to update the password.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "404": { + "description": "User not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "Unauthorized" + } + }, + "security": [ + { + "CustomAuthentication": [ + "DefaultAuthorization" + ] + } + ] + } + }, "/Users/Public": { "get": { "tags": [ @@ -40799,203 +40140,7 @@ } } }, - "/Users/{userId}/FavoriteItems/{itemId}": { - "post": { - "tags": [ - "UserLibrary" - ], - "summary": "Marks an item as a favorite.", - "operationId": "MarkFavoriteItem", - "parameters": [ - { - "name": "userId", - "in": "path", - "description": "User id.", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - }, - { - "name": "itemId", - "in": "path", - "description": "Item id.", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - } - ], - "responses": { - "200": { - "description": "Item marked as favorite.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UserItemDataDto" - } - }, - "application/json; profile=\"CamelCase\"": { - "schema": { - "$ref": "#/components/schemas/UserItemDataDto" - } - }, - "application/json; profile=\"PascalCase\"": { - "schema": { - "$ref": "#/components/schemas/UserItemDataDto" - } - } - } - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - }, - "security": [ - { - "CustomAuthentication": [ - "DefaultAuthorization" - ] - } - ] - }, - "delete": { - "tags": [ - "UserLibrary" - ], - "summary": "Unmarks item as a favorite.", - "operationId": "UnmarkFavoriteItem", - "parameters": [ - { - "name": "userId", - "in": "path", - "description": "User id.", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - }, - { - "name": "itemId", - "in": "path", - "description": "Item id.", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - } - ], - "responses": { - "200": { - "description": "Item unmarked as favorite.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UserItemDataDto" - } - }, - "application/json; profile=\"CamelCase\"": { - "schema": { - "$ref": "#/components/schemas/UserItemDataDto" - } - }, - "application/json; profile=\"PascalCase\"": { - "schema": { - "$ref": "#/components/schemas/UserItemDataDto" - } - } - } - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - }, - "security": [ - { - "CustomAuthentication": [ - "DefaultAuthorization" - ] - } - ] - } - }, - "/Users/{userId}/Items/{itemId}": { - "get": { - "tags": [ - "UserLibrary" - ], - "summary": "Gets an item from a user's library.", - "operationId": "GetItem", - "parameters": [ - { - "name": "userId", - "in": "path", - "description": "User id.", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - }, - { - "name": "itemId", - "in": "path", - "description": "Item id.", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - } - ], - "responses": { - "200": { - "description": "Item returned.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/BaseItemDto" - } - }, - "application/json; profile=\"CamelCase\"": { - "schema": { - "$ref": "#/components/schemas/BaseItemDto" - } - }, - "application/json; profile=\"PascalCase\"": { - "schema": { - "$ref": "#/components/schemas/BaseItemDto" - } - } - } - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - }, - "security": [ - { - "CustomAuthentication": [ - "DefaultAuthorization" - ] - } - ] - } - }, - "/Users/{userId}/Items/{itemId}/Intros": { + "/Items/{itemId}/Intros": { "get": { "tags": [ "UserLibrary" @@ -41005,9 +40150,8 @@ "parameters": [ { "name": "userId", - "in": "path", + "in": "query", "description": "User id.", - "required": true, "schema": { "type": "string", "format": "uuid" @@ -41061,7 +40205,7 @@ ] } }, - "/Users/{userId}/Items/{itemId}/LocalTrailers": { + "/Items/{itemId}/LocalTrailers": { "get": { "tags": [ "UserLibrary" @@ -41071,9 +40215,8 @@ "parameters": [ { "name": "userId", - "in": "path", + "in": "query", "description": "User id.", - "required": true, "schema": { "type": "string", "format": "uuid" @@ -41136,145 +40279,7 @@ ] } }, - "/Users/{userId}/Items/{itemId}/Rating": { - "delete": { - "tags": [ - "UserLibrary" - ], - "summary": "Deletes a user's saved personal rating for an item.", - "operationId": "DeleteUserItemRating", - "parameters": [ - { - "name": "userId", - "in": "path", - "description": "User id.", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - }, - { - "name": "itemId", - "in": "path", - "description": "Item id.", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - } - ], - "responses": { - "200": { - "description": "Personal rating removed.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UserItemDataDto" - } - }, - "application/json; profile=\"CamelCase\"": { - "schema": { - "$ref": "#/components/schemas/UserItemDataDto" - } - }, - "application/json; profile=\"PascalCase\"": { - "schema": { - "$ref": "#/components/schemas/UserItemDataDto" - } - } - } - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - }, - "security": [ - { - "CustomAuthentication": [ - "DefaultAuthorization" - ] - } - ] - }, - "post": { - "tags": [ - "UserLibrary" - ], - "summary": "Updates a user's rating for an item.", - "operationId": "UpdateUserItemRating", - "parameters": [ - { - "name": "userId", - "in": "path", - "description": "User id.", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - }, - { - "name": "itemId", - "in": "path", - "description": "Item id.", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - }, - { - "name": "likes", - "in": "query", - "description": "Whether this M:Jellyfin.Api.Controllers.UserLibraryController.UpdateUserItemRating(System.Guid,System.Guid,System.Nullable{System.Boolean}) is likes.", - "schema": { - "type": "boolean" - } - } - ], - "responses": { - "200": { - "description": "Item rating updated.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UserItemDataDto" - } - }, - "application/json; profile=\"CamelCase\"": { - "schema": { - "$ref": "#/components/schemas/UserItemDataDto" - } - }, - "application/json; profile=\"PascalCase\"": { - "schema": { - "$ref": "#/components/schemas/UserItemDataDto" - } - } - } - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - }, - "security": [ - { - "CustomAuthentication": [ - "DefaultAuthorization" - ] - } - ] - } - }, - "/Users/{userId}/Items/{itemId}/SpecialFeatures": { + "/Items/{itemId}/SpecialFeatures": { "get": { "tags": [ "UserLibrary" @@ -41284,9 +40289,8 @@ "parameters": [ { "name": "userId", - "in": "path", + "in": "query", "description": "User id.", - "required": true, "schema": { "type": "string", "format": "uuid" @@ -41349,7 +40353,7 @@ ] } }, - "/Users/{userId}/Items/Latest": { + "/Items/Latest": { "get": { "tags": [ "UserLibrary" @@ -41359,9 +40363,8 @@ "parameters": [ { "name": "userId", - "in": "path", + "in": "query", "description": "User id.", - "required": true, "schema": { "type": "string", "format": "uuid" @@ -41508,7 +40511,7 @@ ] } }, - "/Users/{userId}/Items/Root": { + "/Items/Root": { "get": { "tags": [ "UserLibrary" @@ -41518,9 +40521,8 @@ "parameters": [ { "name": "userId", - "in": "path", + "in": "query", "description": "User id.", - "required": true, "schema": { "type": "string", "format": "uuid" @@ -41564,7 +40566,354 @@ ] } }, - "/Users/{userId}/GroupingOptions": { + "/UserFavoriteItems/{itemId}": { + "post": { + "tags": [ + "UserLibrary" + ], + "summary": "Marks an item as a favorite.", + "operationId": "MarkFavoriteItem", + "parameters": [ + { + "name": "userId", + "in": "query", + "description": "User id.", + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "itemId", + "in": "path", + "description": "Item id.", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "Item marked as favorite.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserItemDataDto" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/UserItemDataDto" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/UserItemDataDto" + } + } + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + } + }, + "security": [ + { + "CustomAuthentication": [ + "DefaultAuthorization" + ] + } + ] + }, + "delete": { + "tags": [ + "UserLibrary" + ], + "summary": "Unmarks item as a favorite.", + "operationId": "UnmarkFavoriteItem", + "parameters": [ + { + "name": "userId", + "in": "query", + "description": "User id.", + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "itemId", + "in": "path", + "description": "Item id.", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "Item unmarked as favorite.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserItemDataDto" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/UserItemDataDto" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/UserItemDataDto" + } + } + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + } + }, + "security": [ + { + "CustomAuthentication": [ + "DefaultAuthorization" + ] + } + ] + } + }, + "/UserItems/{itemId}/Rating": { + "delete": { + "tags": [ + "UserLibrary" + ], + "summary": "Deletes a user's saved personal rating for an item.", + "operationId": "DeleteUserItemRating", + "parameters": [ + { + "name": "userId", + "in": "query", + "description": "User id.", + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "itemId", + "in": "path", + "description": "Item id.", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "Personal rating removed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserItemDataDto" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/UserItemDataDto" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/UserItemDataDto" + } + } + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + } + }, + "security": [ + { + "CustomAuthentication": [ + "DefaultAuthorization" + ] + } + ] + }, + "post": { + "tags": [ + "UserLibrary" + ], + "summary": "Updates a user's rating for an item.", + "operationId": "UpdateUserItemRating", + "parameters": [ + { + "name": "userId", + "in": "query", + "description": "User id.", + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "itemId", + "in": "path", + "description": "Item id.", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "likes", + "in": "query", + "description": "Whether this M:Jellyfin.Api.Controllers.UserLibraryController.UpdateUserItemRating(System.Nullable{System.Guid},System.Guid,System.Nullable{System.Boolean}) is likes.", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "description": "Item rating updated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserItemDataDto" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/UserItemDataDto" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/UserItemDataDto" + } + } + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + } + }, + "security": [ + { + "CustomAuthentication": [ + "DefaultAuthorization" + ] + } + ] + } + }, + "/UserViews": { + "get": { + "tags": [ + "UserViews" + ], + "summary": "Get user views.", + "operationId": "GetUserViews", + "parameters": [ + { + "name": "userId", + "in": "query", + "description": "User id.", + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "includeExternalContent", + "in": "query", + "description": "Whether or not to include external views such as channels or live tv.", + "schema": { + "type": "boolean" + } + }, + { + "name": "presetViews", + "in": "query", + "description": "Preset views.", + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CollectionType" + } + } + }, + { + "name": "includeHidden", + "in": "query", + "description": "Whether or not to include hidden content.", + "schema": { + "type": "boolean", + "default": false + } + } + ], + "responses": { + "200": { + "description": "User views returned.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BaseItemDtoQueryResult" + } + }, + "application/json; profile=\"CamelCase\"": { + "schema": { + "$ref": "#/components/schemas/BaseItemDtoQueryResult" + } + }, + "application/json; profile=\"PascalCase\"": { + "schema": { + "$ref": "#/components/schemas/BaseItemDtoQueryResult" + } + } + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + } + }, + "security": [ + { + "CustomAuthentication": [ + "DefaultAuthorization" + ] + } + ] + } + }, + "/UserViews/GroupingOptions": { "get": { "tags": [ "UserViews" @@ -41574,9 +40923,8 @@ "parameters": [ { "name": "userId", - "in": "path", + "in": "query", "description": "User id.", - "required": true, "schema": { "type": "string", "format": "uuid" @@ -41649,90 +40997,6 @@ ] } }, - "/Users/{userId}/Views": { - "get": { - "tags": [ - "UserViews" - ], - "summary": "Get user views.", - "operationId": "GetUserViews", - "parameters": [ - { - "name": "userId", - "in": "path", - "description": "User id.", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - }, - { - "name": "includeExternalContent", - "in": "query", - "description": "Whether or not to include external views such as channels or live tv.", - "schema": { - "type": "boolean" - } - }, - { - "name": "presetViews", - "in": "query", - "description": "Preset views.", - "schema": { - "type": "array", - "items": { - "type": "string" - } - } - }, - { - "name": "includeHidden", - "in": "query", - "description": "Whether or not to include hidden content.", - "schema": { - "type": "boolean", - "default": false - } - } - ], - "responses": { - "200": { - "description": "User views returned.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/BaseItemDtoQueryResult" - } - }, - "application/json; profile=\"CamelCase\"": { - "schema": { - "$ref": "#/components/schemas/BaseItemDtoQueryResult" - } - }, - "application/json; profile=\"PascalCase\"": { - "schema": { - "$ref": "#/components/schemas/BaseItemDtoQueryResult" - } - } - } - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - }, - "security": [ - { - "CustomAuthentication": [ - "DefaultAuthorization" - ] - } - ] - } - }, "/Videos/{videoId}/{mediaSourceId}/Attachments/{index}": { "get": { "tags": [ @@ -41985,6 +41249,7 @@ "name": "deviceProfileId", "in": "query", "description": "Optional. The dlna device profile id to utilize.", + "deprecated": true, "schema": { "type": "string" } @@ -42236,6 +41501,13 @@ "in": "query", "description": "Optional. Specify the subtitle delivery method.", "schema": { + "enum": [ + "Encode", + "Embed", + "External", + "Hls", + "Drop" + ], "allOf": [ { "$ref": "#/components/schemas/SubtitleDeliveryMethod" @@ -42368,6 +41640,10 @@ "in": "query", "description": "Optional. The MediaBrowser.Model.Dlna.EncodingContext.", "schema": { + "enum": [ + "Streaming", + "Static" + ], "allOf": [ { "$ref": "#/components/schemas/EncodingContext" @@ -42456,6 +41732,7 @@ "name": "deviceProfileId", "in": "query", "description": "Optional. The dlna device profile id to utilize.", + "deprecated": true, "schema": { "type": "string" } @@ -42707,6 +41984,13 @@ "in": "query", "description": "Optional. Specify the subtitle delivery method.", "schema": { + "enum": [ + "Encode", + "Embed", + "External", + "Hls", + "Drop" + ], "allOf": [ { "$ref": "#/components/schemas/SubtitleDeliveryMethod" @@ -42839,6 +42123,10 @@ "in": "query", "description": "Optional. The MediaBrowser.Model.Dlna.EncodingContext.", "schema": { + "enum": [ + "Streaming", + "Static" + ], "allOf": [ { "$ref": "#/components/schemas/EncodingContext" @@ -43180,6 +42468,13 @@ "in": "query", "description": "Optional. Specify the subtitle delivery method.", "schema": { + "enum": [ + "Encode", + "Embed", + "External", + "Hls", + "Drop" + ], "allOf": [ { "$ref": "#/components/schemas/SubtitleDeliveryMethod" @@ -43312,6 +42607,10 @@ "in": "query", "description": "Optional. The MediaBrowser.Model.Dlna.EncodingContext.", "schema": { + "enum": [ + "Streaming", + "Static" + ], "allOf": [ { "$ref": "#/components/schemas/EncodingContext" @@ -43651,6 +42950,13 @@ "in": "query", "description": "Optional. Specify the subtitle delivery method.", "schema": { + "enum": [ + "Encode", + "Embed", + "External", + "Hls", + "Drop" + ], "allOf": [ { "$ref": "#/components/schemas/SubtitleDeliveryMethod" @@ -43783,6 +43089,10 @@ "in": "query", "description": "Optional. The MediaBrowser.Model.Dlna.EncodingContext.", "schema": { + "enum": [ + "Streaming", + "Static" + ], "allOf": [ { "$ref": "#/components/schemas/EncodingContext" @@ -43966,7 +43276,7 @@ "schema": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/MediaType" } } }, @@ -43977,7 +43287,7 @@ "schema": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/ItemSortBy" } } }, @@ -44177,6 +43487,18 @@ "format": "uuid" }, "DayOfWeek": { + "enum": [ + "Sunday", + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday", + "Everyday", + "Weekday", + "Weekend" + ], "allOf": [ { "$ref": "#/components/schemas/DynamicDayOfWeek" @@ -44246,6 +43568,15 @@ "deprecated": true }, "Severity": { + "enum": [ + "Trace", + "Debug", + "Information", + "Warning", + "Error", + "Critical", + "None" + ], "allOf": [ { "$ref": "#/components/schemas/LogLevel" @@ -44257,6 +43588,72 @@ "additionalProperties": false, "description": "An activity log entry." }, + "ActivityLogEntryMessage": { + "type": "object", + "properties": { + "Data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ActivityLogEntry" + }, + "description": "Gets or sets the data.", + "nullable": true + }, + "MessageId": { + "type": "string", + "description": "Gets or sets the message id.", + "format": "uuid" + }, + "MessageType": { + "enum": [ + "ForceKeepAlive", + "GeneralCommand", + "UserDataChanged", + "Sessions", + "Play", + "SyncPlayCommand", + "SyncPlayGroupUpdate", + "Playstate", + "RestartRequired", + "ServerShuttingDown", + "ServerRestarting", + "LibraryChanged", + "UserDeleted", + "UserUpdated", + "SeriesTimerCreated", + "TimerCreated", + "SeriesTimerCancelled", + "TimerCancelled", + "RefreshProgress", + "ScheduledTaskEnded", + "PackageInstallationCancelled", + "PackageInstallationFailed", + "PackageInstallationCompleted", + "PackageInstalling", + "PackageUninstalled", + "ActivityLogEntry", + "ScheduledTasksInfo", + "ActivityLogEntryStart", + "ActivityLogEntryStop", + "SessionsStart", + "SessionsStop", + "ScheduledTasksInfoStart", + "ScheduledTasksInfoStop", + "KeepAlive" + ], + "allOf": [ + { + "$ref": "#/components/schemas/SessionMessageType" + } + ], + "description": "The different kinds of messages that are used in the WebSocket api.", + "default": "ActivityLogEntry", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "Activity log created message." + }, "ActivityLogEntryQueryResult": { "type": "object", "properties": { @@ -44281,6 +43678,117 @@ }, "additionalProperties": false }, + "ActivityLogEntryStartMessage": { + "type": "object", + "properties": { + "Data": { + "type": "string", + "description": "Gets or sets the data.", + "nullable": true + }, + "MessageType": { + "enum": [ + "ForceKeepAlive", + "GeneralCommand", + "UserDataChanged", + "Sessions", + "Play", + "SyncPlayCommand", + "SyncPlayGroupUpdate", + "Playstate", + "RestartRequired", + "ServerShuttingDown", + "ServerRestarting", + "LibraryChanged", + "UserDeleted", + "UserUpdated", + "SeriesTimerCreated", + "TimerCreated", + "SeriesTimerCancelled", + "TimerCancelled", + "RefreshProgress", + "ScheduledTaskEnded", + "PackageInstallationCancelled", + "PackageInstallationFailed", + "PackageInstallationCompleted", + "PackageInstalling", + "PackageUninstalled", + "ActivityLogEntry", + "ScheduledTasksInfo", + "ActivityLogEntryStart", + "ActivityLogEntryStop", + "SessionsStart", + "SessionsStop", + "ScheduledTasksInfoStart", + "ScheduledTasksInfoStop", + "KeepAlive" + ], + "allOf": [ + { + "$ref": "#/components/schemas/SessionMessageType" + } + ], + "description": "The different kinds of messages that are used in the WebSocket api.", + "default": "ActivityLogEntryStart", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "Activity log entry start message.\r\nData is the timing data encoded as \"$initialDelay,$interval\" in ms." + }, + "ActivityLogEntryStopMessage": { + "type": "object", + "properties": { + "MessageType": { + "enum": [ + "ForceKeepAlive", + "GeneralCommand", + "UserDataChanged", + "Sessions", + "Play", + "SyncPlayCommand", + "SyncPlayGroupUpdate", + "Playstate", + "RestartRequired", + "ServerShuttingDown", + "ServerRestarting", + "LibraryChanged", + "UserDeleted", + "UserUpdated", + "SeriesTimerCreated", + "TimerCreated", + "SeriesTimerCancelled", + "TimerCancelled", + "RefreshProgress", + "ScheduledTaskEnded", + "PackageInstallationCancelled", + "PackageInstallationFailed", + "PackageInstallationCompleted", + "PackageInstalling", + "PackageUninstalled", + "ActivityLogEntry", + "ScheduledTasksInfo", + "ActivityLogEntryStart", + "ActivityLogEntryStop", + "SessionsStart", + "SessionsStop", + "ScheduledTasksInfoStart", + "ScheduledTasksInfoStop", + "KeepAlive" + ], + "allOf": [ + { + "$ref": "#/components/schemas/SessionMessageType" + } + ], + "description": "The different kinds of messages that are used in the WebSocket api.", + "default": "ActivityLogEntryStop", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "Activity log entry stop message." + }, "AddVirtualFolderDto": { "type": "object", "properties": { @@ -44297,37 +43805,6 @@ "additionalProperties": false, "description": "Add virtual folder dto." }, - "AdminNotificationDto": { - "type": "object", - "properties": { - "Name": { - "type": "string", - "description": "Gets or sets the notification name.", - "nullable": true - }, - "Description": { - "type": "string", - "description": "Gets or sets the notification description.", - "nullable": true - }, - "NotificationLevel": { - "allOf": [ - { - "$ref": "#/components/schemas/NotificationLevel" - } - ], - "description": "Gets or sets the notification level.", - "nullable": true - }, - "Url": { - "type": "string", - "description": "Gets or sets the notification url.", - "nullable": true - } - }, - "additionalProperties": false, - "description": "The admin notification dto." - }, "AlbumInfo": { "type": "object", "properties": { @@ -44473,17 +43950,6 @@ }, "additionalProperties": false }, - "Architecture": { - "enum": [ - "X86", - "X64", - "Arm", - "Arm64", - "Wasm", - "S390x" - ], - "type": "string" - }, "ArtistInfo": { "type": "object", "properties": { @@ -44581,6 +44047,15 @@ }, "additionalProperties": false }, + "AudioSpatialFormat": { + "enum": [ + "None", + "DolbyAtmos", + "DTSX" + ], + "type": "string", + "description": "An enum representing formats of spatial audio." + }, "AuthenticateUserByName": { "type": "object", "properties": { @@ -44593,12 +44068,6 @@ "type": "string", "description": "Gets or sets the plain text password.", "nullable": true - }, - "Password": { - "type": "string", - "description": "Gets or sets the sha1-hashed password.", - "nullable": true, - "deprecated": true } }, "additionalProperties": false, @@ -44724,65 +44193,6 @@ }, "additionalProperties": false }, - "BaseItem": { - "type": "object", - "properties": { - "Size": { - "type": "integer", - "format": "int64", - "nullable": true - }, - "Container": { - "type": "string", - "nullable": true - }, - "IsHD": { - "type": "boolean", - "readOnly": true - }, - "IsShortcut": { - "type": "boolean" - }, - "ShortcutPath": { - "type": "string", - "nullable": true - }, - "Width": { - "type": "integer", - "format": "int32" - }, - "Height": { - "type": "integer", - "format": "int32" - }, - "ExtraIds": { - "type": "array", - "items": { - "type": "string", - "format": "uuid" - }, - "nullable": true - }, - "DateLastSaved": { - "type": "string", - "format": "date-time" - }, - "RemoteTrailers": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MediaUrl" - }, - "description": "Gets or sets the remote trailers.", - "nullable": true - }, - "SupportsExternalTransfer": { - "type": "boolean", - "readOnly": true - } - }, - "additionalProperties": false, - "description": "Class BaseItem." - }, "BaseItemDto": { "type": "object", "properties": { @@ -44832,7 +44242,25 @@ "nullable": true }, "ExtraType": { - "type": "string", + "enum": [ + "Unknown", + "Clip", + "Trailer", + "BehindTheScenes", + "DeletedScene", + "Interview", + "Scene", + "Sample", + "ThemeSong", + "ThemeVideo", + "Featurette", + "Short" + ], + "allOf": [ + { + "$ref": "#/components/schemas/ExtraType" + } + ], "nullable": true }, "AirsBeforeSeasonNumber": { @@ -44858,6 +44286,10 @@ "type": "boolean", "nullable": true }, + "HasLyrics": { + "type": "boolean", + "nullable": true + }, "HasSubtitles": { "type": "boolean", "nullable": true @@ -44870,11 +44302,6 @@ "type": "string", "nullable": true }, - "SupportsSync": { - "type": "boolean", - "description": "Gets or sets a value indicating whether [supports synchronize].", - "nullable": true - }, "Container": { "type": "string", "nullable": true @@ -44889,6 +44316,13 @@ "nullable": true }, "Video3DFormat": { + "enum": [ + "HalfSideBySide", + "FullSideBySide", + "FullTopAndBottom", + "HalfTopAndBottom", + "MVC" + ], "allOf": [ { "$ref": "#/components/schemas/Video3DFormat" @@ -45001,6 +44435,10 @@ "nullable": true }, "PlayAccess": { + "enum": [ + "Full", + "None" + ], "allOf": [ { "$ref": "#/components/schemas/PlayAccess" @@ -45086,6 +44524,45 @@ "nullable": true }, "Type": { + "enum": [ + "AggregateFolder", + "Audio", + "AudioBook", + "BasePluginFolder", + "Book", + "BoxSet", + "Channel", + "ChannelFolderItem", + "CollectionFolder", + "Episode", + "Folder", + "Genre", + "ManualPlaylistsFolder", + "Movie", + "LiveTvChannel", + "LiveTvProgram", + "MusicAlbum", + "MusicArtist", + "MusicGenre", + "MusicVideo", + "Person", + "Photo", + "PhotoAlbum", + "Playlist", + "PlaylistsFolder", + "Program", + "Recording", + "Season", + "Series", + "Studio", + "Trailer", + "TvChannel", + "TvProgram", + "UserRootFolder", + "UserView", + "Video", + "Year" + ], "allOf": [ { "$ref": "#/components/schemas/BaseItemKind" @@ -45118,13 +44595,13 @@ }, "ParentLogoItemId": { "type": "string", - "description": "Gets or sets wether the item has a logo, this will hold the Id of the Parent that has one.", + "description": "Gets or sets whether the item has a logo, this will hold the Id of the Parent that has one.", "format": "uuid", "nullable": true }, "ParentBackdropItemId": { "type": "string", - "description": "Gets or sets wether the item has any backdrops, this will hold the Id of the Parent that has one.", + "description": "Gets or sets whether the item has any backdrops, this will hold the Id of the Parent that has one.", "format": "uuid", "nullable": true }, @@ -45245,7 +44722,26 @@ "nullable": true }, "CollectionType": { - "type": "string", + "enum": [ + "unknown", + "movies", + "tvshows", + "music", + "musicvideos", + "trailers", + "homevideos", + "boxsets", + "books", + "photos", + "livetv", + "playlists", + "folders" + ], + "allOf": [ + { + "$ref": "#/components/schemas/CollectionType" + } + ], "description": "Gets or sets the type of the collection.", "nullable": true }, @@ -45297,6 +44793,12 @@ "nullable": true }, "VideoType": { + "enum": [ + "VideoFile", + "Iso", + "Dvd", + "BluRay" + ], "allOf": [ { "$ref": "#/components/schemas/VideoType" @@ -45347,7 +44849,7 @@ }, "ParentArtItemId": { "type": "string", - "description": "Gets or sets wether the item has fan art, this will hold the Id of the Parent that has one.", + "description": "Gets or sets whether the item has fan art, this will hold the Id of the Parent that has one.", "format": "uuid", "nullable": true }, @@ -45480,7 +44982,24 @@ "description": "Gets or sets the chapters.", "nullable": true }, + "Trickplay": { + "type": "object", + "additionalProperties": { + "type": "object", + "additionalProperties": { + "$ref": "#/components/schemas/TrickplayInfo" + } + }, + "description": "Gets or sets the trickplay manifest.", + "nullable": true + }, "LocationType": { + "enum": [ + "FileSystem", + "Remote", + "Virtual", + "Offline" + ], "allOf": [ { "$ref": "#/components/schemas/LocationType" @@ -45490,6 +45009,10 @@ "nullable": true }, "IsoType": { + "enum": [ + "Dvd", + "BluRay" + ], "allOf": [ { "$ref": "#/components/schemas/IsoType" @@ -45499,9 +45022,19 @@ "nullable": true }, "MediaType": { - "type": "string", - "description": "Gets or sets the type of the media.", - "nullable": true + "enum": [ + "Unknown", + "Video", + "Audio", + "Photo", + "Book" + ], + "allOf": [ + { + "$ref": "#/components/schemas/MediaType" + } + ], + "description": "Gets or sets the type of the media." }, "EndDate": { "type": "string", @@ -45607,6 +45140,16 @@ "nullable": true }, "ImageOrientation": { + "enum": [ + "TopLeft", + "TopRight", + "BottomRight", + "BottomLeft", + "LeftTop", + "RightTop", + "RightBottom", + "LeftBottom" + ], "allOf": [ { "$ref": "#/components/schemas/ImageOrientation" @@ -45682,6 +45225,10 @@ "nullable": true }, "ChannelType": { + "enum": [ + "TV", + "Radio" + ], "allOf": [ { "$ref": "#/components/schemas/ChannelType" @@ -45691,6 +45238,14 @@ "nullable": true }, "Audio": { + "enum": [ + "Mono", + "Stereo", + "Dolby", + "DolbyDigital", + "Thx", + "Atmos" + ], "allOf": [ { "$ref": "#/components/schemas/ProgramAudio" @@ -45739,6 +45294,12 @@ "description": "Gets or sets the timer identifier.", "nullable": true }, + "NormalizationGain": { + "type": "number", + "description": "Gets or sets the gain required for audio normalization.", + "format": "float", + "nullable": true + }, "CurrentProgram": { "allOf": [ { @@ -45838,9 +45399,39 @@ "nullable": true }, "Type": { - "type": "string", - "description": "Gets or sets the type.", - "nullable": true + "enum": [ + "Unknown", + "Actor", + "Director", + "Composer", + "Writer", + "GuestStar", + "Producer", + "Conductor", + "Lyricist", + "Arranger", + "Engineer", + "Mixer", + "Remixer", + "Creator", + "Artist", + "AlbumArtist", + "Author", + "Illustrator", + "Penciller", + "Inker", + "Colorist", + "Letterer", + "CoverArtist", + "Editor", + "Translator" + ], + "allOf": [ + { + "$ref": "#/components/schemas/PersonKind" + } + ], + "description": "Gets or sets the type." }, "PrimaryImageTag": { "type": "string", @@ -46174,6 +45765,21 @@ "additionalProperties": false, "description": "Class BufferRequestDto." }, + "CastReceiverApplication": { + "type": "object", + "properties": { + "Id": { + "type": "string", + "description": "Gets or sets the cast receiver application id." + }, + "Name": { + "type": "string", + "description": "Gets or sets the cast receiver application name." + } + }, + "additionalProperties": false, + "description": "The cast receiver application model." + }, "ChannelFeatures": { "type": "object", "properties": { @@ -46352,7 +45958,7 @@ "PlayableMediaTypes": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/MediaType" }, "nullable": true }, @@ -46366,19 +45972,9 @@ "SupportsMediaControl": { "type": "boolean" }, - "SupportsContentUploading": { - "type": "boolean" - }, - "MessageCallbackUrl": { - "type": "string", - "nullable": true - }, "SupportsPersistentIdentifier": { "type": "boolean" }, - "SupportsSync": { - "type": "boolean" - }, "DeviceProfile": { "allOf": [ { @@ -46395,6 +45991,18 @@ "IconUrl": { "type": "string", "nullable": true + }, + "SupportsContentUploading": { + "type": "boolean", + "default": false, + "nullable": true, + "deprecated": true + }, + "SupportsSync": { + "type": "boolean", + "default": false, + "nullable": true, + "deprecated": true } }, "additionalProperties": false @@ -46405,7 +46013,7 @@ "PlayableMediaTypes": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/MediaType" }, "description": "Gets or sets the list of playable media types." }, @@ -46420,23 +46028,10 @@ "type": "boolean", "description": "Gets or sets a value indicating whether session supports media control." }, - "SupportsContentUploading": { - "type": "boolean", - "description": "Gets or sets a value indicating whether session supports content uploading." - }, - "MessageCallbackUrl": { - "type": "string", - "description": "Gets or sets the message callback url.", - "nullable": true - }, "SupportsPersistentIdentifier": { "type": "boolean", "description": "Gets or sets a value indicating whether session supports a persistent identifier." }, - "SupportsSync": { - "type": "boolean", - "description": "Gets or sets a value indicating whether session supports sync." - }, "DeviceProfile": { "allOf": [ { @@ -46455,6 +46050,18 @@ "type": "string", "description": "Gets or sets the icon url.", "nullable": true + }, + "SupportsContentUploading": { + "type": "boolean", + "default": false, + "nullable": true, + "deprecated": true + }, + "SupportsSync": { + "type": "boolean", + "default": false, + "nullable": true, + "deprecated": true } }, "additionalProperties": false, @@ -46475,6 +46082,11 @@ "type": "object", "properties": { "Type": { + "enum": [ + "Video", + "VideoAudio", + "Audio" + ], "allOf": [ { "$ref": "#/components/schemas/CodecType" @@ -46524,18 +46136,38 @@ }, "additionalProperties": false }, + "CollectionType": { + "enum": [ + "unknown", + "movies", + "tvshows", + "music", + "musicvideos", + "trailers", + "homevideos", + "boxsets", + "books", + "photos", + "livetv", + "playlists", + "folders" + ], + "type": "string", + "description": "Collection type." + }, "CollectionTypeOptions": { "enum": [ - "Movies", - "TvShows", - "Music", - "MusicVideos", - "HomeVideos", - "BoxSets", - "Books", - "Mixed" + "movies", + "tvshows", + "music", + "musicvideos", + "homevideos", + "boxsets", + "books", + "mixed" ], - "type": "string" + "type": "string", + "description": "The collection type options." }, "ConfigImageTypes": { "type": "object", @@ -46626,6 +46258,13 @@ "type": "object", "properties": { "Type": { + "enum": [ + "Audio", + "Video", + "Photo", + "Subtitle", + "Lyric" + ], "allOf": [ { "$ref": "#/components/schemas/DlnaProfileType" @@ -46636,8 +46275,7 @@ "type": "array", "items": { "$ref": "#/components/schemas/ProfileCondition" - }, - "nullable": true + } }, "Container": { "type": "string" @@ -46645,25 +46283,6 @@ }, "additionalProperties": false }, - "ControlResponse": { - "type": "object", - "properties": { - "Headers": { - "type": "object", - "additionalProperties": { - "type": "string" - }, - "readOnly": true - }, - "Xml": { - "type": "string" - }, - "IsSuccessful": { - "type": "boolean" - } - }, - "additionalProperties": false - }, "CountryInfo": { "type": "object", "properties": { @@ -46696,8 +46315,7 @@ "properties": { "Name": { "type": "string", - "description": "Gets or sets the name of the new playlist.", - "nullable": true + "description": "Gets or sets the name of the new playlist." }, "Ids": { "type": "array", @@ -46714,21 +46332,45 @@ "nullable": true }, "MediaType": { - "type": "string", + "enum": [ + "Unknown", + "Video", + "Audio", + "Photo", + "Book" + ], + "allOf": [ + { + "$ref": "#/components/schemas/MediaType" + } + ], "description": "Gets or sets the media type.", "nullable": true + }, + "Users": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PlaylistUserPermissions" + }, + "description": "Gets or sets the playlist users." + }, + "IsPublic": { + "type": "boolean", + "description": "Gets or sets a value indicating whether the playlist is public." } }, "additionalProperties": false, "description": "Create new playlist dto." }, "CreateUserByName": { + "required": [ + "Name" + ], "type": "object", "properties": { "Name": { "type": "string", - "description": "Gets or sets the username.", - "nullable": true + "description": "Gets or sets the username." }, "Password": { "type": "string", @@ -46802,51 +46444,6 @@ "additionalProperties": false, "description": "Default directory browser info." }, - "DeviceIdentification": { - "type": "object", - "properties": { - "FriendlyName": { - "type": "string", - "description": "Gets or sets the name of the friendly." - }, - "ModelNumber": { - "type": "string", - "description": "Gets or sets the model number." - }, - "SerialNumber": { - "type": "string", - "description": "Gets or sets the serial number." - }, - "ModelName": { - "type": "string", - "description": "Gets or sets the name of the model." - }, - "ModelDescription": { - "type": "string", - "description": "Gets or sets the model description." - }, - "ModelUrl": { - "type": "string", - "description": "Gets or sets the model URL." - }, - "Manufacturer": { - "type": "string", - "description": "Gets or sets the manufacturer." - }, - "ManufacturerUrl": { - "type": "string", - "description": "Gets or sets the manufacturer URL." - }, - "Headers": { - "type": "array", - "items": { - "$ref": "#/components/schemas/HttpHeaderInfo" - }, - "description": "Gets or sets the headers." - } - }, - "additionalProperties": false - }, "DeviceInfo": { "type": "object", "properties": { @@ -46854,6 +46451,10 @@ "type": "string", "nullable": true }, + "CustomName": { + "type": "string", + "nullable": true + }, "AccessToken": { "type": "string", "description": "Gets or sets the access token.", @@ -46986,108 +46587,6 @@ "description": "Gets or sets the Id.", "nullable": true }, - "Identification": { - "allOf": [ - { - "$ref": "#/components/schemas/DeviceIdentification" - } - ], - "description": "Gets or sets the Identification.", - "nullable": true - }, - "FriendlyName": { - "type": "string", - "description": "Gets or sets the friendly name of the device profile, which can be shown to users.", - "nullable": true - }, - "Manufacturer": { - "type": "string", - "description": "Gets or sets the manufacturer of the device which this profile represents.", - "nullable": true - }, - "ManufacturerUrl": { - "type": "string", - "description": "Gets or sets an url for the manufacturer of the device which this profile represents.", - "nullable": true - }, - "ModelName": { - "type": "string", - "description": "Gets or sets the model name of the device which this profile represents.", - "nullable": true - }, - "ModelDescription": { - "type": "string", - "description": "Gets or sets the model description of the device which this profile represents.", - "nullable": true - }, - "ModelNumber": { - "type": "string", - "description": "Gets or sets the model number of the device which this profile represents.", - "nullable": true - }, - "ModelUrl": { - "type": "string", - "description": "Gets or sets the ModelUrl.", - "nullable": true - }, - "SerialNumber": { - "type": "string", - "description": "Gets or sets the serial number of the device which this profile represents.", - "nullable": true - }, - "EnableAlbumArtInDidl": { - "type": "boolean", - "description": "Gets or sets a value indicating whether EnableAlbumArtInDidl.", - "default": false - }, - "EnableSingleAlbumArtLimit": { - "type": "boolean", - "description": "Gets or sets a value indicating whether EnableSingleAlbumArtLimit.", - "default": false - }, - "EnableSingleSubtitleLimit": { - "type": "boolean", - "description": "Gets or sets a value indicating whether EnableSingleSubtitleLimit.", - "default": false - }, - "SupportedMediaTypes": { - "type": "string", - "description": "Gets or sets the SupportedMediaTypes." - }, - "UserId": { - "type": "string", - "description": "Gets or sets the UserId.", - "nullable": true - }, - "AlbumArtPn": { - "type": "string", - "description": "Gets or sets the AlbumArtPn.", - "nullable": true - }, - "MaxAlbumArtWidth": { - "type": "integer", - "description": "Gets or sets the MaxAlbumArtWidth.", - "format": "int32", - "nullable": true - }, - "MaxAlbumArtHeight": { - "type": "integer", - "description": "Gets or sets the MaxAlbumArtHeight.", - "format": "int32", - "nullable": true - }, - "MaxIconWidth": { - "type": "integer", - "description": "Gets or sets the maximum allowed width of embedded icons.", - "format": "int32", - "nullable": true - }, - "MaxIconHeight": { - "type": "integer", - "description": "Gets or sets the maximum allowed height of embedded icons.", - "format": "int32", - "nullable": true - }, "MaxStreamingBitrate": { "type": "integer", "description": "Gets or sets the maximum allowed bitrate for all streamed content.", @@ -47112,49 +46611,6 @@ "format": "int32", "nullable": true }, - "SonyAggregationFlags": { - "type": "string", - "description": "Gets or sets the content of the aggregationFlags element in the urn:schemas-sonycom:av namespace.", - "nullable": true - }, - "ProtocolInfo": { - "type": "string", - "description": "Gets or sets the ProtocolInfo.", - "nullable": true - }, - "TimelineOffsetSeconds": { - "type": "integer", - "description": "Gets or sets the TimelineOffsetSeconds.", - "format": "int32", - "default": 0 - }, - "RequiresPlainVideoItems": { - "type": "boolean", - "description": "Gets or sets a value indicating whether RequiresPlainVideoItems.", - "default": false - }, - "RequiresPlainFolders": { - "type": "boolean", - "description": "Gets or sets a value indicating whether RequiresPlainFolders.", - "default": false - }, - "EnableMSMediaReceiverRegistrar": { - "type": "boolean", - "description": "Gets or sets a value indicating whether EnableMSMediaReceiverRegistrar.", - "default": false - }, - "IgnoreTranscodeByteRangeRequests": { - "type": "boolean", - "description": "Gets or sets a value indicating whether IgnoreTranscodeByteRangeRequests.", - "default": false - }, - "XmlRootAttributes": { - "type": "array", - "items": { - "$ref": "#/components/schemas/XmlAttribute" - }, - "description": "Gets or sets the XmlRootAttributes." - }, "DirectPlayProfiles": { "type": "array", "items": { @@ -47183,13 +46639,6 @@ }, "description": "Gets or sets the codec profiles." }, - "ResponseProfiles": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ResponseProfile" - }, - "description": "Gets or sets the ResponseProfiles." - }, "SubtitleProfiles": { "type": "array", "items": { @@ -47201,37 +46650,6 @@ "additionalProperties": false, "description": "A MediaBrowser.Model.Dlna.DeviceProfile represents a set of metadata which determines which content a certain device is able to play.\r\n
\r\nSpecifically, it defines the supported containers and\r\ncodecs (video and/or audio, including codec profiles and levels)\r\nthe device is able to direct play (without transcoding or remuxing),\r\nas well as which containers/codecs to transcode to in case it isn't." }, - "DeviceProfileInfo": { - "type": "object", - "properties": { - "Id": { - "type": "string", - "description": "Gets or sets the identifier.", - "nullable": true - }, - "Name": { - "type": "string", - "description": "Gets or sets the name.", - "nullable": true - }, - "Type": { - "allOf": [ - { - "$ref": "#/components/schemas/DeviceProfileType" - } - ], - "description": "Gets or sets the type." - } - }, - "additionalProperties": false - }, - "DeviceProfileType": { - "enum": [ - "System", - "User" - ], - "type": "string" - }, "DirectPlayProfile": { "type": "object", "properties": { @@ -47248,6 +46666,13 @@ "nullable": true }, "Type": { + "enum": [ + "Audio", + "Video", + "Photo", + "Subtitle", + "Lyric" + ], "allOf": [ { "$ref": "#/components/schemas/DlnaProfileType" @@ -47303,6 +46728,10 @@ "description": "Gets or sets the custom prefs." }, "ScrollDirection": { + "enum": [ + "Horizontal", + "Vertical" + ], "allOf": [ { "$ref": "#/components/schemas/ScrollDirection" @@ -47319,6 +46748,10 @@ "description": "Gets or sets a value indicating whether [remember sorting]." }, "SortOrder": { + "enum": [ + "Ascending", + "Descending" + ], "allOf": [ { "$ref": "#/components/schemas/SortOrder" @@ -47339,70 +46772,25 @@ "additionalProperties": false, "description": "Defines the display preferences for any item that supports them (usually Folders)." }, - "DlnaOptions": { - "type": "object", - "properties": { - "EnablePlayTo": { - "type": "boolean", - "description": "Gets or sets a value indicating whether gets or sets a value to indicate the status of the dlna playTo subsystem." - }, - "EnableServer": { - "type": "boolean", - "description": "Gets or sets a value indicating whether gets or sets a value to indicate the status of the dlna server subsystem." - }, - "EnableDebugLog": { - "type": "boolean", - "description": "Gets or sets a value indicating whether detailed dlna server logs are sent to the console/log.\r\nIf the setting \"Emby.Dlna\": \"Debug\" msut be set in logging.default.json for this property to work." - }, - "EnablePlayToTracing": { - "type": "boolean", - "description": "Gets or sets a value indicating whether whether detailed playTo debug logs are sent to the console/log.\r\nIf the setting \"Emby.Dlna.PlayTo\": \"Debug\" msut be set in logging.default.json for this property to work." - }, - "ClientDiscoveryIntervalSeconds": { - "type": "integer", - "description": "Gets or sets the ssdp client discovery interval time (in seconds).\r\nThis is the time after which the server will send a ssdp search request.", - "format": "int32" - }, - "AliveMessageIntervalSeconds": { - "type": "integer", - "description": "Gets or sets the frequency at which ssdp alive notifications are transmitted.", - "format": "int32" - }, - "BlastAliveMessageIntervalSeconds": { - "type": "integer", - "description": "Gets or sets the frequency at which ssdp alive notifications are transmitted. MIGRATING - TO BE REMOVED ONCE WEB HAS BEEN ALTERED.", - "format": "int32" - }, - "DefaultUserId": { - "type": "string", - "description": "Gets or sets the default user account that the dlna server uses.", - "nullable": true - }, - "AutoCreatePlayToProfiles": { - "type": "boolean", - "description": "Gets or sets a value indicating whether playTo device profiles should be created." - }, - "BlastAliveMessages": { - "type": "boolean", - "description": "Gets or sets a value indicating whether to blast alive messages." - }, - "SendOnlyMatchedHost": { - "type": "boolean", - "description": "gets or sets a value indicating whether to send only matched host." - } - }, - "additionalProperties": false, - "description": "The DlnaOptions class contains the user definable parameters for the dlna subsystems." - }, "DlnaProfileType": { "enum": [ "Audio", "Video", "Photo", - "Subtitle" + "Subtitle", + "Lyric" ], "type": "string" }, + "DownMixStereoAlgorithms": { + "enum": [ + "None", + "Dave750", + "NightmodeDialogue" + ], + "type": "string", + "description": "An enum representing an algorithm to downmix 6ch+ to stereo.\r\nAlgorithms sourced from https://superuser.com/questions/852400/properly-downmix-5-1-to-stereo-using-ffmpeg/1410620#1410620." + }, "DynamicDayOfWeek": { "enum": [ "Sunday", @@ -47441,36 +46829,71 @@ "properties": { "EncodingThreadCount": { "type": "integer", + "description": "Gets or sets the thread count used for encoding.", "format": "int32" }, "TranscodingTempPath": { "type": "string", + "description": "Gets or sets the temporary transcoding path.", "nullable": true }, "FallbackFontPath": { "type": "string", + "description": "Gets or sets the path to the fallback font.", "nullable": true }, "EnableFallbackFont": { - "type": "boolean" + "type": "boolean", + "description": "Gets or sets a value indicating whether to use the fallback font." + }, + "EnableAudioVbr": { + "type": "boolean", + "description": "Gets or sets a value indicating whether audio VBR is enabled." }, "DownMixAudioBoost": { "type": "number", + "description": "Gets or sets the audio boost applied when downmixing audio.", "format": "double" }, + "DownMixStereoAlgorithm": { + "enum": [ + "None", + "Dave750", + "NightmodeDialogue" + ], + "allOf": [ + { + "$ref": "#/components/schemas/DownMixStereoAlgorithms" + } + ], + "description": "Gets or sets the algorithm used for downmixing audio to stereo." + }, "MaxMuxingQueueSize": { "type": "integer", + "description": "Gets or sets the maximum size of the muxing queue.", "format": "int32" }, "EnableThrottling": { - "type": "boolean" + "type": "boolean", + "description": "Gets or sets a value indicating whether throttling is enabled." }, "ThrottleDelaySeconds": { "type": "integer", + "description": "Gets or sets the delay after which throttling happens.", + "format": "int32" + }, + "EnableSegmentDeletion": { + "type": "boolean", + "description": "Gets or sets a value indicating whether segment deletion is enabled." + }, + "SegmentKeepSeconds": { + "type": "integer", + "description": "Gets or sets seconds for which segments should be kept before being deleted.", "format": "int32" }, "HardwareAccelerationType": { "type": "string", + "description": "Gets or sets the hardware acceleration type.", "nullable": true }, "EncoderAppPath": { @@ -47485,97 +46908,131 @@ }, "VaapiDevice": { "type": "string", + "description": "Gets or sets the VA-API device.", "nullable": true }, "EnableTonemapping": { - "type": "boolean" + "type": "boolean", + "description": "Gets or sets a value indicating whether tonemapping is enabled." }, "EnableVppTonemapping": { - "type": "boolean" + "type": "boolean", + "description": "Gets or sets a value indicating whether VPP tonemapping is enabled." + }, + "EnableVideoToolboxTonemapping": { + "type": "boolean", + "description": "Gets or sets a value indicating whether videotoolbox tonemapping is enabled." }, "TonemappingAlgorithm": { "type": "string", + "description": "Gets or sets the tone-mapping algorithm.", "nullable": true }, "TonemappingMode": { "type": "string", + "description": "Gets or sets the tone-mapping mode.", "nullable": true }, "TonemappingRange": { "type": "string", + "description": "Gets or sets the tone-mapping range.", "nullable": true }, "TonemappingDesat": { "type": "number", + "description": "Gets or sets the tone-mapping desaturation.", "format": "double" }, "TonemappingPeak": { "type": "number", + "description": "Gets or sets the tone-mapping peak.", "format": "double" }, "TonemappingParam": { "type": "number", + "description": "Gets or sets the tone-mapping parameters.", "format": "double" }, "VppTonemappingBrightness": { "type": "number", + "description": "Gets or sets the VPP tone-mapping brightness.", "format": "double" }, "VppTonemappingContrast": { "type": "number", + "description": "Gets or sets the VPP tone-mapping contrast.", "format": "double" }, "H264Crf": { "type": "integer", + "description": "Gets or sets the H264 CRF.", "format": "int32" }, "H265Crf": { "type": "integer", + "description": "Gets or sets the H265 CRF.", "format": "int32" }, "EncoderPreset": { "type": "string", + "description": "Gets or sets the encoder preset.", "nullable": true }, "DeinterlaceDoubleRate": { - "type": "boolean" + "type": "boolean", + "description": "Gets or sets a value indicating whether the framerate is doubled when deinterlacing." }, "DeinterlaceMethod": { "type": "string", + "description": "Gets or sets the deinterlace method.", "nullable": true }, "EnableDecodingColorDepth10Hevc": { - "type": "boolean" + "type": "boolean", + "description": "Gets or sets a value indicating whether 10bit HEVC decoding is enabled." }, "EnableDecodingColorDepth10Vp9": { - "type": "boolean" + "type": "boolean", + "description": "Gets or sets a value indicating whether 10bit VP9 decoding is enabled." }, "EnableEnhancedNvdecDecoder": { - "type": "boolean" + "type": "boolean", + "description": "Gets or sets a value indicating whether the enhanced NVDEC is enabled." }, "PreferSystemNativeHwDecoder": { - "type": "boolean" + "type": "boolean", + "description": "Gets or sets a value indicating whether the system native hardware decoder should be used." }, "EnableIntelLowPowerH264HwEncoder": { - "type": "boolean" + "type": "boolean", + "description": "Gets or sets a value indicating whether the Intel H264 low-power hardware encoder should be used." }, "EnableIntelLowPowerHevcHwEncoder": { - "type": "boolean" + "type": "boolean", + "description": "Gets or sets a value indicating whether the Intel HEVC low-power hardware encoder should be used." }, "EnableHardwareEncoding": { - "type": "boolean" + "type": "boolean", + "description": "Gets or sets a value indicating whether hardware encoding is enabled." }, "AllowHevcEncoding": { - "type": "boolean" + "type": "boolean", + "description": "Gets or sets a value indicating whether HEVC encoding is enabled." + }, + "AllowAv1Encoding": { + "type": "boolean", + "description": "Gets or sets a value indicating whether AV1 encoding is enabled." }, "EnableSubtitleExtraction": { - "type": "boolean" + "type": "boolean", + "description": "Gets or sets a value indicating whether subtitle extraction is enabled." }, "HardwareDecodingCodecs": { "type": "array", "items": { "type": "string" }, + "description": "Gets or sets the codecs hardware encoding is used for.", "nullable": true }, "AllowOnDemandMetadataBasedKeyframeExtractionForExtensions": { @@ -47583,10 +47040,12 @@ "items": { "type": "string" }, + "description": "Gets or sets the file extensions on-demand metadata based keyframe extraction is enabled for.", "nullable": true } }, - "additionalProperties": false + "additionalProperties": false, + "description": "Class EncodingOptions." }, "EndPointInfo": { "type": "object", @@ -47612,6 +47071,21 @@ "description": "Gets or sets the unique key for this id. This key should be unique across all providers." }, "Type": { + "enum": [ + "Album", + "AlbumArtist", + "Artist", + "BoxSet", + "Episode", + "Movie", + "OtherArtist", + "Person", + "ReleaseGroup", + "Season", + "Series", + "Track", + "Book" + ], "allOf": [ { "$ref": "#/components/schemas/ExternalIdMediaType" @@ -47642,7 +47116,8 @@ "ReleaseGroup", "Season", "Series", - "Track" + "Track", + "Book" ], "type": "string", "description": "The specific media type of an MediaBrowser.Model.Providers.ExternalIdInfo." @@ -47663,15 +47138,22 @@ }, "additionalProperties": false }, - "FFmpegLocation": { + "ExtraType": { "enum": [ - "NotFound", - "SetByArgument", - "Custom", - "System" + "Unknown", + "Clip", + "Trailer", + "BehindTheScenes", + "DeletedScene", + "Interview", + "Scene", + "Sample", + "ThemeSong", + "ThemeVideo", + "Featurette", + "Short" ], - "type": "string", - "description": "Enum describing the location of the FFmpeg tool." + "type": "string" }, "FileSystemEntryInfo": { "type": "object", @@ -47685,6 +47167,12 @@ "description": "Gets the path." }, "Type": { + "enum": [ + "File", + "Directory", + "NetworkComputer", + "NetworkShare" + ], "allOf": [ { "$ref": "#/components/schemas/FileSystemEntryType" @@ -47733,6 +47221,69 @@ "additionalProperties": false, "description": "Class FontFile." }, + "ForceKeepAliveMessage": { + "type": "object", + "properties": { + "Data": { + "type": "integer", + "description": "Gets or sets the data.", + "format": "int32" + }, + "MessageId": { + "type": "string", + "description": "Gets or sets the message id.", + "format": "uuid" + }, + "MessageType": { + "enum": [ + "ForceKeepAlive", + "GeneralCommand", + "UserDataChanged", + "Sessions", + "Play", + "SyncPlayCommand", + "SyncPlayGroupUpdate", + "Playstate", + "RestartRequired", + "ServerShuttingDown", + "ServerRestarting", + "LibraryChanged", + "UserDeleted", + "UserUpdated", + "SeriesTimerCreated", + "TimerCreated", + "SeriesTimerCancelled", + "TimerCancelled", + "RefreshProgress", + "ScheduledTaskEnded", + "PackageInstallationCancelled", + "PackageInstallationFailed", + "PackageInstallationCompleted", + "PackageInstalling", + "PackageUninstalled", + "ActivityLogEntry", + "ScheduledTasksInfo", + "ActivityLogEntryStart", + "ActivityLogEntryStop", + "SessionsStart", + "SessionsStop", + "ScheduledTasksInfoStart", + "ScheduledTasksInfoStop", + "KeepAlive" + ], + "allOf": [ + { + "$ref": "#/components/schemas/SessionMessageType" + } + ], + "description": "The different kinds of messages that are used in the WebSocket api.", + "default": "ForceKeepAlive", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "Force keep alive websocket messages." + }, "ForgotPasswordAction": { "enum": [ "ContactAdmin", @@ -47773,6 +47324,11 @@ "type": "object", "properties": { "Action": { + "enum": [ + "ContactAdmin", + "PinCode", + "InNetworkRequired" + ], "allOf": [ { "$ref": "#/components/schemas/ForgotPasswordAction" @@ -47798,6 +47354,51 @@ "type": "object", "properties": { "Name": { + "enum": [ + "MoveUp", + "MoveDown", + "MoveLeft", + "MoveRight", + "PageUp", + "PageDown", + "PreviousLetter", + "NextLetter", + "ToggleOsd", + "ToggleContextMenu", + "Select", + "Back", + "TakeScreenshot", + "SendKey", + "SendString", + "GoHome", + "GoToSettings", + "VolumeUp", + "VolumeDown", + "Mute", + "Unmute", + "ToggleMute", + "SetVolume", + "SetAudioStreamIndex", + "SetSubtitleStreamIndex", + "ToggleFullscreen", + "DisplayContent", + "GoToSearch", + "DisplayMessage", + "SetRepeatMode", + "ChannelUp", + "ChannelDown", + "Guide", + "ToggleStats", + "PlayMediaSource", + "PlayTrailers", + "SetShuffleQueue", + "PlayState", + "PlayNext", + "ToggleOsdMenu", + "Play", + "SetMaxStreamingBitrate", + "SetPlaybackOrder" + ], "allOf": [ { "$ref": "#/components/schemas/GeneralCommandType" @@ -47819,6 +47420,73 @@ }, "additionalProperties": false }, + "GeneralCommandMessage": { + "type": "object", + "properties": { + "Data": { + "allOf": [ + { + "$ref": "#/components/schemas/GeneralCommand" + } + ], + "description": "Gets or sets the data.", + "nullable": true + }, + "MessageId": { + "type": "string", + "description": "Gets or sets the message id.", + "format": "uuid" + }, + "MessageType": { + "enum": [ + "ForceKeepAlive", + "GeneralCommand", + "UserDataChanged", + "Sessions", + "Play", + "SyncPlayCommand", + "SyncPlayGroupUpdate", + "Playstate", + "RestartRequired", + "ServerShuttingDown", + "ServerRestarting", + "LibraryChanged", + "UserDeleted", + "UserUpdated", + "SeriesTimerCreated", + "TimerCreated", + "SeriesTimerCancelled", + "TimerCancelled", + "RefreshProgress", + "ScheduledTaskEnded", + "PackageInstallationCancelled", + "PackageInstallationFailed", + "PackageInstallationCompleted", + "PackageInstalling", + "PackageUninstalled", + "ActivityLogEntry", + "ScheduledTasksInfo", + "ActivityLogEntryStart", + "ActivityLogEntryStop", + "SessionsStart", + "SessionsStop", + "ScheduledTasksInfoStart", + "ScheduledTasksInfoStop", + "KeepAlive" + ], + "allOf": [ + { + "$ref": "#/components/schemas/SessionMessageType" + } + ], + "description": "The different kinds of messages that are used in the WebSocket api.", + "default": "GeneralCommand", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "General command websocket message." + }, "GeneralCommandType": { "enum": [ "MoveUp", @@ -47862,7 +47530,8 @@ "PlayNext", "ToggleOsdMenu", "Play", - "SetMaxStreamingBitrate" + "SetMaxStreamingBitrate", + "SetPlaybackOrder" ], "type": "string", "description": "This exists simply to identify a set of known commands." @@ -47881,7 +47550,8 @@ "UserId": { "type": "string", "description": "Gets or sets optional. Filter by user id.", - "format": "uuid" + "format": "uuid", + "nullable": true }, "MinStartDate": { "type": "string", @@ -47957,7 +47627,7 @@ "SortBy": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/ItemSortBy" }, "description": "Gets or sets specify one or more sort orders, comma delimited. Options: Name, StartDate.\r\nOptional." }, @@ -48044,6 +47714,12 @@ "description": "Gets the group name." }, "State": { + "enum": [ + "Idle", + "Waiting", + "Paused", + "Playing" + ], "allOf": [ { "$ref": "#/components/schemas/GroupStateType" @@ -48067,6 +47743,48 @@ "additionalProperties": false, "description": "Class GroupInfoDto." }, + "GroupInfoDtoGroupUpdate": { + "type": "object", + "properties": { + "GroupId": { + "type": "string", + "description": "Gets the group identifier.", + "format": "uuid", + "readOnly": true + }, + "Type": { + "enum": [ + "UserJoined", + "UserLeft", + "GroupJoined", + "GroupLeft", + "StateUpdate", + "PlayQueue", + "NotInGroup", + "GroupDoesNotExist", + "CreateGroupDenied", + "JoinGroupDenied", + "LibraryAccessDenied" + ], + "allOf": [ + { + "$ref": "#/components/schemas/GroupUpdateType" + } + ], + "description": "Gets the update type." + }, + "Data": { + "allOf": [ + { + "$ref": "#/components/schemas/GroupInfoDto" + } + ], + "description": "Gets the update data." + } + }, + "additionalProperties": false, + "description": "Class GroupUpdate." + }, "GroupQueueMode": { "enum": [ "Queue", @@ -48102,6 +47820,158 @@ "type": "string", "description": "Enum GroupState." }, + "GroupStateUpdate": { + "type": "object", + "properties": { + "State": { + "enum": [ + "Idle", + "Waiting", + "Paused", + "Playing" + ], + "allOf": [ + { + "$ref": "#/components/schemas/GroupStateType" + } + ], + "description": "Gets the state of the group." + }, + "Reason": { + "enum": [ + "Play", + "SetPlaylistItem", + "RemoveFromPlaylist", + "MovePlaylistItem", + "Queue", + "Unpause", + "Pause", + "Stop", + "Seek", + "Buffer", + "Ready", + "NextItem", + "PreviousItem", + "SetRepeatMode", + "SetShuffleMode", + "Ping", + "IgnoreWait" + ], + "allOf": [ + { + "$ref": "#/components/schemas/PlaybackRequestType" + } + ], + "description": "Gets the reason of the state change." + } + }, + "additionalProperties": false, + "description": "Class GroupStateUpdate." + }, + "GroupStateUpdateGroupUpdate": { + "type": "object", + "properties": { + "GroupId": { + "type": "string", + "description": "Gets the group identifier.", + "format": "uuid", + "readOnly": true + }, + "Type": { + "enum": [ + "UserJoined", + "UserLeft", + "GroupJoined", + "GroupLeft", + "StateUpdate", + "PlayQueue", + "NotInGroup", + "GroupDoesNotExist", + "CreateGroupDenied", + "JoinGroupDenied", + "LibraryAccessDenied" + ], + "allOf": [ + { + "$ref": "#/components/schemas/GroupUpdateType" + } + ], + "description": "Gets the update type." + }, + "Data": { + "allOf": [ + { + "$ref": "#/components/schemas/GroupStateUpdate" + } + ], + "description": "Gets the update data." + } + }, + "additionalProperties": false, + "description": "Class GroupUpdate." + }, + "GroupUpdate": { + "type": "object", + "oneOf": [ + { + "$ref": "#/components/schemas/GroupInfoDtoGroupUpdate" + }, + { + "$ref": "#/components/schemas/GroupStateUpdateGroupUpdate" + }, + { + "$ref": "#/components/schemas/StringGroupUpdate" + }, + { + "$ref": "#/components/schemas/PlayQueueUpdateGroupUpdate" + } + ], + "properties": { + "GroupId": { + "type": "string", + "description": "Gets the group identifier.", + "format": "uuid", + "readOnly": true + }, + "Type": { + "enum": [ + "UserJoined", + "UserLeft", + "GroupJoined", + "GroupLeft", + "StateUpdate", + "PlayQueue", + "NotInGroup", + "GroupDoesNotExist", + "CreateGroupDenied", + "JoinGroupDenied", + "LibraryAccessDenied" + ], + "allOf": [ + { + "$ref": "#/components/schemas/GroupUpdateType" + } + ], + "description": "Gets the update type." + } + }, + "additionalProperties": false, + "description": "Group update without data.", + "discriminator": { + "propertyName": "Type", + "mapping": { + "UserJoined": "#/components/schemas/StringGroupUpdate", + "UserLeft": "#/components/schemas/StringGroupUpdate", + "GroupJoined": "#/components/schemas/GroupInfoDtoGroupUpdate", + "GroupLeft": "#/components/schemas/StringGroupUpdate", + "StateUpdate": "#/components/schemas/GroupStateUpdateGroupUpdate", + "PlayQueue": "#/components/schemas/PlayQueueUpdateGroupUpdate", + "NotInGroup": "#/components/schemas/StringGroupUpdate", + "GroupDoesNotExist": "#/components/schemas/StringGroupUpdate", + "LibraryAccessDenied": "#/components/schemas/StringGroupUpdate" + } + } + }, "GroupUpdateType": { "enum": [ "UserJoined", @@ -48142,40 +48012,12 @@ "NVENC", "V4L2M2M", "VAAPI", - "VideoToolBox" + "VideoToolBox", + "RKMPP" ], "type": "string", "description": "Enum HardwareEncodingType." }, - "HeaderMatchType": { - "enum": [ - "Equals", - "Regex", - "Substring" - ], - "type": "string" - }, - "HttpHeaderInfo": { - "type": "object", - "properties": { - "Name": { - "type": "string", - "nullable": true - }, - "Value": { - "type": "string", - "nullable": true - }, - "Match": { - "allOf": [ - { - "$ref": "#/components/schemas/HeaderMatchType" - } - ] - } - }, - "additionalProperties": false - }, "IgnoreWaitRequestDto": { "type": "object", "properties": { @@ -48187,44 +48029,14 @@ "additionalProperties": false, "description": "Class IgnoreWaitRequestDto." }, - "ImageByNameInfo": { - "type": "object", - "properties": { - "Name": { - "type": "string", - "description": "Gets or sets the name.", - "nullable": true - }, - "Theme": { - "type": "string", - "description": "Gets or sets the theme.", - "nullable": true - }, - "Context": { - "type": "string", - "description": "Gets or sets the context.", - "nullable": true - }, - "FileLength": { - "type": "integer", - "description": "Gets or sets the length of the file.", - "format": "int64" - }, - "Format": { - "type": "string", - "description": "Gets or sets the format.", - "nullable": true - } - }, - "additionalProperties": false - }, "ImageFormat": { "enum": [ "Bmp", "Gif", "Jpg", "Png", - "Webp" + "Webp", + "Svg" ], "type": "string", "description": "Enum ImageOutputFormat." @@ -48233,6 +48045,21 @@ "type": "object", "properties": { "ImageType": { + "enum": [ + "Primary", + "Art", + "Backdrop", + "Banner", + "Logo", + "Thumb", + "Disc", + "Box", + "Screenshot", + "Menu", + "Chapter", + "BoxRear", + "Profile" + ], "allOf": [ { "$ref": "#/components/schemas/ImageType" @@ -48286,6 +48113,21 @@ "type": "object", "properties": { "Type": { + "enum": [ + "Primary", + "Art", + "Backdrop", + "Banner", + "Logo", + "Thumb", + "Disc", + "Box", + "Screenshot", + "Menu", + "Chapter", + "BoxRear", + "Profile" + ], "allOf": [ { "$ref": "#/components/schemas/ImageType" @@ -48337,6 +48179,21 @@ "additionalProperties": false, "description": "Class ImageProviderInfo." }, + "ImageResolution": { + "enum": [ + "MatchSource", + "P144", + "P240", + "P360", + "P480", + "P720", + "P1080", + "P1440", + "P2160" + ], + "type": "string", + "description": "Enum ImageResolution." + }, "ImageSavingConvention": { "enum": [ "Legacy", @@ -48363,6 +48220,98 @@ "type": "string", "description": "Enum ImageType." }, + "InboundKeepAliveMessage": { + "type": "object", + "properties": { + "MessageType": { + "enum": [ + "ForceKeepAlive", + "GeneralCommand", + "UserDataChanged", + "Sessions", + "Play", + "SyncPlayCommand", + "SyncPlayGroupUpdate", + "Playstate", + "RestartRequired", + "ServerShuttingDown", + "ServerRestarting", + "LibraryChanged", + "UserDeleted", + "UserUpdated", + "SeriesTimerCreated", + "TimerCreated", + "SeriesTimerCancelled", + "TimerCancelled", + "RefreshProgress", + "ScheduledTaskEnded", + "PackageInstallationCancelled", + "PackageInstallationFailed", + "PackageInstallationCompleted", + "PackageInstalling", + "PackageUninstalled", + "ActivityLogEntry", + "ScheduledTasksInfo", + "ActivityLogEntryStart", + "ActivityLogEntryStop", + "SessionsStart", + "SessionsStop", + "ScheduledTasksInfoStart", + "ScheduledTasksInfoStop", + "KeepAlive" + ], + "allOf": [ + { + "$ref": "#/components/schemas/SessionMessageType" + } + ], + "description": "The different kinds of messages that are used in the WebSocket api.", + "default": "KeepAlive", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "Keep alive websocket messages." + }, + "InboundWebSocketMessage": { + "type": "object", + "oneOf": [ + { + "$ref": "#/components/schemas/ActivityLogEntryStartMessage" + }, + { + "$ref": "#/components/schemas/ActivityLogEntryStopMessage" + }, + { + "$ref": "#/components/schemas/InboundKeepAliveMessage" + }, + { + "$ref": "#/components/schemas/ScheduledTasksInfoStartMessage" + }, + { + "$ref": "#/components/schemas/ScheduledTasksInfoStopMessage" + }, + { + "$ref": "#/components/schemas/SessionsStartMessage" + }, + { + "$ref": "#/components/schemas/SessionsStopMessage" + } + ], + "description": "Represents the list of possible inbound websocket types", + "discriminator": { + "propertyName": "MessageType", + "mapping": { + "ActivityLogEntryStart": "#/components/schemas/ActivityLogEntryStartMessage", + "ActivityLogEntryStop": "#/components/schemas/ActivityLogEntryStopMessage", + "KeepAlive": "#/components/schemas/InboundKeepAliveMessage", + "ScheduledTasksInfoStart": "#/components/schemas/ScheduledTasksInfoStartMessage", + "ScheduledTasksInfoStop": "#/components/schemas/ScheduledTasksInfoStopMessage", + "SessionsStart": "#/components/schemas/SessionsStartMessage", + "SessionsStop": "#/components/schemas/SessionsStopMessage" + } + } + }, "InstallationInfo": { "type": "object", "properties": { @@ -48539,6 +48488,7 @@ "CanDownload", "ChannelInfo", "Chapters", + "Trickplay", "ChildCount", "CumulativeRunTimeTicks", "CustomRating", @@ -48569,8 +48519,6 @@ "SortName", "SpecialEpisodeNumbers", "Studios", - "BasicSyncInfo", - "SyncInfo", "Taglines", "Tags", "RemoteTrailers", @@ -48614,6 +48562,44 @@ "type": "string", "description": "Enum ItemFilter." }, + "ItemSortBy": { + "enum": [ + "Default", + "AiredEpisodeOrder", + "Album", + "AlbumArtist", + "Artist", + "DateCreated", + "OfficialRating", + "DatePlayed", + "PremiereDate", + "StartDate", + "SortName", + "Name", + "Random", + "Runtime", + "CommunityRating", + "ProductionYear", + "PlayCount", + "CriticRating", + "IsFolder", + "IsUnplayed", + "IsPlayed", + "SeriesSortName", + "VideoBitRate", + "AirTime", + "Studio", + "IsFavoriteOrLiked", + "DateLastContentAdded", + "SeriesDatePlayed", + "ParentIndexNumber", + "IndexNumber", + "SimilarityScore", + "SearchScore" + ], + "type": "string", + "description": "These represent sort orders." + }, "JoinGroupRequestDto": { "type": "object", "properties": { @@ -48635,6 +48621,73 @@ ], "type": "string" }, + "LibraryChangedMessage": { + "type": "object", + "properties": { + "Data": { + "allOf": [ + { + "$ref": "#/components/schemas/LibraryUpdateInfo" + } + ], + "description": "Class LibraryUpdateInfo.", + "nullable": true + }, + "MessageId": { + "type": "string", + "description": "Gets or sets the message id.", + "format": "uuid" + }, + "MessageType": { + "enum": [ + "ForceKeepAlive", + "GeneralCommand", + "UserDataChanged", + "Sessions", + "Play", + "SyncPlayCommand", + "SyncPlayGroupUpdate", + "Playstate", + "RestartRequired", + "ServerShuttingDown", + "ServerRestarting", + "LibraryChanged", + "UserDeleted", + "UserUpdated", + "SeriesTimerCreated", + "TimerCreated", + "SeriesTimerCancelled", + "TimerCancelled", + "RefreshProgress", + "ScheduledTaskEnded", + "PackageInstallationCancelled", + "PackageInstallationFailed", + "PackageInstallationCompleted", + "PackageInstalling", + "PackageUninstalled", + "ActivityLogEntry", + "ScheduledTasksInfo", + "ActivityLogEntryStart", + "ActivityLogEntryStop", + "SessionsStart", + "SessionsStop", + "ScheduledTasksInfoStart", + "ScheduledTasksInfoStop", + "KeepAlive" + ], + "allOf": [ + { + "$ref": "#/components/schemas/SessionMessageType" + } + ], + "description": "The different kinds of messages that are used in the WebSocket api.", + "default": "LibraryChanged", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "Library changed message." + }, "LibraryOptionInfoDto": { "type": "object", "properties": { @@ -48654,18 +48707,30 @@ "LibraryOptions": { "type": "object", "properties": { + "Enabled": { + "type": "boolean" + }, "EnablePhotos": { "type": "boolean" }, "EnableRealtimeMonitor": { "type": "boolean" }, + "EnableLUFSScan": { + "type": "boolean" + }, "EnableChapterImageExtraction": { "type": "boolean" }, "ExtractChapterImagesDuringLibraryScan": { "type": "boolean" }, + "EnableTrickplayImageExtraction": { + "type": "boolean" + }, + "ExtractTrickplayImagesDuringLibraryScan": { + "type": "boolean" + }, "PathInfos": { "type": "array", "items": { @@ -48685,6 +48750,9 @@ "EnableEmbeddedTitles": { "type": "boolean" }, + "EnableEmbeddedExtrasTitles": { + "type": "boolean" + }, "EnableEmbeddedEpisodeInfos": { "type": "boolean" }, @@ -48756,10 +48824,20 @@ "SaveSubtitlesWithMedia": { "type": "boolean" }, + "SaveLyricsWithMedia": { + "type": "boolean", + "default": false + }, "AutomaticallyAddToCollection": { "type": "boolean" }, "AllowEmbeddedSubtitles": { + "enum": [ + "AllowAll", + "AllowText", + "AllowImage", + "AllowNone" + ], "allOf": [ { "$ref": "#/components/schemas/EmbeddedSubtitleOptions" @@ -49097,6 +49175,12 @@ "RecordingPostProcessorArguments": { "type": "string", "nullable": true + }, + "SaveRecordingNFO": { + "type": "boolean" + }, + "SaveRecordingImages": { + "type": "boolean" } }, "additionalProperties": false @@ -49115,6 +49199,10 @@ "nullable": true }, "Status": { + "enum": [ + "Ok", + "Unavailable" + ], "allOf": [ { "$ref": "#/components/schemas/LiveTvServiceStatus" @@ -49202,8 +49290,7 @@ }, "Name": { "type": "string", - "description": "Gets or sets the name.", - "nullable": true + "description": "Gets or sets the name." } }, "additionalProperties": false @@ -49220,6 +49307,104 @@ ], "type": "string" }, + "LyricDto": { + "type": "object", + "properties": { + "Metadata": { + "allOf": [ + { + "$ref": "#/components/schemas/LyricMetadata" + } + ], + "description": "Gets or sets Metadata for the lyrics." + }, + "Lyrics": { + "type": "array", + "items": { + "$ref": "#/components/schemas/LyricLine" + }, + "description": "Gets or sets a collection of individual lyric lines." + } + }, + "additionalProperties": false, + "description": "LyricResponse model." + }, + "LyricLine": { + "type": "object", + "properties": { + "Text": { + "type": "string", + "description": "Gets the text of this lyric line." + }, + "Start": { + "type": "integer", + "description": "Gets the start time in ticks.", + "format": "int64", + "nullable": true + } + }, + "additionalProperties": false, + "description": "Lyric model." + }, + "LyricMetadata": { + "type": "object", + "properties": { + "Artist": { + "type": "string", + "description": "Gets or sets the song artist.", + "nullable": true + }, + "Album": { + "type": "string", + "description": "Gets or sets the album this song is on.", + "nullable": true + }, + "Title": { + "type": "string", + "description": "Gets or sets the title of the song.", + "nullable": true + }, + "Author": { + "type": "string", + "description": "Gets or sets the author of the lyric data.", + "nullable": true + }, + "Length": { + "type": "integer", + "description": "Gets or sets the length of the song in ticks.", + "format": "int64", + "nullable": true + }, + "By": { + "type": "string", + "description": "Gets or sets who the LRC file was created by.", + "nullable": true + }, + "Offset": { + "type": "integer", + "description": "Gets or sets the lyric offset compared to audio in ticks.", + "format": "int64", + "nullable": true + }, + "Creator": { + "type": "string", + "description": "Gets or sets the software used to create the LRC file.", + "nullable": true + }, + "Version": { + "type": "string", + "description": "Gets or sets the version of the creator used.", + "nullable": true + }, + "IsSynced": { + "type": "boolean", + "description": "Gets or sets a value indicating whether this lyric is synced.", + "nullable": true + } + }, + "additionalProperties": false, + "description": "LyricMetadata model." + }, "MediaAttachment": { "type": "object", "properties": { @@ -49262,21 +49447,6 @@ "additionalProperties": false, "description": "Class MediaAttachment." }, - "MediaEncoderPathDto": { - "type": "object", - "properties": { - "Path": { - "type": "string", - "description": "Gets or sets media encoder path." - }, - "PathType": { - "type": "string", - "description": "Gets or sets media encoder path type." - } - }, - "additionalProperties": false, - "description": "Media Encoder Path Dto." - }, "MediaPathDto": { "required": [ "Name" @@ -49334,6 +49504,15 @@ "type": "object", "properties": { "Protocol": { + "enum": [ + "File", + "Http", + "Rtmp", + "Rtsp", + "Udp", + "Rtp", + "Ftp" + ], "allOf": [ { "$ref": "#/components/schemas/MediaProtocol" @@ -49353,6 +49532,15 @@ "nullable": true }, "EncoderProtocol": { + "enum": [ + "File", + "Http", + "Rtmp", + "Rtsp", + "Udp", + "Rtp", + "Ftp" + ], "allOf": [ { "$ref": "#/components/schemas/MediaProtocol" @@ -49361,6 +49549,11 @@ "nullable": true }, "Type": { + "enum": [ + "Default", + "Grouping", + "Placeholder" + ], "allOf": [ { "$ref": "#/components/schemas/MediaSourceType" @@ -49443,6 +49636,12 @@ "type": "boolean" }, "VideoType": { + "enum": [ + "VideoFile", + "Iso", + "Dvd", + "BluRay" + ], "allOf": [ { "$ref": "#/components/schemas/VideoType" @@ -49451,6 +49650,10 @@ "nullable": true }, "IsoType": { + "enum": [ + "Dvd", + "BluRay" + ], "allOf": [ { "$ref": "#/components/schemas/IsoType" @@ -49459,6 +49662,13 @@ "nullable": true }, "Video3DFormat": { + "enum": [ + "HalfSideBySide", + "FullSideBySide", + "FullTopAndBottom", + "HalfTopAndBottom", + "MVC" + ], "allOf": [ { "$ref": "#/components/schemas/Video3DFormat" @@ -49493,6 +49703,11 @@ "nullable": true }, "Timestamp": { + "enum": [ + "None", + "Zero", + "Valid" + ], "allOf": [ { "$ref": "#/components/schemas/TransportStreamTimestamp" @@ -49513,8 +49728,16 @@ "nullable": true }, "TranscodingSubProtocol": { - "type": "string", - "nullable": true + "enum": [ + "http", + "hls" + ], + "allOf": [ + { + "$ref": "#/components/schemas/MediaStreamProtocol" + } + ], + "description": "Media streaming protocol.\r\nLowercase for backwards compatibility." }, "TranscodingContainer": { "type": "string", @@ -49653,15 +49876,37 @@ "nullable": true }, "VideoRange": { - "type": "string", + "enum": [ + "Unknown", + "SDR", + "HDR" + ], + "allOf": [ + { + "$ref": "#/components/schemas/VideoRange" + } + ], "description": "Gets the video range.", - "nullable": true, "readOnly": true }, "VideoRangeType": { - "type": "string", + "enum": [ + "Unknown", + "SDR", + "HDR10", + "HLG", + "DOVI", + "DOVIWithHDR10", + "DOVIWithHLG", + "DOVIWithSDR", + "HDR10Plus" + ], + "allOf": [ + { + "$ref": "#/components/schemas/VideoRangeType" + } + ], "description": "Gets the video range type.", - "nullable": true, "readOnly": true }, "VideoDoViTitle": { @@ -49670,6 +49915,21 @@ "nullable": true, "readOnly": true }, + "AudioSpatialFormat": { + "enum": [ + "None", + "DolbyAtmos", + "DTSX" + ], + "allOf": [ + { + "$ref": "#/components/schemas/AudioSpatialFormat" + } + ], + "description": "Gets the audio spatial format.", + "default": "None", + "readOnly": true + }, "LocalizedUndefined": { "type": "string", "nullable": true @@ -49686,6 +49946,10 @@ "type": "string", "nullable": true }, + "LocalizedHearingImpaired": { + "type": "string", + "nullable": true + }, "DisplayTitle": { "type": "string", "nullable": true, @@ -49752,6 +50016,10 @@ "type": "boolean", "description": "Gets or sets a value indicating whether this instance is forced." }, + "IsHearingImpaired": { + "type": "boolean", + "description": "Gets or sets a value indicating whether this instance is for the hearing impaired." + }, "Height": { "type": "integer", "description": "Gets or sets the height.", @@ -49782,6 +50050,14 @@ "nullable": true }, "Type": { + "enum": [ + "Audio", + "Video", + "Subtitle", + "EmbeddedImage", + "Data", + "Lyric" + ], "allOf": [ { "$ref": "#/components/schemas/MediaStreamType" @@ -49810,6 +50086,13 @@ "description": "Gets or sets a value indicating whether this instance is external." }, "DeliveryMethod": { + "enum": [ + "Encode", + "Embed", + "External", + "Hls", + "Drop" + ], "allOf": [ { "$ref": "#/components/schemas/SubtitleDeliveryMethod" @@ -49861,17 +50144,37 @@ "additionalProperties": false, "description": "Class MediaStream." }, + "MediaStreamProtocol": { + "enum": [ + "http", + "hls" + ], + "type": "string", + "description": "Media streaming protocol.\r\nLowercase for backwards compatibility." + }, "MediaStreamType": { "enum": [ "Audio", "Video", "Subtitle", "EmbeddedImage", - "Data" + "Data", + "Lyric" ], "type": "string", "description": "Enum MediaStreamType." }, + "MediaType": { + "enum": [ + "Unknown", + "Video", + "Audio", + "Photo", + "Book" + ], + "type": "string", + "description": "Media types." + }, "MediaUpdateInfoDto": { "type": "object", "properties": { @@ -49975,7 +50278,26 @@ } }, "ContentType": { - "type": "string", + "enum": [ + "unknown", + "movies", + "tvshows", + "music", + "musicvideos", + "trailers", + "homevideos", + "boxsets", + "books", + "photos", + "livetv", + "playlists", + "folders" + ], + "allOf": [ + { + "$ref": "#/components/schemas/CollectionType" + } + ], "nullable": true }, "ContentTypeOptions": { @@ -50319,6 +50641,14 @@ "NetworkConfiguration": { "type": "object", "properties": { + "BaseUrl": { + "type": "string", + "description": "Gets or sets a value used to specify the URL prefix that your Jellyfin instance can be accessed at." + }, + "EnableHttps": { + "type": "boolean", + "description": "Gets or sets a value indicating whether to use HTTPS." + }, "RequireHttps": { "type": "boolean", "description": "Gets or sets a value indicating whether the server should force connections over HTTPS." @@ -50329,129 +50659,47 @@ }, "CertificatePassword": { "type": "string", - "description": "Gets or sets the password required to access the X.509 certificate data in the file specified by Jellyfin.Networking.Configuration.NetworkConfiguration.CertificatePath." + "description": "Gets or sets the password required to access the X.509 certificate data in the file specified by MediaBrowser.Common.Net.NetworkConfiguration.CertificatePath." }, - "BaseUrl": { - "type": "string", - "description": "Gets or sets a value used to specify the URL prefix that your Jellyfin instance can be accessed at." + "InternalHttpPort": { + "type": "integer", + "description": "Gets or sets the internal HTTP server port.", + "format": "int32" + }, + "InternalHttpsPort": { + "type": "integer", + "description": "Gets or sets the internal HTTPS server port.", + "format": "int32" + }, + "PublicHttpPort": { + "type": "integer", + "description": "Gets or sets the public HTTP port.", + "format": "int32" }, "PublicHttpsPort": { "type": "integer", "description": "Gets or sets the public HTTPS port.", "format": "int32" }, - "HttpServerPortNumber": { - "type": "integer", - "description": "Gets or sets the HTTP server port number.", - "format": "int32" - }, - "HttpsPortNumber": { - "type": "integer", - "description": "Gets or sets the HTTPS server port number.", - "format": "int32" - }, - "EnableHttps": { - "type": "boolean", - "description": "Gets or sets a value indicating whether to use HTTPS." - }, - "PublicPort": { - "type": "integer", - "description": "Gets or sets the public mapped port.", - "format": "int32" - }, - "UPnPCreateHttpPortMap": { - "type": "boolean", - "description": "Gets or sets a value indicating whether the http port should be mapped as part of UPnP automatic port forwarding." - }, - "UDPPortRange": { - "type": "string", - "description": "Gets or sets the UDPPortRange." - }, - "EnableIPV6": { - "type": "boolean", - "description": "Gets or sets a value indicating whether gets or sets IPV6 capability." - }, - "EnableIPV4": { - "type": "boolean", - "description": "Gets or sets a value indicating whether gets or sets IPV4 capability." - }, - "EnableSSDPTracing": { - "type": "boolean", - "description": "Gets or sets a value indicating whether detailed SSDP logs are sent to the console/log.\r\n\"Emby.Dlna\": \"Debug\" must be set in logging.default.json for this property to have any effect." - }, - "SSDPTracingFilter": { - "type": "string", - "description": "Gets or sets the SSDPTracingFilter\r\nGets or sets a value indicating whether an IP address is to be used to filter the detailed ssdp logs that are being sent to the console/log.\r\nIf the setting \"Emby.Dlna\": \"Debug\" msut be set in logging.default.json for this property to work." - }, - "UDPSendCount": { - "type": "integer", - "description": "Gets or sets the number of times SSDP UDP messages are sent.", - "format": "int32" - }, - "UDPSendDelay": { - "type": "integer", - "description": "Gets or sets the delay between each groups of SSDP messages (in ms).", - "format": "int32" - }, - "IgnoreVirtualInterfaces": { - "type": "boolean", - "description": "Gets or sets a value indicating whether address names that match Jellyfin.Networking.Configuration.NetworkConfiguration.VirtualInterfaceNames should be Ignore for the purposes of binding." - }, - "VirtualInterfaceNames": { - "type": "string", - "description": "Gets or sets a value indicating the interfaces that should be ignored. The list can be comma separated. ." - }, - "GatewayMonitorPeriod": { - "type": "integer", - "description": "Gets or sets the time (in seconds) between the pings of SSDP gateway monitor.", - "format": "int32" - }, - "EnableMultiSocketBinding": { - "type": "boolean", - "description": "Gets a value indicating whether multi-socket binding is available.", - "readOnly": true - }, - "TrustAllIP6Interfaces": { - "type": "boolean", - "description": "Gets or sets a value indicating whether all IPv6 interfaces should be treated as on the internal network.\r\nDepending on the address range implemented ULA ranges might not be used." - }, - "HDHomerunPortRange": { - "type": "string", - "description": "Gets or sets the ports that HDHomerun uses." - }, - "PublishedServerUriBySubnet": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Gets or sets the PublishedServerUriBySubnet\r\nGets or sets PublishedServerUri to advertise for specific subnets." - }, - "AutoDiscoveryTracing": { - "type": "boolean", - "description": "Gets or sets a value indicating whether Autodiscovery tracing is enabled." - }, "AutoDiscovery": { "type": "boolean", "description": "Gets or sets a value indicating whether Autodiscovery is enabled." }, - "RemoteIPFilter": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Gets or sets the filter for remote IP connectivity. Used in conjuntion with ." - }, - "IsRemoteIPFilterBlacklist": { - "type": "boolean", - "description": "Gets or sets a value indicating whether contains a blacklist or a whitelist. Default is a whitelist." - }, "EnableUPnP": { "type": "boolean", "description": "Gets or sets a value indicating whether to enable automatic port forwarding." }, + "EnableIPv4": { + "type": "boolean", + "description": "Gets or sets a value indicating whether IPv6 is enabled." + }, + "EnableIPv6": { + "type": "boolean", + "description": "Gets or sets a value indicating whether IPv6 is enabled." + }, "EnableRemoteAccess": { "type": "boolean", - "description": "Gets or sets a value indicating whether access outside of the LAN is permitted." + "description": "Gets or sets a value indicating whether access from outside of the LAN is permitted." }, "LocalNetworkSubnets": { "type": "array", @@ -50472,15 +50720,44 @@ "items": { "type": "string" }, - "description": "Gets or sets the known proxies. If the proxy is a network, it's added to the KnownNetworks." + "description": "Gets or sets the known proxies." + }, + "IgnoreVirtualInterfaces": { + "type": "boolean", + "description": "Gets or sets a value indicating whether address names that match MediaBrowser.Common.Net.NetworkConfiguration.VirtualInterfaceNames should be ignored for the purposes of binding." + }, + "VirtualInterfaceNames": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Gets or sets a value indicating the interface name prefixes that should be ignored. The list can be comma separated and values are case-insensitive. ." }, "EnablePublishedServerUriByRequest": { "type": "boolean", "description": "Gets or sets a value indicating whether the published server uri is based on information in HTTP requests." + }, + "PublishedServerUriBySubnet": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Gets or sets the PublishedServerUriBySubnet\r\nGets or sets PublishedServerUri to advertise for specific subnets." + }, + "RemoteIPFilter": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Gets or sets the filter for remote IP connectivity. Used in conjunction with ." + }, + "IsRemoteIPFilterBlacklist": { + "type": "boolean", + "description": "Gets or sets a value indicating whether contains a blacklist or a whitelist. Default is a whitelist." } }, "additionalProperties": false, - "description": "Defines the Jellyfin.Networking.Configuration.NetworkConfiguration." + "description": "Defines the MediaBrowser.Common.Net.NetworkConfiguration." }, "NewGroupRequestDto": { "type": "object", @@ -50505,201 +50782,6 @@ "additionalProperties": false, "description": "Class NextItemRequestDto." }, - "NotificationDto": { - "type": "object", - "properties": { - "Id": { - "type": "string", - "description": "Gets or sets the notification ID. Defaults to an empty string." - }, - "UserId": { - "type": "string", - "description": "Gets or sets the notification's user ID. Defaults to an empty string." - }, - "Date": { - "type": "string", - "description": "Gets or sets the notification date.", - "format": "date-time" - }, - "IsRead": { - "type": "boolean", - "description": "Gets or sets a value indicating whether the notification has been read. Defaults to false." - }, - "Name": { - "type": "string", - "description": "Gets or sets the notification's name. Defaults to an empty string." - }, - "Description": { - "type": "string", - "description": "Gets or sets the notification's description. Defaults to an empty string." - }, - "Url": { - "type": "string", - "description": "Gets or sets the notification's URL. Defaults to an empty string." - }, - "Level": { - "allOf": [ - { - "$ref": "#/components/schemas/NotificationLevel" - } - ], - "description": "Gets or sets the notification level." - } - }, - "additionalProperties": false, - "description": "The notification DTO." - }, - "NotificationLevel": { - "enum": [ - "Normal", - "Warning", - "Error" - ], - "type": "string" - }, - "NotificationOption": { - "type": "object", - "properties": { - "Type": { - "type": "string", - "nullable": true - }, - "DisabledMonitorUsers": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Gets or sets user Ids to not monitor (it's opt out)." - }, - "SendToUsers": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Gets or sets user Ids to send to (if SendToUserMode == Custom)." - }, - "Enabled": { - "type": "boolean", - "description": "Gets or sets a value indicating whether this MediaBrowser.Model.Notifications.NotificationOption is enabled." - }, - "DisabledServices": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Gets or sets the disabled services." - }, - "SendToUserMode": { - "allOf": [ - { - "$ref": "#/components/schemas/SendToUserType" - } - ], - "description": "Gets or sets the send to user mode." - } - }, - "additionalProperties": false - }, - "NotificationOptions": { - "type": "object", - "properties": { - "Options": { - "type": "array", - "items": { - "$ref": "#/components/schemas/NotificationOption" - }, - "nullable": true - } - }, - "additionalProperties": false - }, - "NotificationResultDto": { - "type": "object", - "properties": { - "Notifications": { - "type": "array", - "items": { - "$ref": "#/components/schemas/NotificationDto" - }, - "description": "Gets or sets the current page of notifications." - }, - "TotalRecordCount": { - "type": "integer", - "description": "Gets or sets the total number of notifications.", - "format": "int32" - } - }, - "additionalProperties": false, - "description": "A list of notifications with the total record count for pagination." - }, - "NotificationsSummaryDto": { - "type": "object", - "properties": { - "UnreadCount": { - "type": "integer", - "description": "Gets or sets the number of unread notifications.", - "format": "int32" - }, - "MaxUnreadNotificationLevel": { - "allOf": [ - { - "$ref": "#/components/schemas/NotificationLevel" - } - ], - "description": "Gets or sets the maximum unread notification level.", - "nullable": true - } - }, - "additionalProperties": false, - "description": "The notification summary DTO." - }, - "NotificationTypeInfo": { - "type": "object", - "properties": { - "Type": { - "type": "string", - "nullable": true - }, - "Name": { - "type": "string", - "nullable": true - }, - "Enabled": { - "type": "boolean" - }, - "Category": { - "type": "string", - "nullable": true - }, - "IsBasedOnUserEvent": { - "type": "boolean" - } - }, - "additionalProperties": false - }, - "ObjectGroupUpdate": { - "type": "object", - "properties": { - "GroupId": { - "type": "string", - "description": "Gets the group identifier.", - "format": "uuid" - }, - "Type": { - "allOf": [ - { - "$ref": "#/components/schemas/GroupUpdateType" - } - ], - "description": "Gets the update type." - }, - "Data": { - "description": "Gets the update data." - } - }, - "additionalProperties": false, - "description": "Class GroupUpdate." - }, "OpenLiveStreamDto": { "type": "object", "properties": { @@ -50785,6 +50867,187 @@ "additionalProperties": false, "description": "Open live stream dto." }, + "OutboundKeepAliveMessage": { + "type": "object", + "properties": { + "MessageId": { + "type": "string", + "description": "Gets or sets the message id.", + "format": "uuid" + }, + "MessageType": { + "enum": [ + "ForceKeepAlive", + "GeneralCommand", + "UserDataChanged", + "Sessions", + "Play", + "SyncPlayCommand", + "SyncPlayGroupUpdate", + "Playstate", + "RestartRequired", + "ServerShuttingDown", + "ServerRestarting", + "LibraryChanged", + "UserDeleted", + "UserUpdated", + "SeriesTimerCreated", + "TimerCreated", + "SeriesTimerCancelled", + "TimerCancelled", + "RefreshProgress", + "ScheduledTaskEnded", + "PackageInstallationCancelled", + "PackageInstallationFailed", + "PackageInstallationCompleted", + "PackageInstalling", + "PackageUninstalled", + "ActivityLogEntry", + "ScheduledTasksInfo", + "ActivityLogEntryStart", + "ActivityLogEntryStop", + "SessionsStart", + "SessionsStop", + "ScheduledTasksInfoStart", + "ScheduledTasksInfoStop", + "KeepAlive" + ], + "allOf": [ + { + "$ref": "#/components/schemas/SessionMessageType" + } + ], + "description": "The different kinds of messages that are used in the WebSocket api.", + "default": "KeepAlive", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "Keep alive websocket messages." + }, + "OutboundWebSocketMessage": { + "type": "object", + "oneOf": [ + { + "$ref": "#/components/schemas/ActivityLogEntryMessage" + }, + { + "$ref": "#/components/schemas/ForceKeepAliveMessage" + }, + { + "$ref": "#/components/schemas/GeneralCommandMessage" + }, + { + "$ref": "#/components/schemas/LibraryChangedMessage" + }, + { + "$ref": "#/components/schemas/OutboundKeepAliveMessage" + }, + { + "$ref": "#/components/schemas/PlayMessage" + }, + { + "$ref": "#/components/schemas/PlaystateMessage" + }, + { + "$ref": "#/components/schemas/PluginInstallationCancelledMessage" + }, + { + "$ref": "#/components/schemas/PluginInstallationCompletedMessage" + }, + { + "$ref": "#/components/schemas/PluginInstallationFailedMessage" + }, + { + "$ref": "#/components/schemas/PluginInstallingMessage" + }, + { + "$ref": "#/components/schemas/PluginUninstalledMessage" + }, + { + "$ref": "#/components/schemas/RefreshProgressMessage" + }, + { + "$ref": "#/components/schemas/RestartRequiredMessage" + }, + { + "$ref": "#/components/schemas/ScheduledTaskEndedMessage" + }, + { + "$ref": "#/components/schemas/ScheduledTasksInfoMessage" + }, + { + "$ref": "#/components/schemas/SeriesTimerCancelledMessage" + }, + { + "$ref": "#/components/schemas/SeriesTimerCreatedMessage" + }, + { + "$ref": "#/components/schemas/ServerRestartingMessage" + }, + { + "$ref": "#/components/schemas/ServerShuttingDownMessage" + }, + { + "$ref": "#/components/schemas/SessionsMessage" + }, + { + "$ref": "#/components/schemas/SyncPlayCommandMessage" + }, + { + "$ref": "#/components/schemas/SyncPlayGroupUpdateCommandMessage" + }, + { + "$ref": "#/components/schemas/TimerCancelledMessage" + }, + { + "$ref": "#/components/schemas/TimerCreatedMessage" + }, + { + "$ref": "#/components/schemas/UserDataChangedMessage" + }, + { + "$ref": "#/components/schemas/UserDeletedMessage" + }, + { + "$ref": "#/components/schemas/UserUpdatedMessage" + } + ], + "description": "Represents the list of possible outbound websocket types", + "discriminator": { + "propertyName": "MessageType", + "mapping": { + "ActivityLogEntry": "#/components/schemas/ActivityLogEntryMessage", + "ForceKeepAlive": "#/components/schemas/ForceKeepAliveMessage", + "GeneralCommand": "#/components/schemas/GeneralCommandMessage", + "LibraryChanged": "#/components/schemas/LibraryChangedMessage", + "KeepAlive": "#/components/schemas/OutboundKeepAliveMessage", + "Play": "#/components/schemas/PlayMessage", + "Playstate": "#/components/schemas/PlaystateMessage", + "PackageInstallationCancelled": "#/components/schemas/PluginInstallationCancelledMessage", + "PackageInstallationCompleted": "#/components/schemas/PluginInstallationCompletedMessage", + "PackageInstallationFailed": "#/components/schemas/PluginInstallationFailedMessage", + "PackageInstalling": "#/components/schemas/PluginInstallingMessage", + "PackageUninstalled": "#/components/schemas/PluginUninstalledMessage", + "RefreshProgress": "#/components/schemas/RefreshProgressMessage", + "RestartRequired": "#/components/schemas/RestartRequiredMessage", + "ScheduledTaskEnded": "#/components/schemas/ScheduledTaskEndedMessage", + "ScheduledTasksInfo": "#/components/schemas/ScheduledTasksInfoMessage", + "SeriesTimerCancelled": "#/components/schemas/SeriesTimerCancelledMessage", + "SeriesTimerCreated": "#/components/schemas/SeriesTimerCreatedMessage", + "ServerRestarting": "#/components/schemas/ServerRestartingMessage", + "ServerShuttingDown": "#/components/schemas/ServerShuttingDownMessage", + "Sessions": "#/components/schemas/SessionsMessage", + "SyncPlayCommand": "#/components/schemas/SyncPlayCommandMessage", + "SyncPlayGroupUpdate": "#/components/schemas/SyncPlayGroupUpdateCommandMessage", + "TimerCancelled": "#/components/schemas/TimerCancelledMessage", + "TimerCreated": "#/components/schemas/TimerCreatedMessage", + "UserDataChanged": "#/components/schemas/UserDataChangedMessage", + "UserDeleted": "#/components/schemas/UserDeletedMessage", + "UserUpdated": "#/components/schemas/UserUpdatedMessage" + } + } + }, "PackageInfo": { "type": "object", "properties": { @@ -50840,7 +51103,8 @@ "Value": { "type": "integer", "description": "Gets or sets the value.", - "format": "int32" + "format": "int32", + "nullable": true } }, "additionalProperties": false, @@ -50861,6 +51125,37 @@ "additionalProperties": false, "description": "Defines the MediaBrowser.Model.Configuration.PathSubstitution." }, + "PersonKind": { + "enum": [ + "Unknown", + "Actor", + "Director", + "Composer", + "Writer", + "GuestStar", + "Producer", + "Conductor", + "Lyricist", + "Arranger", + "Engineer", + "Mixer", + "Remixer", + "Creator", + "Artist", + "AlbumArtist", + "Author", + "Illustrator", + "Penciller", + "Inker", + "Colorist", + "Letterer", + "CoverArtist", + "Editor", + "Translator" + ], + "type": "string", + "description": "The person kind." + }, "PersonLookupInfo": { "type": "object", "properties": { @@ -51104,6 +51399,11 @@ "nullable": true }, "ErrorCode": { + "enum": [ + "NotAllowed", + "NoCompatibleStream", + "RateLimitExceeded" + ], "allOf": [ { "$ref": "#/components/schemas/PlaybackErrorCode" @@ -51116,6 +51416,14 @@ "additionalProperties": false, "description": "Class PlaybackInfoResponse." }, + "PlaybackOrder": { + "enum": [ + "Default", + "Shuffle" + ], + "type": "string", + "description": "Enum PlaybackOrder." + }, "PlaybackProgressInfo": { "type": "object", "properties": { @@ -51194,6 +51502,11 @@ "nullable": true }, "PlayMethod": { + "enum": [ + "Transcode", + "DirectStream", + "DirectPlay" + ], "allOf": [ { "$ref": "#/components/schemas/PlayMethod" @@ -51212,6 +51525,11 @@ "nullable": true }, "RepeatMode": { + "enum": [ + "RepeatNone", + "RepeatAll", + "RepeatOne" + ], "allOf": [ { "$ref": "#/components/schemas/RepeatMode" @@ -51219,6 +51537,18 @@ ], "description": "Gets or sets the repeat mode." }, + "PlaybackOrder": { + "enum": [ + "Default", + "Shuffle" + ], + "allOf": [ + { + "$ref": "#/components/schemas/PlaybackOrder" + } + ], + "description": "Gets or sets the playback order." + }, "NowPlayingQueue": { "type": "array", "items": { @@ -51234,6 +51564,29 @@ "additionalProperties": false, "description": "Class PlaybackProgressInfo." }, + "PlaybackRequestType": { + "enum": [ + "Play", + "SetPlaylistItem", + "RemoveFromPlaylist", + "MovePlaylistItem", + "Queue", + "Unpause", + "Pause", + "Stop", + "Seek", + "Buffer", + "Ready", + "NextItem", + "PreviousItem", + "SetRepeatMode", + "SetShuffleMode", + "Ping", + "IgnoreWait" + ], + "type": "string", + "description": "Enum PlaybackRequestType." + }, "PlaybackStartInfo": { "type": "object", "properties": { @@ -51312,6 +51665,11 @@ "nullable": true }, "PlayMethod": { + "enum": [ + "Transcode", + "DirectStream", + "DirectPlay" + ], "allOf": [ { "$ref": "#/components/schemas/PlayMethod" @@ -51330,6 +51688,11 @@ "nullable": true }, "RepeatMode": { + "enum": [ + "RepeatNone", + "RepeatAll", + "RepeatOne" + ], "allOf": [ { "$ref": "#/components/schemas/RepeatMode" @@ -51337,6 +51700,18 @@ ], "description": "Gets or sets the repeat mode." }, + "PlaybackOrder": { + "enum": [ + "Default", + "Shuffle" + ], + "allOf": [ + { + "$ref": "#/components/schemas/PlaybackOrder" + } + ], + "description": "Gets or sets the playback order." + }, "NowPlayingQueue": { "type": "array", "items": { @@ -51474,6 +51849,11 @@ "nullable": true }, "PlayMethod": { + "enum": [ + "Transcode", + "DirectStream", + "DirectPlay" + ], "allOf": [ { "$ref": "#/components/schemas/PlayMethod" @@ -51483,6 +51863,11 @@ "nullable": true }, "RepeatMode": { + "enum": [ + "RepeatNone", + "RepeatAll", + "RepeatOne" + ], "allOf": [ { "$ref": "#/components/schemas/RepeatMode" @@ -51490,6 +51875,18 @@ ], "description": "Gets or sets the repeat mode." }, + "PlaybackOrder": { + "enum": [ + "Default", + "Shuffle" + ], + "allOf": [ + { + "$ref": "#/components/schemas/PlaybackOrder" + } + ], + "description": "Gets or sets the playback order." + }, "LiveStreamId": { "type": "string", "description": "Gets or sets the now playing live stream identifier.", @@ -51507,6 +51904,89 @@ }, "additionalProperties": false }, + "PlaylistUserPermissions": { + "type": "object", + "properties": { + "UserId": { + "type": "string", + "description": "Gets or sets the user id.", + "format": "uuid" + }, + "CanEdit": { + "type": "boolean", + "description": "Gets or sets a value indicating whether the user has edit permissions." + } + }, + "additionalProperties": false, + "description": "Class to hold data on user permissions for playlists." + }, + "PlayMessage": { + "type": "object", + "properties": { + "Data": { + "allOf": [ + { + "$ref": "#/components/schemas/PlayRequest" + } + ], + "description": "Class PlayRequest.", + "nullable": true + }, + "MessageId": { + "type": "string", + "description": "Gets or sets the message id.", + "format": "uuid" + }, + "MessageType": { + "enum": [ + "ForceKeepAlive", + "GeneralCommand", + "UserDataChanged", + "Sessions", + "Play", + "SyncPlayCommand", + "SyncPlayGroupUpdate", + "Playstate", + "RestartRequired", + "ServerShuttingDown", + "ServerRestarting", + "LibraryChanged", + "UserDeleted", + "UserUpdated", + "SeriesTimerCreated", + "TimerCreated", + "SeriesTimerCancelled", + "TimerCancelled", + "RefreshProgress", + "ScheduledTaskEnded", + "PackageInstallationCancelled", + "PackageInstallationFailed", + "PackageInstallationCompleted", + "PackageInstalling", + "PackageUninstalled", + "ActivityLogEntry", + "ScheduledTasksInfo", + "ActivityLogEntryStart", + "ActivityLogEntryStop", + "SessionsStart", + "SessionsStop", + "ScheduledTasksInfoStart", + "ScheduledTasksInfoStop", + "KeepAlive" + ], + "allOf": [ + { + "$ref": "#/components/schemas/SessionMessageType" + } + ], + "description": "The different kinds of messages that are used in the WebSocket api.", + "default": "Play", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "Play command websocket message." + }, "PlayMethod": { "enum": [ "Transcode", @@ -51515,6 +51995,142 @@ ], "type": "string" }, + "PlayQueueUpdate": { + "type": "object", + "properties": { + "Reason": { + "enum": [ + "NewPlaylist", + "SetCurrentItem", + "RemoveItems", + "MoveItem", + "Queue", + "QueueNext", + "NextItem", + "PreviousItem", + "RepeatMode", + "ShuffleMode" + ], + "allOf": [ + { + "$ref": "#/components/schemas/PlayQueueUpdateReason" + } + ], + "description": "Gets the request type that originated this update." + }, + "LastUpdate": { + "type": "string", + "description": "Gets the UTC time of the last change to the playing queue.", + "format": "date-time" + }, + "Playlist": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SyncPlayQueueItem" + }, + "description": "Gets the playlist." + }, + "PlayingItemIndex": { + "type": "integer", + "description": "Gets the playing item index in the playlist.", + "format": "int32" + }, + "StartPositionTicks": { + "type": "integer", + "description": "Gets the start position ticks.", + "format": "int64" + }, + "IsPlaying": { + "type": "boolean", + "description": "Gets a value indicating whether the current item is playing." + }, + "ShuffleMode": { + "enum": [ + "Sorted", + "Shuffle" + ], + "allOf": [ + { + "$ref": "#/components/schemas/GroupShuffleMode" + } + ], + "description": "Gets the shuffle mode." + }, + "RepeatMode": { + "enum": [ + "RepeatOne", + "RepeatAll", + "RepeatNone" + ], + "allOf": [ + { + "$ref": "#/components/schemas/GroupRepeatMode" + } + ], + "description": "Gets the repeat mode." + } + }, + "additionalProperties": false, + "description": "Class PlayQueueUpdate." + }, + "PlayQueueUpdateGroupUpdate": { + "type": "object", + "properties": { + "GroupId": { + "type": "string", + "description": "Gets the group identifier.", + "format": "uuid", + "readOnly": true + }, + "Type": { + "enum": [ + "UserJoined", + "UserLeft", + "GroupJoined", + "GroupLeft", + "StateUpdate", + "PlayQueue", + "NotInGroup", + "GroupDoesNotExist", + "CreateGroupDenied", + "JoinGroupDenied", + "LibraryAccessDenied" + ], + "allOf": [ + { + "$ref": "#/components/schemas/GroupUpdateType" + } + ], + "description": "Gets the update type." + }, + "Data": { + "allOf": [ + { + "$ref": "#/components/schemas/PlayQueueUpdate" + } + ], + "description": "Gets the update data." + } + }, + "additionalProperties": false, + "description": "Class GroupUpdate." + }, + "PlayQueueUpdateReason": { + "enum": [ + "NewPlaylist", + "SetCurrentItem", + "RemoveItems", + "MoveItem", + "Queue", + "QueueNext", + "NextItem", + "PreviousItem", + "RepeatMode", + "ShuffleMode" + ], + "type": "string", + "description": "Enum PlayQueueUpdateReason." + }, "PlayRequest": { "type": "object", "properties": { @@ -51534,6 +52150,13 @@ "nullable": true }, "PlayCommand": { + "enum": [ + "PlayNow", + "PlayNext", + "PlayLast", + "PlayInstantMix", + "PlayShuffle" + ], "allOf": [ { "$ref": "#/components/schemas/PlayCommand" @@ -51609,10 +52232,88 @@ "type": "string", "description": "Enum PlaystateCommand." }, + "PlaystateMessage": { + "type": "object", + "properties": { + "Data": { + "allOf": [ + { + "$ref": "#/components/schemas/PlaystateRequest" + } + ], + "description": "Gets or sets the data.", + "nullable": true + }, + "MessageId": { + "type": "string", + "description": "Gets or sets the message id.", + "format": "uuid" + }, + "MessageType": { + "enum": [ + "ForceKeepAlive", + "GeneralCommand", + "UserDataChanged", + "Sessions", + "Play", + "SyncPlayCommand", + "SyncPlayGroupUpdate", + "Playstate", + "RestartRequired", + "ServerShuttingDown", + "ServerRestarting", + "LibraryChanged", + "UserDeleted", + "UserUpdated", + "SeriesTimerCreated", + "TimerCreated", + "SeriesTimerCancelled", + "TimerCancelled", + "RefreshProgress", + "ScheduledTaskEnded", + "PackageInstallationCancelled", + "PackageInstallationFailed", + "PackageInstallationCompleted", + "PackageInstalling", + "PackageUninstalled", + "ActivityLogEntry", + "ScheduledTasksInfo", + "ActivityLogEntryStart", + "ActivityLogEntryStop", + "SessionsStart", + "SessionsStop", + "ScheduledTasksInfoStart", + "ScheduledTasksInfoStop", + "KeepAlive" + ], + "allOf": [ + { + "$ref": "#/components/schemas/SessionMessageType" + } + ], + "description": "The different kinds of messages that are used in the WebSocket api.", + "default": "Playstate", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "Playstate message." + }, "PlaystateRequest": { "type": "object", "properties": { "Command": { + "enum": [ + "Stop", + "Pause", + "Unpause", + "NextTrack", + "PreviousTrack", + "Seek", + "Rewind", + "FastForward", + "PlayPause" + ], "allOf": [ { "$ref": "#/components/schemas/PlaystateCommand" @@ -51667,6 +52368,15 @@ "description": "Gets or sets a value indicating whether this plugin has a valid image." }, "Status": { + "enum": [ + "Active", + "Restart", + "Deleted", + "Superceded", + "Malfunctioned", + "NotSupported", + "Disabled" + ], "allOf": [ { "$ref": "#/components/schemas/PluginStatus" @@ -51678,6 +52388,274 @@ "additionalProperties": false, "description": "This is a serializable stub class that is used by the api to provide information about installed plugins." }, + "PluginInstallationCancelledMessage": { + "type": "object", + "properties": { + "Data": { + "allOf": [ + { + "$ref": "#/components/schemas/InstallationInfo" + } + ], + "description": "Class InstallationInfo.", + "nullable": true + }, + "MessageId": { + "type": "string", + "description": "Gets or sets the message id.", + "format": "uuid" + }, + "MessageType": { + "enum": [ + "ForceKeepAlive", + "GeneralCommand", + "UserDataChanged", + "Sessions", + "Play", + "SyncPlayCommand", + "SyncPlayGroupUpdate", + "Playstate", + "RestartRequired", + "ServerShuttingDown", + "ServerRestarting", + "LibraryChanged", + "UserDeleted", + "UserUpdated", + "SeriesTimerCreated", + "TimerCreated", + "SeriesTimerCancelled", + "TimerCancelled", + "RefreshProgress", + "ScheduledTaskEnded", + "PackageInstallationCancelled", + "PackageInstallationFailed", + "PackageInstallationCompleted", + "PackageInstalling", + "PackageUninstalled", + "ActivityLogEntry", + "ScheduledTasksInfo", + "ActivityLogEntryStart", + "ActivityLogEntryStop", + "SessionsStart", + "SessionsStop", + "ScheduledTasksInfoStart", + "ScheduledTasksInfoStop", + "KeepAlive" + ], + "allOf": [ + { + "$ref": "#/components/schemas/SessionMessageType" + } + ], + "description": "The different kinds of messages that are used in the WebSocket api.", + "default": "PackageInstallationCancelled", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "Plugin installation cancelled message." + }, + "PluginInstallationCompletedMessage": { + "type": "object", + "properties": { + "Data": { + "allOf": [ + { + "$ref": "#/components/schemas/InstallationInfo" + } + ], + "description": "Class InstallationInfo.", + "nullable": true + }, + "MessageId": { + "type": "string", + "description": "Gets or sets the message id.", + "format": "uuid" + }, + "MessageType": { + "enum": [ + "ForceKeepAlive", + "GeneralCommand", + "UserDataChanged", + "Sessions", + "Play", + "SyncPlayCommand", + "SyncPlayGroupUpdate", + "Playstate", + "RestartRequired", + "ServerShuttingDown", + "ServerRestarting", + "LibraryChanged", + "UserDeleted", + "UserUpdated", + "SeriesTimerCreated", + "TimerCreated", + "SeriesTimerCancelled", + "TimerCancelled", + "RefreshProgress", + "ScheduledTaskEnded", + "PackageInstallationCancelled", + "PackageInstallationFailed", + "PackageInstallationCompleted", + "PackageInstalling", + "PackageUninstalled", + "ActivityLogEntry", + "ScheduledTasksInfo", + "ActivityLogEntryStart", + "ActivityLogEntryStop", + "SessionsStart", + "SessionsStop", + "ScheduledTasksInfoStart", + "ScheduledTasksInfoStop", + "KeepAlive" + ], + "allOf": [ + { + "$ref": "#/components/schemas/SessionMessageType" + } + ], + "description": "The different kinds of messages that are used in the WebSocket api.", + "default": "PackageInstallationCompleted", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "Plugin installation completed message." + }, + "PluginInstallationFailedMessage": { + "type": "object", + "properties": { + "Data": { + "allOf": [ + { + "$ref": "#/components/schemas/InstallationInfo" + } + ], + "description": "Class InstallationInfo.", + "nullable": true + }, + "MessageId": { + "type": "string", + "description": "Gets or sets the message id.", + "format": "uuid" + }, + "MessageType": { + "enum": [ + "ForceKeepAlive", + "GeneralCommand", + "UserDataChanged", + "Sessions", + "Play", + "SyncPlayCommand", + "SyncPlayGroupUpdate", + "Playstate", + "RestartRequired", + "ServerShuttingDown", + "ServerRestarting", + "LibraryChanged", + "UserDeleted", + "UserUpdated", + "SeriesTimerCreated", + "TimerCreated", + "SeriesTimerCancelled", + "TimerCancelled", + "RefreshProgress", + "ScheduledTaskEnded", + "PackageInstallationCancelled", + "PackageInstallationFailed", + "PackageInstallationCompleted", + "PackageInstalling", + "PackageUninstalled", + "ActivityLogEntry", + "ScheduledTasksInfo", + "ActivityLogEntryStart", + "ActivityLogEntryStop", + "SessionsStart", + "SessionsStop", + "ScheduledTasksInfoStart", + "ScheduledTasksInfoStop", + "KeepAlive" + ], + "allOf": [ + { + "$ref": "#/components/schemas/SessionMessageType" + } + ], + "description": "The different kinds of messages that are used in the WebSocket api.", + "default": "PackageInstallationFailed", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "Plugin installation failed message." + }, + "PluginInstallingMessage": { + "type": "object", + "properties": { + "Data": { + "allOf": [ + { + "$ref": "#/components/schemas/InstallationInfo" + } + ], + "description": "Class InstallationInfo.", + "nullable": true + }, + "MessageId": { + "type": "string", + "description": "Gets or sets the message id.", + "format": "uuid" + }, + "MessageType": { + "enum": [ + "ForceKeepAlive", + "GeneralCommand", + "UserDataChanged", + "Sessions", + "Play", + "SyncPlayCommand", + "SyncPlayGroupUpdate", + "Playstate", + "RestartRequired", + "ServerShuttingDown", + "ServerRestarting", + "LibraryChanged", + "UserDeleted", + "UserUpdated", + "SeriesTimerCreated", + "TimerCreated", + "SeriesTimerCancelled", + "TimerCancelled", + "RefreshProgress", + "ScheduledTaskEnded", + "PackageInstallationCancelled", + "PackageInstallationFailed", + "PackageInstallationCompleted", + "PackageInstalling", + "PackageUninstalled", + "ActivityLogEntry", + "ScheduledTasksInfo", + "ActivityLogEntryStart", + "ActivityLogEntryStop", + "SessionsStart", + "SessionsStop", + "ScheduledTasksInfoStart", + "ScheduledTasksInfoStop", + "KeepAlive" + ], + "allOf": [ + { + "$ref": "#/components/schemas/SessionMessageType" + } + ], + "description": "The different kinds of messages that are used in the WebSocket api.", + "default": "PackageInstalling", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "Package installing message." + }, "PluginStatus": { "enum": [ "Active", @@ -51691,6 +52669,73 @@ "type": "string", "description": "Plugin load status." }, + "PluginUninstalledMessage": { + "type": "object", + "properties": { + "Data": { + "allOf": [ + { + "$ref": "#/components/schemas/PluginInfo" + } + ], + "description": "This is a serializable stub class that is used by the api to provide information about installed plugins.", + "nullable": true + }, + "MessageId": { + "type": "string", + "description": "Gets or sets the message id.", + "format": "uuid" + }, + "MessageType": { + "enum": [ + "ForceKeepAlive", + "GeneralCommand", + "UserDataChanged", + "Sessions", + "Play", + "SyncPlayCommand", + "SyncPlayGroupUpdate", + "Playstate", + "RestartRequired", + "ServerShuttingDown", + "ServerRestarting", + "LibraryChanged", + "UserDeleted", + "UserUpdated", + "SeriesTimerCreated", + "TimerCreated", + "SeriesTimerCancelled", + "TimerCancelled", + "RefreshProgress", + "ScheduledTaskEnded", + "PackageInstallationCancelled", + "PackageInstallationFailed", + "PackageInstallationCompleted", + "PackageInstalling", + "PackageUninstalled", + "ActivityLogEntry", + "ScheduledTasksInfo", + "ActivityLogEntryStart", + "ActivityLogEntryStop", + "SessionsStart", + "SessionsStop", + "ScheduledTasksInfoStart", + "ScheduledTasksInfoStop", + "KeepAlive" + ], + "allOf": [ + { + "$ref": "#/components/schemas/SessionMessageType" + } + ], + "description": "The different kinds of messages that are used in the WebSocket api.", + "default": "PackageUninstalled", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "Plugin uninstalled message." + }, "PreviousItemRequestDto": { "type": "object", "properties": { @@ -51730,10 +52775,28 @@ }, "additionalProperties": { } }, + "ProcessPriorityClass": { + "enum": [ + "Normal", + "Idle", + "High", + "RealTime", + "BelowNormal", + "AboveNormal" + ], + "type": "string" + }, "ProfileCondition": { "type": "object", "properties": { "Condition": { + "enum": [ + "Equals", + "NotEquals", + "LessThanEqual", + "GreaterThanEqual", + "EqualsAny" + ], "allOf": [ { "$ref": "#/components/schemas/ProfileConditionType" @@ -51741,6 +52804,32 @@ ] }, "Property": { + "enum": [ + "AudioChannels", + "AudioBitrate", + "AudioProfile", + "Width", + "Height", + "Has64BitOffsets", + "PacketLength", + "VideoBitDepth", + "VideoBitrate", + "VideoFramerate", + "VideoLevel", + "VideoProfile", + "VideoTimestamp", + "IsAnamorphic", + "RefFrames", + "NumAudioStreams", + "NumVideoStreams", + "IsSecondaryAudio", + "VideoCodecTag", + "IsAvc", + "IsInterlaced", + "AudioSampleRate", + "AudioBitDepth", + "VideoRangeType" + ], "allOf": [ { "$ref": "#/components/schemas/ProfileConditionValue" @@ -51833,7 +52922,8 @@ "OperatingSystem": { "type": "string", "description": "Gets or sets the operating system.", - "nullable": true + "nullable": true, + "deprecated": true }, "Id": { "type": "string", @@ -51929,6 +53019,10 @@ "description": "Gets or sets the items to enqueue." }, "Mode": { + "enum": [ + "Queue", + "QueueNext" + ], "allOf": [ { "$ref": "#/components/schemas/GroupQueueMode" @@ -52038,6 +53132,14 @@ "nullable": true }, "RecommendationType": { + "enum": [ + "SimilarToRecentlyPlayed", + "SimilarToLikedItem", + "HasDirectorFromRecentlyPlayed", + "HasActorFromRecentlyPlayed", + "HasLikedDirector", + "HasLikedActor" + ], "allOf": [ { "$ref": "#/components/schemas/RecommendationType" @@ -52078,6 +53180,73 @@ ], "type": "string" }, + "RefreshProgressMessage": { + "type": "object", + "properties": { + "Data": { + "type": "object", + "additionalProperties": { + "type": "string", + "nullable": true + }, + "description": "Gets or sets the data.", + "nullable": true + }, + "MessageId": { + "type": "string", + "description": "Gets or sets the message id.", + "format": "uuid" + }, + "MessageType": { + "enum": [ + "ForceKeepAlive", + "GeneralCommand", + "UserDataChanged", + "Sessions", + "Play", + "SyncPlayCommand", + "SyncPlayGroupUpdate", + "Playstate", + "RestartRequired", + "ServerShuttingDown", + "ServerRestarting", + "LibraryChanged", + "UserDeleted", + "UserUpdated", + "SeriesTimerCreated", + "TimerCreated", + "SeriesTimerCancelled", + "TimerCancelled", + "RefreshProgress", + "ScheduledTaskEnded", + "PackageInstallationCancelled", + "PackageInstallationFailed", + "PackageInstallationCompleted", + "PackageInstalling", + "PackageUninstalled", + "ActivityLogEntry", + "ScheduledTasksInfo", + "ActivityLogEntryStart", + "ActivityLogEntryStop", + "SessionsStart", + "SessionsStop", + "ScheduledTasksInfoStart", + "ScheduledTasksInfoStop", + "KeepAlive" + ], + "allOf": [ + { + "$ref": "#/components/schemas/SessionMessageType" + } + ], + "description": "The different kinds of messages that are used in the WebSocket api.", + "default": "RefreshProgress", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "Refresh progress message." + }, "RemoteImageInfo": { "type": "object", "properties": { @@ -52126,6 +53295,21 @@ "nullable": true }, "Type": { + "enum": [ + "Primary", + "Art", + "Backdrop", + "Banner", + "Logo", + "Thumb", + "Disc", + "Box", + "Screenshot", + "Menu", + "Chapter", + "BoxRear", + "Profile" + ], "allOf": [ { "$ref": "#/components/schemas/ImageType" @@ -52134,6 +53318,10 @@ "description": "Gets or sets the type." }, "RatingType": { + "enum": [ + "Score", + "Likes" + ], "allOf": [ { "$ref": "#/components/schemas/RatingType" @@ -52173,6 +53361,29 @@ "additionalProperties": false, "description": "Class RemoteImageResult." }, + "RemoteLyricInfoDto": { + "type": "object", + "properties": { + "Id": { + "type": "string", + "description": "Gets or sets the id for the lyric." + }, + "ProviderName": { + "type": "string", + "description": "Gets the provider name." + }, + "Lyrics": { + "allOf": [ + { + "$ref": "#/components/schemas/LyricDto" + } + ], + "description": "Gets the lyrics." + } + }, + "additionalProperties": false, + "description": "The remote lyric info dto." + }, "RemoteSearchResult": { "type": "object", "properties": { @@ -52287,6 +53498,11 @@ "format": "float", "nullable": true }, + "FrameRate": { + "type": "number", + "format": "float", + "nullable": true + }, "DownloadCount": { "type": "integer", "format": "int32", @@ -52295,6 +53511,22 @@ "IsHashMatch": { "type": "boolean", "nullable": true + }, + "AiTranslated": { + "type": "boolean", + "nullable": true + }, + "MachineTranslated": { + "type": "boolean", + "nullable": true + }, + "Forced": { + "type": "boolean", + "nullable": true + }, + "HearingImpaired": { + "type": "boolean", + "nullable": true } }, "additionalProperties": false @@ -52308,7 +53540,7 @@ "type": "string", "format": "uuid" }, - "description": "Gets or sets the playlist identifiers ot the items. Ignored when clearing the playlist." + "description": "Gets or sets the playlist identifiers of the items. Ignored when clearing the playlist." }, "ClearPlaylist": { "type": "boolean", @@ -52351,45 +53583,307 @@ "additionalProperties": false, "description": "Class RepositoryInfo." }, - "ResponseProfile": { + "RestartRequiredMessage": { "type": "object", "properties": { - "Container": { + "MessageId": { "type": "string", - "nullable": true + "description": "Gets or sets the message id.", + "format": "uuid" }, - "AudioCodec": { - "type": "string", - "nullable": true - }, - "VideoCodec": { - "type": "string", - "nullable": true - }, - "Type": { + "MessageType": { + "enum": [ + "ForceKeepAlive", + "GeneralCommand", + "UserDataChanged", + "Sessions", + "Play", + "SyncPlayCommand", + "SyncPlayGroupUpdate", + "Playstate", + "RestartRequired", + "ServerShuttingDown", + "ServerRestarting", + "LibraryChanged", + "UserDeleted", + "UserUpdated", + "SeriesTimerCreated", + "TimerCreated", + "SeriesTimerCancelled", + "TimerCancelled", + "RefreshProgress", + "ScheduledTaskEnded", + "PackageInstallationCancelled", + "PackageInstallationFailed", + "PackageInstallationCompleted", + "PackageInstalling", + "PackageUninstalled", + "ActivityLogEntry", + "ScheduledTasksInfo", + "ActivityLogEntryStart", + "ActivityLogEntryStop", + "SessionsStart", + "SessionsStop", + "ScheduledTasksInfoStart", + "ScheduledTasksInfoStop", + "KeepAlive" + ], "allOf": [ { - "$ref": "#/components/schemas/DlnaProfileType" + "$ref": "#/components/schemas/SessionMessageType" } - ] - }, - "OrgPn": { - "type": "string", - "nullable": true - }, - "MimeType": { - "type": "string", - "nullable": true - }, - "Conditions": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ProfileCondition" - }, - "nullable": true + ], + "description": "The different kinds of messages that are used in the WebSocket api.", + "default": "RestartRequired", + "readOnly": true } }, - "additionalProperties": false + "additionalProperties": false, + "description": "Restart required." + }, + "ScheduledTaskEndedMessage": { + "type": "object", + "properties": { + "Data": { + "allOf": [ + { + "$ref": "#/components/schemas/TaskResult" + } + ], + "description": "Class TaskExecutionInfo.", + "nullable": true + }, + "MessageId": { + "type": "string", + "description": "Gets or sets the message id.", + "format": "uuid" + }, + "MessageType": { + "enum": [ + "ForceKeepAlive", + "GeneralCommand", + "UserDataChanged", + "Sessions", + "Play", + "SyncPlayCommand", + "SyncPlayGroupUpdate", + "Playstate", + "RestartRequired", + "ServerShuttingDown", + "ServerRestarting", + "LibraryChanged", + "UserDeleted", + "UserUpdated", + "SeriesTimerCreated", + "TimerCreated", + "SeriesTimerCancelled", + "TimerCancelled", + "RefreshProgress", + "ScheduledTaskEnded", + "PackageInstallationCancelled", + "PackageInstallationFailed", + "PackageInstallationCompleted", + "PackageInstalling", + "PackageUninstalled", + "ActivityLogEntry", + "ScheduledTasksInfo", + "ActivityLogEntryStart", + "ActivityLogEntryStop", + "SessionsStart", + "SessionsStop", + "ScheduledTasksInfoStart", + "ScheduledTasksInfoStop", + "KeepAlive" + ], + "allOf": [ + { + "$ref": "#/components/schemas/SessionMessageType" + } + ], + "description": "The different kinds of messages that are used in the WebSocket api.", + "default": "ScheduledTaskEnded", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "Scheduled task ended message." + }, + "ScheduledTasksInfoMessage": { + "type": "object", + "properties": { + "Data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TaskInfo" + }, + "description": "Gets or sets the data.", + "nullable": true + }, + "MessageId": { + "type": "string", + "description": "Gets or sets the message id.", + "format": "uuid" + }, + "MessageType": { + "enum": [ + "ForceKeepAlive", + "GeneralCommand", + "UserDataChanged", + "Sessions", + "Play", + "SyncPlayCommand", + "SyncPlayGroupUpdate", + "Playstate", + "RestartRequired", + "ServerShuttingDown", + "ServerRestarting", + "LibraryChanged", + "UserDeleted", + "UserUpdated", + "SeriesTimerCreated", + "TimerCreated", + "SeriesTimerCancelled", + "TimerCancelled", + "RefreshProgress", + "ScheduledTaskEnded", + "PackageInstallationCancelled", + "PackageInstallationFailed", + "PackageInstallationCompleted", + "PackageInstalling", + "PackageUninstalled", + "ActivityLogEntry", + "ScheduledTasksInfo", + "ActivityLogEntryStart", + "ActivityLogEntryStop", + "SessionsStart", + "SessionsStop", + "ScheduledTasksInfoStart", + "ScheduledTasksInfoStop", + "KeepAlive" + ], + "allOf": [ + { + "$ref": "#/components/schemas/SessionMessageType" + } + ], + "description": "The different kinds of messages that are used in the WebSocket api.", + "default": "ScheduledTasksInfo", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "Scheduled tasks info message." + }, + "ScheduledTasksInfoStartMessage": { + "type": "object", + "properties": { + "Data": { + "type": "string", + "description": "Gets or sets the data.", + "nullable": true + }, + "MessageType": { + "enum": [ + "ForceKeepAlive", + "GeneralCommand", + "UserDataChanged", + "Sessions", + "Play", + "SyncPlayCommand", + "SyncPlayGroupUpdate", + "Playstate", + "RestartRequired", + "ServerShuttingDown", + "ServerRestarting", + "LibraryChanged", + "UserDeleted", + "UserUpdated", + "SeriesTimerCreated", + "TimerCreated", + "SeriesTimerCancelled", + "TimerCancelled", + "RefreshProgress", + "ScheduledTaskEnded", + "PackageInstallationCancelled", + "PackageInstallationFailed", + "PackageInstallationCompleted", + "PackageInstalling", + "PackageUninstalled", + "ActivityLogEntry", + "ScheduledTasksInfo", + "ActivityLogEntryStart", + "ActivityLogEntryStop", + "SessionsStart", + "SessionsStop", + "ScheduledTasksInfoStart", + "ScheduledTasksInfoStop", + "KeepAlive" + ], + "allOf": [ + { + "$ref": "#/components/schemas/SessionMessageType" + } + ], + "description": "The different kinds of messages that are used in the WebSocket api.", + "default": "ScheduledTasksInfoStart", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "Scheduled tasks info start message.\r\nData is the timing data encoded as \"$initialDelay,$interval\" in ms." + }, + "ScheduledTasksInfoStopMessage": { + "type": "object", + "properties": { + "MessageType": { + "enum": [ + "ForceKeepAlive", + "GeneralCommand", + "UserDataChanged", + "Sessions", + "Play", + "SyncPlayCommand", + "SyncPlayGroupUpdate", + "Playstate", + "RestartRequired", + "ServerShuttingDown", + "ServerRestarting", + "LibraryChanged", + "UserDeleted", + "UserUpdated", + "SeriesTimerCreated", + "TimerCreated", + "SeriesTimerCancelled", + "TimerCancelled", + "RefreshProgress", + "ScheduledTaskEnded", + "PackageInstallationCancelled", + "PackageInstallationFailed", + "PackageInstallationCompleted", + "PackageInstalling", + "PackageUninstalled", + "ActivityLogEntry", + "ScheduledTasksInfo", + "ActivityLogEntryStart", + "ActivityLogEntryStop", + "SessionsStart", + "SessionsStop", + "ScheduledTasksInfoStart", + "ScheduledTasksInfoStop", + "KeepAlive" + ], + "allOf": [ + { + "$ref": "#/components/schemas/SessionMessageType" + } + ], + "description": "The different kinds of messages that are used in the WebSocket api.", + "default": "ScheduledTasksInfoStop", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "Scheduled tasks info stop message." }, "ScrollDirection": { "enum": [ @@ -52405,21 +53899,21 @@ "ItemId": { "type": "string", "description": "Gets or sets the item id.", - "format": "uuid" + "format": "uuid", + "deprecated": true }, "Id": { "type": "string", + "description": "Gets or sets the item id.", "format": "uuid" }, "Name": { "type": "string", - "description": "Gets or sets the name.", - "nullable": true + "description": "Gets or sets the name." }, "MatchedTerm": { "type": "string", - "description": "Gets or sets the matched term.", - "nullable": true + "description": "Gets or sets the matched term." }, "IndexNumber": { "type": "integer", @@ -52465,12 +53959,55 @@ "nullable": true }, "Type": { - "type": "string", - "description": "Gets or sets the type.", - "nullable": true + "enum": [ + "AggregateFolder", + "Audio", + "AudioBook", + "BasePluginFolder", + "Book", + "BoxSet", + "Channel", + "ChannelFolderItem", + "CollectionFolder", + "Episode", + "Folder", + "Genre", + "ManualPlaylistsFolder", + "Movie", + "LiveTvChannel", + "LiveTvProgram", + "MusicAlbum", + "MusicArtist", + "MusicGenre", + "MusicVideo", + "Person", + "Photo", + "PhotoAlbum", + "Playlist", + "PlaylistsFolder", + "Program", + "Recording", + "Season", + "Series", + "Studio", + "Trailer", + "TvChannel", + "TvProgram", + "UserRootFolder", + "UserView", + "Video", + "Year" + ], + "allOf": [ + { + "$ref": "#/components/schemas/BaseItemKind" + } + ], + "description": "Gets or sets the type." }, "IsFolder": { "type": "boolean", + "description": "Gets or sets a value indicating whether this instance is folder.", "nullable": true }, "RunTimeTicks": { @@ -52480,17 +54017,29 @@ "nullable": true }, "MediaType": { - "type": "string", - "description": "Gets or sets the type of the media.", - "nullable": true + "enum": [ + "Unknown", + "Video", + "Audio", + "Photo", + "Book" + ], + "allOf": [ + { + "$ref": "#/components/schemas/MediaType" + } + ], + "description": "Gets or sets the type of the media." }, "StartDate": { "type": "string", + "description": "Gets or sets the start date.", "format": "date-time", "nullable": true }, "EndDate": { "type": "string", + "description": "Gets or sets the end date.", "format": "date-time", "nullable": true }, @@ -52501,6 +54050,7 @@ }, "Status": { "type": "string", + "description": "Gets or sets the status.", "nullable": true }, "Album": { @@ -52510,7 +54060,9 @@ }, "AlbumId": { "type": "string", - "format": "uuid" + "description": "Gets or sets the album id.", + "format": "uuid", + "nullable": true }, "AlbumArtist": { "type": "string", @@ -52522,8 +54074,7 @@ "items": { "type": "string" }, - "description": "Gets or sets the artists.", - "nullable": true + "description": "Gets or sets the artists." }, "SongCount": { "type": "integer", @@ -52540,7 +54091,8 @@ "ChannelId": { "type": "string", "description": "Gets or sets the channel identifier.", - "format": "uuid" + "format": "uuid", + "nullable": true }, "ChannelName": { "type": "string", @@ -52613,6 +54165,12 @@ "nullable": true }, "Command": { + "enum": [ + "Unpause", + "Pause", + "Stop", + "Seek" + ], "allOf": [ { "$ref": "#/components/schemas/SendCommandType" @@ -52639,14 +54197,6 @@ "type": "string", "description": "Enum SendCommandType." }, - "SendToUserType": { - "enum": [ - "All", - "Admins", - "Custom" - ], - "type": "string" - }, "SeriesInfo": { "type": "object", "properties": { @@ -52741,10 +54291,145 @@ "SeriesStatus": { "enum": [ "Continuing", - "Ended" + "Ended", + "Unreleased" ], "type": "string", - "description": "Enum SeriesStatus." + "description": "The status of a series." + }, + "SeriesTimerCancelledMessage": { + "type": "object", + "properties": { + "Data": { + "allOf": [ + { + "$ref": "#/components/schemas/TimerEventInfo" + } + ], + "description": "Gets or sets the data.", + "nullable": true + }, + "MessageId": { + "type": "string", + "description": "Gets or sets the message id.", + "format": "uuid" + }, + "MessageType": { + "enum": [ + "ForceKeepAlive", + "GeneralCommand", + "UserDataChanged", + "Sessions", + "Play", + "SyncPlayCommand", + "SyncPlayGroupUpdate", + "Playstate", + "RestartRequired", + "ServerShuttingDown", + "ServerRestarting", + "LibraryChanged", + "UserDeleted", + "UserUpdated", + "SeriesTimerCreated", + "TimerCreated", + "SeriesTimerCancelled", + "TimerCancelled", + "RefreshProgress", + "ScheduledTaskEnded", + "PackageInstallationCancelled", + "PackageInstallationFailed", + "PackageInstallationCompleted", + "PackageInstalling", + "PackageUninstalled", + "ActivityLogEntry", + "ScheduledTasksInfo", + "ActivityLogEntryStart", + "ActivityLogEntryStop", + "SessionsStart", + "SessionsStop", + "ScheduledTasksInfoStart", + "ScheduledTasksInfoStop", + "KeepAlive" + ], + "allOf": [ + { + "$ref": "#/components/schemas/SessionMessageType" + } + ], + "description": "The different kinds of messages that are used in the WebSocket api.", + "default": "SeriesTimerCancelled", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "Series timer cancelled message." + }, + "SeriesTimerCreatedMessage": { + "type": "object", + "properties": { + "Data": { + "allOf": [ + { + "$ref": "#/components/schemas/TimerEventInfo" + } + ], + "description": "Gets or sets the data.", + "nullable": true + }, + "MessageId": { + "type": "string", + "description": "Gets or sets the message id.", + "format": "uuid" + }, + "MessageType": { + "enum": [ + "ForceKeepAlive", + "GeneralCommand", + "UserDataChanged", + "Sessions", + "Play", + "SyncPlayCommand", + "SyncPlayGroupUpdate", + "Playstate", + "RestartRequired", + "ServerShuttingDown", + "ServerRestarting", + "LibraryChanged", + "UserDeleted", + "UserUpdated", + "SeriesTimerCreated", + "TimerCreated", + "SeriesTimerCancelled", + "TimerCancelled", + "RefreshProgress", + "ScheduledTaskEnded", + "PackageInstallationCancelled", + "PackageInstallationFailed", + "PackageInstallationCompleted", + "PackageInstalling", + "PackageUninstalled", + "ActivityLogEntry", + "ScheduledTasksInfo", + "ActivityLogEntryStart", + "ActivityLogEntryStop", + "SessionsStart", + "SessionsStop", + "ScheduledTasksInfoStart", + "ScheduledTasksInfoStop", + "KeepAlive" + ], + "allOf": [ + { + "$ref": "#/components/schemas/SessionMessageType" + } + ], + "description": "The different kinds of messages that are used in the WebSocket api.", + "default": "SeriesTimerCreated", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "Series timer created message." }, "SeriesTimerInfoDto": { "type": "object", @@ -52859,6 +54544,12 @@ "description": "Gets or sets a value indicating whether this instance is post padding required." }, "KeepUntil": { + "enum": [ + "UntilDeleted", + "UntilSpaceNeeded", + "UntilWatched", + "UntilDate" + ], "allOf": [ { "$ref": "#/components/schemas/KeepUntil" @@ -52893,6 +54584,11 @@ "nullable": true }, "DayPattern": { + "enum": [ + "Daily", + "Weekdays", + "Weekends" + ], "allOf": [ { "$ref": "#/components/schemas/DayPattern" @@ -53067,12 +54763,26 @@ "description": "Gets or sets the remaining minutes of a book that can be played while still saving playstate. If this percentage is crossed playstate will be reset to the beginning and the item will be marked watched.", "format": "int32" }, + "InactiveSessionThreshold": { + "type": "integer", + "description": "Gets or sets the threshold in minutes after a inactive session gets closed automatically.\r\nIf set to 0 the check for inactive sessions gets disabled.", + "format": "int32" + }, "LibraryMonitorDelay": { "type": "integer", "description": "Gets or sets the delay in seconds that we will wait after a file system change to try and discover what has been added/removed\r\nSome delay is necessary with some items because their creation is not atomic. It involves the creation of several\r\ndifferent directories and files.", "format": "int32" }, + "LibraryUpdateDuration": { + "type": "integer", + "description": "Gets or sets the duration in seconds that we will wait after a library updated event before executing the library changed notification.", + "format": "int32" + }, "ImageSavingConvention": { + "enum": [ + "Legacy", + "Compatible" + ], "allOf": [ { "$ref": "#/components/schemas/ImageSavingConvention" @@ -53181,6 +54891,50 @@ "AllowClientLogUpload": { "type": "boolean", "description": "Gets or sets a value indicating whether clients should be allowed to upload logs." + }, + "DummyChapterDuration": { + "type": "integer", + "description": "Gets or sets the dummy chapter duration in seconds, use 0 (zero) or less to disable generation alltogether.", + "format": "int32" + }, + "ChapterImageResolution": { + "enum": [ + "MatchSource", + "P144", + "P240", + "P360", + "P480", + "P720", + "P1080", + "P1440", + "P2160" + ], + "allOf": [ + { + "$ref": "#/components/schemas/ImageResolution" + } + ], + "description": "Gets or sets the chapter image resolution." + }, + "ParallelImageEncodingLimit": { + "type": "integer", + "description": "Gets or sets the limit for parallel image encoding.", + "format": "int32" + }, + "CastReceiverApplications": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CastReceiverApplication" + }, + "description": "Gets or sets the list of cast receiver applications." + }, + "TrickplayOptions": { + "allOf": [ + { + "$ref": "#/components/schemas/TrickplayOptions" + } + ], + "description": "Gets or sets the trickplay options." } }, "additionalProperties": false, @@ -53210,6 +54964,122 @@ "additionalProperties": false, "description": "The server discovery info model." }, + "ServerRestartingMessage": { + "type": "object", + "properties": { + "MessageId": { + "type": "string", + "description": "Gets or sets the message id.", + "format": "uuid" + }, + "MessageType": { + "enum": [ + "ForceKeepAlive", + "GeneralCommand", + "UserDataChanged", + "Sessions", + "Play", + "SyncPlayCommand", + "SyncPlayGroupUpdate", + "Playstate", + "RestartRequired", + "ServerShuttingDown", + "ServerRestarting", + "LibraryChanged", + "UserDeleted", + "UserUpdated", + "SeriesTimerCreated", + "TimerCreated", + "SeriesTimerCancelled", + "TimerCancelled", + "RefreshProgress", + "ScheduledTaskEnded", + "PackageInstallationCancelled", + "PackageInstallationFailed", + "PackageInstallationCompleted", + "PackageInstalling", + "PackageUninstalled", + "ActivityLogEntry", + "ScheduledTasksInfo", + "ActivityLogEntryStart", + "ActivityLogEntryStop", + "SessionsStart", + "SessionsStop", + "ScheduledTasksInfoStart", + "ScheduledTasksInfoStop", + "KeepAlive" + ], + "allOf": [ + { + "$ref": "#/components/schemas/SessionMessageType" + } + ], + "description": "The different kinds of messages that are used in the WebSocket api.", + "default": "ServerRestarting", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "Server restarting down message." + }, + "ServerShuttingDownMessage": { + "type": "object", + "properties": { + "MessageId": { + "type": "string", + "description": "Gets or sets the message id.", + "format": "uuid" + }, + "MessageType": { + "enum": [ + "ForceKeepAlive", + "GeneralCommand", + "UserDataChanged", + "Sessions", + "Play", + "SyncPlayCommand", + "SyncPlayGroupUpdate", + "Playstate", + "RestartRequired", + "ServerShuttingDown", + "ServerRestarting", + "LibraryChanged", + "UserDeleted", + "UserUpdated", + "SeriesTimerCreated", + "TimerCreated", + "SeriesTimerCancelled", + "TimerCancelled", + "RefreshProgress", + "ScheduledTaskEnded", + "PackageInstallationCancelled", + "PackageInstallationFailed", + "PackageInstallationCompleted", + "PackageInstalling", + "PackageUninstalled", + "ActivityLogEntry", + "ScheduledTasksInfo", + "ActivityLogEntryStart", + "ActivityLogEntryStop", + "SessionsStart", + "SessionsStop", + "ScheduledTasksInfoStart", + "ScheduledTasksInfoStop", + "KeepAlive" + ], + "allOf": [ + { + "$ref": "#/components/schemas/SessionMessageType" + } + ], + "description": "The different kinds of messages that are used in the WebSocket api.", + "default": "ServerShuttingDown", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "Server shutting down message." + }, "SessionInfo": { "type": "object", "properties": { @@ -53244,7 +55114,7 @@ "PlayableMediaTypes": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/MediaType" }, "description": "Gets the playable media types.", "nullable": true, @@ -53280,6 +55150,12 @@ "description": "Gets or sets the last playback check in.", "format": "date-time" }, + "LastPausedDate": { + "type": "string", + "description": "Gets or sets the last paused date.", + "format": "date-time", + "nullable": true + }, "DeviceName": { "type": "string", "description": "Gets or sets the name of the device.", @@ -53296,16 +55172,7 @@ "$ref": "#/components/schemas/BaseItemDto" } ], - "description": "Gets or sets the now playing item.", - "nullable": true - }, - "FullNowPlayingItem": { - "allOf": [ - { - "$ref": "#/components/schemas/BaseItem" - } - ], - "description": "Class BaseItem.", + "description": "This is strictly used as a data transfer object from the api layer.\r\nThis holds information about a BaseItem in a format that is convenient for the client.", "nullable": true }, "NowViewingItem": { @@ -53430,6 +55297,183 @@ "type": "string", "description": "The different kinds of messages that are used in the WebSocket api." }, + "SessionsMessage": { + "type": "object", + "properties": { + "Data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SessionInfo" + }, + "description": "Gets or sets the data.", + "nullable": true + }, + "MessageId": { + "type": "string", + "description": "Gets or sets the message id.", + "format": "uuid" + }, + "MessageType": { + "enum": [ + "ForceKeepAlive", + "GeneralCommand", + "UserDataChanged", + "Sessions", + "Play", + "SyncPlayCommand", + "SyncPlayGroupUpdate", + "Playstate", + "RestartRequired", + "ServerShuttingDown", + "ServerRestarting", + "LibraryChanged", + "UserDeleted", + "UserUpdated", + "SeriesTimerCreated", + "TimerCreated", + "SeriesTimerCancelled", + "TimerCancelled", + "RefreshProgress", + "ScheduledTaskEnded", + "PackageInstallationCancelled", + "PackageInstallationFailed", + "PackageInstallationCompleted", + "PackageInstalling", + "PackageUninstalled", + "ActivityLogEntry", + "ScheduledTasksInfo", + "ActivityLogEntryStart", + "ActivityLogEntryStop", + "SessionsStart", + "SessionsStop", + "ScheduledTasksInfoStart", + "ScheduledTasksInfoStop", + "KeepAlive" + ], + "allOf": [ + { + "$ref": "#/components/schemas/SessionMessageType" + } + ], + "description": "The different kinds of messages that are used in the WebSocket api.", + "default": "Sessions", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "Sessions message." + }, + "SessionsStartMessage": { + "type": "object", + "properties": { + "Data": { + "type": "string", + "description": "Gets or sets the data.", + "nullable": true + }, + "MessageType": { + "enum": [ + "ForceKeepAlive", + "GeneralCommand", + "UserDataChanged", + "Sessions", + "Play", + "SyncPlayCommand", + "SyncPlayGroupUpdate", + "Playstate", + "RestartRequired", + "ServerShuttingDown", + "ServerRestarting", + "LibraryChanged", + "UserDeleted", + "UserUpdated", + "SeriesTimerCreated", + "TimerCreated", + "SeriesTimerCancelled", + "TimerCancelled", + "RefreshProgress", + "ScheduledTaskEnded", + "PackageInstallationCancelled", + "PackageInstallationFailed", + "PackageInstallationCompleted", + "PackageInstalling", + "PackageUninstalled", + "ActivityLogEntry", + "ScheduledTasksInfo", + "ActivityLogEntryStart", + "ActivityLogEntryStop", + "SessionsStart", + "SessionsStop", + "ScheduledTasksInfoStart", + "ScheduledTasksInfoStop", + "KeepAlive" + ], + "allOf": [ + { + "$ref": "#/components/schemas/SessionMessageType" + } + ], + "description": "The different kinds of messages that are used in the WebSocket api.", + "default": "SessionsStart", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "Sessions start message.\r\nData is the timing data encoded as \"$initialDelay,$interval\" in ms." + }, + "SessionsStopMessage": { + "type": "object", + "properties": { + "MessageType": { + "enum": [ + "ForceKeepAlive", + "GeneralCommand", + "UserDataChanged", + "Sessions", + "Play", + "SyncPlayCommand", + "SyncPlayGroupUpdate", + "Playstate", + "RestartRequired", + "ServerShuttingDown", + "ServerRestarting", + "LibraryChanged", + "UserDeleted", + "UserUpdated", + "SeriesTimerCreated", + "TimerCreated", + "SeriesTimerCancelled", + "TimerCancelled", + "RefreshProgress", + "ScheduledTaskEnded", + "PackageInstallationCancelled", + "PackageInstallationFailed", + "PackageInstallationCompleted", + "PackageInstalling", + "PackageUninstalled", + "ActivityLogEntry", + "ScheduledTasksInfo", + "ActivityLogEntryStart", + "ActivityLogEntryStop", + "SessionsStart", + "SessionsStop", + "ScheduledTasksInfoStart", + "ScheduledTasksInfoStop", + "KeepAlive" + ], + "allOf": [ + { + "$ref": "#/components/schemas/SessionMessageType" + } + ], + "description": "The different kinds of messages that are used in the WebSocket api.", + "default": "SessionsStop", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "Sessions stop message." + }, "SessionUserInfo": { "type": "object", "properties": { @@ -53487,6 +55531,11 @@ "type": "object", "properties": { "Mode": { + "enum": [ + "RepeatOne", + "RepeatAll", + "RepeatNone" + ], "allOf": [ { "$ref": "#/components/schemas/GroupRepeatMode" @@ -53502,6 +55551,10 @@ "type": "object", "properties": { "Mode": { + "enum": [ + "Sorted", + "Shuffle" + ], "allOf": [ { "$ref": "#/components/schemas/GroupShuffleMode" @@ -53678,6 +55731,44 @@ "additionalProperties": false, "description": "The startup user DTO." }, + "StringGroupUpdate": { + "type": "object", + "properties": { + "GroupId": { + "type": "string", + "description": "Gets the group identifier.", + "format": "uuid", + "readOnly": true + }, + "Type": { + "enum": [ + "UserJoined", + "UserLeft", + "GroupJoined", + "GroupLeft", + "StateUpdate", + "PlayQueue", + "NotInGroup", + "GroupDoesNotExist", + "CreateGroupDenied", + "JoinGroupDenied", + "LibraryAccessDenied" + ], + "allOf": [ + { + "$ref": "#/components/schemas/GroupUpdateType" + } + ], + "description": "Gets the update type." + }, + "Data": { + "type": "string", + "description": "Gets the update data." + } + }, + "additionalProperties": false, + "description": "Class GroupUpdate." + }, "SubtitleDeliveryMethod": { "enum": [ "Encode", @@ -53747,6 +55838,13 @@ "nullable": true }, "Method": { + "enum": [ + "Encode", + "Embed", + "External", + "Hls", + "Drop" + ], "allOf": [ { "$ref": "#/components/schemas/SubtitleDeliveryMethod" @@ -53769,6 +55867,158 @@ }, "additionalProperties": false }, + "SyncPlayCommandMessage": { + "type": "object", + "properties": { + "Data": { + "allOf": [ + { + "$ref": "#/components/schemas/SendCommand" + } + ], + "description": "Class SendCommand.", + "nullable": true + }, + "MessageId": { + "type": "string", + "description": "Gets or sets the message id.", + "format": "uuid" + }, + "MessageType": { + "enum": [ + "ForceKeepAlive", + "GeneralCommand", + "UserDataChanged", + "Sessions", + "Play", + "SyncPlayCommand", + "SyncPlayGroupUpdate", + "Playstate", + "RestartRequired", + "ServerShuttingDown", + "ServerRestarting", + "LibraryChanged", + "UserDeleted", + "UserUpdated", + "SeriesTimerCreated", + "TimerCreated", + "SeriesTimerCancelled", + "TimerCancelled", + "RefreshProgress", + "ScheduledTaskEnded", + "PackageInstallationCancelled", + "PackageInstallationFailed", + "PackageInstallationCompleted", + "PackageInstalling", + "PackageUninstalled", + "ActivityLogEntry", + "ScheduledTasksInfo", + "ActivityLogEntryStart", + "ActivityLogEntryStop", + "SessionsStart", + "SessionsStop", + "ScheduledTasksInfoStart", + "ScheduledTasksInfoStop", + "KeepAlive" + ], + "allOf": [ + { + "$ref": "#/components/schemas/SessionMessageType" + } + ], + "description": "The different kinds of messages that are used in the WebSocket api.", + "default": "SyncPlayCommand", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "Sync play command." + }, + "SyncPlayGroupUpdateCommandMessage": { + "type": "object", + "properties": { + "Data": { + "allOf": [ + { + "$ref": "#/components/schemas/GroupUpdate" + } + ], + "description": "Group update without data.", + "nullable": true + }, + "MessageId": { + "type": "string", + "description": "Gets or sets the message id.", + "format": "uuid" + }, + "MessageType": { + "enum": [ + "ForceKeepAlive", + "GeneralCommand", + "UserDataChanged", + "Sessions", + "Play", + "SyncPlayCommand", + "SyncPlayGroupUpdate", + "Playstate", + "RestartRequired", + "ServerShuttingDown", + "ServerRestarting", + "LibraryChanged", + "UserDeleted", + "UserUpdated", + "SeriesTimerCreated", + "TimerCreated", + "SeriesTimerCancelled", + "TimerCancelled", + "RefreshProgress", + "ScheduledTaskEnded", + "PackageInstallationCancelled", + "PackageInstallationFailed", + "PackageInstallationCompleted", + "PackageInstalling", + "PackageUninstalled", + "ActivityLogEntry", + "ScheduledTasksInfo", + "ActivityLogEntryStart", + "ActivityLogEntryStop", + "SessionsStart", + "SessionsStop", + "ScheduledTasksInfoStart", + "ScheduledTasksInfoStop", + "KeepAlive" + ], + "allOf": [ + { + "$ref": "#/components/schemas/SessionMessageType" + } + ], + "description": "The different kinds of messages that are used in the WebSocket api.", + "default": "SyncPlayGroupUpdate", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "Untyped sync play command." + }, + "SyncPlayQueueItem": { + "type": "object", + "properties": { + "ItemId": { + "type": "string", + "description": "Gets the item identifier.", + "format": "uuid" + }, + "PlaylistItemId": { + "type": "string", + "description": "Gets the playlist identifier of the item.", + "format": "uuid", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "Class QueueItem." + }, "SyncPlayUserAccessType": { "enum": [ "CreateAndJoinGroups", @@ -53804,7 +56054,8 @@ "OperatingSystem": { "type": "string", "description": "Gets or sets the operating system.", - "nullable": true + "nullable": true, + "deprecated": true }, "Id": { "type": "string", @@ -53819,7 +56070,8 @@ "OperatingSystemDisplayName": { "type": "string", "description": "Gets or sets the display name of the operating system.", - "nullable": true + "nullable": true, + "deprecated": true }, "PackageName": { "type": "string", @@ -53852,10 +56104,14 @@ }, "CanSelfRestart": { "type": "boolean", - "description": "Gets or sets a value indicating whether this instance can self restart." + "description": "Gets or sets a value indicating whether this instance can self restart.", + "default": true, + "deprecated": true }, "CanLaunchWebBrowser": { - "type": "boolean" + "type": "boolean", + "default": false, + "deprecated": true }, "ProgramDataPath": { "type": "string", @@ -53892,26 +56148,31 @@ "description": "Gets or sets the transcode path.", "nullable": true }, + "CastReceiverApplications": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CastReceiverApplication" + }, + "description": "Gets or sets the list of cast receiver applications.", + "nullable": true + }, "HasUpdateAvailable": { "type": "boolean", "description": "Gets or sets a value indicating whether this instance has update available.", + "default": false, "deprecated": true }, "EncoderLocation": { - "allOf": [ - { - "$ref": "#/components/schemas/FFmpegLocation" - } - ], - "description": "Enum describing the location of the FFmpeg tool.", + "type": "string", + "default": "System", + "nullable": true, "deprecated": true }, "SystemArchitecture": { - "allOf": [ - { - "$ref": "#/components/schemas/Architecture" - } - ] + "type": "string", + "default": "X64", + "nullable": true, + "deprecated": true } }, "additionalProperties": false, @@ -53936,6 +56197,11 @@ "nullable": true }, "State": { + "enum": [ + "Idle", + "Cancelling", + "Running" + ], "allOf": [ { "$ref": "#/components/schemas/TaskState" @@ -54008,6 +56274,12 @@ "format": "date-time" }, "Status": { + "enum": [ + "Completed", + "Failed", + "Cancelled", + "Aborted" + ], "allOf": [ { "$ref": "#/components/schemas/TaskCompletionStatus" @@ -54074,6 +56346,15 @@ "nullable": true }, "DayOfWeek": { + "enum": [ + "Sunday", + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday" + ], "allOf": [ { "$ref": "#/components/schemas/DayOfWeek" @@ -54122,6 +56403,140 @@ "additionalProperties": false, "description": "Class ThemeMediaResult." }, + "TimerCancelledMessage": { + "type": "object", + "properties": { + "Data": { + "allOf": [ + { + "$ref": "#/components/schemas/TimerEventInfo" + } + ], + "description": "Gets or sets the data.", + "nullable": true + }, + "MessageId": { + "type": "string", + "description": "Gets or sets the message id.", + "format": "uuid" + }, + "MessageType": { + "enum": [ + "ForceKeepAlive", + "GeneralCommand", + "UserDataChanged", + "Sessions", + "Play", + "SyncPlayCommand", + "SyncPlayGroupUpdate", + "Playstate", + "RestartRequired", + "ServerShuttingDown", + "ServerRestarting", + "LibraryChanged", + "UserDeleted", + "UserUpdated", + "SeriesTimerCreated", + "TimerCreated", + "SeriesTimerCancelled", + "TimerCancelled", + "RefreshProgress", + "ScheduledTaskEnded", + "PackageInstallationCancelled", + "PackageInstallationFailed", + "PackageInstallationCompleted", + "PackageInstalling", + "PackageUninstalled", + "ActivityLogEntry", + "ScheduledTasksInfo", + "ActivityLogEntryStart", + "ActivityLogEntryStop", + "SessionsStart", + "SessionsStop", + "ScheduledTasksInfoStart", + "ScheduledTasksInfoStop", + "KeepAlive" + ], + "allOf": [ + { + "$ref": "#/components/schemas/SessionMessageType" + } + ], + "description": "The different kinds of messages that are used in the WebSocket api.", + "default": "TimerCancelled", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "Timer cancelled message." + }, + "TimerCreatedMessage": { + "type": "object", + "properties": { + "Data": { + "allOf": [ + { + "$ref": "#/components/schemas/TimerEventInfo" + } + ], + "description": "Gets or sets the data.", + "nullable": true + }, + "MessageId": { + "type": "string", + "description": "Gets or sets the message id.", + "format": "uuid" + }, + "MessageType": { + "enum": [ + "ForceKeepAlive", + "GeneralCommand", + "UserDataChanged", + "Sessions", + "Play", + "SyncPlayCommand", + "SyncPlayGroupUpdate", + "Playstate", + "RestartRequired", + "ServerShuttingDown", + "ServerRestarting", + "LibraryChanged", + "UserDeleted", + "UserUpdated", + "SeriesTimerCreated", + "TimerCreated", + "SeriesTimerCancelled", + "TimerCancelled", + "RefreshProgress", + "ScheduledTaskEnded", + "PackageInstallationCancelled", + "PackageInstallationFailed", + "PackageInstallationCompleted", + "PackageInstalling", + "PackageUninstalled", + "ActivityLogEntry", + "ScheduledTasksInfo", + "ActivityLogEntryStart", + "ActivityLogEntryStop", + "SessionsStart", + "SessionsStop", + "ScheduledTasksInfoStart", + "ScheduledTasksInfoStop", + "KeepAlive" + ], + "allOf": [ + { + "$ref": "#/components/schemas/SessionMessageType" + } + ], + "description": "The different kinds of messages that are used in the WebSocket api.", + "default": "TimerCreated", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "Timer created message." + }, "TimerEventInfo": { "type": "object", "properties": { @@ -54249,6 +56664,12 @@ "description": "Gets or sets a value indicating whether this instance is post padding required." }, "KeepUntil": { + "enum": [ + "UntilDeleted", + "UntilSpaceNeeded", + "UntilWatched", + "UntilDate" + ], "allOf": [ { "$ref": "#/components/schemas/KeepUntil" @@ -54256,6 +56677,15 @@ ] }, "Status": { + "enum": [ + "New", + "InProgress", + "Completed", + "Cancelled", + "ConflictedOk", + "ConflictedNotOk", + "Error" + ], "allOf": [ { "$ref": "#/components/schemas/RecordingStatus" @@ -54495,6 +56925,15 @@ "nullable": true }, "HardwareAccelerationType": { + "enum": [ + "AMF", + "QSV", + "NVENC", + "V4L2M2M", + "VAAPI", + "VideoToolBox", + "RKMPP" + ], "allOf": [ { "$ref": "#/components/schemas/HardwareEncodingType" @@ -54503,6 +56942,33 @@ "nullable": true }, "TranscodeReasons": { + "enum": [ + "ContainerNotSupported", + "VideoCodecNotSupported", + "AudioCodecNotSupported", + "SubtitleCodecNotSupported", + "AudioIsExternal", + "SecondaryAudioNotSupported", + "VideoProfileNotSupported", + "VideoLevelNotSupported", + "VideoResolutionNotSupported", + "VideoBitDepthNotSupported", + "VideoFramerateNotSupported", + "RefFramesNotSupported", + "AnamorphicVideoNotSupported", + "InterlacedVideoNotSupported", + "AudioChannelsNotSupported", + "AudioProfileNotSupported", + "AudioSampleRateNotSupported", + "AudioBitDepthNotSupported", + "ContainerBitrateExceedsLimit", + "VideoBitrateNotSupported", + "AudioBitrateNotSupported", + "UnknownVideoStreamInfo", + "UnknownAudioStreamInfo", + "DirectPlayError", + "VideoRangeTypeNotSupported" + ], "type": "array", "items": { "$ref": "#/components/schemas/TranscodeReason" @@ -54518,6 +56984,13 @@ "type": "string" }, "Type": { + "enum": [ + "Audio", + "Video", + "Photo", + "Subtitle", + "Lyric" + ], "allOf": [ { "$ref": "#/components/schemas/DlnaProfileType" @@ -54531,7 +57004,16 @@ "type": "string" }, "Protocol": { - "type": "string" + "enum": [ + "http", + "hls" + ], + "allOf": [ + { + "$ref": "#/components/schemas/MediaStreamProtocol" + } + ], + "description": "Media streaming protocol.\r\nLowercase for backwards compatibility." }, "EstimateContentLength": { "type": "boolean", @@ -54542,6 +57024,10 @@ "default": false }, "TranscodeSeekInfo": { + "enum": [ + "Auto", + "Bytes" + ], "allOf": [ { "$ref": "#/components/schemas/TranscodeSeekInfo" @@ -54554,6 +57040,10 @@ "default": false }, "Context": { + "enum": [ + "Streaming", + "Static" + ], "allOf": [ { "$ref": "#/components/schemas/EncodingContext" @@ -54600,6 +57090,137 @@ ], "type": "string" }, + "TrickplayInfo": { + "type": "object", + "properties": { + "Width": { + "type": "integer", + "description": "Gets or sets width of an individual thumbnail.", + "format": "int32" + }, + "Height": { + "type": "integer", + "description": "Gets or sets height of an individual thumbnail.", + "format": "int32" + }, + "TileWidth": { + "type": "integer", + "description": "Gets or sets amount of thumbnails per row.", + "format": "int32" + }, + "TileHeight": { + "type": "integer", + "description": "Gets or sets amount of thumbnails per column.", + "format": "int32" + }, + "ThumbnailCount": { + "type": "integer", + "description": "Gets or sets total amount of non-black thumbnails.", + "format": "int32" + }, + "Interval": { + "type": "integer", + "description": "Gets or sets interval in milliseconds between each trickplay thumbnail.", + "format": "int32" + }, + "Bandwidth": { + "type": "integer", + "description": "Gets or sets peak bandwith usage in bits per second.", + "format": "int32" + } + }, + "additionalProperties": false, + "description": "An entity representing the metadata for a group of trickplay tiles." + }, + "TrickplayOptions": { + "type": "object", + "properties": { + "EnableHwAcceleration": { + "type": "boolean", + "description": "Gets or sets a value indicating whether or not to use HW acceleration." + }, + "EnableHwEncoding": { + "type": "boolean", + "description": "Gets or sets a value indicating whether or not to use HW accelerated MJPEG encoding." + }, + "ScanBehavior": { + "enum": [ + "Blocking", + "NonBlocking" + ], + "allOf": [ + { + "$ref": "#/components/schemas/TrickplayScanBehavior" + } + ], + "description": "Gets or sets the behavior used by trickplay provider on library scan/update." + }, + "ProcessPriority": { + "enum": [ + "Normal", + "Idle", + "High", + "RealTime", + "BelowNormal", + "AboveNormal" + ], + "allOf": [ + { + "$ref": "#/components/schemas/ProcessPriorityClass" + } + ], + "description": "Gets or sets the process priority for the ffmpeg process." + }, + "Interval": { + "type": "integer", + "description": "Gets or sets the interval, in ms, between each new trickplay image.", + "format": "int32" + }, + "WidthResolutions": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + }, + "description": "Gets or sets the target width resolutions, in px, to generates preview images for." + }, + "TileWidth": { + "type": "integer", + "description": "Gets or sets number of tile images to allow in X dimension.", + "format": "int32" + }, + "TileHeight": { + "type": "integer", + "description": "Gets or sets number of tile images to allow in Y dimension.", + "format": "int32" + }, + "Qscale": { + "type": "integer", + "description": "Gets or sets the ffmpeg output quality level.", + "format": "int32" + }, + "JpegQuality": { + "type": "integer", + "description": "Gets or sets the jpeg quality to use for image tiles.", + "format": "int32" + }, + "ProcessThreads": { + "type": "integer", + "description": "Gets or sets the number of threads to be used by ffmpeg.", + "format": "int32" + } + }, + "additionalProperties": false, + "description": "Class TrickplayOptions." + }, + "TrickplayScanBehavior": { + "enum": [ + "Blocking", + "NonBlocking" + ], + "type": "string", + "description": "Enum TrickplayScanBehavior." + }, "TunerChannelMapping": { "type": "object", "properties": { @@ -54665,6 +57286,9 @@ "UserAgent": { "type": "string", "nullable": true + }, + "IgnoreDts": { + "type": "boolean" } }, "additionalProperties": false @@ -54773,26 +57397,119 @@ "additionalProperties": false, "description": "Update library options dto." }, - "UpdateUserEasyPassword": { + "UpdatePlaylistDto": { "type": "object", "properties": { - "NewPassword": { + "Name": { "type": "string", - "description": "Gets or sets the new sha1-hashed password.", + "description": "Gets or sets the name of the new playlist.", "nullable": true }, - "NewPw": { - "type": "string", - "description": "Gets or sets the new password.", + "Ids": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + }, + "description": "Gets or sets item ids of the playlist.", "nullable": true }, - "ResetPassword": { + "Users": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PlaylistUserPermissions" + }, + "description": "Gets or sets the playlist users.", + "nullable": true + }, + "IsPublic": { "type": "boolean", - "description": "Gets or sets a value indicating whether to reset the password." + "description": "Gets or sets a value indicating whether the playlist is public.", + "nullable": true } }, "additionalProperties": false, - "description": "The update user easy password request body." + "description": "Update existing playlist dto. Fields set to `null` will not be updated and keep their current values." + }, + "UpdatePlaylistUserDto": { + "type": "object", + "properties": { + "CanEdit": { + "type": "boolean", + "description": "Gets or sets a value indicating whether the user can edit the playlist.", + "nullable": true + } + }, + "additionalProperties": false, + "description": "Update existing playlist user dto. Fields set to `null` will not be updated and keep their current values." + }, + "UpdateUserItemDataDto": { + "type": "object", + "properties": { + "Rating": { + "type": "number", + "description": "Gets or sets the rating.", + "format": "double", + "nullable": true + }, + "PlayedPercentage": { + "type": "number", + "description": "Gets or sets the played percentage.", + "format": "double", + "nullable": true + }, + "UnplayedItemCount": { + "type": "integer", + "description": "Gets or sets the unplayed item count.", + "format": "int32", + "nullable": true + }, + "PlaybackPositionTicks": { + "type": "integer", + "description": "Gets or sets the playback position ticks.", + "format": "int64", + "nullable": true + }, + "PlayCount": { + "type": "integer", + "description": "Gets or sets the play count.", + "format": "int32", + "nullable": true + }, + "IsFavorite": { + "type": "boolean", + "description": "Gets or sets a value indicating whether this instance is favorite.", + "nullable": true + }, + "Likes": { + "type": "boolean", + "description": "Gets or sets a value indicating whether this MediaBrowser.Model.Dto.UpdateUserItemDataDto is likes.", + "nullable": true + }, + "LastPlayedDate": { + "type": "string", + "description": "Gets or sets the last played date.", + "format": "date-time", + "nullable": true + }, + "Played": { + "type": "boolean", + "description": "Gets or sets a value indicating whether this MediaBrowser.Model.Dto.UserItemDataDto is played.", + "nullable": true + }, + "Key": { + "type": "string", + "description": "Gets or sets the key.", + "nullable": true + }, + "ItemId": { + "type": "string", + "description": "Gets or sets the item identifier.", + "nullable": true + } + }, + "additionalProperties": false, + "description": "This is used by the api to get information about a item user data." }, "UpdateUserPassword": { "type": "object", @@ -54825,6 +57542,7 @@ "Data", "Format", "IsForced", + "IsHearingImpaired", "Language" ], "type": "object", @@ -54841,6 +57559,10 @@ "type": "boolean", "description": "Gets or sets a value indicating whether the subtitle is forced." }, + "IsHearingImpaired": { + "type": "boolean", + "description": "Gets or sets a value indicating whether the subtitle is for hearing impaired." + }, "Data": { "type": "string", "description": "Gets or sets the subtitle data." @@ -54872,10 +57594,18 @@ "GroupedFolders": { "type": "array", "items": { - "type": "string" + "type": "string", + "format": "uuid" } }, "SubtitleMode": { + "enum": [ + "Default", + "Always", + "OnlyForced", + "None", + "Smart" + ], "allOf": [ { "$ref": "#/components/schemas/SubtitlePlaybackMode" @@ -54892,19 +57622,22 @@ "OrderedViews": { "type": "array", "items": { - "type": "string" + "type": "string", + "format": "uuid" } }, "LatestItemsExcludes": { "type": "array", "items": { - "type": "string" + "type": "string", + "format": "uuid" } }, "MyMediaExcludes": { "type": "array", "items": { - "type": "string" + "type": "string", + "format": "uuid" } }, "HidePlayedInLatest": { @@ -54918,11 +57651,166 @@ }, "EnableNextEpisodeAutoPlay": { "type": "boolean" + }, + "CastReceiverId": { + "type": "string", + "description": "Gets or sets the id of the selected cast receiver.", + "nullable": true } }, "additionalProperties": false, "description": "Class UserConfiguration." }, + "UserDataChangedMessage": { + "type": "object", + "properties": { + "Data": { + "allOf": [ + { + "$ref": "#/components/schemas/UserDataChangeInfo" + } + ], + "description": "Class UserDataChangeInfo.", + "nullable": true + }, + "MessageId": { + "type": "string", + "description": "Gets or sets the message id.", + "format": "uuid" + }, + "MessageType": { + "enum": [ + "ForceKeepAlive", + "GeneralCommand", + "UserDataChanged", + "Sessions", + "Play", + "SyncPlayCommand", + "SyncPlayGroupUpdate", + "Playstate", + "RestartRequired", + "ServerShuttingDown", + "ServerRestarting", + "LibraryChanged", + "UserDeleted", + "UserUpdated", + "SeriesTimerCreated", + "TimerCreated", + "SeriesTimerCancelled", + "TimerCancelled", + "RefreshProgress", + "ScheduledTaskEnded", + "PackageInstallationCancelled", + "PackageInstallationFailed", + "PackageInstallationCompleted", + "PackageInstalling", + "PackageUninstalled", + "ActivityLogEntry", + "ScheduledTasksInfo", + "ActivityLogEntryStart", + "ActivityLogEntryStop", + "SessionsStart", + "SessionsStop", + "ScheduledTasksInfoStart", + "ScheduledTasksInfoStop", + "KeepAlive" + ], + "allOf": [ + { + "$ref": "#/components/schemas/SessionMessageType" + } + ], + "description": "The different kinds of messages that are used in the WebSocket api.", + "default": "UserDataChanged", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "User data changed message." + }, + "UserDataChangeInfo": { + "type": "object", + "properties": { + "UserId": { + "type": "string", + "description": "Gets or sets the user id.", + "nullable": true + }, + "UserDataList": { + "type": "array", + "items": { + "$ref": "#/components/schemas/UserItemDataDto" + }, + "description": "Gets or sets the user data list.", + "nullable": true + } + }, + "additionalProperties": false, + "description": "Class UserDataChangeInfo." + }, + "UserDeletedMessage": { + "type": "object", + "properties": { + "Data": { + "type": "string", + "description": "Gets or sets the data.", + "format": "uuid" + }, + "MessageId": { + "type": "string", + "description": "Gets or sets the message id.", + "format": "uuid" + }, + "MessageType": { + "enum": [ + "ForceKeepAlive", + "GeneralCommand", + "UserDataChanged", + "Sessions", + "Play", + "SyncPlayCommand", + "SyncPlayGroupUpdate", + "Playstate", + "RestartRequired", + "ServerShuttingDown", + "ServerRestarting", + "LibraryChanged", + "UserDeleted", + "UserUpdated", + "SeriesTimerCreated", + "TimerCreated", + "SeriesTimerCancelled", + "TimerCancelled", + "RefreshProgress", + "ScheduledTaskEnded", + "PackageInstallationCancelled", + "PackageInstallationFailed", + "PackageInstallationCompleted", + "PackageInstalling", + "PackageUninstalled", + "ActivityLogEntry", + "ScheduledTasksInfo", + "ActivityLogEntryStart", + "ActivityLogEntryStop", + "SessionsStart", + "SessionsStop", + "ScheduledTasksInfoStart", + "ScheduledTasksInfoStop", + "KeepAlive" + ], + "allOf": [ + { + "$ref": "#/components/schemas/SessionMessageType" + } + ], + "description": "The different kinds of messages that are used in the WebSocket api.", + "default": "UserDeleted", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "User deleted message." + }, "UserDto": { "type": "object", "properties": { @@ -54961,7 +57849,8 @@ }, "HasConfiguredEasyPassword": { "type": "boolean", - "description": "Gets or sets a value indicating whether this instance has configured easy password." + "description": "Gets or sets a value indicating whether this instance has configured easy password.", + "deprecated": true }, "EnableAutoLogin": { "type": "boolean", @@ -55073,6 +57962,10 @@ "description": "Class UserItemDataDto." }, "UserPolicy": { + "required": [ + "AuthenticationProviderId", + "PasswordResetProviderId" + ], "type": "object", "properties": { "IsAdministrator": { @@ -55083,6 +57976,21 @@ "type": "boolean", "description": "Gets or sets a value indicating whether this instance is hidden." }, + "EnableCollectionManagement": { + "type": "boolean", + "description": "Gets or sets a value indicating whether this instance can manage collections.", + "default": false + }, + "EnableSubtitleManagement": { + "type": "boolean", + "description": "Gets or sets a value indicating whether this instance can manage subtitles.", + "default": false + }, + "EnableLyricManagement": { + "type": "boolean", + "description": "Gets or sets a value indicating whether this user can manage lyrics.", + "default": false + }, "IsDisabled": { "type": "boolean", "description": "Gets or sets a value indicating whether this instance is disabled." @@ -55100,6 +58008,13 @@ }, "nullable": true }, + "AllowedTags": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true + }, "EnableUserPreferenceAccess": { "type": "boolean" }, @@ -55235,14 +58150,17 @@ "format": "int32" }, "AuthenticationProviderId": { - "type": "string", - "nullable": true + "type": "string" }, "PasswordResetProviderId": { - "type": "string", - "nullable": true + "type": "string" }, "SyncPlayAccess": { + "enum": [ + "CreateAndJoinGroups", + "JoinGroups", + "None" + ], "allOf": [ { "$ref": "#/components/schemas/SyncPlayUserAccessType" @@ -55253,6 +58171,73 @@ }, "additionalProperties": false }, + "UserUpdatedMessage": { + "type": "object", + "properties": { + "Data": { + "allOf": [ + { + "$ref": "#/components/schemas/UserDto" + } + ], + "description": "Class UserDto.", + "nullable": true + }, + "MessageId": { + "type": "string", + "description": "Gets or sets the message id.", + "format": "uuid" + }, + "MessageType": { + "enum": [ + "ForceKeepAlive", + "GeneralCommand", + "UserDataChanged", + "Sessions", + "Play", + "SyncPlayCommand", + "SyncPlayGroupUpdate", + "Playstate", + "RestartRequired", + "ServerShuttingDown", + "ServerRestarting", + "LibraryChanged", + "UserDeleted", + "UserUpdated", + "SeriesTimerCreated", + "TimerCreated", + "SeriesTimerCancelled", + "TimerCancelled", + "RefreshProgress", + "ScheduledTaskEnded", + "PackageInstallationCancelled", + "PackageInstallationFailed", + "PackageInstallationCompleted", + "PackageInstalling", + "PackageUninstalled", + "ActivityLogEntry", + "ScheduledTasksInfo", + "ActivityLogEntryStart", + "ActivityLogEntryStop", + "SessionsStart", + "SessionsStop", + "ScheduledTasksInfoStart", + "ScheduledTasksInfoStop", + "KeepAlive" + ], + "allOf": [ + { + "$ref": "#/components/schemas/SessionMessageType" + } + ], + "description": "The different kinds of messages that are used in the WebSocket api.", + "default": "UserUpdated", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "User updated message." + }, "UtcTimeResponse": { "type": "object", "properties": { @@ -55350,6 +58335,30 @@ ], "type": "string" }, + "VideoRange": { + "enum": [ + "Unknown", + "SDR", + "HDR" + ], + "type": "string", + "description": "An enum representing video ranges." + }, + "VideoRangeType": { + "enum": [ + "Unknown", + "SDR", + "HDR10", + "HLG", + "DOVI", + "DOVIWithHDR10", + "DOVIWithHLG", + "DOVIWithSDR", + "HDR10Plus" + ], + "type": "string", + "description": "An enum representing types of video ranges." + }, "VideoType": { "enum": [ "VideoFile", @@ -55377,6 +58386,16 @@ "nullable": true }, "CollectionType": { + "enum": [ + "movies", + "tvshows", + "music", + "musicvideos", + "homevideos", + "boxsets", + "books", + "mixed" + ], "allOf": [ { "$ref": "#/components/schemas/CollectionTypeOptions" @@ -55433,6 +58452,18 @@ "additionalProperties": false, "description": "Provides the MAC address and port for wake-on-LAN functionality." }, + "WebSocketMessage": { + "type": "object", + "oneOf": [ + { + "$ref": "#/components/schemas/InboundWebSocketMessage" + }, + { + "$ref": "#/components/schemas/OutboundWebSocketMessage" + } + ], + "description": "Represents the possible websocket types" + }, "XbmcMetadataOptions": { "type": "object", "properties": { @@ -55454,23 +58485,6 @@ } }, "additionalProperties": false - }, - "XmlAttribute": { - "type": "object", - "properties": { - "Name": { - "type": "string", - "description": "Gets or sets the name of the attribute.", - "nullable": true - }, - "Value": { - "type": "string", - "description": "Gets or sets the value of the attribute.", - "nullable": true - } - }, - "additionalProperties": false, - "description": "Defines the MediaBrowser.Model.Dlna.XmlAttribute." } }, "securitySchemes": { @@ -55482,4 +58496,4 @@ } } } -} \ No newline at end of file +}