From 2b3714cd8f6e6fd898dd4ff8f198d79dcaf72094 Mon Sep 17 00:00:00 2001 From: PangMo5 Date: Sat, 18 Jun 2022 04:47:37 +0900 Subject: [PATCH] Correspond to jellyfin 10.8 Update Package --- .github/workflows/ci.yml | 14 +- .../BaseItemDto+VideoPlayerViewModel.swift | 24 +-- .../BaseItemDtoExtensions.swift | 2 +- Shared/Objects/DeviceProfileBuilder.swift | 12 +- Shared/Singleton/SessionManager.swift | 14 +- Shared/ViewModels/HomeViewModel.swift | 2 +- Shared/ViewModels/LatestMediaViewModel.swift | 2 +- .../ViewModels/LibrarySearchViewModel.swift | 8 +- Shared/ViewModels/LibraryViewModel.swift | 8 +- .../ViewModels/LiveTVChannelsViewModel.swift | 22 +-- .../ViewModels/LiveTVProgramsViewModel.swift | 96 ++++++------ .../VideoPlayerViewModel.swift | 148 +++++++++--------- .../Components/LandscapeItemElement.swift | 6 +- .../Components/PortraitItemElement.swift | 8 +- Swiftfin tvOS/Views/MovieLibrariesView.swift | 2 +- Swiftfin tvOS/Views/TVLibrariesView.swift | 2 +- .../xcshareddata/swiftpm/Package.resolved | 30 ++-- WidgetExtension/NextUpWidget.swift | 4 +- 18 files changed, 198 insertions(+), 206 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 12b61e76..d67dc461 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,12 +20,12 @@ jobs: steps: - uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: '13.3' + xcode-version: '13.4.1' - name: Checkout uses: actions/checkout@v1 - - uses: actions/cache@v2 + - uses: actions/cache@v3 id: carthage-cache with: path: Carthage @@ -37,18 +37,12 @@ jobs: run: carthage update --use-xcframeworks --cache-builds - name: Cache Swift packages - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: .build key: ${{ runner.os }}-${{ matrix.scheme }}-spm-${{ hashFiles('**/Package.resolved') }} restore-keys: | - ${{ runner.os }}-${{ matrix.scheme }}-spm2- - - - name: Cache DerivedData folder - uses: actions/cache@v2 - with: - path: "~/Library/Developer/Xcode/DerivedData" - key: ${{ runner.os }}-${{ matrix.scheme }}-deriveddata + ${{ runner.os }}-${{ matrix.scheme }}-spm- - name: xcodebuild! run: | diff --git a/Shared/Extensions/JellyfinAPIExtensions/BaseItemDto+VideoPlayerViewModel.swift b/Shared/Extensions/JellyfinAPIExtensions/BaseItemDto+VideoPlayerViewModel.swift index 34a0176b..d8a7be36 100644 --- a/Shared/Extensions/JellyfinAPIExtensions/BaseItemDto+VideoPlayerViewModel.swift +++ b/Shared/Extensions/JellyfinAPIExtensions/BaseItemDto+VideoPlayerViewModel.swift @@ -22,18 +22,18 @@ extension BaseItemDto { builder.setMaxBitrate(bitrate: tempOverkillBitrate) let profile = builder.buildProfile() - let playbackInfo = PlaybackInfoDto(userId: SessionManager.main.currentLogin.user.id, - maxStreamingBitrate: tempOverkillBitrate, - startTimeTicks: self.userData?.playbackPositionTicks ?? 0, - deviceProfile: profile, - autoOpenLiveStream: true) + let getPostedPlaybackInfoRequest = GetPostedPlaybackInfoRequest(userId: SessionManager.main.currentLogin.user.id, + maxStreamingBitrate: tempOverkillBitrate, + startTimeTicks: self.userData?.playbackPositionTicks ?? 0, + deviceProfile: profile, + autoOpenLiveStream: true) return MediaInfoAPI.getPostedPlaybackInfo(itemId: self.id!, userId: SessionManager.main.currentLogin.user.id, maxStreamingBitrate: tempOverkillBitrate, startTimeTicks: self.userData?.playbackPositionTicks ?? 0, autoOpenLiveStream: true, - playbackInfoDto: playbackInfo) + getPostedPlaybackInfoRequest: getPostedPlaybackInfoRequest) .map { response -> [VideoPlayerViewModel] in let mediaSources = response.mediaSources! @@ -177,18 +177,18 @@ extension BaseItemDto { builder.setMaxBitrate(bitrate: tempOverkillBitrate) let profile = builder.buildProfile() - let playbackInfo = PlaybackInfoDto(userId: SessionManager.main.currentLogin.user.id, - maxStreamingBitrate: tempOverkillBitrate, - startTimeTicks: self.userData?.playbackPositionTicks ?? 0, - deviceProfile: profile, - autoOpenLiveStream: true) + let getPostedPlaybackInfoRequest = GetPostedPlaybackInfoRequest(userId: SessionManager.main.currentLogin.user.id, + maxStreamingBitrate: tempOverkillBitrate, + startTimeTicks: self.userData?.playbackPositionTicks ?? 0, + deviceProfile: profile, + autoOpenLiveStream: true) return MediaInfoAPI.getPostedPlaybackInfo(itemId: self.id!, userId: SessionManager.main.currentLogin.user.id, maxStreamingBitrate: tempOverkillBitrate, startTimeTicks: self.userData?.playbackPositionTicks ?? 0, autoOpenLiveStream: true, - playbackInfoDto: playbackInfo) + getPostedPlaybackInfoRequest: getPostedPlaybackInfoRequest) .map { response -> [VideoPlayerViewModel] in let mediaSources = response.mediaSources! diff --git a/Shared/Extensions/JellyfinAPIExtensions/BaseItemDtoExtensions.swift b/Shared/Extensions/JellyfinAPIExtensions/BaseItemDtoExtensions.swift index 4e5c2308..4ed88455 100644 --- a/Shared/Extensions/JellyfinAPIExtensions/BaseItemDtoExtensions.swift +++ b/Shared/Extensions/JellyfinAPIExtensions/BaseItemDtoExtensions.swift @@ -266,7 +266,7 @@ public extension BaseItemDto { } var itemType: ItemType { - guard let originalType = type, let knownType = ItemType(rawValue: originalType) else { return .unknown } + guard let originalType = type, let knownType = ItemType(rawValue: originalType.rawValue) else { return .unknown } return knownType } diff --git a/Shared/Objects/DeviceProfileBuilder.swift b/Shared/Objects/DeviceProfileBuilder.swift index 2be6e185..7c94a452 100644 --- a/Shared/Objects/DeviceProfileBuilder.swift +++ b/Shared/Objects/DeviceProfileBuilder.swift @@ -42,7 +42,7 @@ class DeviceProfileBuilder { self.bitrate = bitrate } - public func buildProfile() -> DeviceProfile { + public func buildProfile() -> ClientCapabilitiesDeviceProfile { let maxStreamingBitrate = bitrate let maxStaticBitrate = bitrate let musicStreamingTranscodingBitrate = bitrate @@ -147,10 +147,12 @@ class DeviceProfileBuilder { let responseProfiles: [ResponseProfile] = [ResponseProfile(container: "m4v", type: .video, mimeType: "video/mp4")] - let profile = DeviceProfile(maxStreamingBitrate: maxStreamingBitrate, maxStaticBitrate: maxStaticBitrate, - musicStreamingTranscodingBitrate: musicStreamingTranscodingBitrate, - directPlayProfiles: directPlayProfiles, transcodingProfiles: transcodingProfiles, containerProfiles: [], - codecProfiles: codecProfiles, responseProfiles: responseProfiles, subtitleProfiles: subtitleProfiles) + let profile = ClientCapabilitiesDeviceProfile(maxStreamingBitrate: maxStreamingBitrate, maxStaticBitrate: maxStaticBitrate, + musicStreamingTranscodingBitrate: musicStreamingTranscodingBitrate, + directPlayProfiles: directPlayProfiles, transcodingProfiles: transcodingProfiles, + containerProfiles: [], + codecProfiles: codecProfiles, responseProfiles: responseProfiles, + subtitleProfiles: subtitleProfiles) return profile } diff --git a/Shared/Singleton/SessionManager.swift b/Shared/Singleton/SessionManager.swift index 5ee49bac..c17c45b8 100644 --- a/Shared/Singleton/SessionManager.swift +++ b/Shared/Singleton/SessionManager.swift @@ -40,7 +40,7 @@ final class SessionManager { let accessToken = user.accessToken else { fatalError("No associated server or access token for last user?") } guard let existingServer = SwiftfinStore.dataStack.fetchExisting(server) else { return } - JellyfinAPI.basePath = server.currentURI + JellyfinAPIAPI.basePath = server.currentURI setAuthHeader(with: accessToken.value) currentLogin = (server: existingServer.state, user: user.state) } @@ -78,7 +78,7 @@ final class SessionManager { uri = String(uri.dropLast()) } - JellyfinAPI.basePath = uri + JellyfinAPIAPI.basePath = uri return SystemAPI.getPublicSystemInfo() .tryMap { response -> (SwiftfinStore.Models.StoredServer, UnsafeDataTransaction) in @@ -188,9 +188,9 @@ final class SessionManager { { setAuthHeader(with: "") - JellyfinAPI.basePath = server.currentURI + JellyfinAPIAPI.basePath = server.currentURI - return UserAPI.authenticateUserByName(authenticateUserByName: AuthenticateUserByName(username: username, pw: password)) + return UserAPI.authenticateUserByName(authenticateUserByNameRequest: .init(username: username, pw: password)) .tryMap { response -> (SwiftfinStore.Models.StoredServer, SwiftfinStore.Models.StoredUser, UnsafeDataTransaction) in guard let accessToken = response.accessToken else { throw JellyfinAPIError("Access token missing from network call") } @@ -251,7 +251,7 @@ final class SessionManager { // MARK: loginUser func loginUser(server: SwiftfinStore.State.Server, user: SwiftfinStore.State.User) { - JellyfinAPI.basePath = server.currentURI + JellyfinAPIAPI.basePath = server.currentURI Defaults[.lastServerUserID] = user.id setAuthHeader(with: user.accessToken) currentLogin = (server: server, user: user) @@ -262,7 +262,7 @@ final class SessionManager { func logout() { currentLogin = nil - JellyfinAPI.basePath = "" + JellyfinAPIAPI.basePath = "" setAuthHeader(with: "") Defaults[.lastServerUserID] = nil Notifications[.didSignOut].post() @@ -339,6 +339,6 @@ final class SessionManager { header.append("Version=\"\(appVersion ?? "0.0.1")\", ") header.append("Token=\"\(accessToken)\"") - JellyfinAPI.customHeaders["X-Emby-Authorization"] = header + JellyfinAPIAPI.customHeaders["X-Emby-Authorization"] = header } } diff --git a/Shared/ViewModels/HomeViewModel.swift b/Shared/ViewModels/HomeViewModel.swift index 617cd852..cb455dde 100644 --- a/Shared/ViewModels/HomeViewModel.swift +++ b/Shared/ViewModels/HomeViewModel.swift @@ -135,7 +135,7 @@ final class HomeViewModel: ViewModel { .people, .chapters, ], - includeItemTypes: ["Movie", "Series"], + includeItemTypes: [.movie, .series], enableImageTypes: [.primary, .backdrop, .thumb], enableUserData: true, limit: 8) diff --git a/Shared/ViewModels/LatestMediaViewModel.swift b/Shared/ViewModels/LatestMediaViewModel.swift index 85012299..84bdef4d 100644 --- a/Shared/ViewModels/LatestMediaViewModel.swift +++ b/Shared/ViewModels/LatestMediaViewModel.swift @@ -36,7 +36,7 @@ final class LatestMediaViewModel: ViewModel { .genres, .people, ], - includeItemTypes: ["Series", "Movie"], + includeItemTypes: [.series, .movie], enableUserData: true, limit: 12) .trackActivity(loading) .sink(receiveCompletion: { [weak self] completion in diff --git a/Shared/ViewModels/LibrarySearchViewModel.swift b/Shared/ViewModels/LibrarySearchViewModel.swift index 6613425a..3e843f21 100644 --- a/Shared/ViewModels/LibrarySearchViewModel.swift +++ b/Shared/ViewModels/LibrarySearchViewModel.swift @@ -94,7 +94,7 @@ final class LibrarySearchViewModel: ViewModel { limit: 20, recursive: true, parentId: parentID, - includeItemTypes: ["Movie", "Series"], + includeItemTypes: [.movie, .series], sortBy: ["IsFavoriteOrLiked", "Random"], imageTypeLimit: 0, enableTotalRecordCount: false, @@ -113,7 +113,7 @@ final class LibrarySearchViewModel: ViewModel { ItemsAPI.getItemsByUserId(userId: SessionManager.main.currentLogin.user.id, limit: 50, recursive: true, searchTerm: query, sortOrder: [.ascending], parentId: parentID, fields: [.primaryImageAspectRatio, .seriesPrimaryImage, .seasonUserData, .overview, .genres, .people], - includeItemTypes: [ItemType.movie.rawValue], sortBy: ["SortName"], enableUserData: true, + includeItemTypes: [.movie], sortBy: ["SortName"], enableUserData: true, enableImages: true) .trackActivity(loading) .receive(on: DispatchQueue.main) @@ -126,7 +126,7 @@ final class LibrarySearchViewModel: ViewModel { ItemsAPI.getItemsByUserId(userId: SessionManager.main.currentLogin.user.id, limit: 50, recursive: true, searchTerm: query, sortOrder: [.ascending], parentId: parentID, fields: [.primaryImageAspectRatio, .seriesPrimaryImage, .seasonUserData, .overview, .genres, .people], - includeItemTypes: [ItemType.series.rawValue], sortBy: ["SortName"], enableUserData: true, + includeItemTypes: [.series], sortBy: ["SortName"], enableUserData: true, enableImages: true) .trackActivity(loading) .receive(on: DispatchQueue.main) @@ -139,7 +139,7 @@ final class LibrarySearchViewModel: ViewModel { ItemsAPI.getItemsByUserId(userId: SessionManager.main.currentLogin.user.id, limit: 50, recursive: true, searchTerm: query, sortOrder: [.ascending], parentId: parentID, fields: [.primaryImageAspectRatio, .seriesPrimaryImage, .seasonUserData, .overview, .genres, .people], - includeItemTypes: [ItemType.episode.rawValue], sortBy: ["SortName"], enableUserData: true, + includeItemTypes: [.episode], sortBy: ["SortName"], enableUserData: true, enableImages: true) .trackActivity(loading) .receive(on: DispatchQueue.main) diff --git a/Shared/ViewModels/LibraryViewModel.swift b/Shared/ViewModels/LibraryViewModel.swift index ca88cd82..09114026 100644 --- a/Shared/ViewModels/LibraryViewModel.swift +++ b/Shared/ViewModels/LibraryViewModel.swift @@ -99,18 +99,18 @@ final class LibraryViewModel: ViewModel { self.person != nil || self.genre != nil || self.studio != nil - let includeItemTypes: [String] + let includeItemTypes: [BaseItemKind] if filters.filters.contains(.isFavorite) { - includeItemTypes = ["Movie", "Series", "Season", "Episode", "BoxSet"] + includeItemTypes = [.movie, .series, .season, .episode, .boxSet] } else { - includeItemTypes = ["Movie", "Series", "BoxSet"] + (Defaults[.showFlattenView] ? [] : ["Folder"]) + includeItemTypes = [.movie, .series, .boxSet] + (Defaults[.showFlattenView] ? [] : [.folder]) } ItemsAPI.getItemsByUserId(userId: SessionManager.main.currentLogin.user.id, startIndex: currentPage * pageItemSize, limit: pageItemSize, recursive: queryRecursive, searchTerm: nil, - sortOrder: filters.sortOrder, + sortOrder: filters.sortOrder.compactMap { SortOrder(rawValue: $0.rawValue) }, parentId: parentID, fields: [ .primaryImageAspectRatio, diff --git a/Shared/ViewModels/LiveTVChannelsViewModel.swift b/Shared/ViewModels/LiveTVChannelsViewModel.swift index d66b8fb4..8f5f1b0b 100644 --- a/Shared/ViewModels/LiveTVChannelsViewModel.swift +++ b/Shared/ViewModels/LiveTVChannelsViewModel.swift @@ -106,18 +106,18 @@ final class LiveTVChannelsViewModel: ViewModel { let minEndDate = Date.now.addComponentsToDate(hours: -1) let maxStartDate = minEndDate.addComponentsToDate(hours: 6) - let getProgramsDto = GetProgramsDto(channelIds: channelIds, - userId: SessionManager.main.currentLogin.user.id, - maxStartDate: maxStartDate, - minEndDate: minEndDate, - sortBy: ["StartDate"], - enableImages: true, - enableTotalRecordCount: false, - imageTypeLimit: 1, - enableImageTypes: [.primary], - enableUserData: false) + let getProgramsRequest = GetProgramsRequest(channelIds: channelIds, + userId: SessionManager.main.currentLogin.user.id, + maxStartDate: maxStartDate, + minEndDate: minEndDate, + sortBy: ["StartDate"], + enableImages: true, + enableTotalRecordCount: false, + imageTypeLimit: 1, + enableImageTypes: [.primary], + enableUserData: false) - LiveTvAPI.getPrograms(getProgramsDto: getProgramsDto) + LiveTvAPI.getPrograms(getProgramsRequest: getProgramsRequest) .trackActivity(loading) .sink(receiveCompletion: { [weak self] completion in self?.handleAPIRequestError(completion: completion) diff --git a/Shared/ViewModels/LiveTVProgramsViewModel.swift b/Shared/ViewModels/LiveTVProgramsViewModel.swift index 09e88b67..b0b94946 100644 --- a/Shared/ViewModels/LiveTVProgramsViewModel.swift +++ b/Shared/ViewModels/LiveTVProgramsViewModel.swift @@ -86,19 +86,19 @@ final class LiveTVProgramsViewModel: ViewModel { } private func getSeries() { - let getProgramsDto = GetProgramsDto(userId: SessionManager.main.currentLogin.user.id, - hasAired: false, - isMovie: false, - isSeries: true, - isNews: false, - isKids: false, - isSports: false, - limit: 9, - enableTotalRecordCount: false, - enableImageTypes: [.primary, .thumb], - fields: [.channelInfo, .primaryImageAspectRatio]) + let getProgramsRequest = GetProgramsRequest(userId: SessionManager.main.currentLogin.user.id, + hasAired: false, + isMovie: false, + isSeries: true, + isNews: false, + isKids: false, + isSports: false, + limit: 9, + enableTotalRecordCount: false, + enableImageTypes: [.primary, .thumb], + fields: [.channelInfo, .primaryImageAspectRatio]) - LiveTvAPI.getPrograms(getProgramsDto: getProgramsDto) + LiveTvAPI.getPrograms(getProgramsRequest: getProgramsRequest) .trackActivity(loading) .sink(receiveCompletion: { [weak self] completion in self?.handleAPIRequestError(completion: completion) @@ -111,19 +111,19 @@ final class LiveTVProgramsViewModel: ViewModel { } private func getMovies() { - let getProgramsDto = GetProgramsDto(userId: SessionManager.main.currentLogin.user.id, - hasAired: false, - isMovie: true, - isSeries: false, - isNews: false, - isKids: false, - isSports: false, - limit: 9, - enableTotalRecordCount: false, - enableImageTypes: [.primary, .thumb], - fields: [.channelInfo, .primaryImageAspectRatio]) + let getProgramsRequest = GetProgramsRequest(userId: SessionManager.main.currentLogin.user.id, + hasAired: false, + isMovie: true, + isSeries: false, + isNews: false, + isKids: false, + isSports: false, + limit: 9, + enableTotalRecordCount: false, + enableImageTypes: [.primary, .thumb], + fields: [.channelInfo, .primaryImageAspectRatio]) - LiveTvAPI.getPrograms(getProgramsDto: getProgramsDto) + LiveTvAPI.getPrograms(getProgramsRequest: getProgramsRequest) .trackActivity(loading) .sink(receiveCompletion: { [weak self] completion in self?.handleAPIRequestError(completion: completion) @@ -136,15 +136,15 @@ final class LiveTVProgramsViewModel: ViewModel { } private func getSports() { - let getProgramsDto = GetProgramsDto(userId: SessionManager.main.currentLogin.user.id, - hasAired: false, - isSports: true, - limit: 9, - enableTotalRecordCount: false, - enableImageTypes: [.primary, .thumb], - fields: [.channelInfo, .primaryImageAspectRatio]) + let getProgramsRequest = GetProgramsRequest(userId: SessionManager.main.currentLogin.user.id, + hasAired: false, + isSports: true, + limit: 9, + enableTotalRecordCount: false, + enableImageTypes: [.primary, .thumb], + fields: [.channelInfo, .primaryImageAspectRatio]) - LiveTvAPI.getPrograms(getProgramsDto: getProgramsDto) + LiveTvAPI.getPrograms(getProgramsRequest: getProgramsRequest) .trackActivity(loading) .sink(receiveCompletion: { [weak self] completion in self?.handleAPIRequestError(completion: completion) @@ -157,15 +157,15 @@ final class LiveTVProgramsViewModel: ViewModel { } private func getKids() { - let getProgramsDto = GetProgramsDto(userId: SessionManager.main.currentLogin.user.id, - hasAired: false, - isKids: true, - limit: 9, - enableTotalRecordCount: false, - enableImageTypes: [.primary, .thumb], - fields: [.channelInfo, .primaryImageAspectRatio]) + let getProgramsRequest = GetProgramsRequest(userId: SessionManager.main.currentLogin.user.id, + hasAired: false, + isKids: true, + limit: 9, + enableTotalRecordCount: false, + enableImageTypes: [.primary, .thumb], + fields: [.channelInfo, .primaryImageAspectRatio]) - LiveTvAPI.getPrograms(getProgramsDto: getProgramsDto) + LiveTvAPI.getPrograms(getProgramsRequest: getProgramsRequest) .trackActivity(loading) .sink(receiveCompletion: { [weak self] completion in self?.handleAPIRequestError(completion: completion) @@ -178,15 +178,15 @@ final class LiveTVProgramsViewModel: ViewModel { } private func getNews() { - let getProgramsDto = GetProgramsDto(userId: SessionManager.main.currentLogin.user.id, - hasAired: false, - isNews: true, - limit: 9, - enableTotalRecordCount: false, - enableImageTypes: [.primary, .thumb], - fields: [.channelInfo, .primaryImageAspectRatio]) + let getProgramsRequest = GetProgramsRequest(userId: SessionManager.main.currentLogin.user.id, + hasAired: false, + isNews: true, + limit: 9, + enableTotalRecordCount: false, + enableImageTypes: [.primary, .thumb], + fields: [.channelInfo, .primaryImageAspectRatio]) - LiveTvAPI.getPrograms(getProgramsDto: getProgramsDto) + LiveTvAPI.getPrograms(getProgramsRequest: getProgramsRequest) .trackActivity(loading) .sink(receiveCompletion: { [weak self] completion in self?.handleAPIRequestError(completion: completion) diff --git a/Shared/ViewModels/VideoPlayerViewModel/VideoPlayerViewModel.swift b/Shared/ViewModels/VideoPlayerViewModel/VideoPlayerViewModel.swift index 5c8a6ef3..5d6a4372 100644 --- a/Shared/ViewModels/VideoPlayerViewModel/VideoPlayerViewModel.swift +++ b/Shared/ViewModels/VideoPlayerViewModel/VideoPlayerViewModel.swift @@ -206,7 +206,7 @@ final class VideoPlayerViewModel: ViewModel { // During scrubbing, many progress reports were spammed // Send only the current report after a delay private var progressReportTimer: Timer? - private var lastProgressReport: PlaybackProgressInfo? + private var lastProgressReport: ReportPlaybackProgressRequest? // MARK: init @@ -474,28 +474,27 @@ extension VideoPlayerViewModel { let subtitleStreamIndex = subtitlesEnabled ? selectedSubtitleStreamIndex : nil - let startInfo = PlaybackStartInfo(canSeek: true, - item: item, - itemId: item.id, - sessionId: response.playSessionId, - mediaSourceId: item.id, - audioStreamIndex: selectedAudioStreamIndex, - subtitleStreamIndex: subtitleStreamIndex, - isPaused: false, - isMuted: false, - positionTicks: item.userData?.playbackPositionTicks, - playbackStartTimeTicks: startTimeTicks, - volumeLevel: 100, - brightness: 100, - aspectRatio: nil, - playMethod: .directPlay, - liveStreamId: nil, - playSessionId: response.playSessionId, - repeatMode: .repeatNone, - nowPlayingQueue: nil, - playlistItemId: "playlistItem0") + let reportPlaybackStartRequest = ReportPlaybackStartRequest(canSeek: true, + itemId: item.id, + sessionId: response.playSessionId, + mediaSourceId: item.id, + audioStreamIndex: selectedAudioStreamIndex, + subtitleStreamIndex: subtitleStreamIndex, + isPaused: false, + isMuted: false, + positionTicks: item.userData?.playbackPositionTicks, + playbackStartTimeTicks: startTimeTicks, + volumeLevel: 100, + brightness: 100, + aspectRatio: nil, + playMethod: .directPlay, + liveStreamId: nil, + playSessionId: response.playSessionId, + repeatMode: .repeatNone, + nowPlayingQueue: nil, + playlistItemId: "playlistItem0") - PlaystateAPI.reportPlaybackStart(playbackStartInfo: startInfo) + PlaystateAPI.reportPlaybackStart(reportPlaybackStartRequest: reportPlaybackStartRequest) .sink { completion in self.handleAPIRequestError(completion: completion) } receiveValue: { _ in @@ -509,28 +508,27 @@ extension VideoPlayerViewModel { func sendPauseReport(paused: Bool) { let subtitleStreamIndex = subtitlesEnabled ? selectedSubtitleStreamIndex : nil - let pauseInfo = PlaybackStartInfo(canSeek: true, - item: item, - itemId: item.id, - sessionId: response.playSessionId, - mediaSourceId: item.id, - audioStreamIndex: selectedAudioStreamIndex, - subtitleStreamIndex: subtitleStreamIndex, - isPaused: paused, - isMuted: false, - positionTicks: currentSecondTicks, - playbackStartTimeTicks: startTimeTicks, - volumeLevel: 100, - brightness: 100, - aspectRatio: nil, - playMethod: .directPlay, - liveStreamId: nil, - playSessionId: response.playSessionId, - repeatMode: .repeatNone, - nowPlayingQueue: nil, - playlistItemId: "playlistItem0") + let reportPlaybackStartRequest = ReportPlaybackStartRequest(canSeek: true, + itemId: item.id, + sessionId: response.playSessionId, + mediaSourceId: item.id, + audioStreamIndex: selectedAudioStreamIndex, + subtitleStreamIndex: subtitleStreamIndex, + isPaused: paused, + isMuted: false, + positionTicks: currentSecondTicks, + playbackStartTimeTicks: startTimeTicks, + volumeLevel: 100, + brightness: 100, + aspectRatio: nil, + playMethod: .directPlay, + liveStreamId: nil, + playSessionId: response.playSessionId, + repeatMode: .repeatNone, + nowPlayingQueue: nil, + playlistItemId: "playlistItem0") - PlaystateAPI.reportPlaybackStart(playbackStartInfo: pauseInfo) + PlaystateAPI.reportPlaybackStart(reportPlaybackStartRequest: reportPlaybackStartRequest) .sink { completion in self.handleAPIRequestError(completion: completion) } receiveValue: { _ in @@ -544,26 +542,25 @@ extension VideoPlayerViewModel { func sendProgressReport() { let subtitleStreamIndex = subtitlesEnabled ? selectedSubtitleStreamIndex : nil - let progressInfo = PlaybackProgressInfo(canSeek: true, - item: item, - itemId: item.id, - sessionId: response.playSessionId, - mediaSourceId: item.id, - audioStreamIndex: selectedAudioStreamIndex, - subtitleStreamIndex: subtitleStreamIndex, - isPaused: false, - isMuted: false, - positionTicks: currentSecondTicks, - playbackStartTimeTicks: startTimeTicks, - volumeLevel: nil, - brightness: nil, - aspectRatio: nil, - playMethod: .directPlay, - liveStreamId: nil, - playSessionId: response.playSessionId, - repeatMode: .repeatNone, - nowPlayingQueue: nil, - playlistItemId: "playlistItem0") + let progressInfo = ReportPlaybackProgressRequest(canSeek: true, + itemId: item.id, + sessionId: response.playSessionId, + mediaSourceId: item.id, + audioStreamIndex: selectedAudioStreamIndex, + subtitleStreamIndex: subtitleStreamIndex, + isPaused: false, + isMuted: false, + positionTicks: currentSecondTicks, + playbackStartTimeTicks: startTimeTicks, + volumeLevel: nil, + brightness: nil, + aspectRatio: nil, + playMethod: .directPlay, + liveStreamId: nil, + playSessionId: response.playSessionId, + repeatMode: .repeatNone, + nowPlayingQueue: nil, + playlistItemId: "playlistItem0") lastProgressReport = progressInfo @@ -574,7 +571,7 @@ extension VideoPlayerViewModel { private func _sendProgressReport() { guard let lastProgressReport = lastProgressReport else { return } - PlaystateAPI.reportPlaybackProgress(playbackProgressInfo: lastProgressReport) + PlaystateAPI.reportPlaybackProgress(reportPlaybackProgressRequest: lastProgressReport) .sink { completion in self.handleAPIRequestError(completion: completion) } receiveValue: { _ in @@ -588,19 +585,18 @@ extension VideoPlayerViewModel { // MARK: sendStopReport func sendStopReport() { - let stopInfo = PlaybackStopInfo(item: item, - itemId: item.id, - sessionId: response.playSessionId, - mediaSourceId: item.id, - positionTicks: currentSecondTicks, - liveStreamId: nil, - playSessionId: response.playSessionId, - failed: nil, - nextMediaType: nil, - playlistItemId: "playlistItem0", - nowPlayingQueue: nil) + let reportPlaybackStoppedRequest = ReportPlaybackStoppedRequest(itemId: item.id, + sessionId: response.playSessionId, + mediaSourceId: item.id, + positionTicks: currentSecondTicks, + liveStreamId: nil, + playSessionId: response.playSessionId, + failed: nil, + nextMediaType: nil, + playlistItemId: "playlistItem0", + nowPlayingQueue: nil) - PlaystateAPI.reportPlaybackStopped(playbackStopInfo: stopInfo) + PlaystateAPI.reportPlaybackStopped(reportPlaybackStoppedRequest: reportPlaybackStoppedRequest) .sink { completion in self.handleAPIRequestError(completion: completion) } receiveValue: { _ in diff --git a/Swiftfin tvOS/Components/LandscapeItemElement.swift b/Swiftfin tvOS/Components/LandscapeItemElement.swift index a5217b96..a379bc8d 100644 --- a/Swiftfin tvOS/Components/LandscapeItemElement.swift +++ b/Swiftfin tvOS/Components/LandscapeItemElement.swift @@ -46,9 +46,9 @@ struct LandscapeItemElement: View { var body: some View { VStack { - ImageView(item.type == "Episode" && !(inSeasonView ?? false) ? item.getSeriesBackdropImage(maxWidth: 445) : item + ImageView(item.type == .episode && !(inSeasonView ?? false) ? item.getSeriesBackdropImage(maxWidth: 445) : item .getBackdropImage(maxWidth: 445), - blurHash: item.type == "Episode" ? item.getSeriesBackdropImageBlurHash() : item.getBackdropImageBlurHash()) + blurHash: item.type == .episode ? item.getSeriesBackdropImageBlurHash() : item.getBackdropImageBlurHash()) .frame(width: 445, height: 250) .cornerRadius(10) .ignoresSafeArea() @@ -97,7 +97,7 @@ struct LandscapeItemElement: View { .lineLimit(1) .frame(width: 445) } else { - Text(item.type == "Episode" ? "\(item.seriesName ?? "") • \(item.getEpisodeLocator() ?? "")" : item.name ?? "") + Text(item.type == .episode ? "\(item.seriesName ?? "") • \(item.getEpisodeLocator() ?? "")" : item.name ?? "") .font(.callout) .fontWeight(.semibold) .lineLimit(1) diff --git a/Swiftfin tvOS/Components/PortraitItemElement.swift b/Swiftfin tvOS/Components/PortraitItemElement.swift index 1f7d6323..555a1acb 100644 --- a/Swiftfin tvOS/Components/PortraitItemElement.swift +++ b/Swiftfin tvOS/Components/PortraitItemElement.swift @@ -21,8 +21,8 @@ struct PortraitItemElement: View { var body: some View { VStack { - ImageView(item.type == "Episode" ? item.getSeriesPrimaryImage(maxWidth: 200) : item.getPrimaryImage(maxWidth: 200), - blurHash: item.type == "Episode" ? item.getSeriesPrimaryImageBlurHash() : item.getPrimaryImageBlurHash()) + ImageView(item.type == .episode ? item.getSeriesPrimaryImage(maxWidth: 200) : item.getPrimaryImage(maxWidth: 200), + blurHash: item.type == .episode ? item.getSeriesPrimaryImageBlurHash() : item.getPrimaryImageBlurHash()) .frame(width: 200, height: 300) .cornerRadius(10) .shadow(radius: focused ? 10.0 : 0) @@ -58,12 +58,12 @@ struct PortraitItemElement: View { .opacity(1), alignment: .topTrailing).opacity(1) Text(item.title) .frame(width: 200, height: 30, alignment: .center) - if item.type == "Movie" || item.type == "Series" { + if item.type == .movie || item.type == .series { Text("\(String(item.productionYear ?? 0)) • \(item.officialRating ?? "N/A")") .foregroundColor(.secondary) .font(.caption) .fontWeight(.medium) - } else if item.type == "Season" { + } else if item.type == .season { Text("\(item.name ?? "") • \(String(item.productionYear ?? 0))") .foregroundColor(.secondary) .font(.caption) diff --git a/Swiftfin tvOS/Views/MovieLibrariesView.swift b/Swiftfin tvOS/Views/MovieLibrariesView.swift index 88022ef6..21886620 100644 --- a/Swiftfin tvOS/Views/MovieLibrariesView.swift +++ b/Swiftfin tvOS/Views/MovieLibrariesView.swift @@ -47,7 +47,7 @@ struct MovieLibrariesView: View { } cell: { _, cell in GeometryReader { _ in if let item = cell.item { - if item.type != "Folder" { + if item.type != .folder { Button { self.movieLibrariesRouter.route(to: \.library, item) } label: { diff --git a/Swiftfin tvOS/Views/TVLibrariesView.swift b/Swiftfin tvOS/Views/TVLibrariesView.swift index eb88be0b..66d04128 100644 --- a/Swiftfin tvOS/Views/TVLibrariesView.swift +++ b/Swiftfin tvOS/Views/TVLibrariesView.swift @@ -47,7 +47,7 @@ struct TVLibrariesView: View { } cell: { _, cell in GeometryReader { _ in if let item = cell.item { - if item.type != "Folder" { + if item.type != .folder { Button { self.tvLibrariesRouter.route(to: \.library, item) } label: { diff --git a/Swiftfin.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Swiftfin.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index d0bf750d..30f55566 100644 --- a/Swiftfin.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Swiftfin.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -14,8 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/Flight-School/AnyCodable", "state" : { - "revision" : "11423ef0c756e8a1f6b4bb576dab9d97bc016c70", - "version" : "0.6.4" + "revision" : "f9fda69a7b704d46fb5123005f2f7e43dbb8a0fa", + "version" : "0.6.5" } }, { @@ -32,8 +32,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/CombineCommunity/CombineExt", "state" : { - "revision" : "0880829102152185190064fd17847a7c681d2127", - "version" : "1.5.1" + "revision" : "38a4d4cb01f8c7750671c786d33dfbea00cbd131", + "version" : "1.6.1" } }, { @@ -50,8 +50,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/sindresorhus/Defaults", "state" : { - "revision" : "119f654d44f7b90f00dc11f7dd1c94a36f12576b", - "version" : "6.2.1" + "revision" : "981ccb0a01c54abbe3c12ccb8226108527bbf115", + "version" : "6.3.0" } }, { @@ -78,16 +78,16 @@ "location" : "https://github.com/jellyfin/jellyfin-sdk-swift", "state" : { "branch" : "main", - "revision" : "f315671ced976a7ec75ce6a1f37e00b4880cbf4b" + "revision" : "9d6e46b94d2178116ee3546b03dfb67e19e3a93a" } }, { "identity" : "nuke", "kind" : "remoteSourceControl", - "location" : "https://github.com/kean/Nuke.git", + "location" : "https://github.com/kean/Nuke", "state" : { - "revision" : "0ea7545b5c918285aacc044dc75048625c8257cc", - "version" : "10.8.0" + "revision" : "a002b7fd786f2df2ed4333fe73a9727499fd9d97", + "version" : "10.11.2" } }, { @@ -95,8 +95,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/kean/NukeUI", "state" : { - "revision" : "17f26c07e6b1d3b9258287f99f528111fcd7b7ad", - "version" : "0.8.1" + "revision" : "ebfed3c9a4e97e310b0ff8ee0fffe5579887a825", + "version" : "0.8.2" } }, { @@ -122,8 +122,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/rundfunk47/stinsen", "state" : { - "revision" : "36d97964075dc770046ddef9346a29bfa8982d6d", - "version" : "2.0.7" + "revision" : "add05384b65ae3e39474f51ab5b9281abd238c60", + "version" : "2.0.9" } }, { @@ -168,7 +168,7 @@ "location" : "https://github.com/spacenation/swiftui-sliders", "state" : { "branch" : "master", - "revision" : "538e16b35ad7a066a8f5624da9ecee6327886bf7" + "revision" : "5ba8614462a7ed4bd47a93fbca6c281599f74337" } }, { diff --git a/WidgetExtension/NextUpWidget.swift b/WidgetExtension/NextUpWidget.swift index 91ef20a2..c22d8b6d 100644 --- a/WidgetExtension/NextUpWidget.swift +++ b/WidgetExtension/NextUpWidget.swift @@ -32,7 +32,7 @@ struct NextUpWidgetProvider: TimelineProvider { let savedUser = currentLogin.user var tempCancellables = Set() - JellyfinAPI.basePath = server.currentURI + JellyfinAPIAPI.basePath = server.currentURI TvShowsAPI.getNextUp(userId: savedUser.id, limit: 3, fields: [.primaryImageAspectRatio, .seriesPrimaryImage, .seasonUserData, .overview, .genres, .people], imageTypeLimit: 1, enableImageTypes: [.primary, .backdrop, .thumb]) @@ -77,7 +77,7 @@ struct NextUpWidgetProvider: TimelineProvider { var tempCancellables = Set() - JellyfinAPI.basePath = server.currentURI + JellyfinAPIAPI.basePath = server.currentURI TvShowsAPI.getNextUp(userId: savedUser.id, limit: 3, fields: [.primaryImageAspectRatio, .seriesPrimaryImage, .seasonUserData, .overview, .genres, .people], imageTypeLimit: 1, enableImageTypes: [.primary, .backdrop, .thumb])