Fixed icon 😭 + Fixed subtitle loader

This commit is contained in:
cranci1 2025-09-06 17:43:23 +02:00
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

View file

@ -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