mirror of
https://github.com/cranci1/Sora.git
synced 2026-01-11 20:10:24 +00:00
Circular Progress fixes + Continue watching fixes
This commit is contained in:
parent
4ce60d957d
commit
9b29b40ff3
2 changed files with 60 additions and 55 deletions
|
|
@ -11,49 +11,51 @@ class ContinueWatchingManager {
|
||||||
static let shared = ContinueWatchingManager()
|
static let shared = ContinueWatchingManager()
|
||||||
private let storageKey = "continueWatchingItems"
|
private let storageKey = "continueWatchingItems"
|
||||||
private let lastCleanupKey = "lastContinueWatchingCleanup"
|
private let lastCleanupKey = "lastContinueWatchingCleanup"
|
||||||
|
|
||||||
private init() {
|
private init() {
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(handleiCloudSync), name: .iCloudSyncDidComplete, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(handleiCloudSync), name: .iCloudSyncDidComplete, object: nil)
|
||||||
performCleanupIfNeeded()
|
performCleanupIfNeeded()
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func handleiCloudSync() {
|
@objc private func handleiCloudSync() {
|
||||||
NotificationCenter.default.post(name: .ContinueWatchingDidUpdate, object: nil)
|
NotificationCenter.default.post(name: .ContinueWatchingDidUpdate, object: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func performCleanupIfNeeded() {
|
private func performCleanupIfNeeded() {
|
||||||
let lastCleanup = UserDefaults.standard.double(forKey: lastCleanupKey)
|
let lastCleanup = UserDefaults.standard.double(forKey: lastCleanupKey)
|
||||||
let currentTime = Date().timeIntervalSince1970
|
let currentTime = Date().timeIntervalSince1970
|
||||||
|
|
||||||
if currentTime - lastCleanup > 86400 {
|
if currentTime - lastCleanup > 86400 {
|
||||||
cleanupOldEpisodes()
|
cleanupOldEpisodes()
|
||||||
UserDefaults.standard.set(currentTime, forKey: lastCleanupKey)
|
UserDefaults.standard.set(currentTime, forKey: lastCleanupKey)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func cleanupOldEpisodes() {
|
private func cleanupOldEpisodes() {
|
||||||
var items = fetchItems()
|
var items = fetchItems()
|
||||||
var itemsToRemove: Set<UUID> = []
|
var itemsToRemove: Set<UUID> = []
|
||||||
|
|
||||||
let groupedItems = Dictionary(grouping: items) { item in
|
let groupedItems = Dictionary(grouping: items) { item in
|
||||||
let title = item.mediaTitle.replacingOccurrences(of: "Episode \\d+.*$", with: "", options: .regularExpression)
|
let title = item.mediaTitle.replacingOccurrences(of: "Episode \\d+.*$", with: "", options: .regularExpression)
|
||||||
.trimmingCharacters(in: .whitespacesAndNewlines)
|
.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
return title
|
return title
|
||||||
}
|
}
|
||||||
|
|
||||||
for (_, showEpisodes) in groupedItems {
|
for (_, showEpisodes) in groupedItems {
|
||||||
let sortedEpisodes = showEpisodes.sorted { $0.episodeNumber < $1.episodeNumber }
|
let sortedEpisodes = showEpisodes.sorted { $0.episodeNumber < $1.episodeNumber }
|
||||||
|
|
||||||
for i in 0..<sortedEpisodes.count - 1 {
|
for i in 0..<sortedEpisodes.count - 1 {
|
||||||
let currentEpisode = sortedEpisodes[i]
|
let currentEpisode = sortedEpisodes[i]
|
||||||
let nextEpisode = sortedEpisodes[i + 1]
|
let nextEpisode = sortedEpisodes[i + 1]
|
||||||
|
|
||||||
if currentEpisode.progress >= 0.8 && nextEpisode.episodeNumber > currentEpisode.episodeNumber {
|
let remainingTimePercentage = UserDefaults.standard.object(forKey: "remainingTimePercentage") != nil ? UserDefaults.standard.double(forKey: "remainingTimePercentage") : 90.0
|
||||||
|
let threshold = (100.0 - remainingTimePercentage) / 100.0
|
||||||
|
if currentEpisode.progress >= threshold && nextEpisode.episodeNumber > currentEpisode.episodeNumber {
|
||||||
itemsToRemove.insert(currentEpisode.id)
|
itemsToRemove.insert(currentEpisode.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !itemsToRemove.isEmpty {
|
if !itemsToRemove.isEmpty {
|
||||||
items.removeAll { itemsToRemove.contains($0.id) }
|
items.removeAll { itemsToRemove.contains($0.id) }
|
||||||
if let data = try? JSONEncoder().encode(items) {
|
if let data = try? JSONEncoder().encode(items) {
|
||||||
|
|
@ -61,59 +63,59 @@ class ContinueWatchingManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func save(item: ContinueWatchingItem) {
|
func save(item: ContinueWatchingItem) {
|
||||||
// Use real playback times
|
|
||||||
let lastKey = "lastPlayedTime_\(item.fullUrl)"
|
let lastKey = "lastPlayedTime_\(item.fullUrl)"
|
||||||
let totalKey = "totalTime_\(item.fullUrl)"
|
let totalKey = "totalTime_\(item.fullUrl)"
|
||||||
let lastPlayed = UserDefaults.standard.double(forKey: lastKey)
|
let lastPlayed = UserDefaults.standard.double(forKey: lastKey)
|
||||||
let totalTime = UserDefaults.standard.double(forKey: totalKey)
|
let totalTime = UserDefaults.standard.double(forKey: totalKey)
|
||||||
|
|
||||||
// Compute up-to-date progress
|
|
||||||
let actualProgress: Double
|
let actualProgress: Double
|
||||||
if totalTime > 0 {
|
if totalTime > 0 {
|
||||||
actualProgress = min(max(lastPlayed / totalTime, 0), 1)
|
actualProgress = min(max(lastPlayed / totalTime, 0), 1)
|
||||||
} else {
|
} else {
|
||||||
actualProgress = item.progress
|
actualProgress = item.progress
|
||||||
}
|
|
||||||
|
let remainingTimePercentage = UserDefaults.standard.object(forKey: "remainingTimePercentage") != nil ? UserDefaults.standard.double(forKey: "remainingTimePercentage") : 90.0
|
||||||
// If watched ≥ 90%, remove it
|
let threshold = (100.0 - remainingTimePercentage) / 100.0
|
||||||
if actualProgress >= 0.9 {
|
if actualProgress >= threshold {
|
||||||
remove(item: item)
|
remove(item: item)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise update progress and remove old episodes from the same show
|
var updatedItem = item
|
||||||
var updatedItem = item
|
updatedItem.progress = actualProgress
|
||||||
updatedItem.progress = actualProgress
|
|
||||||
|
var items = fetchItems()
|
||||||
var items = fetchItems()
|
|
||||||
|
let showTitle = item.mediaTitle.replacingOccurrences(of: "Episode \\d+.*$", with: "", options: .regularExpression)
|
||||||
let showTitle = item.mediaTitle.replacingOccurrences(of: "Episode \\d+.*$", with: "", options: .regularExpression)
|
|
||||||
.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
||||||
|
|
||||||
items.removeAll { existingItem in
|
|
||||||
let existingShowTitle = existingItem.mediaTitle.replacingOccurrences(of: "Episode \\d+.*$", with: "", options: .regularExpression)
|
|
||||||
.trimmingCharacters(in: .whitespacesAndNewlines)
|
.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
|
|
||||||
return showTitle == existingShowTitle &&
|
items.removeAll { existingItem in
|
||||||
existingItem.episodeNumber < item.episodeNumber &&
|
let existingShowTitle = existingItem.mediaTitle.replacingOccurrences(of: "Episode \\d+.*$", with: "", options: .regularExpression)
|
||||||
existingItem.progress >= 0.8
|
.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
}
|
|
||||||
|
let remainingTimePercentage = UserDefaults.standard.object(forKey: "remainingTimePercentage") != nil ? UserDefaults.standard.double(forKey: "remainingTimePercentage") : 90.0
|
||||||
items.removeAll { existing in
|
let threshold = (100.0 - remainingTimePercentage) / 100.0
|
||||||
existing.fullUrl == item.fullUrl &&
|
return showTitle == existingShowTitle &&
|
||||||
existing.episodeNumber == item.episodeNumber &&
|
existingItem.episodeNumber < item.episodeNumber &&
|
||||||
existing.module.metadata.sourceName == item.module.metadata.sourceName
|
existingItem.progress >= threshold
|
||||||
}
|
}
|
||||||
|
|
||||||
items.append(updatedItem)
|
items.removeAll { existing in
|
||||||
|
existing.fullUrl == item.fullUrl &&
|
||||||
if let data = try? JSONEncoder().encode(items) {
|
existing.episodeNumber == item.episodeNumber &&
|
||||||
UserDefaults.standard.set(data, forKey: storageKey)
|
existing.module.metadata.sourceName == item.module.metadata.sourceName
|
||||||
|
}
|
||||||
|
|
||||||
|
items.append(updatedItem)
|
||||||
|
|
||||||
|
if let data = try? JSONEncoder().encode(items) {
|
||||||
|
UserDefaults.standard.set(data, forKey: storageKey)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchItems() -> [ContinueWatchingItem] {
|
func fetchItems() -> [ContinueWatchingItem] {
|
||||||
guard
|
guard
|
||||||
let data = UserDefaults.standard.data(forKey: storageKey),
|
let data = UserDefaults.standard.data(forKey: storageKey),
|
||||||
|
|
@ -121,7 +123,7 @@ class ContinueWatchingManager {
|
||||||
else {
|
else {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
var seen = Set<String>()
|
var seen = Set<String>()
|
||||||
let unique = raw.reversed().filter { item in
|
let unique = raw.reversed().filter { item in
|
||||||
let key = "\(item.fullUrl)|\(item.module.metadata.sourceName)|\(item.episodeNumber)"
|
let key = "\(item.fullUrl)|\(item.module.metadata.sourceName)|\(item.episodeNumber)"
|
||||||
|
|
@ -132,10 +134,10 @@ class ContinueWatchingManager {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}.reversed()
|
}.reversed()
|
||||||
|
|
||||||
return Array(unique)
|
return Array(unique)
|
||||||
}
|
}
|
||||||
|
|
||||||
func remove(item: ContinueWatchingItem) {
|
func remove(item: ContinueWatchingItem) {
|
||||||
var items = fetchItems()
|
var items = fetchItems()
|
||||||
items.removeAll { $0.id == item.id }
|
items.removeAll { $0.id == item.id }
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,10 @@ struct CircularProgressBar: View {
|
||||||
.rotationEffect(Angle(degrees: 270.0))
|
.rotationEffect(Angle(degrees: 270.0))
|
||||||
.animation(.linear, value: progress)
|
.animation(.linear, value: progress)
|
||||||
|
|
||||||
if progress >= 0.9 {
|
let remainingTimePercentage = UserDefaults.standard.object(forKey: "remainingTimePercentage") != nil ? UserDefaults.standard.double(forKey: "remainingTimePercentage") : 90.0
|
||||||
|
let threshold = (100.0 - remainingTimePercentage) / 100.0
|
||||||
|
|
||||||
|
if progress >= threshold {
|
||||||
Image(systemName: "checkmark")
|
Image(systemName: "checkmark")
|
||||||
.font(.system(size: 12))
|
.font(.system(size: 12))
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue