From c54d4d799af3b5ff5527563fe2494be0294874a0 Mon Sep 17 00:00:00 2001 From: Ibrahim Sulejmenov Date: Tue, 18 Mar 2025 17:22:58 +0100 Subject: [PATCH] Implemented season seperator for tv shows --- Sora/Views/MediaInfoView/MediaInfoView.swift | 167 +++++++++++++++---- 1 file changed, 130 insertions(+), 37 deletions(-) diff --git a/Sora/Views/MediaInfoView/MediaInfoView.swift b/Sora/Views/MediaInfoView/MediaInfoView.swift index 98a19af..5ce7cb2 100644 --- a/Sora/Views/MediaInfoView/MediaInfoView.swift +++ b/Sora/Views/MediaInfoView/MediaInfoView.swift @@ -38,6 +38,7 @@ struct MediaInfoView: View { @State private var selectedEpisodeNumber: Int = 0 @State private var selectedEpisodeImage: String = "" + @State private var selectedSeason: Int = 0 @AppStorage("externalPlayer") private var externalPlayer: String = "Default" @AppStorage("episodeChunkSize") private var episodeChunkSize: Int = 100 @@ -48,6 +49,17 @@ struct MediaInfoView: View { @State private var selectedRange: Range = 0..<100 + private var isGroupedBySeasons: Bool { + var lastEpisodeNumber = 0 + for episode in episodeLinks { + if episode.number == 1 && lastEpisodeNumber > 1 { + return true + } + lastEpisodeNumber = episode.number + } + return false + } + var body: some View { Group { if isLoading { @@ -190,7 +202,7 @@ struct MediaInfoView: View { Spacer() - if episodeLinks.count > episodeChunkSize { + if !isGroupedBySeasons, episodeLinks.count > episodeChunkSize { Menu { ForEach(generateRanges(), id: \.self) { range in Button(action: { @@ -204,44 +216,105 @@ struct MediaInfoView: View { .font(.system(size: 14)) .foregroundColor(.accentColor) } - } - } - - ForEach(episodeLinks.indices.filter { selectedRange.contains($0) }, id: \.self) { i in - let ep = episodeLinks[i] - let lastPlayedTime = UserDefaults.standard.double(forKey: "lastPlayedTime_\(ep.href)") - let totalTime = UserDefaults.standard.double(forKey: "totalTime_\(ep.href)") - let progress = totalTime > 0 ? lastPlayedTime / totalTime : 0 - - EpisodeCell( - episodeIndex: i, - episode: ep.href, - episodeID: ep.number - 1, - progress: progress, - itemID: itemID ?? 0, - onTap: { imageUrl in - if !isFetchingEpisode { - selectedEpisodeNumber = ep.number - selectedEpisodeImage = imageUrl - fetchStream(href: ep.href) - AnalyticsManager.shared.sendEvent( - event: "watch", - additionalData: ["title": title, "episode": ep.number] - ) + } else if isGroupedBySeasons { + let seasons = groupedEpisodes() + if !seasons.isEmpty { + Menu { + ForEach(0.. 0 ? lastPlayedTime / totalTime : 0 + + EpisodeCell( + episodeIndex: i, + episode: ep.href, + episodeID: ep.number - 1, + progress: progress, + itemID: itemID ?? 0, + onTap: { imageUrl in + if !isFetchingEpisode { + selectedEpisodeNumber = ep.number + selectedEpisodeImage = imageUrl + fetchStream(href: ep.href) + AnalyticsManager.shared.sendEvent( + event: "watch", + additionalData: ["title": title, "episode": ep.number] + ) + } + }, + onMarkAllPrevious: { + for idx in 0.. 0 ? lastPlayedTime / totalTime : 0 + + EpisodeCell( + episodeIndex: i, + episode: ep.href, + episodeID: ep.number - 1, + progress: progress, + itemID: itemID ?? 0, + onTap: { imageUrl in + if !isFetchingEpisode { + selectedEpisodeNumber = ep.number + selectedEpisodeImage = imageUrl + fetchStream(href: ep.href) + AnalyticsManager.shared.sendEvent( + event: "watch", + additionalData: ["title": title, "episode": ep.number] + ) + } + }, + onMarkAllPrevious: { + for idx in 0.. [[EpisodeLink]] { + guard !episodeLinks.isEmpty else { return [] } + var groups: [[EpisodeLink]] = [] + var currentGroup: [EpisodeLink] = [] + + for ep in episodeLinks { + if let last = currentGroup.last, ep.number <= last.number { + groups.append(currentGroup) + currentGroup = [ep] + } else { + currentGroup.append(ep) + } + } + + if !currentGroup.isEmpty { + groups.append(currentGroup) + } + return groups + } + func fetchDetails() { DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { Task {