Skip to content

Commit 1e7e59e

Browse files
authored
SubFileParser.kt
1 parent 2737a68 commit 1e7e59e

1 file changed

Lines changed: 96 additions & 2 deletions

File tree

Lines changed: 96 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,97 @@
1-
package com.subpilot.data
1+
package com.geekneuron.subpilot.subtitle
22

3-
object SubFileParser { ... }
3+
import okio.BufferedSource
4+
import okio.FileSystem
5+
import okio.Path
6+
import okio.buffer
7+
import okio.use
8+
9+
sealed class SubtitleEntry(open val startMs: Long, open val endMs: Long, open val text: String)
10+
11+
data class SRTEntry(
12+
override val startMs: Long,
13+
override val endMs: Long,
14+
override val text: String
15+
) : SubtitleEntry(startMs, endMs, text)
16+
17+
data class ASSEntry(
18+
override val startMs: Long,
19+
override val endMs: Long,
20+
override val text: String,
21+
val style: String = "Default"
22+
) : SubtitleEntry(startMs, endMs, text)
23+
24+
object SubFileParser {
25+
fun parse(fileSystem: FileSystem, path: Path): List<SubtitleEntry> {
26+
val extension = path.name.substringAfterLast(".").lowercase()
27+
return when (extension) {
28+
"srt" -> parseSRT(fileSystem, path)
29+
"ass" -> parseASS(fileSystem, path)
30+
else -> emptyList()
31+
}
32+
}
33+
34+
private fun parseSRT(fs: FileSystem, path: Path): List<SRTEntry> {
35+
val result = mutableListOf<SRTEntry>()
36+
fs.source(path).buffer().use { source ->
37+
val lines = source.readUtf8().split("\r?\n")
38+
var i = 0
39+
while (i < lines.size) {
40+
val index = lines[i].trim()
41+
i++
42+
if (i >= lines.size) break
43+
val timeLine = lines[i++].trim()
44+
val timeParts = timeLine.split(" --> ")
45+
if (timeParts.size != 2) continue
46+
val startMs = parseSrtTime(timeParts[0])
47+
val endMs = parseSrtTime(timeParts[1])
48+
val textLines = mutableListOf<String>()
49+
while (i < lines.size && lines[i].isNotBlank()) {
50+
textLines.add(lines[i++])
51+
}
52+
result.add(SRTEntry(startMs, endMs, textLines.joinToString("\n")))
53+
while (i < lines.size && lines[i].isBlank()) i++
54+
}
55+
}
56+
return result
57+
}
58+
59+
private fun parseSrtTime(timeStr: String): Long {
60+
val parts = timeStr.split(":", ",")
61+
val h = parts[0].toInt()
62+
val m = parts[1].toInt()
63+
val s = parts[2].toInt()
64+
val ms = parts[3].toInt()
65+
return ((h * 3600 + m * 60 + s) * 1000L + ms)
66+
}
67+
68+
private fun parseASS(fs: FileSystem, path: Path): List<ASSEntry> {
69+
val result = mutableListOf<ASSEntry>()
70+
var dialogueFound = false
71+
fs.source(path).buffer().use { source ->
72+
source.readUtf8LineSequence().forEach { line ->
73+
if (line.startsWith("[Events]")) {
74+
dialogueFound = true
75+
} else if (dialogueFound && line.startsWith("Dialogue:")) {
76+
val parts = line.split(",", limit = 10)
77+
if (parts.size >= 10) {
78+
val startMs = parseAssTime(parts[1])
79+
val endMs = parseAssTime(parts[2])
80+
val text = parts[9].trim()
81+
result.add(ASSEntry(startMs, endMs, text))
82+
}
83+
}
84+
}
85+
}
86+
return result
87+
}
88+
89+
private fun parseAssTime(timeStr: String): Long {
90+
val parts = timeStr.split(":", ".")
91+
val h = parts[0].toInt()
92+
val m = parts[1].toInt()
93+
val s = parts[2].toInt()
94+
val cs = parts[3].toInt()
95+
return ((h * 3600 + m * 60 + s) * 1000L + cs * 10)
96+
}
97+
}

0 commit comments

Comments
 (0)