Skip to content

Commit 8a2fdd4

Browse files
committed
Add syncTargetLine to merge specification
Fixes #7
1 parent b38282f commit 8a2fdd4

File tree

4 files changed

+100
-34
lines changed

4 files changed

+100
-34
lines changed

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,11 @@ subs {
7070
from(glob("$episode/eizouken$episode-ts*.ass"))
7171

7272
from("songs/eizouken_OP.ass") {
73-
syncTo(getAs<Duration>("opshift"))
73+
syncTargetTime(getAs<Duration>("opshift"))
7474
}
7575

7676
from("songs/eizouken_ED.ass") {
77-
syncTo(getAs<Duration>("edshift"))
77+
syncTargetTime(getAs<Duration>("edshift"))
7878
}
7979
}
8080

@@ -668,11 +668,11 @@ subs {
668668
from(getList("typesetting"))
669669

670670
from(get("OP")) {
671-
syncTo(getAs<Duration>("opsync"))
671+
syncTargetTime(getAs<Duration>("opsync"))
672672
}
673673

674674
from(get("ED")) {
675-
syncTo(getAs<Duration>("edsync"))
675+
syncTargetTime(getAs<Duration>("edsync"))
676676
}
677677
}
678678

src/main/kotlin/myaa/subkt/ass/parser.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,8 @@ sealed class EventLineAccessor<T>(field: String) : LineAccessor<T, EventLine>(fi
438438
object MARGIN_V : EventLineAccessor<Int>("MarginV")
439439
object EFFECT : EventLineAccessor<String>("Effect")
440440
object TEXT : EventLineAccessor<String>("Text")
441+
442+
override fun toString() = field
441443
}
442444

