mirror of
https://github.com/cranci1/Sora.git
synced 2026-01-11 20:10:24 +00:00
Fixed icon 😭 + Fixed subtitle loader
This commit is contained in:
parent
cc65bc1634
commit
7b0827422e
4 changed files with 99 additions and 42 deletions
Binary file not shown.
|
Before Width: | Height: | Size: 161 KiB After Width: | Height: | Size: 88 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 161 KiB After Width: | Height: | Size: 88 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 161 KiB After Width: | Height: | Size: 77 KiB |
|
|
@ -5,7 +5,6 @@
|
|||
// Created by Francesco on 15/02/25.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import Foundation
|
||||
|
||||
struct SubtitleCue: Identifiable {
|
||||
|
|
@ -27,24 +26,30 @@ class VTTSubtitlesLoader: ObservableObject {
|
|||
func load(from urlString: String) {
|
||||
guard let url = URL(string: urlString) else { return }
|
||||
|
||||
let format = determineSubtitleFormat(from: url)
|
||||
|
||||
URLSession.shared.dataTask(with: url) { data, _, error in
|
||||
guard let data = data,
|
||||
let content = String(data: data, encoding: .utf8),
|
||||
error == nil else { return }
|
||||
URLSession.shared.dataTask(with: url) { data, response, error in
|
||||
guard let responseData = data,
|
||||
let subtitleContent = String(data: responseData, encoding: .utf8),
|
||||
!subtitleContent.isEmpty,
|
||||
error == nil else {
|
||||
DispatchQueue.main.async {
|
||||
self.cues = []
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
let detectedFormat = self.determineSubtitleFormat(from: url)
|
||||
|
||||
DispatchQueue.main.async {
|
||||
switch format {
|
||||
switch detectedFormat {
|
||||
case .vtt:
|
||||
self.cues = self.parseVTT(content: content)
|
||||
self.cues = self.parseVTT(content: subtitleContent)
|
||||
case .srt:
|
||||
self.cues = self.parseSRT(content: content)
|
||||
self.cues = self.parseSRT(content: subtitleContent)
|
||||
case .unknown:
|
||||
if content.trimmed.hasPrefix("WEBVTT") {
|
||||
self.cues = self.parseVTT(content: content)
|
||||
if subtitleContent.trimmingCharacters(in: .whitespacesAndNewlines).hasPrefix("WEBVTT") {
|
||||
self.cues = self.parseVTT(content: subtitleContent)
|
||||
} else {
|
||||
self.cues = self.parseSRT(content: content)
|
||||
self.cues = self.parseSRT(content: subtitleContent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -64,46 +69,70 @@ class VTTSubtitlesLoader: ObservableObject {
|
|||
}
|
||||
|
||||
private func parseVTT(content: String) -> [SubtitleCue] {
|
||||
var cues: [SubtitleCue] = []
|
||||
let lines = content.components(separatedBy: .newlines)
|
||||
var index = 0
|
||||
let contentLines = content.components(separatedBy: .newlines)
|
||||
var subtitleCues: [SubtitleCue] = []
|
||||
var activeStartTime: Double?
|
||||
var activeEndTime: Double?
|
||||
var activeSubtitleText: String = ""
|
||||
var processingCueContent = false
|
||||
|
||||
while index < lines.count {
|
||||
let line = lines[index].trimmingCharacters(in: .whitespaces)
|
||||
if line.isEmpty || line == "WEBVTT" {
|
||||
index += 1
|
||||
for (lineIndex, currentLine) in contentLines.enumerated() {
|
||||
let cleanedLine = currentLine.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
|
||||
if cleanedLine.isEmpty || cleanedLine == "WEBVTT" || cleanedLine.starts(with: "NOTE") {
|
||||
if processingCueContent && !activeSubtitleText.isEmpty {
|
||||
if let beginTime = activeStartTime, let finishTime = activeEndTime {
|
||||
subtitleCues.append(SubtitleCue(startTime: beginTime, endTime: finishTime, text: activeSubtitleText))
|
||||
}
|
||||
processingCueContent = false
|
||||
activeStartTime = nil
|
||||
activeEndTime = nil
|
||||
activeSubtitleText = ""
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if !line.contains("-->") {
|
||||
index += 1
|
||||
if index >= lines.count { break }
|
||||
if cleanedLine.contains("-->") {
|
||||
if processingCueContent && !activeSubtitleText.isEmpty {
|
||||
if let beginTime = activeStartTime, let finishTime = activeEndTime {
|
||||
subtitleCues.append(SubtitleCue(startTime: beginTime, endTime: finishTime, text: activeSubtitleText))
|
||||
}
|
||||
}
|
||||
|
||||
let timeComponents = cleanedLine.components(separatedBy: "-->").map { $0.trimmingCharacters(in: .whitespaces) }
|
||||
if timeComponents.count == 2 {
|
||||
activeStartTime = parseVTTTime(timeComponents[0])
|
||||
activeEndTime = parseVTTTime(timeComponents[1])
|
||||
activeSubtitleText = ""
|
||||
processingCueContent = true
|
||||
}
|
||||
} else if processingCueContent {
|
||||
if !activeSubtitleText.isEmpty {
|
||||
activeSubtitleText += "\n"
|
||||
}
|
||||
activeSubtitleText += cleanedLine
|
||||
|
||||
let isFinalLine = lineIndex == contentLines.count - 1
|
||||
let followingLine = isFinalLine ? "" : contentLines[lineIndex + 1].trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
if isFinalLine || followingLine.isEmpty || followingLine.contains("-->") {
|
||||
if let beginTime = activeStartTime, let finishTime = activeEndTime {
|
||||
subtitleCues.append(SubtitleCue(startTime: beginTime, endTime: finishTime, text: activeSubtitleText))
|
||||
activeStartTime = nil
|
||||
activeEndTime = nil
|
||||
activeSubtitleText = ""
|
||||
processingCueContent = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let timeLine = lines[index]
|
||||
let times = timeLine.components(separatedBy: "-->")
|
||||
if times.count < 2 {
|
||||
index += 1
|
||||
continue
|
||||
}
|
||||
|
||||
let startTime = parseTimecode(times[0].trimmingCharacters(in: .whitespaces))
|
||||
let endTime = parseTimecode(times[1].trimmingCharacters(in: .whitespaces))
|
||||
index += 1
|
||||
var cueText = ""
|
||||
while index < lines.count && !lines[index].trimmingCharacters(in: .whitespaces).isEmpty {
|
||||
cueText += lines[index] + "\n"
|
||||
index += 1
|
||||
}
|
||||
cues.append(SubtitleCue(startTime: startTime, endTime: endTime, text: cueText.trimmingCharacters(in: .whitespacesAndNewlines)))
|
||||
}
|
||||
return cues
|
||||
|
||||
return subtitleCues
|
||||
}
|
||||
|
||||
private func parseSRT(content: String) -> [SubtitleCue] {
|
||||
var cues: [SubtitleCue] = []
|
||||
let normalizedContent = content.replacingOccurrences(of: "\r\n", with: "\n")
|
||||
.replacingOccurrences(of: "\r", with: "\n")
|
||||
.replacingOccurrences(of: "\r", with: "\n")
|
||||
let blocks = normalizedContent.components(separatedBy: "\n\n")
|
||||
|
||||
for block in blocks {
|
||||
|
|
@ -130,6 +159,34 @@ class VTTSubtitlesLoader: ObservableObject {
|
|||
return cues
|
||||
}
|
||||
|
||||
private func parseVTTTime(_ timeString: String) -> Double {
|
||||
let timeSegments = timeString.components(separatedBy: ":")
|
||||
guard timeSegments.count >= 2 else { return 0 }
|
||||
|
||||
var hourValue = 0.0
|
||||
var minuteValue = 0.0
|
||||
var secondValue = 0.0
|
||||
|
||||
if timeSegments.count == 3 {
|
||||
hourValue = Double(timeSegments[0]) ?? 0
|
||||
minuteValue = Double(timeSegments[1]) ?? 0
|
||||
let secondComponents = timeSegments[2].components(separatedBy: ".")
|
||||
secondValue = Double(secondComponents[0]) ?? 0
|
||||
if secondComponents.count > 1 {
|
||||
secondValue += Double("0." + secondComponents[1]) ?? 0
|
||||
}
|
||||
} else {
|
||||
minuteValue = Double(timeSegments[0]) ?? 0
|
||||
let secondComponents = timeSegments[1].components(separatedBy: ".")
|
||||
secondValue = Double(secondComponents[0]) ?? 0
|
||||
if secondComponents.count > 1 {
|
||||
secondValue += Double("0." + secondComponents[1]) ?? 0
|
||||
}
|
||||
}
|
||||
|
||||
return hourValue * 3600 + minuteValue * 60 + secondValue
|
||||
}
|
||||
|
||||
private func parseTimecode(_ timeString: String) -> Double {
|
||||
let parts = timeString.components(separatedBy: ":")
|
||||
var seconds = 0.0
|
||||
|
|
|
|||
Loading…
Reference in a new issue