im cooked

This commit is contained in:
cranci1 2025-01-09 17:14:02 +01:00
parent 4ae5517cb8
commit 06cf7b9733
4 changed files with 179 additions and 0 deletions

View file

@ -23,6 +23,7 @@
133D7C972D2BE2AF0075467E /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 133D7C962D2BE2AF0075467E /* Kingfisher */; };
138AA1B82D2D66FD0021F9DF /* EpisodeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 138AA1B62D2D66FD0021F9DF /* EpisodeCell.swift */; };
138AA1B92D2D66FD0021F9DF /* CircularProgressBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 138AA1B72D2D66FD0021F9DF /* CircularProgressBar.swift */; };
13DC0C462D302C7500D0F966 /* VideoPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13DC0C452D302C7500D0F966 /* VideoPlayer.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
@ -43,6 +44,7 @@
138AA1B62D2D66FD0021F9DF /* EpisodeCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EpisodeCell.swift; sourceTree = "<group>"; };
138AA1B72D2D66FD0021F9DF /* CircularProgressBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CircularProgressBar.swift; sourceTree = "<group>"; };
13DC0C412D2EC9BA00D0F966 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
13DC0C452D302C7500D0F966 /* VideoPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPlayer.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -76,6 +78,7 @@
133D7C6C2D2BE2500075467E /* Sora */ = {
isa = PBXGroup;
children = (
13DC0C442D302C6A00D0F966 /* MediaPlayer */,
13DC0C412D2EC9BA00D0F966 /* Info.plist */,
133D7C852D2BE2640075467E /* Utils */,
133D7C7B2D2BE2630075467E /* Views */,
@ -168,6 +171,14 @@
path = EpisodeCell;
sourceTree = "<group>";
};
13DC0C442D302C6A00D0F966 /* MediaPlayer */ = {
isa = PBXGroup;
children = (
13DC0C452D302C7500D0F966 /* VideoPlayer.swift */,
);
path = MediaPlayer;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@ -244,6 +255,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
13DC0C462D302C7500D0F966 /* VideoPlayer.swift in Sources */,
133D7C902D2BE2640075467E /* SettingsView.swift in Sources */,
133D7C932D2BE2640075467E /* Modules.swift in Sources */,
133D7C702D2BE2500075467E /* ContentView.swift in Sources */,

View file

@ -0,0 +1,98 @@
//
// VideoPlayer.swift
// Sora
//
// Created by Francesco on 09/01/25.
//
import UIKit
import AVKit
class VideoPlayerViewController: UIViewController {
let module: ScrapingModule
var player: AVPlayer?
var playerViewController: AVPlayerViewController?
var timeObserverToken: Any?
var streamUrl: String?
var fullUrl: String = ""
init(module: ScrapingModule) {
self.module = module
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
guard let streamUrl = streamUrl, let url = URL(string: streamUrl) else {
return
}
var request = URLRequest(url: url)
if streamUrl.contains("ascdn") {
request.addValue("\(module.metadata.baseUrl)", forHTTPHeaderField: "Referer")
}
let asset = AVURLAsset(url: url, options: ["AVURLAssetHTTPHeaderFieldsKey": request.allHTTPHeaderFields ?? [:]])
let playerItem = AVPlayerItem(asset: asset)
player = AVPlayer(playerItem: playerItem)
playerViewController = AVPlayerViewController()
playerViewController?.player = player
addPeriodicTimeObserver(fullURL: fullUrl)
if let playerViewController = playerViewController {
playerViewController.view.frame = self.view.frame
self.view.addSubview(playerViewController.view)
self.addChild(playerViewController)
playerViewController.didMove(toParent: self)
}
let lastPlayedTime = UserDefaults.standard.double(forKey: "lastPlayedTime_\(fullUrl)")
if lastPlayedTime > 0 {
let seekTime = CMTime(seconds: lastPlayedTime, preferredTimescale: 1)
self.player?.seek(to: seekTime) { _ in
self.player?.play()
}
} else {
self.player?.play()
}
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
player?.play()
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
player?.pause()
if let timeObserverToken = timeObserverToken {
player?.removeTimeObserver(timeObserverToken)
self.timeObserverToken = nil
}
}
func addPeriodicTimeObserver(fullURL: String) {
guard let player = self.player else { return }
let interval = CMTime(seconds: 1.0, preferredTimescale: CMTimeScale(NSEC_PER_SEC))
timeObserverToken = player.addPeriodicTimeObserver(forInterval: interval, queue: .main) { time in
guard let currentItem = player.currentItem,
currentItem.duration.seconds.isFinite else {
return
}
let currentTime = time.seconds
let duration = currentItem.duration.seconds
UserDefaults.standard.set(currentTime, forKey: "lastPlayedTime_\(fullURL)")
UserDefaults.standard.set(duration, forKey: "totalTime_\(fullURL)")
}
}
}

View file

@ -121,4 +121,37 @@ class JSController: ObservableObject {
}
}.resume()
}
func fetchStreamUrl(episodeUrl: String, completion: @escaping (String?) -> Void) {
guard let url = URL(string: episodeUrl) else {
completion(nil)
return
}
URLSession.custom.dataTask(with: url) { [weak self] data, _, error in
guard let self = self else { return }
if let error = error {
print("Network error: \(error)")
DispatchQueue.main.async { completion(nil) }
return
}
guard let data = data, let html = String(data: data, encoding: .utf8) else {
print("Failed to decode HTML")
DispatchQueue.main.async { completion(nil) }
return
}
if let parseFunction = self.context.objectForKeyedSubscript("extractStreamUrl"),
let streamUrl = parseFunction.call(withArguments: [html]).toString() {
DispatchQueue.main.async {
completion(streamUrl)
}
} else {
print("Failed to extract stream URL")
DispatchQueue.main.async { completion(nil) }
}
}.resume()
}
}

View file

@ -165,6 +165,9 @@ struct MediaInfoView: View {
let progress = totalTime > 0 ? lastPlayedTime / totalTime : 0
EpisodeCell(episode: ep.href, episodeID: ep.number - 1, progress: progress, itemID: itemID ?? 0)
.onTapGesture {
fetchStream(href: ep.href)
}
}
}
}
@ -212,6 +215,39 @@ struct MediaInfoView: View {
}
}
func fetchStream(href: String) {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
Task {
do {
let jsContent = try moduleManager.getModuleContent(module)
jsController.loadScript(jsContent)
jsController.fetchStreamUrl(episodeUrl: href) { streamUrl in
if let url = streamUrl {
playStream(url: url, fullURL: url)
}
}
} catch {
print("Error loading module: \(error)")
self.isLoading = false
}
}
}
}
func playStream(url: String, fullURL: String) {
DispatchQueue.main.async {
let videoPlayerViewController = VideoPlayerViewController(module: module)
videoPlayerViewController.streamUrl = url
videoPlayerViewController.fullUrl = fullURL
videoPlayerViewController.modalPresentationStyle = .fullScreen
if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
let rootVC = windowScene.windows.first?.rootViewController {
rootVC.present(videoPlayerViewController, animated: true, completion: nil)
}
}
}
private func openSafariViewController(with urlString: String) {
guard let url = URL(string: urlString) else {
print("Unable to open the webpage")