diff --git a/Sora/MediaUtils/CustomPlayer/CustomPlayer.swift b/Sora/MediaUtils/CustomPlayer/CustomPlayer.swift index c83f79c..6a57dc0 100644 --- a/Sora/MediaUtils/CustomPlayer/CustomPlayer.swift +++ b/Sora/MediaUtils/CustomPlayer/CustomPlayer.swift @@ -368,8 +368,6 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele setupPipIfSupported() setupTimeBatteryIndicator() setupTopRowLayout() - self.loadLocalSkipTimestampsIfAvailable() - updateSkipButtonsVisibility() if !isSkip85Visible { @@ -401,7 +399,6 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele self?.malID = mal self?.fetchSkipTimes(type: "op") self?.fetchSkipTimes(type: "ed") - self?.loadLocalSkipTimestampsIfAvailable() case .failure(let error): Logger.shared.log("Unable to fetch MAL ID: \(error)",type:"Error") } @@ -3866,57 +3863,4 @@ class GradientBlurButton: UIButton { cleanupVisualEffects() super.removeFromSuperview() } - -private func loadLocalSkipTimestampsIfAvailable() { - // Try from subtitle file path first - var candidateURLs: [URL] = [] - if let sub = subtitlesURL, !sub.isEmpty, let u = URL(string: sub) { - candidateURLs.append(u) - } - if let u = URL(string: streamURL) { - candidateURLs.append(u) - } - for u in candidateURLs { - // Ensure file URL - let fileURL: URL - if u.isFileURL { - fileURL = u - } else if let url = URL(string: u.absoluteString), url.isFileURL { - fileURL = url - } else { - continue - } - let base = fileURL.deletingPathExtension() - let candidates = [ - base.appendingPathExtension("skip.json"), - base.deletingLastPathComponent().appendingPathComponent(base.lastPathComponent + ".skip.json") - ] - for jsonURL in candidates { - if FileManager.default.fileExists(atPath: jsonURL.path), - let data = try? Data(contentsOf: jsonURL), - let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] { - if let op = json["op"] as? [String: Any], - let s = op["start"] as? Double, let e = op["end"] as? Double { - self.skipIntervals.op = CMTimeRange( - start: CMTime(seconds: s, preferredTimescale: 600), - end: CMTime(seconds: e, preferredTimescale: 600) - ) - } - if let ed = json["ed"] as? [String: Any], - let s = ed["start"] as? Double, let e = ed["end"] as? Double { - self.skipIntervals.ed = CMTimeRange( - start: CMTime(seconds: s, preferredTimescale: 600), - end: CMTime(seconds: e, preferredTimescale: 600) - ) - } - if self.duration > 0 { - self.updateSegments() - } - self.updateSkipButtonsVisibility() - return - } - } - } -} - } diff --git a/Sora/Utlis & Misc/DownloadUtils/DownloadModels.swift b/Sora/Utlis & Misc/DownloadUtils/DownloadModels.swift index 74f3f9d..685a943 100644 --- a/Sora/Utlis & Misc/DownloadUtils/DownloadModels.swift +++ b/Sora/Utlis & Misc/DownloadUtils/DownloadModels.swift @@ -414,9 +414,7 @@ struct AssetMetadata: Codable { let episodeTitle: String? let seasonNumber: Int? - - let anilistId: Int? -init( + init( title: String, overview: String? = nil, posterURL: URL? = nil, @@ -427,8 +425,7 @@ init( episode: Int? = nil, showPosterURL: URL? = nil, episodeTitle: String? = nil, - seasonNumber: Int? = nil, - anilistId: Int? = nil + seasonNumber: Int? = nil ) { self.title = title self.overview = overview @@ -441,9 +438,7 @@ init( self.showPosterURL = showPosterURL self.episodeTitle = episodeTitle self.seasonNumber = seasonNumber - - self.anilistId = anilistId -} + } } // MARK: - New Group Model diff --git a/Sora/Utlis & Misc/JSLoader/Downloads/JSController+Downloader.swift b/Sora/Utlis & Misc/JSLoader/Downloads/JSController+Downloader.swift index 07c4fd4..810a917 100644 --- a/Sora/Utlis & Misc/JSLoader/Downloads/JSController+Downloader.swift +++ b/Sora/Utlis & Misc/JSLoader/Downloads/JSController+Downloader.swift @@ -14,9 +14,7 @@ struct DownloadRequest { let headers: [String: String] let title: String? let imageURL: URL? - - let aniListID: Int? -let isEpisode: Bool + let isEpisode: Bool let showTitle: String? let season: Int? let episode: Int? @@ -59,11 +57,10 @@ extension JSController { subtitleURL: URL? = nil, showPosterURL: URL? = nil, completionHandler: ((Bool, String) -> Void)? = nil) { - let pendingAni = UserDefaults.standard.object(forKey: "PendingAniListIDForDownload") as? Int let request = DownloadRequest( url: url, headers: headers, title: title, imageURL: imageURL, isEpisode: isEpisode, showTitle: showTitle, season: season, - episode: episode, subtitleURL: subtitleURL, showPosterURL: showPosterURL, aniListID: pendingAni + episode: episode, subtitleURL: subtitleURL, showPosterURL: showPosterURL ) logDownloadStart(request: request) @@ -95,7 +92,13 @@ extension JSController { self.logM3U8QualitySelected(quality: selectedQuality) if let qualityURL = URL(string: selectedQuality.url) { - let qualityRequest = DownloadRequest(url: qualityURL, headers: request.headers, title: request.title, imageURL: request.imageURL, isEpisode: request.isEpisode, showTitle: request.showTitle, season: request.season, episode: request.episode, subtitleURL: request.subtitleURL, showPosterURL: request.showPosterURL, aniListID: request.aniListID) + let qualityRequest = DownloadRequest( + url: qualityURL, headers: request.headers, title: request.title, + imageURL: request.imageURL, isEpisode: request.isEpisode, + showTitle: request.showTitle, season: request.season, + episode: request.episode, subtitleURL: request.subtitleURL, + showPosterURL: request.showPosterURL + ) self.downloadWithOriginalMethod(request: qualityRequest, completionHandler: completionHandler) } else { self.logM3U8InvalidURL() diff --git a/Sora/Utlis & Misc/JSLoader/Downloads/JSController-Downloads.swift b/Sora/Utlis & Misc/JSLoader/Downloads/JSController-Downloads.swift index 19a0b38..024d44a 100644 --- a/Sora/Utlis & Misc/JSLoader/Downloads/JSController-Downloads.swift +++ b/Sora/Utlis & Misc/JSLoader/Downloads/JSController-Downloads.swift @@ -114,13 +114,11 @@ extension JSController { subtitleURL: URL? = nil, showPosterURL: URL? = nil, module: ScrapingModule? = nil, - aniListID: Int? = nil, completionHandler: ((Bool, String) -> Void)? = nil ) { // If a module is provided, use the stream type aware download if let module = module { // Use the stream type aware download method - if let anilist = aniListID { UserDefaults.standard.set(anilist, forKey: "PendingAniListIDForDownload") } else { UserDefaults.standard.removeObject(forKey: "PendingAniListIDForDownload") } downloadWithStreamTypeSupport( url: url, headers: headers, @@ -1216,10 +1214,7 @@ extension JSController: AVAssetDownloadDelegate { } // If there's a subtitle URL, download it now that the video is saved - - // Save OP/ED skip timestamps JSON in parallel - saveSkipTimestampsJSON(for: persistentURL, anilistId: newAsset.metadata?.anilistId, episodeNumber: newAsset.metadata?.episode) -if let subtitleURL = download.subtitleURL { + if let subtitleURL = download.subtitleURL { downloadSubtitle(subtitleURL: subtitleURL, assetID: newAsset.id.uuidString) } else { // No subtitle URL, so we can consider the download complete @@ -1652,68 +1647,3 @@ enum DownloadQueueStatus: Equatable { /// Download has been completed case completed } - - -// MARK: - AniSkip Timestamps Saving -private func saveSkipTimestampsJSON(for videoURL: URL, anilistId: Int?, episodeNumber: Int?) { - // Determine destination JSON path next to the video file - let base = videoURL.deletingPathExtension() - let jsonURL = base.appendingPathExtension("skip.json") - - func writeJSON(op: (Double,Double)?, ed: (Double,Double)?, mal: Int?) { - var dict: [String: Any] = [ - "source": "aniskip", - "createdAt": ISO8601DateFormatter().string(from: Date()) - ] - if let mal = mal { dict["malId"] = mal } - if let aid = anilistId { dict["anilistId"] = aid } - if let ep = episodeNumber { dict["episode"] = ep } - if let op = op { - dict["op"] = ["start": op.0, "end": op.1] - } - if let ed = ed { - dict["ed"] = ["start": ed.0, "end": ed.1] - } - do { - let data = try JSONSerialization.data(withJSONObject: dict, options: [.prettyPrinted]) - try data.write(to: jsonURL, options: .atomic) - } catch { - print("Failed to write skip JSON: \(error.localizedDescription)") - } - } - - guard let anilistId = anilistId, anilistId > 0, let ep = episodeNumber else { - // No IDs; nothing to fetch - return - } - // Map AniList -> MAL then fetch AniSkip for both OP and ED - AniListMutation().fetchMalID(animeId: anilistId) { result in - switch result { - case .success(let mal): - let group = DispatchGroup() - var opInterval: (Double,Double)? = nil - var edInterval: (Double,Double)? = nil - - func fetch(type: String, assign: @escaping ((Double,Double))->Void) { - guard let url = URL(string: "https://api.aniskip.com/v2/skip-times/\(mal)/\(ep)?types=\(type)&episodeLength=0") else { return } - group.enter() - URLSession.shared.dataTask(with: url) { data, _, _ in - defer { group.leave() } - guard let data = data, - let resp = try? JSONDecoder().decode(AniSkipResponse.self, from: data), - resp.found, let interval = resp.results.first?.interval else { return } - assign((interval.startTime, interval.endTime)) - }.resume() - } - - fetch(type: "op") { opInterval = $0 } - fetch(type: "ed") { edInterval = $0 } - - group.notify(queue: .global(qos: .utility)) { - writeJSON(op: opInterval, ed: edInterval, mal: mal) - } - case .failure(let e): - print("Failed to map AniList to MAL for skip JSON: \(e.localizedDescription)") - } - } -} \ No newline at end of file diff --git a/Sora/Views/MediaInfoView/EpisodeCell/EpisodeCell.swift b/Sora/Views/MediaInfoView/EpisodeCell/EpisodeCell.swift index 5788c17..8e4fc9f 100644 --- a/Sora/Views/MediaInfoView/EpisodeCell/EpisodeCell.swift +++ b/Sora/Views/MediaInfoView/EpisodeCell/EpisodeCell.swift @@ -691,7 +691,7 @@ private extension EpisodeCell { let fullEpisodeTitle = episodeTitle.isEmpty ? baseTitle : "\(baseTitle): \(episodeTitle)" let animeTitle = parentTitle.isEmpty ? "Unknown Anime" : parentTitle - jsController.startDownload( + jsController.downloadWithStreamTypeSupport( url: url, headers: headers, title: fullEpisodeTitle,