mirror of
https://github.com/cranci1/Sora.git
synced 2026-03-11 17:45:37 +00:00
im cooked
This commit is contained in:
parent
4ae5517cb8
commit
06cf7b9733
4 changed files with 179 additions and 0 deletions
|
|
@ -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 */,
|
||||
|
|
|
|||
98
Sora/MediaPlayer/VideoPlayer.swift
Normal file
98
Sora/MediaPlayer/VideoPlayer.swift
Normal 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)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
Loading…
Reference in a new issue