100 lines
3 KiB
Go
100 lines
3 KiB
Go
package sync
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"sub-cli/internal/format/srt"
|
|
"sub-cli/internal/model"
|
|
)
|
|
|
|
// syncSRTFiles synchronizes two SRT files
|
|
func syncSRTFiles(sourceFile, targetFile string) error {
|
|
sourceEntries, err := srt.Parse(sourceFile)
|
|
if err != nil {
|
|
return fmt.Errorf("error parsing source SRT file: %w", err)
|
|
}
|
|
|
|
targetEntries, err := srt.Parse(targetFile)
|
|
if err != nil {
|
|
return fmt.Errorf("error parsing target SRT file: %w", err)
|
|
}
|
|
|
|
// Check if entry counts match
|
|
if len(sourceEntries) != len(targetEntries) {
|
|
fmt.Printf("Warning: Source (%d entries) and target (%d entries) have different entry counts. Timeline will be adjusted.\n",
|
|
len(sourceEntries), len(targetEntries))
|
|
}
|
|
|
|
// Sync the timelines
|
|
syncedEntries := syncSRTTimeline(sourceEntries, targetEntries)
|
|
|
|
// Write the synced entries to the target file
|
|
return srt.Generate(syncedEntries, targetFile)
|
|
}
|
|
|
|
// syncSRTTimeline applies the timing from source SRT entries to target SRT entries
|
|
func syncSRTTimeline(sourceEntries, targetEntries []model.SRTEntry) []model.SRTEntry {
|
|
result := make([]model.SRTEntry, len(targetEntries))
|
|
|
|
// Copy target entries
|
|
copy(result, targetEntries)
|
|
|
|
// If source is empty, just return the target entries as is
|
|
if len(sourceEntries) == 0 {
|
|
// Ensure proper sequence numbering
|
|
for i := range result {
|
|
result[i].Number = i + 1
|
|
}
|
|
return result
|
|
}
|
|
|
|
// If source and target have the same number of entries, directly apply timings
|
|
if len(sourceEntries) == len(targetEntries) {
|
|
for i := range result {
|
|
result[i].StartTime = sourceEntries[i].StartTime
|
|
result[i].EndTime = sourceEntries[i].EndTime
|
|
}
|
|
} else {
|
|
// If entry counts differ, scale the timing
|
|
for i := range result {
|
|
// Calculate scaled index
|
|
sourceIdx := 0
|
|
if len(sourceEntries) > 1 {
|
|
sourceIdx = i * (len(sourceEntries) - 1) / (len(targetEntries) - 1)
|
|
}
|
|
|
|
// Ensure the index is within bounds
|
|
if sourceIdx >= len(sourceEntries) {
|
|
sourceIdx = len(sourceEntries) - 1
|
|
}
|
|
|
|
// Apply the scaled timing
|
|
result[i].StartTime = sourceEntries[sourceIdx].StartTime
|
|
|
|
// Calculate end time: if not the last entry, use duration from source
|
|
if i < len(result)-1 {
|
|
// If next source entry exists, calculate duration
|
|
var duration model.Timestamp
|
|
if sourceIdx+1 < len(sourceEntries) {
|
|
duration = calculateDuration(sourceEntries[sourceIdx].StartTime, sourceEntries[sourceIdx+1].StartTime)
|
|
} else {
|
|
// If no next source entry, use the source's end time (usually a few seconds after start)
|
|
duration = calculateDuration(sourceEntries[sourceIdx].StartTime, sourceEntries[sourceIdx].EndTime)
|
|
}
|
|
|
|
// Apply duration to next start time
|
|
result[i].EndTime = addDuration(result[i].StartTime, duration)
|
|
} else {
|
|
// For the last entry, add a fixed duration (e.g., 3 seconds)
|
|
result[i].EndTime = sourceEntries[sourceIdx].EndTime
|
|
}
|
|
}
|
|
}
|
|
|
|
// Ensure proper sequence numbering
|
|
for i := range result {
|
|
result[i].Number = i + 1
|
|
}
|
|
|
|
return result
|
|
}
|