From 6fc185a5064dba59f5dbe3bc0b531f2d54a8b58a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Feb 2026 09:50:45 +0000 Subject: [PATCH 1/8] Initial plan From 394a1ed164e0970a83d852c176584198ea4ef987 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Feb 2026 09:52:32 +0000 Subject: [PATCH 2/8] Persist aniListID in AssetMetadata to fix AniList progress for downloaded episodes Co-authored-by: scigward <162128369+scigward@users.noreply.github.com> --- Sora/Utlis & Misc/DownloadUtils/DownloadModels.swift | 5 ++++- .../JSLoader/Downloads/JSController+Downloader.swift | 3 ++- .../JSLoader/Downloads/JSController-Downloads.swift | 7 ++++--- Sora/Views/DownloadView.swift | 2 +- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/Sora/Utlis & Misc/DownloadUtils/DownloadModels.swift b/Sora/Utlis & Misc/DownloadUtils/DownloadModels.swift index 894d176..4a1d891 100644 --- a/Sora/Utlis & Misc/DownloadUtils/DownloadModels.swift +++ b/Sora/Utlis & Misc/DownloadUtils/DownloadModels.swift @@ -415,6 +415,7 @@ struct AssetMetadata: Codable { let seasonNumber: Int? /// Indicates whether this episode is a filler (derived from metadata at download time) let isFiller: Bool? + let aniListID: Int? init( title: String, @@ -428,7 +429,8 @@ struct AssetMetadata: Codable { showPosterURL: URL? = nil, episodeTitle: String? = nil, seasonNumber: Int? = nil, - isFiller: Bool? = nil + isFiller: Bool? = nil, + aniListID: Int? = nil ) { self.title = title self.overview = overview @@ -442,6 +444,7 @@ struct AssetMetadata: Codable { self.episodeTitle = episodeTitle self.seasonNumber = seasonNumber self.isFiller = isFiller + self.aniListID = aniListID } } diff --git a/Sora/Utlis & Misc/JSLoader/Downloads/JSController+Downloader.swift b/Sora/Utlis & Misc/JSLoader/Downloads/JSController+Downloader.swift index 2639d00..997e0cb 100644 --- a/Sora/Utlis & Misc/JSLoader/Downloads/JSController+Downloader.swift +++ b/Sora/Utlis & Misc/JSLoader/Downloads/JSController+Downloader.swift @@ -382,7 +382,8 @@ extension JSController { showPosterURL: request.showPosterURL ?? request.imageURL, episodeTitle: nil, seasonNumber: nil, - isFiller: request.isFiller + isFiller: request.isFiller, + aniListID: request.aniListID ) } diff --git a/Sora/Utlis & Misc/JSLoader/Downloads/JSController-Downloads.swift b/Sora/Utlis & Misc/JSLoader/Downloads/JSController-Downloads.swift index b24ba77..a4c584b 100644 --- a/Sora/Utlis & Misc/JSLoader/Downloads/JSController-Downloads.swift +++ b/Sora/Utlis & Misc/JSLoader/Downloads/JSController-Downloads.swift @@ -156,14 +156,15 @@ extension JSController { let assetMetadata = AssetMetadata( title: downloadTitle, overview: nil, - posterURL: imageURL, // Episode thumbnail + posterURL: imageURL, backdropURL: imageURL, releaseDate: nil, showTitle: animeTitle, season: season, episode: episode, - showPosterURL: showPosterURL, // Main show poster - isFiller: isFiller + showPosterURL: showPosterURL, + isFiller: isFiller, + aniListID: aniListID ) // Create the download ID now so we can use it for notifications diff --git a/Sora/Views/DownloadView.swift b/Sora/Views/DownloadView.swift index 986fb50..5c3f7c2 100644 --- a/Sora/Views/DownloadView.swift +++ b/Sora/Views/DownloadView.swift @@ -281,7 +281,7 @@ struct DownloadView: View { } }, subtitlesURL: asset.localSubtitleURL?.absoluteString, - aniListID: 0, + aniListID: asset.metadata?.aniListID ?? 0, totalEpisodes: asset.metadata?.episode ?? 0, episodeImageUrl: asset.metadata?.posterURL?.absoluteString ?? "", headers: nil From 0339ab3e24f1ea0d783e96384494e00eac857bbf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Feb 2026 10:09:46 +0000 Subject: [PATCH 3/8] Restore inline posterURL comments removed in previous commit Co-authored-by: scigward <162128369+scigward@users.noreply.github.com> --- .../JSLoader/Downloads/JSController-Downloads.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sora/Utlis & Misc/JSLoader/Downloads/JSController-Downloads.swift b/Sora/Utlis & Misc/JSLoader/Downloads/JSController-Downloads.swift index a4c584b..a0d11f4 100644 --- a/Sora/Utlis & Misc/JSLoader/Downloads/JSController-Downloads.swift +++ b/Sora/Utlis & Misc/JSLoader/Downloads/JSController-Downloads.swift @@ -156,13 +156,13 @@ extension JSController { let assetMetadata = AssetMetadata( title: downloadTitle, overview: nil, - posterURL: imageURL, + posterURL: imageURL, // Episode thumbnail backdropURL: imageURL, releaseDate: nil, showTitle: animeTitle, season: season, episode: episode, - showPosterURL: showPosterURL, + showPosterURL: showPosterURL, // Main show poster isFiller: isFiller, aniListID: aniListID ) From 605e67f6f8b3ff8749467a50b44a4a65d0a1ec48 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Feb 2026 10:42:26 +0000 Subject: [PATCH 4/8] Fix incorrect COMPLETED status when playing downloaded episodes Co-authored-by: scigward <162128369+scigward@users.noreply.github.com> --- Sora/Views/DownloadView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sora/Views/DownloadView.swift b/Sora/Views/DownloadView.swift index 5c3f7c2..bdf5520 100644 --- a/Sora/Views/DownloadView.swift +++ b/Sora/Views/DownloadView.swift @@ -282,7 +282,7 @@ struct DownloadView: View { }, subtitlesURL: asset.localSubtitleURL?.absoluteString, aniListID: asset.metadata?.aniListID ?? 0, - totalEpisodes: asset.metadata?.episode ?? 0, + totalEpisodes: 0, episodeImageUrl: asset.metadata?.posterURL?.absoluteString ?? "", headers: nil ) From a8b7728281ed32570c44dad09d46637090372862 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Feb 2026 10:53:48 +0000 Subject: [PATCH 5/8] Persist totalEpisodes through download pipeline; fix showPosterURL fallback in createAssetMetadata Co-authored-by: scigward <162128369+scigward@users.noreply.github.com> --- .../DownloadUtils/DownloadModels.swift | 5 ++++- .../Downloads/JSController+Downloader.swift | 19 +++++++++++++------ .../Downloads/JSController-Downloads.swift | 5 ++++- .../JSController-StreamTypeDownload.swift | 3 +++ Sora/Views/DownloadView.swift | 2 +- .../EpisodeCell/EpisodeCell.swift | 3 ++- 6 files changed, 27 insertions(+), 10 deletions(-) diff --git a/Sora/Utlis & Misc/DownloadUtils/DownloadModels.swift b/Sora/Utlis & Misc/DownloadUtils/DownloadModels.swift index 4a1d891..3933965 100644 --- a/Sora/Utlis & Misc/DownloadUtils/DownloadModels.swift +++ b/Sora/Utlis & Misc/DownloadUtils/DownloadModels.swift @@ -416,6 +416,7 @@ struct AssetMetadata: Codable { /// Indicates whether this episode is a filler (derived from metadata at download time) let isFiller: Bool? let aniListID: Int? + let totalEpisodes: Int? init( title: String, @@ -430,7 +431,8 @@ struct AssetMetadata: Codable { episodeTitle: String? = nil, seasonNumber: Int? = nil, isFiller: Bool? = nil, - aniListID: Int? = nil + aniListID: Int? = nil, + totalEpisodes: Int? = nil ) { self.title = title self.overview = overview @@ -445,6 +447,7 @@ struct AssetMetadata: Codable { self.seasonNumber = seasonNumber self.isFiller = isFiller self.aniListID = aniListID + self.totalEpisodes = totalEpisodes } } diff --git a/Sora/Utlis & Misc/JSLoader/Downloads/JSController+Downloader.swift b/Sora/Utlis & Misc/JSLoader/Downloads/JSController+Downloader.swift index 997e0cb..8a01ef3 100644 --- a/Sora/Utlis & Misc/JSLoader/Downloads/JSController+Downloader.swift +++ b/Sora/Utlis & Misc/JSLoader/Downloads/JSController+Downloader.swift @@ -23,10 +23,11 @@ struct DownloadRequest { let aniListID: Int? let malID: Int? let isFiller: Bool? + let totalEpisodes: Int? init(url: URL, headers: [String: String], title: String? = nil, imageURL: URL? = nil, isEpisode: Bool = false, showTitle: String? = nil, season: Int? = nil, - episode: Int? = nil, subtitleURL: URL? = nil, showPosterURL: URL? = nil, aniListID: Int? = nil, malID: Int? = nil, isFiller: Bool? = nil) { + episode: Int? = nil, subtitleURL: URL? = nil, showPosterURL: URL? = nil, aniListID: Int? = nil, malID: Int? = nil, isFiller: Bool? = nil, totalEpisodes: Int? = nil) { self.url = url self.headers = headers self.title = title @@ -40,6 +41,7 @@ struct DownloadRequest { self.aniListID = aniListID self.malID = malID self.isFiller = isFiller + self.totalEpisodes = totalEpisodes } } @@ -62,6 +64,7 @@ extension JSController { showTitle: String? = nil, season: Int? = nil, episode: Int? = nil, subtitleURL: URL? = nil, showPosterURL: URL? = nil, aniListID: Int? = nil, malID: Int? = nil, isFiller: Bool? = nil, + totalEpisodes: Int? = nil, completionHandler: ((Bool, String) -> Void)? = nil) { @@ -69,7 +72,7 @@ extension JSController { url: url, headers: headers, title: title, imageURL: imageURL, isEpisode: isEpisode, showTitle: showTitle, season: season, episode: episode, subtitleURL: subtitleURL, showPosterURL: showPosterURL, - aniListID: aniListID, malID: malID, isFiller: isFiller + aniListID: aniListID, malID: malID, isFiller: isFiller, totalEpisodes: totalEpisodes ) logDownloadStart(request: request) @@ -114,7 +117,8 @@ extension JSController { showPosterURL: request.showPosterURL, aniListID: request.aniListID, malID: request.malID, - isFiller: request.isFiller + isFiller: request.isFiller, + totalEpisodes: request.totalEpisodes ) self.downloadWithOriginalMethod(request: qualityRequest, completionHandler: completionHandler) } else { @@ -142,6 +146,7 @@ extension JSController { imageURL: URL? = nil, isEpisode: Bool = false, showTitle: String? = nil, season: Int? = nil, episode: Int? = nil, subtitleURL: URL? = nil, showPosterURL: URL? = nil, aniListID: Int? = nil, malID: Int? = nil, isFiller: Bool? = nil, + totalEpisodes: Int? = nil, completionHandler: ((Bool, String) -> Void)? = nil) { @@ -149,7 +154,7 @@ extension JSController { url: url, headers: headers, title: title, imageURL: imageURL, isEpisode: isEpisode, showTitle: showTitle, season: season, episode: episode, subtitleURL: subtitleURL, showPosterURL: showPosterURL, - aniListID: aniListID, malID: malID, isFiller: isFiller + aniListID: aniListID, malID: malID, isFiller: isFiller, totalEpisodes: totalEpisodes ) downloadMP4(request: request, completionHandler: completionHandler) @@ -379,11 +384,12 @@ extension JSController { showTitle: request.showTitle, season: request.season, episode: request.episode, - showPosterURL: request.showPosterURL ?? request.imageURL, + showPosterURL: request.showPosterURL, episodeTitle: nil, seasonNumber: nil, isFiller: request.isFiller, - aniListID: request.aniListID + aniListID: request.aniListID, + totalEpisodes: request.totalEpisodes ) } @@ -437,6 +443,7 @@ extension JSController { aniListID: request.aniListID, malID: request.malID, isFiller: request.isFiller, + totalEpisodes: request.totalEpisodes, completionHandler: completionHandler ) } diff --git a/Sora/Utlis & Misc/JSLoader/Downloads/JSController-Downloads.swift b/Sora/Utlis & Misc/JSLoader/Downloads/JSController-Downloads.swift index a0d11f4..2107065 100644 --- a/Sora/Utlis & Misc/JSLoader/Downloads/JSController-Downloads.swift +++ b/Sora/Utlis & Misc/JSLoader/Downloads/JSController-Downloads.swift @@ -117,6 +117,7 @@ extension JSController { aniListID: Int? = nil, malID: Int? = nil, isFiller: Bool? = nil, + totalEpisodes: Int? = nil, completionHandler: ((Bool, String) -> Void)? = nil ) { // If a module is provided, use the stream type aware download @@ -137,6 +138,7 @@ extension JSController { aniListID: aniListID, malID: malID, isFiller: isFiller, + totalEpisodes: totalEpisodes, completionHandler: completionHandler ) return @@ -164,7 +166,8 @@ extension JSController { episode: episode, showPosterURL: showPosterURL, // Main show poster isFiller: isFiller, - aniListID: aniListID + aniListID: aniListID, + totalEpisodes: totalEpisodes ) // Create the download ID now so we can use it for notifications diff --git a/Sora/Utlis & Misc/JSLoader/Downloads/JSController-StreamTypeDownload.swift b/Sora/Utlis & Misc/JSLoader/Downloads/JSController-StreamTypeDownload.swift index e8ede8b..fabef1a 100644 --- a/Sora/Utlis & Misc/JSLoader/Downloads/JSController-StreamTypeDownload.swift +++ b/Sora/Utlis & Misc/JSLoader/Downloads/JSController-StreamTypeDownload.swift @@ -39,6 +39,7 @@ extension JSController { aniListID: Int? = nil, malID: Int? = nil, isFiller: Bool? = nil, + totalEpisodes: Int? = nil, completionHandler: ((Bool, String) -> Void)? = nil ) { let streamType = module.metadata.streamType.lowercased() @@ -59,6 +60,7 @@ extension JSController { aniListID: aniListID, malID: malID, isFiller: isFiller, + totalEpisodes: totalEpisodes, completionHandler: completionHandler ) }else { @@ -77,6 +79,7 @@ extension JSController { aniListID: aniListID, malID: malID, isFiller: isFiller, + totalEpisodes: totalEpisodes, completionHandler: completionHandler ) } diff --git a/Sora/Views/DownloadView.swift b/Sora/Views/DownloadView.swift index bdf5520..febc378 100644 --- a/Sora/Views/DownloadView.swift +++ b/Sora/Views/DownloadView.swift @@ -282,7 +282,7 @@ struct DownloadView: View { }, subtitlesURL: asset.localSubtitleURL?.absoluteString, aniListID: asset.metadata?.aniListID ?? 0, - totalEpisodes: 0, + totalEpisodes: asset.metadata?.totalEpisodes ?? 0, episodeImageUrl: asset.metadata?.posterURL?.absoluteString ?? "", headers: nil ) diff --git a/Sora/Views/MediaInfoView/EpisodeCell/EpisodeCell.swift b/Sora/Views/MediaInfoView/EpisodeCell/EpisodeCell.swift index 15d78af..60c4c41 100644 --- a/Sora/Views/MediaInfoView/EpisodeCell/EpisodeCell.swift +++ b/Sora/Views/MediaInfoView/EpisodeCell/EpisodeCell.swift @@ -747,7 +747,8 @@ private extension EpisodeCell { showPosterURL: showPosterImageURL, aniListID: itemID, malID: malIDFromParent, - isFiller: isFiller + isFiller: isFiller, + totalEpisodes: totalEpisodes ) { success, message in if success { Logger.shared.log("Started download for Episode \(self.episodeID + 1): \(self.episode)", type: "Download") From a17cd250bb8e5aa320df2038b4772fec4cae8f69 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Feb 2026 10:59:52 +0000 Subject: [PATCH 6/8] Fix hero poster pinned to top in EnhancedShowEpisodesView Co-authored-by: scigward <162128369+scigward@users.noreply.github.com> --- Sora/Views/DownloadView.swift | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/Sora/Views/DownloadView.swift b/Sora/Views/DownloadView.swift index febc378..c64df14 100644 --- a/Sora/Views/DownloadView.swift +++ b/Sora/Views/DownloadView.swift @@ -1108,24 +1108,27 @@ struct EnhancedShowEpisodesView: View { @ViewBuilder private var heroImageSection: some View { - if let posterURL = group.posterURL { - LazyImage(url: posterURL) { state in - if let uiImage = state.imageContainer?.image { - Image(uiImage: uiImage) - .resizable() - .aspectRatio(contentMode: .fill) - } else { - placeholderGradient + VStack(spacing: 0) { + if let posterURL = group.posterURL { + LazyImage(url: posterURL) { state in + if let uiImage = state.imageContainer?.image { + Image(uiImage: uiImage) + .resizable() + .aspectRatio(contentMode: .fill) + } else { + placeholderGradient + } } - } - .ignoresSafeArea(.all) - .frame(maxWidth: .infinity, maxHeight: 400) - .clipped() - } else { - placeholderGradient .ignoresSafeArea(.all) .frame(maxWidth: .infinity, maxHeight: 400) .clipped() + } else { + placeholderGradient + .ignoresSafeArea(.all) + .frame(maxWidth: .infinity, maxHeight: 400) + .clipped() + } + Spacer() } } From f5e0b8e73bf0d114982780b20eeb5090f9033a45 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Feb 2026 11:09:48 +0000 Subject: [PATCH 7/8] Match upstream Sora poster layout in EnhancedShowEpisodesView Co-authored-by: scigward <162128369+scigward@users.noreply.github.com> --- Sora/Views/DownloadView.swift | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/Sora/Views/DownloadView.swift b/Sora/Views/DownloadView.swift index c64df14..e844cd0 100644 --- a/Sora/Views/DownloadView.swift +++ b/Sora/Views/DownloadView.swift @@ -1050,7 +1050,6 @@ struct EnhancedShowEpisodesView: View { var body: some View { ZStack { - heroImageSection mainScrollView .navigationBarHidden(true) .ignoresSafeArea(.container, edges: .top) @@ -1099,7 +1098,10 @@ struct EnhancedShowEpisodesView: View { @ViewBuilder private var mainScrollView: some View { ScrollView(showsIndicators: false) { - contentContainer + ZStack(alignment: .top) { + heroImageSection + contentContainer + } } .onAppear { UIScrollView.appearance().bounces = false @@ -1108,27 +1110,22 @@ struct EnhancedShowEpisodesView: View { @ViewBuilder private var heroImageSection: some View { - VStack(spacing: 0) { + Group { if let posterURL = group.posterURL { - LazyImage(url: posterURL) { state in + LazyImage(url: posterURL) { @MainActor state in if let uiImage = state.imageContainer?.image { Image(uiImage: uiImage) .resizable() .aspectRatio(contentMode: .fill) + .frame(width: UIScreen.main.bounds.width, height: 700) + .clipped() } else { placeholderGradient } } - .ignoresSafeArea(.all) - .frame(maxWidth: .infinity, maxHeight: 400) - .clipped() } else { placeholderGradient - .ignoresSafeArea(.all) - .frame(maxWidth: .infinity, maxHeight: 400) - .clipped() } - Spacer() } } @@ -1145,6 +1142,8 @@ struct EnhancedShowEpisodesView: View { endPoint: .bottomTrailing ) ) + .frame(width: UIScreen.main.bounds.width, height: 700) + .clipped() } @ViewBuilder From fe8debf509fc1fd70b650881d9c5b8624e8d66fc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Feb 2026 15:30:44 +0000 Subject: [PATCH 8/8] Restore showPosterURL ?? imageURL fallback in createAssetMetadata Co-authored-by: scigward <162128369+scigward@users.noreply.github.com> --- .../JSLoader/Downloads/JSController+Downloader.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sora/Utlis & Misc/JSLoader/Downloads/JSController+Downloader.swift b/Sora/Utlis & Misc/JSLoader/Downloads/JSController+Downloader.swift index 8a01ef3..073766c 100644 --- a/Sora/Utlis & Misc/JSLoader/Downloads/JSController+Downloader.swift +++ b/Sora/Utlis & Misc/JSLoader/Downloads/JSController+Downloader.swift @@ -384,7 +384,7 @@ extension JSController { showTitle: request.showTitle, season: request.season, episode: request.episode, - showPosterURL: request.showPosterURL, + showPosterURL: request.showPosterURL ?? request.imageURL, episodeTitle: nil, seasonNumber: nil, isFiller: request.isFiller,