diff --git a/Sora/Utils/ContinueWatching/ContinueWatchingItem.swift b/Sora/Utils/ContinueWatching/ContinueWatchingItem.swift index 7154e4b..3d7d737 100644 --- a/Sora/Utils/ContinueWatching/ContinueWatchingItem.swift +++ b/Sora/Utils/ContinueWatching/ContinueWatchingItem.swift @@ -12,7 +12,7 @@ struct ContinueWatchingItem: Codable, Identifiable { let imageUrl: String let episodeNumber: Int let mediaTitle: String - let progress: Double + var progress: Double let streamUrl: String let fullUrl: String let subtitles: String? diff --git a/Sora/Utils/ContinueWatching/ContinueWatchingManager.swift b/Sora/Utils/ContinueWatching/ContinueWatchingManager.swift index 1fa8567..bfda215 100644 --- a/Sora/Utils/ContinueWatching/ContinueWatchingManager.swift +++ b/Sora/Utils/ContinueWatching/ContinueWatchingManager.swift @@ -20,25 +20,43 @@ class ContinueWatchingManager { } func save(item: ContinueWatchingItem) { - if item.progress >= 0.9 { + // Read the real playback times + let lastKey = "lastPlayedTime_\(item.fullUrl)" + let totalKey = "totalTime_\(item.fullUrl)" + let lastPlayed = UserDefaults.standard.double(forKey: lastKey) + let totalTime = UserDefaults.standard.double(forKey: totalKey) + + // Compute up-to-date progress + let actualProgress: Double + if totalTime > 0 { + actualProgress = min(max(lastPlayed / totalTime, 0), 1) + } else { + actualProgress = item.progress + } + + // If watched ≥ 90%, remove it + if actualProgress >= 0.9 { remove(item: item) return } - var items = fetchItems() + // Otherwise update progress and re-save + var updatedItem = item + updatedItem.progress = actualProgress + var items = fetchItems() items.removeAll { existing in existing.fullUrl == item.fullUrl && existing.episodeNumber == item.episodeNumber && existing.module.metadata.sourceName == item.module.metadata.sourceName } - - items.append(item) + items.append(updatedItem) if let data = try? JSONEncoder().encode(items) { UserDefaults.standard.set(data, forKey: storageKey) } } + func fetchItems() -> [ContinueWatchingItem] { guard diff --git a/Sora/Views/DownloadView.swift b/Sora/Views/DownloadView.swift index 3cdae21..6da69cf 100644 --- a/Sora/Views/DownloadView.swift +++ b/Sora/Views/DownloadView.swift @@ -123,17 +123,18 @@ struct DownloadView: View { Image(systemName: "arrow.down.circle") .font(.system(size: 60)) .foregroundColor(.gray) - .padding() + .padding(4) Text("No Active Downloads") .font(.title2) .foregroundColor(.gray) + .padding(3) Text("Download episodes from the episode list") .font(.subheadline) .foregroundColor(.gray) .multilineTextAlignment(.center) - .padding() + } .frame(maxWidth: .infinity, maxHeight: .infinity) } @@ -143,17 +144,17 @@ struct DownloadView: View { Image(systemName: "arrow.down.circle") .font(.system(size: 60)) .foregroundColor(.gray) - .padding() + .padding(4) Text("No Downloads") .font(.title2) .foregroundColor(.gray) + .padding(3) - Text("Your downloaded assets will appear here") + Text("Your downloaded episodes will appear here") .font(.subheadline) .foregroundColor(.gray) .multilineTextAlignment(.center) - .padding() } .frame(maxWidth: .infinity, maxHeight: .infinity) } diff --git a/Sora/Views/LibraryView/LibraryView.swift b/Sora/Views/LibraryView/LibraryView.swift index 3d24c3d..5ee687a 100644 --- a/Sora/Views/LibraryView/LibraryView.swift +++ b/Sora/Views/LibraryView/LibraryView.swift @@ -369,14 +369,27 @@ struct ContinueWatchingCell: View { } private func updateProgress() { - let lastPlayedTime = UserDefaults.standard.double(forKey: "lastPlayedTime_\(item.fullUrl)") - let totalTime = UserDefaults.standard.double(forKey: "totalTime_\(item.fullUrl)") + // grab the true playback times + let lastPlayed = UserDefaults.standard.double(forKey: "lastPlayedTime_\(item.fullUrl)") + let totalTime = UserDefaults.standard.double(forKey: "totalTime_\(item.fullUrl)") + // compute a clean 0…1 ratio + let ratio: Double if totalTime > 0 { - let ratio = lastPlayedTime / totalTime - currentProgress = max(0, min(ratio, 1)) + ratio = min(max(lastPlayed / totalTime, 0), 1) } else { - currentProgress = max(0, min(item.progress, 1)) + ratio = min(max(item.progress, 0), 1) + } + currentProgress = ratio + + if ratio >= 0.9 { + // >90% watched? drop it immediately + removeItem() + } else { + // otherwise persist the latest progress + var updated = item + updated.progress = ratio + ContinueWatchingManager.shared.save(item: updated) } } } diff --git a/Sora/Views/SettingsView/SettingsSubViews/SettingsViewData.swift b/Sora/Views/SettingsView/SettingsSubViews/SettingsViewData.swift index e6a5b19..78be70f 100644 --- a/Sora/Views/SettingsView/SettingsSubViews/SettingsViewData.swift +++ b/Sora/Views/SettingsView/SettingsSubViews/SettingsViewData.swift @@ -23,6 +23,11 @@ struct SettingsViewData: View { @State private var isImageCachingEnabled: Bool = true @State private var isMemoryOnlyMode: Bool = false + enum ActiveAlert { case eraseData, removeDocs, removeMovPkg } + + @State private var showAlert = false + @State private var activeAlert: ActiveAlert = .eraseData + var body: some View { Form { // New section for cache settings @@ -86,7 +91,8 @@ struct SettingsViewData: View { HStack { Button(action: { - showRemoveDocumentsAlert = true + activeAlert = .removeDocs + showAlert = true }) { Text("Remove All Files in Documents") } @@ -109,7 +115,8 @@ struct SettingsViewData: View { } Button(action: { - showEraseAppDataAlert = true + activeAlert = .eraseData + showAlert = true }) { Text("Erase all App Data") } @@ -125,37 +132,32 @@ struct SettingsViewData: View { calculateCacheSize() updateSizes() } - .alert(isPresented: $showEraseAppDataAlert) { - Alert( - title: Text("Erase App Data"), - message: Text("Are you sure you want to erase all app data? This action cannot be undone."), - primaryButton: .destructive(Text("Erase")) { - eraseAppData() - }, - secondaryButton: .cancel() + .alert(isPresented: $showAlert) { + switch activeAlert { + case .eraseData: + return Alert( + title: Text("Erase App Data"), + message: Text("Are you sure you want to erase all app data? This action cannot be undone."), + primaryButton: .destructive(Text("Erase")) { eraseAppData() }, + secondaryButton: .cancel() ) - } - .alert(isPresented: $showRemoveDocumentsAlert) { - Alert( - title: Text("Remove Documents"), - message: Text("Are you sure you want to remove all files in the Documents folder? This will remove all modules."), - primaryButton: .destructive(Text("Remove")) { - removeAllFilesInDocuments() - }, - secondaryButton: .cancel() + case .removeDocs: + return Alert( + title: Text("Remove Documents"), + message: Text("Are you sure you want to remove all files in the Documents folder? This will remove all modules."), + primaryButton: .destructive(Text("Remove")) { removeAllFilesInDocuments() }, + secondaryButton: .cancel() ) - } - .alert(isPresented: $showRemoveMovPkgAlert) { - Alert( - title: Text("Remove Downloads"), - message: Text("Are you sure you want to remove all Downloads?"), - primaryButton: .destructive(Text("Remove")) { - removeMovPkgFiles() - }, - secondaryButton: .cancel() + case .removeMovPkg: + return Alert( + title: Text("Remove Downloads"), + message: Text("Are you sure you want to remove all Downloads?"), + primaryButton: .destructive(Text("Remove")) { removeMovPkgFiles() }, + secondaryButton: .cancel() ) + } } - } + } // Calculate and update the combined cache size func calculateCacheSize() {