443445
/**

src/main/kotlin/myaa/subkt/tasks/asstasks.kt

Lines changed: 92 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,11 @@ enum class ErrorMode {
7373
* @sample myaa.subkt.tasks.samples.mergeSample
7474
*/
7575
open class Merge : ASSTask() {
76+
data class LineSpecification(
77+
@get:Input val field: EventLineAccessor<String>,
78+
@get:Input val value: String
79+
)
80+
7681
/**
7782
* Defines how the files associated with this specification should be merged.
7883
*/
@@ -89,29 +94,57 @@ open class Merge : ASSTask() {
8994
@get:Input
9095
val shiftBy = defaultProperty(Duration.ZERO)
9196

97+
private val _syncSourceLine =
98+
defaultProperty(LineSpecification(EventLineAccessor.EFFECT, "sync"))
99+
100+
@get:Nested
101+
@get:Optional
102+
val syncSourceLine: Provider<LineSpecification> = _syncSourceLine
103+
92104
/**
93-
* Shifts the lines of the source file so that the start time of the sync line,
94-
* as specified by [syncLine] and [syncField], lines up with the specified [Duration].
105+
* Specifies a line in the included file (e.g. a song file) to serve as
106+
* a reference for shifting all lines in this file. All lines
107+
* will be shifted so that the start time of the specified source line
108+
* matches the time specified by [syncTargetTime] or the start time
109+
* of the line specified by [syncTargetLine].
110+
*
111+
* @param fieldValue The value which identifies the source sync line
112+
* @param fieldName The field in which to look for [fieldValue]
95113
*/
96-
@get:Input
114+
fun syncSourceLine(
115+
fieldValue: String,
116+
fieldName: EventLineAccessor<String> = EventLineAccessor.EFFECT
117+
) {
118+
_syncSourceLine.set(LineSpecification(fieldName, fieldValue))
119+
}
120+
121+
private val _syncTargetLine = project.objects.property<LineSpecification>()
122+
123+
@get:Nested
97124
@get:Optional
98-
val syncTo = project.objects.property<Duration>()
125+
val syncTargetLine: Provider<LineSpecification> = _syncTargetLine
99126

100127
/**
101-
* An [EventLineAccessor] specifying what field to match the value specified by
102-
* [syncLine] to in order to identify the sync line.
103-
* Defaults to the effect field.
128+
* Specifies a target line which the line specified by [syncSourceLine]
129+
* should be shifted to. The target line may be present anywhere in the merged file.
130+
*
131+
* @param fieldValue The value which identifies the target sync line
132+
* @param fieldName The field in which to look for [fieldValue]
104133
*/
105-
@get:Input
106-
val syncField = defaultProperty<EventLineAccessor<String>>(EventLineAccessor.EFFECT)
134+
fun syncTargetLine(
135+
fieldValue: String,
136+
fieldName: EventLineAccessor<String> = EventLineAccessor.EFFECT
137+
) {
138+
_syncTargetLine.set(LineSpecification(fieldName, fieldValue))
139+
}
107140

108141
/**
109-
* The value that identifies the sync line used by [syncTo]. The sync line
110-
* must have the specified value in the field specified by [syncField].
111-
* Defaults to `sync`.
142+
* Specifies a target time which the line specified by [syncSourceLine]
143+
* should be shifted to.
112144
*/
113145
@get:Input
114-
val syncLine = defaultProperty("sync")
146+
@get:Optional
147+
val syncTargetTime = project.objects.property<Duration>()
115148
}
116149

117150
/**
@@ -206,7 +239,7 @@ open class Merge : ASSTask() {
206239
val spec = MergeSpecification().apply {
207240
incrementLayer(line.layer)
208241
if (line.effect == "import-shifted") {
209-
syncTo(line.start)
242+
syncTargetTime(line.start)
210243
}
211244
}
212245
val merged = template.resolveSibling(line.text)
@@ -238,31 +271,65 @@ open class Merge : ASSTask() {
238271
}
239272

240273
override fun buildAss(): ASSFile {
274+
val targetLineSpecs = mutableListOf<LineSpecification>()
275+
276+
// read ass files, preliminary processing, collect target sync line specifications
241277
val files = _sources.get().flatMap { (files, spec) ->
242-
project.files(files).map { file -> ASSFile(file).also { ass ->
278+
project.files(files).map { file ->
279+
val ass = ASSFile(file)
280+
243281
ass.events.lines.forEach { line ->
244282
line.layer += spec.incrementLayer.get()
245283
line.start += spec.shiftBy.get()
246284
line.end += spec.shiftBy.get()
247285
}
248286

249-
spec.syncTo.orNull?.let { syncTo ->
250-
val referenceLine = ass.events.lines.find {
251-
getMaybeComment(it, spec.syncField.get()) == spec.syncLine.get()
252-
} ?: error("Could not find sync line in ${file.name}")
287+
spec.syncTargetLine.orNull?.let {
288+
targetLineSpecs.add(it)
289+
}
253290

254-
val delta = syncTo - referenceLine.start
291+
ass to spec
292+
}
293+
}
255294

256-
ass.events.lines.forEach {
257-
it.start += delta
258-
it.end += delta
295+
// find lines corresponding to target sync line specifications
296+
val targetLines = files.flatMap { (ass, _) -> ass.events.lines }.mapNotNull { line ->
297+
targetLineSpecs.find { getMaybeComment(line, it.field) == it.value }?.let { it to line }
298+
}.groupBy({ it.first }, { it.second }).mapValues { (spec, lines) ->
299+
when {
300+
lines.size > 1 -> error("duplicate target sync lines with value ${spec.value} " +
301+
"in field ${spec.field}")
302+
else -> lines[0]
303+
}
304+
}
305+
306+
// shift lines based on start time of target lines or explicit target times
307+
files.forEach { (ass, spec) ->
308+
val targetTime = spec.syncTargetTime.orNull
309+
?: spec.syncTargetLine.orNull?.let { targetSpec ->
310+
targetLines[targetSpec]?.start ?: error(
311+
"could not find target sync line with value ${targetSpec.value} " +
312+
"in field ${targetSpec.field} in merged file")
259313
}
314+
315+
if (targetTime != null) {
316+
val sourceSpec = spec.syncSourceLine.get()
317+
val sourceLine = ass.events.lines.find {
318+
getMaybeComment(it, sourceSpec.field) == sourceSpec.value
319+
} ?: error("could not find source sync line with value ${sourceSpec.value}" +
320+
"in field ${sourceSpec.field} in file ${ass.file?.name}")
321+
val sourceTime = sourceLine.start
322+
323+
val delta = targetTime - sourceTime
324+
ass.events.lines.forEach {
325+
it.start += delta
326+
it.end += delta
260327
}
261-
} }
328+
}
262329
}
263330

264331
val conflictingInfoSet = conflictingScriptInfo.get().toSet()
265-
val outAss = files.foldIndexed(ASSFile()) { i, acc, assFile ->
332+
val outAss = files.map { (ass, _) -> ass }.foldIndexed(ASSFile()) { i, acc, assFile ->
266333
val existingStyles = acc.styles.lines.associate {
267334
it.name to acc.styles.serializeLine(it)
268335
}

src/samples/kotlin/tasks.kt

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
package myaa.subkt.tasks.samples
22

33
import io.ktor.http.ContentType
4-
import io.ktor.http.HttpMethod
5-
import myaa.subkt.ass.*
64
import myaa.subkt.tasks.*
75
import myaa.subkt.ass.EventLineAccessor
86
import myaa.subkt.ass.assTime
9-
import myaa.subkt.tasks.*
107
import myaa.subkt.tasks.Anidex.*
118
import myaa.subkt.tasks.Mux.*
129
import myaa.subkt.tasks.Nyaa.*
@@ -25,8 +22,8 @@ fun Subs.mergeSample() {
2522
incrementLayer(10)
2623
}
2724
from("OP.ass") {
28-
syncField(EventLineAccessor.ACTOR)
29-
syncTo("0:10:24.66".assTime)
25+
syncSourceLine("sync", EventLineAccessor.ACTOR)
26+
syncTargetTime("0:10:24.66".assTime)
3027
}
3128
from(glob("typesetting-*.ass"))
3229
onScriptInfoConflict(ErrorMode.FAIL)

0 commit comments

Comments
 (0)