mirror of
https://github.com/cranci1/Sora.git
synced 2026-03-11 17:45:37 +00:00
opsi my fault
This commit is contained in:
parent
cc4c75f88a
commit
7c5cec2285
3 changed files with 55 additions and 116 deletions
|
|
@ -1,81 +0,0 @@
|
||||||
//
|
|
||||||
// SubtitleManager.swift
|
|
||||||
// Sora
|
|
||||||
//
|
|
||||||
// Created by Francesco on 10/06/25.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
import Foundation
|
|
||||||
import AVFoundation
|
|
||||||
|
|
||||||
class SubtitleManager {
|
|
||||||
static let shared = SubtitleManager()
|
|
||||||
private let subtitleLoader = VTTSubtitlesLoader()
|
|
||||||
|
|
||||||
private init() {}
|
|
||||||
|
|
||||||
func loadSubtitles(from url: URL) async throws -> [SubtitleCue] {
|
|
||||||
return await withCheckedContinuation { continuation in
|
|
||||||
subtitleLoader.load(from: url.absoluteString)
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
|
||||||
continuation.resume(returning: self.subtitleLoader.cues)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func createSubtitleOverlay(for cues: [SubtitleCue], player: AVPlayer) -> SubtitleOverlayView {
|
|
||||||
let overlay = SubtitleOverlayView()
|
|
||||||
let interval = CMTime(seconds: 0.1, preferredTimescale: CMTimeScale(NSEC_PER_SEC))
|
|
||||||
|
|
||||||
player.addPeriodicTimeObserver(forInterval: interval, queue: .main) { time in
|
|
||||||
let currentTime = time.seconds
|
|
||||||
let currentCue = cues.first { cue in
|
|
||||||
currentTime >= cue.startTime && currentTime <= cue.endTime
|
|
||||||
}
|
|
||||||
overlay.update(with: currentCue?.text ?? "")
|
|
||||||
}
|
|
||||||
|
|
||||||
return overlay
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class SubtitleOverlayView: UIView {
|
|
||||||
private let label: UILabel = {
|
|
||||||
let label = UILabel()
|
|
||||||
label.numberOfLines = 0
|
|
||||||
label.textAlignment = .center
|
|
||||||
label.textColor = .white
|
|
||||||
label.font = .systemFont(ofSize: 16, weight: .medium)
|
|
||||||
label.layer.shadowColor = UIColor.black.cgColor
|
|
||||||
label.layer.shadowOffset = CGSize(width: 1, height: 1)
|
|
||||||
label.layer.shadowOpacity = 0.8
|
|
||||||
label.layer.shadowRadius = 2
|
|
||||||
return label
|
|
||||||
}()
|
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
|
||||||
super.init(frame: frame)
|
|
||||||
setupView()
|
|
||||||
}
|
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
|
||||||
super.init(coder: coder)
|
|
||||||
setupView()
|
|
||||||
}
|
|
||||||
|
|
||||||
private func setupView() {
|
|
||||||
backgroundColor = .clear
|
|
||||||
addSubview(label)
|
|
||||||
label.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
NSLayoutConstraint.activate([
|
|
||||||
label.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20),
|
|
||||||
label.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -20),
|
|
||||||
label.centerYAnchor.constraint(equalTo: centerYAnchor)
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
func update(with text: String) {
|
|
||||||
label.text = text
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -24,7 +24,8 @@ class VideoPlayerViewController: UIViewController {
|
||||||
var episodeNumber: Int = 0
|
var episodeNumber: Int = 0
|
||||||
var episodeImageUrl: String = ""
|
var episodeImageUrl: String = ""
|
||||||
var mediaTitle: String = ""
|
var mediaTitle: String = ""
|
||||||
var subtitleOverlay: SubtitleOverlayView?
|
var subtitlesLoader: VTTSubtitlesLoader?
|
||||||
|
var subtitleLabel: UILabel?
|
||||||
|
|
||||||
init(module: ScrapingModule) {
|
init(module: ScrapingModule) {
|
||||||
self.module = module
|
self.module = module
|
||||||
|
|
@ -35,6 +36,54 @@ class VideoPlayerViewController: UIViewController {
|
||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func setupSubtitles() {
|
||||||
|
guard !subtitles.isEmpty && UserDefaults.standard.bool(forKey: "subtitlesEnabled"),
|
||||||
|
let subtitleURL = URL(string: subtitles) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
subtitlesLoader = VTTSubtitlesLoader()
|
||||||
|
setupSubtitleLabel()
|
||||||
|
|
||||||
|
subtitlesLoader?.load(from: subtitles)
|
||||||
|
|
||||||
|
let interval = CMTime(seconds: 0.1, preferredTimescale: CMTimeScale(NSEC_PER_SEC))
|
||||||
|
player?.addPeriodicTimeObserver(forInterval: interval, queue: .main) { [weak self] time in
|
||||||
|
self?.updateSubtitles(at: time.seconds)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setupSubtitleLabel() {
|
||||||
|
let label = UILabel()
|
||||||
|
label.numberOfLines = 0
|
||||||
|
label.textAlignment = .center
|
||||||
|
label.textColor = .white
|
||||||
|
label.font = .systemFont(ofSize: 16, weight: .medium)
|
||||||
|
label.layer.shadowColor = UIColor.black.cgColor
|
||||||
|
label.layer.shadowOffset = CGSize(width: 1, height: 1)
|
||||||
|
label.layer.shadowOpacity = 0.8
|
||||||
|
label.layer.shadowRadius = 2
|
||||||
|
|
||||||
|
guard let playerView = playerViewController?.view else { return }
|
||||||
|
playerView.addSubview(label)
|
||||||
|
label.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
|
||||||
|
NSLayoutConstraint.activate([
|
||||||
|
label.leadingAnchor.constraint(equalTo: playerView.leadingAnchor, constant: 16),
|
||||||
|
label.trailingAnchor.constraint(equalTo: playerView.trailingAnchor, constant: -16),
|
||||||
|
label.bottomAnchor.constraint(equalTo: playerView.bottomAnchor, constant: -32)
|
||||||
|
])
|
||||||
|
|
||||||
|
self.subtitleLabel = label
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updateSubtitles(at time: Double) {
|
||||||
|
let currentSubtitle = subtitlesLoader?.cues.first { cue in
|
||||||
|
time >= cue.startTime && time <= cue.endTime
|
||||||
|
}
|
||||||
|
subtitleLabel?.text = currentSubtitle?.text ?? ""
|
||||||
|
}
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
|
@ -69,21 +118,7 @@ class VideoPlayerViewController: UIViewController {
|
||||||
playerViewController.didMove(toParent: self)
|
playerViewController.didMove(toParent: self)
|
||||||
|
|
||||||
if !subtitles.isEmpty && UserDefaults.standard.bool(forKey: "subtitlesEnabled") {
|
if !subtitles.isEmpty && UserDefaults.standard.bool(forKey: "subtitlesEnabled") {
|
||||||
if let subtitleURL = URL(string: subtitles) {
|
setupSubtitles()
|
||||||
Task {
|
|
||||||
do {
|
|
||||||
let subtitleCues = try await SubtitleManager.shared.loadSubtitles(from: subtitleURL)
|
|
||||||
await MainActor.run {
|
|
||||||
if let player = self.player {
|
|
||||||
let overlay = SubtitleManager.shared.createSubtitleOverlay(for: subtitleCues, player: player)
|
|
||||||
self.addSubtitleOverlay(overlay)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
Logger.shared.log("Failed to load subtitles: \(error.localizedDescription)", type: "Error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -177,22 +212,6 @@ class VideoPlayerViewController: UIViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func addSubtitleOverlay(_ overlay: SubtitleOverlayView) {
|
|
||||||
subtitleOverlay?.removeFromSuperview()
|
|
||||||
subtitleOverlay = overlay
|
|
||||||
|
|
||||||
guard let playerView = playerViewController?.view else { return }
|
|
||||||
playerView.addSubview(overlay)
|
|
||||||
overlay.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
|
|
||||||
NSLayoutConstraint.activate([
|
|
||||||
overlay.leadingAnchor.constraint(equalTo: playerView.leadingAnchor),
|
|
||||||
overlay.trailingAnchor.constraint(equalTo: playerView.trailingAnchor),
|
|
||||||
overlay.bottomAnchor.constraint(equalTo: playerView.bottomAnchor),
|
|
||||||
overlay.heightAnchor.constraint(equalToConstant: 100)
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
|
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
|
||||||
if UserDefaults.standard.bool(forKey: "alwaysLandscape") {
|
if UserDefaults.standard.bool(forKey: "alwaysLandscape") {
|
||||||
return .landscape
|
return .landscape
|
||||||
|
|
@ -214,6 +233,8 @@ class VideoPlayerViewController: UIViewController {
|
||||||
if let timeObserverToken = timeObserverToken {
|
if let timeObserverToken = timeObserverToken {
|
||||||
player?.removeTimeObserver(timeObserverToken)
|
player?.removeTimeObserver(timeObserverToken)
|
||||||
}
|
}
|
||||||
subtitleOverlay?.removeFromSuperview()
|
subtitleLabel?.removeFromSuperview()
|
||||||
|
subtitleLabel = nil
|
||||||
|
subtitlesLoader = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
{
|
{
|
||||||
"originHash" : "e12f82ce5205016ea66a114308acd41450cfe950ccb1aacfe0e26181d2036fa4",
|
|
||||||
"pins" : [
|
"pins" : [
|
||||||
{
|
{
|
||||||
"identity" : "drops",
|
"identity" : "drops",
|
||||||
|
|
@ -29,5 +28,5 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"version" : 3
|
"version" : 2
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue