chore: seperate large files
This commit is contained in:
parent
ebbf516689
commit
76e1298ded
44 changed files with 5745 additions and 4173 deletions
465
internal/sync/ass_test.go
Normal file
465
internal/sync/ass_test.go
Normal file
|
@ -0,0 +1,465 @@
|
|||
package sync
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"sub-cli/internal/model"
|
||||
)
|
||||
|
||||
func TestSyncASSTimeline(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
source model.ASSFile
|
||||
target model.ASSFile
|
||||
verify func(t *testing.T, result model.ASSFile)
|
||||
}{
|
||||
{
|
||||
name: "Equal event counts",
|
||||
source: model.ASSFile{
|
||||
ScriptInfo: map[string]string{"Title": "Source ASS"},
|
||||
Styles: []model.ASSStyle{
|
||||
{
|
||||
Name: "Default",
|
||||
Properties: map[string]string{
|
||||
"Format": "Name, Fontname, Fontsize, PrimaryColour",
|
||||
"Style": "Default,Arial,20,&H00FFFFFF",
|
||||
},
|
||||
},
|
||||
},
|
||||
Events: []model.ASSEvent{
|
||||
{
|
||||
Type: "Dialogue",
|
||||
Layer: 0,
|
||||
StartTime: model.Timestamp{Seconds: 1},
|
||||
EndTime: model.Timestamp{Seconds: 4},
|
||||
Style: "Default",
|
||||
Text: "Source line one.",
|
||||
},
|
||||
{
|
||||
Type: "Dialogue",
|
||||
Layer: 0,
|
||||
StartTime: model.Timestamp{Seconds: 5},
|
||||
EndTime: model.Timestamp{Seconds: 8},
|
||||
Style: "Default",
|
||||
Text: "Source line two.",
|
||||
},
|
||||
{
|
||||
Type: "Dialogue",
|
||||
Layer: 0,
|
||||
StartTime: model.Timestamp{Seconds: 9},
|
||||
EndTime: model.Timestamp{Seconds: 12},
|
||||
Style: "Default",
|
||||
Text: "Source line three.",
|
||||
},
|
||||
},
|
||||
},
|
||||
target: model.ASSFile{
|
||||
ScriptInfo: map[string]string{"Title": "Target ASS"},
|
||||
Styles: []model.ASSStyle{
|
||||
{
|
||||
Name: "Default",
|
||||
Properties: map[string]string{
|
||||
"Format": "Name, Fontname, Fontsize, PrimaryColour",
|
||||
"Style": "Default,Arial,20,&H00FFFFFF",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Alternate",
|
||||
Properties: map[string]string{
|
||||
"Format": "Name, Fontname, Fontsize, PrimaryColour",
|
||||
"Style": "Alternate,Times New Roman,20,&H0000FFFF",
|
||||
},
|
||||
},
|
||||
},
|
||||
Events: []model.ASSEvent{
|
||||
{
|
||||
Type: "Dialogue",
|
||||
Layer: 0,
|
||||
StartTime: model.Timestamp{Minutes: 1, Seconds: 0},
|
||||
EndTime: model.Timestamp{Minutes: 1, Seconds: 3},
|
||||
Style: "Default",
|
||||
Text: "Target line one.",
|
||||
},
|
||||
{
|
||||
Type: "Dialogue",
|
||||
Layer: 0,
|
||||
StartTime: model.Timestamp{Minutes: 1, Seconds: 5},
|
||||
EndTime: model.Timestamp{Minutes: 1, Seconds: 8},
|
||||
Style: "Alternate",
|
||||
Text: "Target line two.",
|
||||
},
|
||||
{
|
||||
Type: "Dialogue",
|
||||
Layer: 0,
|
||||
StartTime: model.Timestamp{Minutes: 1, Seconds: 10},
|
||||
EndTime: model.Timestamp{Minutes: 1, Seconds: 13},
|
||||
Style: "Default",
|
||||
Text: "Target line three.",
|
||||
},
|
||||
},
|
||||
},
|
||||
verify: func(t *testing.T, result model.ASSFile) {
|
||||
if len(result.Events) != 3 {
|
||||
t.Errorf("Expected 3 events, got %d", len(result.Events))
|
||||
return
|
||||
}
|
||||
|
||||
// Check that source timings are applied to target events
|
||||
if result.Events[0].StartTime.Seconds != 1 || result.Events[0].EndTime.Seconds != 4 {
|
||||
t.Errorf("First event timing mismatch: got %+v", result.Events[0])
|
||||
}
|
||||
|
||||
if result.Events[1].StartTime.Seconds != 5 || result.Events[1].EndTime.Seconds != 8 {
|
||||
t.Errorf("Second event timing mismatch: got %+v", result.Events[1])
|
||||
}
|
||||
|
||||
if result.Events[2].StartTime.Seconds != 9 || result.Events[2].EndTime.Seconds != 12 {
|
||||
t.Errorf("Third event timing mismatch: got %+v", result.Events[2])
|
||||
}
|
||||
|
||||
// Check that target content and styles are preserved
|
||||
if result.Events[0].Text != "Target line one." {
|
||||
t.Errorf("Content should be preserved, got: %s", result.Events[0].Text)
|
||||
}
|
||||
|
||||
if result.Events[1].Style != "Alternate" {
|
||||
t.Errorf("Style should be preserved, got: %s", result.Events[1].Style)
|
||||
}
|
||||
|
||||
// Check that script info and style definitions are preserved
|
||||
if result.ScriptInfo["Title"] != "Target ASS" {
|
||||
t.Errorf("ScriptInfo should be preserved, got: %+v", result.ScriptInfo)
|
||||
}
|
||||
|
||||
if len(result.Styles) != 2 {
|
||||
t.Errorf("Expected 2 styles, got %d", len(result.Styles))
|
||||
}
|
||||
|
||||
if result.Styles[1].Name != "Alternate" {
|
||||
t.Errorf("Style definitions should be preserved, got: %+v", result.Styles[1])
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "More target events than source",
|
||||
source: model.ASSFile{
|
||||
Events: []model.ASSEvent{
|
||||
{
|
||||
Type: "Dialogue",
|
||||
Layer: 0,
|
||||
StartTime: model.Timestamp{Seconds: 1},
|
||||
EndTime: model.Timestamp{Seconds: 4},
|
||||
Style: "Default",
|
||||
Text: "Source line one.",
|
||||
},
|
||||
{
|
||||
Type: "Dialogue",
|
||||
Layer: 0,
|
||||
StartTime: model.Timestamp{Seconds: 5},
|
||||
EndTime: model.Timestamp{Seconds: 8},
|
||||
Style: "Default",
|
||||
Text: "Source line two.",
|
||||
},
|
||||
},
|
||||
},
|
||||
target: model.ASSFile{
|
||||
Events: []model.ASSEvent{
|
||||
{
|
||||
Type: "Dialogue",
|
||||
Layer: 0,
|
||||
StartTime: model.Timestamp{Minutes: 1, Seconds: 0},
|
||||
EndTime: model.Timestamp{Minutes: 1, Seconds: 3},
|
||||
Style: "Default",
|
||||
Text: "Target line one.",
|
||||
},
|
||||
{
|
||||
Type: "Dialogue",
|
||||
Layer: 0,
|
||||
StartTime: model.Timestamp{Minutes: 1, Seconds: 5},
|
||||
EndTime: model.Timestamp{Minutes: 1, Seconds: 8},
|
||||
Style: "Default",
|
||||
Text: "Target line two.",
|
||||
},
|
||||
{
|
||||
Type: "Dialogue",
|
||||
Layer: 0,
|
||||
StartTime: model.Timestamp{Minutes: 1, Seconds: 10},
|
||||
EndTime: model.Timestamp{Minutes: 1, Seconds: 13},
|
||||
Style: "Default",
|
||||
Text: "Target line three.",
|
||||
},
|
||||
},
|
||||
},
|
||||
verify: func(t *testing.T, result model.ASSFile) {
|
||||
if len(result.Events) != 3 {
|
||||
t.Errorf("Expected 3 events, got %d", len(result.Events))
|
||||
return
|
||||
}
|
||||
|
||||
// First event should use first source timing
|
||||
if result.Events[0].StartTime.Seconds != 1 {
|
||||
t.Errorf("First event should have first source timing, got: %+v", result.Events[0].StartTime)
|
||||
}
|
||||
|
||||
// Last event should use last source timing
|
||||
if result.Events[2].StartTime.Seconds != 5 {
|
||||
t.Errorf("Last event should have last source timing, got: %+v", result.Events[2].StartTime)
|
||||
}
|
||||
|
||||
// Verify content is preserved
|
||||
if result.Events[2].Text != "Target line three." {
|
||||
t.Errorf("Content should be preserved, got: %s", result.Events[2].Text)
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "More source events than target",
|
||||
source: model.ASSFile{
|
||||
Events: []model.ASSEvent{
|
||||
{
|
||||
Type: "Dialogue",
|
||||
Layer: 0,
|
||||
StartTime: model.Timestamp{Seconds: 1},
|
||||
EndTime: model.Timestamp{Seconds: 3},
|
||||
Style: "Default",
|
||||
Text: "Source line one.",
|
||||
},
|
||||
{
|
||||
Type: "Dialogue",
|
||||
Layer: 0,
|
||||
StartTime: model.Timestamp{Seconds: 4},
|
||||
EndTime: model.Timestamp{Seconds: 6},
|
||||
Style: "Default",
|
||||
Text: "Source line two.",
|
||||
},
|
||||
{
|
||||
Type: "Dialogue",
|
||||
Layer: 0,
|
||||
StartTime: model.Timestamp{Seconds: 7},
|
||||
EndTime: model.Timestamp{Seconds: 9},
|
||||
Style: "Default",
|
||||
Text: "Source line three.",
|
||||
},
|
||||
{
|
||||
Type: "Dialogue",
|
||||
Layer: 0,
|
||||
StartTime: model.Timestamp{Seconds: 10},
|
||||
EndTime: model.Timestamp{Seconds: 12},
|
||||
Style: "Default",
|
||||
Text: "Source line four.",
|
||||
},
|
||||
},
|
||||
},
|
||||
target: model.ASSFile{
|
||||
Events: []model.ASSEvent{
|
||||
{
|
||||
Type: "Dialogue",
|
||||
Layer: 0,
|
||||
StartTime: model.Timestamp{Minutes: 1, Seconds: 0},
|
||||
EndTime: model.Timestamp{Minutes: 1, Seconds: 3},
|
||||
Style: "Default",
|
||||
Text: "Target line one.",
|
||||
},
|
||||
{
|
||||
Type: "Dialogue",
|
||||
Layer: 0,
|
||||
StartTime: model.Timestamp{Minutes: 1, Seconds: 5},
|
||||
EndTime: model.Timestamp{Minutes: 1, Seconds: 8},
|
||||
Style: "Default",
|
||||
Text: "Target line two.",
|
||||
},
|
||||
},
|
||||
},
|
||||
verify: func(t *testing.T, result model.ASSFile) {
|
||||
if len(result.Events) != 2 {
|
||||
t.Errorf("Expected 2 events, got %d", len(result.Events))
|
||||
return
|
||||
}
|
||||
|
||||
// First event should have first source timing
|
||||
if result.Events[0].StartTime.Seconds != 1 {
|
||||
t.Errorf("First event should have first source timing, got: %+v", result.Events[0].StartTime)
|
||||
}
|
||||
|
||||
// Last event should have last source timing
|
||||
if result.Events[1].StartTime.Seconds != 10 {
|
||||
t.Errorf("Last event should have last source timing, got: %+v", result.Events[1].StartTime)
|
||||
}
|
||||
|
||||
// Check that target content is preserved
|
||||
if result.Events[0].Text != "Target line one." || result.Events[1].Text != "Target line two." {
|
||||
t.Errorf("Content should be preserved, got: %+v", result.Events)
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Empty target events",
|
||||
source: model.ASSFile{
|
||||
Events: []model.ASSEvent{
|
||||
{
|
||||
Type: "Dialogue",
|
||||
Layer: 0,
|
||||
StartTime: model.Timestamp{Seconds: 1},
|
||||
EndTime: model.Timestamp{Seconds: 4},
|
||||
Style: "Default",
|
||||
Text: "Source line one.",
|
||||
},
|
||||
},
|
||||
},
|
||||
target: model.ASSFile{
|
||||
ScriptInfo: map[string]string{"Title": "Empty Target"},
|
||||
Events: []model.ASSEvent{},
|
||||
},
|
||||
verify: func(t *testing.T, result model.ASSFile) {
|
||||
if len(result.Events) != 0 {
|
||||
t.Errorf("Expected 0 events, got %d", len(result.Events))
|
||||
}
|
||||
|
||||
// ScriptInfo should be preserved
|
||||
if result.ScriptInfo["Title"] != "Empty Target" {
|
||||
t.Errorf("ScriptInfo should be preserved, got: %+v", result.ScriptInfo)
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Empty source events",
|
||||
source: model.ASSFile{
|
||||
Events: []model.ASSEvent{},
|
||||
},
|
||||
target: model.ASSFile{
|
||||
ScriptInfo: map[string]string{"Title": "Target with content"},
|
||||
Events: []model.ASSEvent{
|
||||
{
|
||||
Type: "Dialogue",
|
||||
Layer: 0,
|
||||
StartTime: model.Timestamp{Seconds: 10},
|
||||
EndTime: model.Timestamp{Seconds: 15},
|
||||
Style: "Default",
|
||||
Text: "Target line one.",
|
||||
},
|
||||
},
|
||||
},
|
||||
verify: func(t *testing.T, result model.ASSFile) {
|
||||
if len(result.Events) != 1 {
|
||||
t.Errorf("Expected 1 event, got %d", len(result.Events))
|
||||
return
|
||||
}
|
||||
|
||||
// Timing should be preserved since source is empty
|
||||
if result.Events[0].StartTime.Seconds != 10 || result.Events[0].EndTime.Seconds != 15 {
|
||||
t.Errorf("Timing should match target when source is empty, got: %+v", result.Events[0])
|
||||
}
|
||||
|
||||
// Content should be preserved
|
||||
if result.Events[0].Text != "Target line one." {
|
||||
t.Errorf("Content should be preserved, got: %s", result.Events[0].Text)
|
||||
}
|
||||
|
||||
// Title should be preserved
|
||||
if result.ScriptInfo["Title"] != "Target with content" {
|
||||
t.Errorf("Title should be preserved, got: %+v", result.ScriptInfo)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
result := syncASSTimeline(tc.source, tc.target)
|
||||
|
||||
if tc.verify != nil {
|
||||
tc.verify(t, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSyncASSFiles(t *testing.T) {
|
||||
// Create temporary test directory
|
||||
tempDir := t.TempDir()
|
||||
|
||||
// Test case for testing the sync of ASS files
|
||||
sourceContent := `[Script Info]
|
||||
ScriptType: v4.00+
|
||||
PlayResX: 640
|
||||
PlayResY: 480
|
||||
Timer: 100.0000
|
||||
Title: Source ASS File
|
||||
|
||||
[V4+ Styles]
|
||||
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
|
||||
Style: Default,Arial,20,&H00FFFFFF,&H000000FF,&H00000000,&H00000000,0,0,0,0,100,100,0,0,1,2,2,2,10,10,10,1
|
||||
|
||||
[Events]
|
||||
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
|
||||
Dialogue: 0,0:00:01.00,0:00:04.00,Default,,0,0,0,,Source line one.
|
||||
Dialogue: 0,0:00:05.00,0:00:08.00,Default,,0,0,0,,Source line two.
|
||||
Dialogue: 0,0:00:09.00,0:00:12.00,Default,,0,0,0,,Source line three.
|
||||
`
|
||||
|
||||
targetContent := `[Script Info]
|
||||
ScriptType: v4.00+
|
||||
PlayResX: 640
|
||||
PlayResY: 480
|
||||
Timer: 100.0000
|
||||
Title: Target ASS File
|
||||
|
||||
[V4+ Styles]
|
||||
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
|
||||
Style: Default,Arial,20,&H00FFFFFF,&H000000FF,&H00000000,&H00000000,0,0,0,0,100,100,0,0,1,2,2,2,10,10,10,1
|
||||
Style: Alternate,Arial,20,&H0000FFFF,&H000000FF,&H00000000,&H00000000,0,0,0,0,100,100,0,0,1,2,2,2,10,10,10,1
|
||||
|
||||
[Events]
|
||||
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
|
||||
Dialogue: 0,0:01:00.00,0:01:03.00,Default,,0,0,0,,Target line one.
|
||||
Dialogue: 0,0:01:05.00,0:01:08.00,Alternate,,0,0,0,,Target line two.
|
||||
Dialogue: 0,0:01:10.00,0:01:13.00,Default,,0,0,0,,Target line three.
|
||||
`
|
||||
|
||||
sourceFile := filepath.Join(tempDir, "source.ass")
|
||||
targetFile := filepath.Join(tempDir, "target.ass")
|
||||
|
||||
// Write test files
|
||||
if err := os.WriteFile(sourceFile, []byte(sourceContent), 0644); err != nil {
|
||||
t.Fatalf("Failed to write source file: %v", err)
|
||||
}
|
||||
|
||||
if err := os.WriteFile(targetFile, []byte(targetContent), 0644); err != nil {
|
||||
t.Fatalf("Failed to write target file: %v", err)
|
||||
}
|
||||
|
||||
// Run syncASSFiles
|
||||
err := syncASSFiles(sourceFile, targetFile)
|
||||
if err != nil {
|
||||
t.Fatalf("syncASSFiles returned error: %v", err)
|
||||
}
|
||||
|
||||
// Read the modified target file
|
||||
modifiedContent, err := os.ReadFile(targetFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to read modified file: %v", err)
|
||||
}
|
||||
|
||||
// Verify the result
|
||||
// Should have source timings
|
||||
if !strings.Contains(string(modifiedContent), "0:00:01.00") {
|
||||
t.Errorf("Output should have source timing 0:00:01.00, got: %s", string(modifiedContent))
|
||||
}
|
||||
|
||||
// Should preserve target content and styles
|
||||
if !strings.Contains(string(modifiedContent), "Target line one.") {
|
||||
t.Errorf("Output should preserve target content, got: %s", string(modifiedContent))
|
||||
}
|
||||
|
||||
if !strings.Contains(string(modifiedContent), "Style: Alternate") {
|
||||
t.Errorf("Output should preserve target styles, got: %s", string(modifiedContent))
|
||||
}
|
||||
|
||||
// Should preserve title
|
||||
if !strings.Contains(string(modifiedContent), "Title: Target ASS File") {
|
||||
t.Errorf("Output should preserve target title, got: %s", string(modifiedContent))
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue