mirror of
https://github.com/cranci1/Sora.git
synced 2026-04-05 09:09:46 +00:00
hhh
This commit is contained in:
parent
334deec484
commit
970dbf2654
5 changed files with 14 additions and 142 deletions
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Reference in a new issue