mirror of
https://github.com/cranci1/Sora.git
synced 2026-04-21 00:22:12 +00:00
few bug fixes (#136)
This commit is contained in:
parent
30aa66bf2a
commit
d26f066da0
5 changed files with 78 additions and 44 deletions
|
|
@ -12,7 +12,7 @@ struct ContinueWatchingItem: Codable, Identifiable {
|
||||||
let imageUrl: String
|
let imageUrl: String
|
||||||
let episodeNumber: Int
|
let episodeNumber: Int
|
||||||
let mediaTitle: String
|
let mediaTitle: String
|
||||||
let progress: Double
|
var progress: Double
|
||||||
let streamUrl: String
|
let streamUrl: String
|
||||||
let fullUrl: String
|
let fullUrl: String
|
||||||
let subtitles: String?
|
let subtitles: String?
|
||||||
|
|
|
||||||
|
|
@ -20,25 +20,43 @@ class ContinueWatchingManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
func save(item: ContinueWatchingItem) {
|
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)
|
remove(item: item)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var items = fetchItems()
|
// Otherwise update progress and re-save
|
||||||
|
var updatedItem = item
|
||||||
|
updatedItem.progress = actualProgress
|
||||||
|
|
||||||
|
var items = fetchItems()
|
||||||
items.removeAll { existing in
|
items.removeAll { existing in
|
||||||
existing.fullUrl == item.fullUrl &&
|
existing.fullUrl == item.fullUrl &&
|
||||||
existing.episodeNumber == item.episodeNumber &&
|
existing.episodeNumber == item.episodeNumber &&
|
||||||
existing.module.metadata.sourceName == item.module.metadata.sourceName
|
existing.module.metadata.sourceName == item.module.metadata.sourceName
|
||||||
}
|
}
|
||||||
|
items.append(updatedItem)
|
||||||
items.append(item)
|
|
||||||
|
|
||||||
if let data = try? JSONEncoder().encode(items) {
|
if let data = try? JSONEncoder().encode(items) {
|
||||||
UserDefaults.standard.set(data, forKey: storageKey)
|
UserDefaults.standard.set(data, forKey: storageKey)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func fetchItems() -> [ContinueWatchingItem] {
|
func fetchItems() -> [ContinueWatchingItem] {
|
||||||
guard
|
guard
|
||||||
|
|
|
||||||
|
|
@ -123,17 +123,18 @@ struct DownloadView: View {
|
||||||
Image(systemName: "arrow.down.circle")
|
Image(systemName: "arrow.down.circle")
|
||||||
.font(.system(size: 60))
|
.font(.system(size: 60))
|
||||||
.foregroundColor(.gray)
|
.foregroundColor(.gray)
|
||||||
.padding()
|
.padding(4)
|
||||||
|
|
||||||
Text("No Active Downloads")
|
Text("No Active Downloads")
|
||||||
.font(.title2)
|
.font(.title2)
|
||||||
.foregroundColor(.gray)
|
.foregroundColor(.gray)
|
||||||
|
.padding(3)
|
||||||
|
|
||||||
Text("Download episodes from the episode list")
|
Text("Download episodes from the episode list")
|
||||||
.font(.subheadline)
|
.font(.subheadline)
|
||||||
.foregroundColor(.gray)
|
.foregroundColor(.gray)
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
.padding()
|
|
||||||
}
|
}
|
||||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
}
|
}
|
||||||
|
|
@ -143,17 +144,17 @@ struct DownloadView: View {
|
||||||
Image(systemName: "arrow.down.circle")
|
Image(systemName: "arrow.down.circle")
|
||||||
.font(.system(size: 60))
|
.font(.system(size: 60))
|
||||||
.foregroundColor(.gray)
|
.foregroundColor(.gray)
|
||||||
.padding()
|
.padding(4)
|
||||||
|
|
||||||
Text("No Downloads")
|
Text("No Downloads")
|
||||||
.font(.title2)
|
.font(.title2)
|
||||||
.foregroundColor(.gray)
|
.foregroundColor(.gray)
|
||||||
|
.padding(3)
|
||||||
|
|
||||||
Text("Your downloaded assets will appear here")
|
Text("Your downloaded episodes will appear here")
|
||||||
.font(.subheadline)
|
.font(.subheadline)
|
||||||
.foregroundColor(.gray)
|
.foregroundColor(.gray)
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
.padding()
|
|
||||||
}
|
}
|
||||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -369,14 +369,27 @@ struct ContinueWatchingCell: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updateProgress() {
|
private func updateProgress() {
|
||||||
let lastPlayedTime = UserDefaults.standard.double(forKey: "lastPlayedTime_\(item.fullUrl)")
|
// grab the true playback times
|
||||||
let totalTime = UserDefaults.standard.double(forKey: "totalTime_\(item.fullUrl)")
|
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 {
|
if totalTime > 0 {
|
||||||
let ratio = lastPlayedTime / totalTime
|
ratio = min(max(lastPlayed / totalTime, 0), 1)
|
||||||
currentProgress = max(0, min(ratio, 1))
|
|
||||||
} else {
|
} 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,11 @@ struct SettingsViewData: View {
|
||||||
@State private var isImageCachingEnabled: Bool = true
|
@State private var isImageCachingEnabled: Bool = true
|
||||||
@State private var isMemoryOnlyMode: Bool = false
|
@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 {
|
var body: some View {
|
||||||
Form {
|
Form {
|
||||||
// New section for cache settings
|
// New section for cache settings
|
||||||
|
|
@ -86,7 +91,8 @@ struct SettingsViewData: View {
|
||||||
|
|
||||||
HStack {
|
HStack {
|
||||||
Button(action: {
|
Button(action: {
|
||||||
showRemoveDocumentsAlert = true
|
activeAlert = .removeDocs
|
||||||
|
showAlert = true
|
||||||
}) {
|
}) {
|
||||||
Text("Remove All Files in Documents")
|
Text("Remove All Files in Documents")
|
||||||
}
|
}
|
||||||
|
|
@ -109,7 +115,8 @@ struct SettingsViewData: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
Button(action: {
|
Button(action: {
|
||||||
showEraseAppDataAlert = true
|
activeAlert = .eraseData
|
||||||
|
showAlert = true
|
||||||
}) {
|
}) {
|
||||||
Text("Erase all App Data")
|
Text("Erase all App Data")
|
||||||
}
|
}
|
||||||
|
|
@ -125,37 +132,32 @@ struct SettingsViewData: View {
|
||||||
calculateCacheSize()
|
calculateCacheSize()
|
||||||
updateSizes()
|
updateSizes()
|
||||||
}
|
}
|
||||||
.alert(isPresented: $showEraseAppDataAlert) {
|
.alert(isPresented: $showAlert) {
|
||||||
Alert(
|
switch activeAlert {
|
||||||
title: Text("Erase App Data"),
|
case .eraseData:
|
||||||
message: Text("Are you sure you want to erase all app data? This action cannot be undone."),
|
return Alert(
|
||||||
primaryButton: .destructive(Text("Erase")) {
|
title: Text("Erase App Data"),
|
||||||
eraseAppData()
|
message: Text("Are you sure you want to erase all app data? This action cannot be undone."),
|
||||||
},
|
primaryButton: .destructive(Text("Erase")) { eraseAppData() },
|
||||||
secondaryButton: .cancel()
|
secondaryButton: .cancel()
|
||||||
)
|
)
|
||||||
}
|
case .removeDocs:
|
||||||
.alert(isPresented: $showRemoveDocumentsAlert) {
|
return Alert(
|
||||||
Alert(
|
title: Text("Remove Documents"),
|
||||||
title: Text("Remove Documents"),
|
message: Text("Are you sure you want to remove all files in the Documents folder? This will remove all modules."),
|
||||||
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() },
|
||||||
primaryButton: .destructive(Text("Remove")) {
|
secondaryButton: .cancel()
|
||||||
removeAllFilesInDocuments()
|
|
||||||
},
|
|
||||||
secondaryButton: .cancel()
|
|
||||||
)
|
)
|
||||||
}
|
case .removeMovPkg:
|
||||||
.alert(isPresented: $showRemoveMovPkgAlert) {
|
return Alert(
|
||||||
Alert(
|
title: Text("Remove Downloads"),
|
||||||
title: Text("Remove Downloads"),
|
message: Text("Are you sure you want to remove all Downloads?"),
|
||||||
message: Text("Are you sure you want to remove all Downloads?"),
|
primaryButton: .destructive(Text("Remove")) { removeMovPkgFiles() },
|
||||||
primaryButton: .destructive(Text("Remove")) {
|
secondaryButton: .cancel()
|
||||||
removeMovPkgFiles()
|
|
||||||
},
|
|
||||||
secondaryButton: .cancel()
|
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate and update the combined cache size
|
// Calculate and update the combined cache size
|
||||||
func calculateCacheSize() {
|
func calculateCacheSize() {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue