From 517ccb8983c874319e7cb952a2b4730612130ca1 Mon Sep 17 00:00:00 2001 From: Seiike <122684677+Seeike@users.noreply.github.com> Date: Sun, 16 Mar 2025 17:55:38 +0100 Subject: [PATCH 1/3] bookmark logic fixed "Mark all previous" counting in the logger and removing a series from bookmarks when you open them from the home tab it no longer kicks you out --- Sora/Views/LibraryView/LibraryManager.swift | 8 +++++ Sora/Views/LibraryView/LibraryView.swift | 34 ++++++++++++++++++-- Sora/Views/MediaInfoView/MediaInfoView.swift | 2 +- 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/Sora/Views/LibraryView/LibraryManager.swift b/Sora/Views/LibraryView/LibraryManager.swift index 19712fb..b65d6ae 100644 --- a/Sora/Views/LibraryView/LibraryManager.swift +++ b/Sora/Views/LibraryView/LibraryManager.swift @@ -35,6 +35,14 @@ class LibraryManager: ObservableObject { loadBookmarks() } + func removeBookmark(item: LibraryItem) { + if let index = bookmarks.firstIndex(where: { $0.id == item.id }) { + bookmarks.remove(at: index) + Logger.shared.log("Removed series \(item.id) from bookmarks.",type: "Debug") + saveBookmarks() + } + } + private func loadBookmarks() { guard let data = UserDefaults.standard.data(forKey: bookmarksKey) else { Logger.shared.log("No bookmarks data found in UserDefaults.", type: "Error") diff --git a/Sora/Views/LibraryView/LibraryView.swift b/Sora/Views/LibraryView/LibraryView.swift index 1e5612f..a73b676 100644 --- a/Sora/Views/LibraryView/LibraryView.swift +++ b/Sora/Views/LibraryView/LibraryView.swift @@ -20,6 +20,10 @@ struct LibraryView: View { @State private var continueWatchingItems: [ContinueWatchingItem] = [] @State private var isLandscape: Bool = UIDevice.current.orientation.isLandscape + // New state variables to handle bookmark navigation explicitly + @State private var selectedBookmark: LibraryItem? = nil + @State private var isDetailActive: Bool = false + private let columns = [ GridItem(.adaptive(minimum: 150), spacing: 12) ] @@ -82,7 +86,10 @@ struct LibraryView: View { ForEach(libraryManager.bookmarks) { item in if let module = moduleManager.modules.first(where: { $0.id.uuidString == item.moduleId }) { - NavigationLink(destination: MediaInfoView(title: item.title, imageUrl: item.imageUrl, href: item.href, module: module)) { + Button(action: { + selectedBookmark = item + isDetailActive = true + }) { VStack(alignment: .leading) { ZStack { KFImage(URL(string: item.imageUrl)) @@ -114,6 +121,13 @@ struct LibraryView: View { .multilineTextAlignment(.leading) } } + .contextMenu { + Button(role: .destructive, action: { + libraryManager.removeBookmark(item: item) + }) { + Label("Remove from Bookmarks", systemImage: "trash") + } + } } } } @@ -127,6 +141,22 @@ struct LibraryView: View { } } .padding(.vertical, 20) + NavigationLink( + destination: Group { + if let bookmark = selectedBookmark, + let module = moduleManager.modules.first(where: { $0.id.uuidString == bookmark.moduleId }) { + MediaInfoView(title: bookmark.title, + imageUrl: bookmark.imageUrl, + href: bookmark.href, + module: module) + } else { + Text("No Data Available") + } + }, + isActive: $isDetailActive + ) { + EmptyView() + } } .navigationTitle("Library") .onAppear { @@ -179,7 +209,7 @@ struct ContinueWatchingSection: View { ScrollView(.horizontal, showsIndicators: false) { HStack(spacing: 8) { ForEach(Array(items.reversed())) { item in - ContinueWatchingCell(item: item,markAsWatched: { + ContinueWatchingCell(item: item, markAsWatched: { markAsWatched(item) }, removeItem: { removeItem(item) diff --git a/Sora/Views/MediaInfoView/MediaInfoView.swift b/Sora/Views/MediaInfoView/MediaInfoView.swift index 2ad0986..a6d33d1 100644 --- a/Sora/Views/MediaInfoView/MediaInfoView.swift +++ b/Sora/Views/MediaInfoView/MediaInfoView.swift @@ -237,7 +237,7 @@ struct MediaInfoView: View { UserDefaults.standard.set(99999999.0, forKey: "totalTime_\(href)") } refreshTrigger.toggle() - Logger.shared.log("Marked \(ep.number) episodes watched within anime \"\(title)\".", type: "General") + Logger.shared.log("Marked \(ep.number - 1) episodes watched within anime \"\(title)\".", type: "General") } ) .id(refreshTrigger) From de08c149184c1a63fe0d1479df0ac3a6132cf5ad Mon Sep 17 00:00:00 2001 From: Seiike <122684677+Seeike@users.noreply.github.com> Date: Sun, 16 Mar 2025 22:20:09 +0100 Subject: [PATCH 2/3] skip increments logic --- .../CustomPlayer/CustomPlayer.swift | 70 +++++++++++++++---- .../SettingsSubViews/SettingsViewPlayer.swift | 20 +++++- 2 files changed, 75 insertions(+), 15 deletions(-) diff --git a/Sora/Utils/MediaPlayer/CustomPlayer/CustomPlayer.swift b/Sora/Utils/MediaPlayer/CustomPlayer/CustomPlayer.swift index 9db9b58..54e6401 100644 --- a/Sora/Utils/MediaPlayer/CustomPlayer/CustomPlayer.swift +++ b/Sora/Utils/MediaPlayer/CustomPlayer/CustomPlayer.swift @@ -207,12 +207,24 @@ class CustomMediaPlayerViewController: UIViewController { blackCoverView.trailingAnchor.constraint(equalTo: view.trailingAnchor) ]) - backwardButton = UIImageView(image: UIImage(systemName: "gobackward.10")) + backwardButton = UIImageView(image: UIImage(systemName: "gobackward")) backwardButton.tintColor = .white backwardButton.contentMode = .scaleAspectFit backwardButton.isUserInteractionEnabled = true + + // 1) Tap gesture → normal skip let backwardTap = UITapGestureRecognizer(target: self, action: #selector(seekBackward)) + backwardTap.numberOfTapsRequired = 1 backwardButton.addGestureRecognizer(backwardTap) + + // 2) Long-press gesture → hold skip + let backwardLongPress = UILongPressGestureRecognizer(target: self, action: #selector(seekBackwardLongPress(_:))) + backwardLongPress.minimumPressDuration = 0.5 // Adjust as needed + backwardButton.addGestureRecognizer(backwardLongPress) + + // Make sure the tap doesn’t fire if the long-press is recognized + backwardTap.require(toFail: backwardLongPress) + controlsContainerView.addSubview(backwardButton) backwardButton.translatesAutoresizingMaskIntoConstraints = false @@ -225,14 +237,23 @@ class CustomMediaPlayerViewController: UIViewController { controlsContainerView.addSubview(playPauseButton) playPauseButton.translatesAutoresizingMaskIntoConstraints = false - forwardButton = UIImageView(image: UIImage(systemName: "goforward.10")) - forwardButton.tintColor = .white - forwardButton.contentMode = .scaleAspectFit - forwardButton.isUserInteractionEnabled = true - let forwardTap = UITapGestureRecognizer(target: self, action: #selector(seekForward)) - forwardButton.addGestureRecognizer(forwardTap) - controlsContainerView.addSubview(forwardButton) - forwardButton.translatesAutoresizingMaskIntoConstraints = false + forwardButton = UIImageView(image: UIImage(systemName: "goforward")) + forwardButton.tintColor = .white + forwardButton.contentMode = .scaleAspectFit + forwardButton.isUserInteractionEnabled = true + + let forwardTap = UITapGestureRecognizer(target: self, action: #selector(seekForward)) + forwardTap.numberOfTapsRequired = 1 + forwardButton.addGestureRecognizer(forwardTap) + + let forwardLongPress = UILongPressGestureRecognizer(target: self, action: #selector(seekForwardLongPress(_:))) + forwardLongPress.minimumPressDuration = 0.5 + forwardButton.addGestureRecognizer(forwardLongPress) + + forwardTap.require(toFail: forwardLongPress) + + controlsContainerView.addSubview(forwardButton) + forwardButton.translatesAutoresizingMaskIntoConstraints = false let sliderView = MusicProgressSlider( value: Binding(get: { self.sliderViewModel.sliderValue }, @@ -538,13 +559,36 @@ class CustomMediaPlayerViewController: UIViewController { } } - @objc func seekBackward() { - currentTimeVal = max(currentTimeVal - 10, 0) - player.seek(to: CMTime(seconds: currentTimeVal, preferredTimescale: 600)) + @objc func seekBackwardLongPress(_ gesture: UILongPressGestureRecognizer) { + // Only do the skip when the gesture first begins + if gesture.state == .began { + let holdValue = UserDefaults.standard.double(forKey: "skipIncrementHold") + let finalSkip = holdValue > 0 ? holdValue : 30 // fallback to 30 if not set + currentTimeVal = max(currentTimeVal - finalSkip, 0) + player.seek(to: CMTime(seconds: currentTimeVal, preferredTimescale: 600)) + } + } + + @objc func seekForwardLongPress(_ gesture: UILongPressGestureRecognizer) { + if gesture.state == .began { + let holdValue = UserDefaults.standard.double(forKey: "skipIncrementHold") + let finalSkip = holdValue > 0 ? holdValue : 30 + currentTimeVal = min(currentTimeVal + finalSkip, duration) + player.seek(to: CMTime(seconds: currentTimeVal, preferredTimescale: 600)) + } } + @objc func seekBackward() { + let skipValue = UserDefaults.standard.double(forKey: "skipIncrement") + let finalSkip = skipValue > 0 ? skipValue : 10 + currentTimeVal = max(currentTimeVal - finalSkip, 0) + player.seek(to: CMTime(seconds: currentTimeVal, preferredTimescale: 600)) + } + @objc func seekForward() { - currentTimeVal = min(currentTimeVal + 10, duration) + let skipValue = UserDefaults.standard.double(forKey: "skipIncrement") + let finalSkip = skipValue > 0 ? skipValue : 10 + currentTimeVal = min(currentTimeVal + finalSkip, duration) player.seek(to: CMTime(seconds: currentTimeVal, preferredTimescale: 600)) } diff --git a/Sora/Views/SettingsView/SettingsSubViews/SettingsViewPlayer.swift b/Sora/Views/SettingsView/SettingsSubViews/SettingsViewPlayer.swift index 7eadefe..c37ecd2 100644 --- a/Sora/Views/SettingsView/SettingsSubViews/SettingsViewPlayer.swift +++ b/Sora/Views/SettingsView/SettingsSubViews/SettingsViewPlayer.swift @@ -13,12 +13,14 @@ struct SettingsViewPlayer: View { @AppStorage("hideNextButton") private var isHideNextButton = false @AppStorage("rememberPlaySpeed") private var isRememberPlaySpeed = false @AppStorage("holdSpeedPlayer") private var holdSpeedPlayer: Double = 2.0 + @AppStorage("skipIncrement") private var skipIncrement: Double = 10.0 + @AppStorage("skipIncrementHold") private var skipIncrementHold: Double = 30.0 private let mediaPlayers = ["Default", "VLC", "OutPlayer", "Infuse", "nPlayer", "Sora"] var body: some View { Form { - Section(header: Text("Media Player"), footer: Text("Some features are limited to the Sora and Default player, such as ForceLandscape and holdSpeed")) { + Section(header: Text("Media Player"), footer: Text("Some features are limited to the Sora and Default player, such as ForceLandscape, holdSpeed and custom time skip increments.")) { HStack { Text("Media Player") Spacer() @@ -56,7 +58,21 @@ struct SettingsViewPlayer: View { } } } - + Section(header: Text("Skip Settings")) { + // Normal skip + HStack { + Text("Tap Skip:") + Spacer() + Stepper("\(Int(skipIncrement))s", value: $skipIncrement, in: 5...300, step: 5) + } + + // Long-press skip + HStack { + Text("Long press Skip:") + Spacer() + Stepper("\(Int(skipIncrementHold))s", value: $skipIncrementHold, in: 5...300, step: 5) + } + } SubtitleSettingsSection() } .navigationTitle("Player") From 91087854bb82eabd6f2375d6f1f16507686931b9 Mon Sep 17 00:00:00 2001 From: Seiike <122684677+Seeike@users.noreply.github.com> Date: Mon, 17 Mar 2025 20:55:34 +0100 Subject: [PATCH 3/3] here you go mario --- Sora/Views/LibraryView/LibraryView.swift | 25 +----------------------- Sulfur.xcodeproj/project.pbxproj | 8 ++++---- 2 files changed, 5 insertions(+), 28 deletions(-) diff --git a/Sora/Views/LibraryView/LibraryView.swift b/Sora/Views/LibraryView/LibraryView.swift index a73b676..87087c0 100644 --- a/Sora/Views/LibraryView/LibraryView.swift +++ b/Sora/Views/LibraryView/LibraryView.swift @@ -20,10 +20,6 @@ struct LibraryView: View { @State private var continueWatchingItems: [ContinueWatchingItem] = [] @State private var isLandscape: Bool = UIDevice.current.orientation.isLandscape - // New state variables to handle bookmark navigation explicitly - @State private var selectedBookmark: LibraryItem? = nil - @State private var isDetailActive: Bool = false - private let columns = [ GridItem(.adaptive(minimum: 150), spacing: 12) ] @@ -86,10 +82,7 @@ struct LibraryView: View { ForEach(libraryManager.bookmarks) { item in if let module = moduleManager.modules.first(where: { $0.id.uuidString == item.moduleId }) { - Button(action: { - selectedBookmark = item - isDetailActive = true - }) { + NavigationLink(destination: MediaInfoView(title: item.title, imageUrl: item.imageUrl, href: item.href, module: module)) { VStack(alignment: .leading) { ZStack { KFImage(URL(string: item.imageUrl)) @@ -141,22 +134,6 @@ struct LibraryView: View { } } .padding(.vertical, 20) - NavigationLink( - destination: Group { - if let bookmark = selectedBookmark, - let module = moduleManager.modules.first(where: { $0.id.uuidString == bookmark.moduleId }) { - MediaInfoView(title: bookmark.title, - imageUrl: bookmark.imageUrl, - href: bookmark.href, - module: module) - } else { - Text("No Data Available") - } - }, - isActive: $isDetailActive - ) { - EmptyView() - } } .navigationTitle("Library") .onAppear { diff --git a/Sulfur.xcodeproj/project.pbxproj b/Sulfur.xcodeproj/project.pbxproj index d5494e1..7ec2d98 100644 --- a/Sulfur.xcodeproj/project.pbxproj +++ b/Sulfur.xcodeproj/project.pbxproj @@ -58,9 +58,9 @@ 13DC0C462D302C7500D0F966 /* VideoPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13DC0C452D302C7500D0F966 /* VideoPlayer.swift */; }; 13EA2BD52D32D97400C1EBD7 /* CustomPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13EA2BD12D32D97400C1EBD7 /* CustomPlayer.swift */; }; 13EA2BD62D32D97400C1EBD7 /* Double+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13EA2BD32D32D97400C1EBD7 /* Double+Extension.swift */; }; - 13EA2BD72D32D97400C1EBD7 /* MusicProgressSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13EA2BD42D32D97400C1EBD7 /* MusicProgressSlider.swift */; }; 13EA2BD92D32D98400C1EBD7 /* NormalPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13EA2BD82D32D98400C1EBD7 /* NormalPlayer.swift */; }; 1E9FF1D32D403E49008AC100 /* SettingsViewLoggerFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E9FF1D22D403E42008AC100 /* SettingsViewLoggerFilter.swift */; }; + 1EAC7A322D888BC50083984D /* MusicProgressSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EAC7A312D888BC50083984D /* MusicProgressSlider.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -115,9 +115,9 @@ 13DC0C452D302C7500D0F966 /* VideoPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPlayer.swift; sourceTree = ""; }; 13EA2BD12D32D97400C1EBD7 /* CustomPlayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomPlayer.swift; sourceTree = ""; }; 13EA2BD32D32D97400C1EBD7 /* Double+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Double+Extension.swift"; sourceTree = ""; }; - 13EA2BD42D32D97400C1EBD7 /* MusicProgressSlider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MusicProgressSlider.swift; sourceTree = ""; }; 13EA2BD82D32D98400C1EBD7 /* NormalPlayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NormalPlayer.swift; sourceTree = ""; }; 1E9FF1D22D403E42008AC100 /* SettingsViewLoggerFilter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewLoggerFilter.swift; sourceTree = ""; }; + 1EAC7A312D888BC50083984D /* MusicProgressSlider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MusicProgressSlider.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -437,7 +437,7 @@ isa = PBXGroup; children = ( 13EA2BD32D32D97400C1EBD7 /* Double+Extension.swift */, - 13EA2BD42D32D97400C1EBD7 /* MusicProgressSlider.swift */, + 1EAC7A312D888BC50083984D /* MusicProgressSlider.swift */, ); path = Components; sourceTree = ""; @@ -532,7 +532,6 @@ 1334FF4F2D786C9E007E289F /* TMDB-Trending.swift in Sources */, 13CBEFDA2D5F7D1200D011EE /* String.swift in Sources */, 130C6BFA2D53AB1F00DC1432 /* SettingsViewData.swift in Sources */, - 13EA2BD72D32D97400C1EBD7 /* MusicProgressSlider.swift in Sources */, 1334FF542D787217007E289F /* TMDBRequest.swift in Sources */, 1E9FF1D32D403E49008AC100 /* SettingsViewLoggerFilter.swift in Sources */, 13EA2BD92D32D98400C1EBD7 /* NormalPlayer.swift in Sources */, @@ -556,6 +555,7 @@ 1327FBA92D758DEA00FC6689 /* UIDevice+Model.swift in Sources */, 138AA1B82D2D66FD0021F9DF /* EpisodeCell.swift in Sources */, 133D7C8C2D2BE2640075467E /* SearchView.swift in Sources */, + 1EAC7A322D888BC50083984D /* MusicProgressSlider.swift in Sources */, 133D7C942D2BE2640075467E /* JSController.swift in Sources */, 133D7C922D2BE2640075467E /* URLSession.swift in Sources */, 133D7C912D2BE2640075467E /* SettingsViewModule.swift in Sources */,