Thanks to visit codestin.com
Credit goes to github.com

Skip to content

A Go library and CLI tool for generating ASS (Advanced SubStation Alpha) subtitle files with word-by-word fade-in effects. Perfect for creating dynamic subtitles where words appear sequentially with smooth fade transitions.

License

Notifications You must be signed in to change notification settings

kperreau/wordsubgen

Repository files navigation

wordsubgen

A Go library and CLI tool for generating ASS (Advanced SubStation Alpha) subtitle files with word-by-word fade-in effects. Perfect for creating dynamic subtitles where words appear sequentially with smooth fade transitions.

Features

  • Word-by-word fade effects: Each word fades in sequentially using ASS \alpha tags
  • Structured JSON input: Generate from timed words (word, start, end in seconds) with --json / --json-file; StartDelay shifts all timings (e.g. 1500 ms = 1.5 s offset)
  • Karaoke support: Optional karaoke mode with \k tags for word highlighting
  • Shadow effects: Customizable shadow with directional control using \xshad and \yshad tags
  • Flexible configuration: Customizable fonts, colors, timing, alignment, and styling
  • Library API: Use as a Go library in your own projects
  • CLI tool: Easy-to-use command-line interface with structured logging

Demo

Here's a visual example of the word-by-word fade effect generated by wordsubgen:

Word-by-word fade effect demo

This demo was generated using the following command:

wordsubgen --lines "This is a super test|Hello world \!"  --font "Outfit" --fontsize 85 --shadow-enabled --shadow-x 3 --shadow-y 8 --width 1080 --height 734 --out subtitle.ass

Installation

CLI Tool Installation

To install the command-line tool:

go install github.com/kperreau/wordsubgen/cmd/wordsubgen@latest

Package Installation (for Go projects)

To use wordsubgen as a library in your Go project:

go get github.com/kperreau/wordsubgen@latest

Then import it in your Go code:

import "github.com/kperreau/wordsubgen"

Development Installation

For development work on the wordsubgen project itself:

git clone https://github.com/kperreau/wordsubgen.git
cd wordsubgen
make deps
make build

Or install the CLI to your GOPATH/bin:

make install  # Installs to GOPATH/bin

Requirements

  • Go 1.25.0+: Required for building from source
  • Make: Required for using the Makefile (optional, you can use go build directly)

Quick Start

CLI Usage

Generate subtitles from command line:

# Basic usage
wordsubgen --lines "Hello world|This is a test" --out subtitle.ass

# Custom styling
wordsubgen --lines "Hello world" --color "#FF0000" --fontsize 72 --delay 500

# Karaoke mode
wordsubgen --lines "Hello world" --karaoke --delay 400

# From file
wordsubgen --file input.txt --out subtitle.ass

# From structured JSON (words with start/end in seconds); --start-delay 1500 = 1.5 s shift
wordsubgen --json-file words.json --start-delay 1500 --out subtitle.ass

# Show help with current default values
wordsubgen --help

Library Usage

package main

import (
    "log"
    "github.com/kperreau/wordsubgen"
)

func main() {
    // Create configuration
    cfg := wordsubgen.DefaultConfig()
    cfg.FontSize = 48
    cfg.PrimaryColor = wordsubgen.ColorToASS("#FF0000")
    cfg.PerWordDelay = 500

    // Enable shadow with diagonal direction (more downward than sideways)
    cfg.ShadowEnabled = true
    cfg.ShadowX = 3  // Horizontal offset
    cfg.ShadowY = 8  // Vertical offset (more downward)

    // Generate ASS content
    lines := []string{"Hello world", "This is a test"}
    content, err := wordsubgen.GenerateASS(cfg, lines)
    if err != nil {
        log.Fatal(err)
    }

    // Write to file
    err = wordsubgen.WriteASS("output.ass", content, cfg.Logger)
    if err != nil {
        log.Fatal(err)
    }
}

Structured JSON Input

You can generate subtitles from a JSON structure with a segments array. Each segment is a phrase with a words array; each word has word, start and end (in seconds). Extra fields anywhere are ignored.

{
  "segments": [
    {
      "words": [
        {"word": "Découvrez", "start": 0.162, "end": 1.024},
        {"word": "cette", "start": 1.064, "end": 1.344},
        {"word": "superbe", "start": 1.404, "end": 1.845}
      ]
    },
    {
      "words": [
        {"word": "Deuxième", "start": 2.0, "end": 2.5},
        {"word": "phrase", "start": 2.6, "end": 3.0}
      ]
    }
  ]
}
  • StartDelay (e.g. --start-delay 1500): adds 1.5 seconds to every start and end so subtitles start 1.5 s later in the video.
  • PerWordDelay is ignored (timings come from the JSON).
  • FadeDuration is still used for the word fade-in effect.

CLI

wordsubgen --json-file words.json --start-delay 1500 --out subtitle.ass
wordsubgen --json '{"segments":[{"words":[{"word":"Hello","start":0,"end":0.5},{"word":"world","start":0.6,"end":1.2}]}]}' --out subtitle.ass

Library

data, _ := os.ReadFile("words.json")
phrases, err := wordsubgen.ParseStructuredJSON(data)
if err != nil {
    log.Fatal(err)
}
cfg := wordsubgen.DefaultConfig()
cfg.StartDelay = 1500 // 1.5 s shift
content, err := wordsubgen.GenerateASSFromStructured(cfg, phrases)
// then wordsubgen.WriteASS(...)

Flexible Logging Interface

wordsubgen features a flexible logging interface that allows you to inject your own logger implementation. The library comes with several built-in loggers and supports popular logging libraries like zerolog (recommended), logrus, and others.

Built-in Loggers

// Default logger (uses Go's standard log package)
cfg.Logger = wordsubgen.NewDefaultLogger()

// No-operation logger (silent, useful for tests)
cfg.Logger = wordsubgen.NewNoOpLogger()

Custom Logger Implementation

type MyCustomLogger struct{}

func (l *MyCustomLogger) Debug(msg string, fields ...wordsubgen.Field) {
    // Your debug implementation
}

func (l *MyCustomLogger) Info(msg string, fields ...wordsubgen.Field) {
    // Your info implementation
}

func (l *MyCustomLogger) Error(msg string, fields ...wordsubgen.Field) {
    // Your error implementation
}

func (l *MyCustomLogger) Warn(msg string, fields ...wordsubgen.Field) {
    // Your warning implementation
}

// Use your custom logger
cfg.Logger = &MyCustomLogger{}

Logging Features

  • Structured logging: Support for key-value fields
  • Multiple log levels: Debug, Info, Error, Warn
  • Zero dependencies: No forced logging library dependencies
  • Flexible: Easy to integrate with any logging system
  • zerolog support: Recommended for high-performance structured logging

Configuration Reference

Video Settings

Option Type Default Description
Width int 1080 Video width in pixels
Height int 1920 Video height in pixels

Font Settings

Option Type Default Description
FontName string "Arial" Font family name
FontSize int 64 Font size in points

Colors (ASS format: &H00BBGGRR)

Option Type Default Description
PrimaryColor string "&H00FFFFFF" Text color (white)
SecondaryColor string "&H0000FFFF" Karaoke highlight color (yellow)
OutlineColor string "&H00000000" Outline color (black)
BackColor string "&H64000000" Background color (semi-transparent)

Color Conversion: Use ColorToASS() to convert hex colors (#RRGGBB) to ASS format, and ColorToHex() to convert ASS colors back to hex format.

Style Settings

Option Type Default Description
Bold bool true Bold text
Italic bool false Italic text
Underline bool false Underlined text
StrikeOut bool false Strikethrough text

Scaling and Spacing

Option Type Default Description
ScaleX int 100 Horizontal scale (%)
ScaleY int 100 Vertical scale (%)
Spacing int 0 Character spacing
Angle int 0 Text rotation angle

Border and Shadow

Option Type Default Description
BorderStyle int 1 Border style (1=outline, 3=box)
Outline int 4 Outline width
Shadow int 0 Shadow width

Shadow Effects

Option Type Default Description
ShadowEnabled bool false Enable/disable shadow effect
ShadowX int 3 Horizontal shadow offset (-50 to 50)
ShadowY int 8 Vertical shadow offset (-50 to 50)

Shadow Direction: Positive values move the shadow right (X) and down (Y). Negative values move left and up. The default values (X=3, Y=8) create a diagonal shadow that's more downward than sideways.

Alignment

Option Type Default Description
Alignment int 2 Text alignment (1=bottom-left, 2=bottom-center, 3=bottom-right, etc.)

Margins

Option Type Default Description
MarginL int 40 Left margin
MarginR int 40 Right margin
MarginV int 120 Vertical margin

Timing Settings

Option Type Default Description
StartDelay int 0 Delay before starting subtitles (milliseconds). In structured JSON mode: added to all word start/end times (e.g. 1500 = 1.5 s shift).
PerWordDelay int 300 Delay between words (milliseconds). Ignored when using structured JSON (GenerateASSFromStructured).
FadeDuration int 140 Fade duration (milliseconds)
LineHold int 2000 Line hold duration (milliseconds). Not applied in structured JSON mode.
LineGap int 0 Gap between lines (milliseconds). Not applied in structured JSON mode.

Features

Option Type Default Description
Karaoke bool false Enable karaoke mode

Encoding

Option Type Default Description
Encoding int 1 Text encoding (1=ANSI, 0=Unicode)

CLI Reference

Note: All default values are dynamically generated from the DefaultConfig() function. Use --help to see the current default values.

Input Options (mutually exclusive)

  • --lines string: Input lines separated by | (e.g., 'Hello world|Second line')
  • --file string: Input file with lines (one per line)
  • --json string: Structured JSON: {"segments":[{"words":[{"word":"...","start":0.1,"end":0.5},...]},...]}. start/end in seconds. Extra fields ignored.
  • --json-file string: Path to a structured JSON file (same format as --json).

With --json or --json-file: --delay (PerWordDelay) is ignored; --start-delay shifts all word timings (e.g. --start-delay 1500 adds 1.5 s to every start/end).

Output Options

  • --out string: Output ASS file path (default: output.ass)

Video Settings

  • --width int: Video width (default: 1080)
  • --height int: Video height (default: 1920)

Font Settings

  • --font string: Font name (default: Arial)
  • --fontsize int: Font size (default: 64)

Colors (hex format)

  • --color string: Primary text color (default: #FFFFFF)
  • --secondary string: Secondary color for karaoke (default: #FFFF00)
  • --outline string: Outline color (default: #000000)
  • --background string: Background color in ASS format (default: #64000000)

Style Settings

  • --bold: Bold text (default: true)
  • --italic: Italic text (default: false)
  • --underline: Underline text (default: false)
  • --strikeout: Strikeout text (default: false)

Scaling

  • --scalex int: Horizontal scale % (default: 100)
  • --scaley int: Vertical scale % (default: 100)
  • --spacing int: Character spacing (default: 0)
  • --angle int: Text angle (default: 0)

Border and Shadow

  • --borderstyle int: Border style (default: 1)
  • --outlinewidth int: Outline width (default: 4)
  • --shadow int: Shadow width (default: 0)

Shadow Effects

  • --shadow-enabled: Enable shadow effect (default: false)
  • --shadow-x int: Horizontal shadow offset (default: 3)
  • --shadow-y int: Vertical shadow offset (default: 8)

Alignment

  • --alignment int: Text alignment (1=bottom-left, 2=bottom-center, 3=bottom-right, etc.) (default: 2)

Margins

  • --marginl int: Left margin (default: 40)
  • --marginr int: Right margin (default: 40)
  • --marginv int: Vertical margin (default: 120)

Timing

  • --start-delay int: Delay before starting subtitles in ms (default: 0)
  • --delay int: Delay between words in ms (default: 300)
  • --fade int: Fade duration in ms (default: 140)
  • --hold int: Line hold duration in ms (default: 2000)
  • --gap int: Gap between lines in ms (default: 0)

Features

  • --karaoke: Enable karaoke mode (default: false)

Other

  • --help: Show help with current default values

Examples

Basic Example

wordsubgen --lines "Hello world|This is a test" --out subtitle.ass

Custom Styling

wordsubgen --lines "Hello world" --fontsize 48 --color "#FF0000" --bold

Karaoke Mode

wordsubgen --lines "Hello world" --karaoke --delay 500

From File

echo -e "First line\nSecond line\nThird line" > input.txt
wordsubgen --file input.txt --out subtitle.ass

Shadow Effects

# Diagonal shadow (more downward than sideways)
wordsubgen --lines "Hello world" --shadow-enabled --shadow-x 3 --shadow-y 8

# Horizontal shadow (more sideways)
wordsubgen --lines "Hello world" --shadow-enabled --shadow-x 8 --shadow-y 3

# No shadow (explicitly disabled)
wordsubgen --lines "Hello world" --shadow-enabled=false

Start Delay

# Delay subtitles by 100ms
wordsubgen --lines "Hello world" --start-delay 100

# Delay subtitles by 1 second
wordsubgen --lines "Hello world" --start-delay 1000

# Delay subtitles by 2.5 seconds
wordsubgen --lines "Hello world" --start-delay 2500

Structured JSON

# From file with 1.5 s global delay
wordsubgen --json-file words.json --start-delay 1500 --out subtitle.ass

# Inline JSON (one phrase)
wordsubgen --json '{"segments":[{"words":[{"word":"Hello","start":0,"end":0.5},{"word":"world","start":0.6,"end":1.2}]}]}' --out subtitle.ass

Quick Demo with Makefile

# Generate sample files
make run-cli         # Basic sample
make run-cli-custom  # Custom styling
make run-cli-karaoke # Karaoke mode

Development

Project Structure

wordsubgen/
├── cmd/wordsubgen/
│   ├── main.go                    # CLI application entry point
│   └── wordsubgen                 # Built binary (after make build)
├── assets/                        # Assets
│   └── example.gif                # Demo animation
├── config.go                      # Configuration and validation
├── config_test.go                 # Configuration tests
├── generator.go                   # ASS generation logic
├── generator_test.go              # Generator tests
├── integration_test.go            # Integration tests
├── logger.go                      # Flexible logging interface
├── logger_test.go                 # Logger tests
├── writer.go                      # File I/O operations
├── writer_test.go                 # Writer tests
├── go.mod                         # Go module definition
├── go.sum                         # Go module checksums
├── Makefile                       # Build automation
├── README.md                      # Documentation
├── LICENSE                        # MIT License
├── input.txt                      # Sample input file
├── subs.exemple.ass               # Example subtitle file
└── subtitle.ass                   # Generated subtitle file

Building

make build          # Build CLI binary
make install        # Install CLI to GOPATH/bin
make clean          # Clean build artifacts

Testing

make test           # Run all tests
make test-coverage  # Run tests with coverage report
make check          # Run fmt, lint, and test

Running Examples

make run-cli         # Run CLI with sample input
make run-cli-custom  # Run CLI with custom styling
make run-cli-karaoke # Run CLI with karaoke mode

Code Quality

make fmt            # Format code
make lint           # Lint code
make deps           # Install dependencies

Development Workflow

make dev            # Complete dev workflow: deps, fmt, lint, test, build
make release        # Build release binaries for multiple platforms

Available Make Targets

make help           # Show all available targets

Testing

The project includes test coverage:

  • Unit tests: Individual component testing (*_test.go files)

Run tests with:

make test           # Run all tests
make test-coverage  # Generate coverage report
make ci-test        # Run all tests for ci

Usage Examples

Basic Library Usage with Custom Logger

package main

import (
    "log"
    "github.com/kperreau/wordsubgen"
)

func main() {
    // Create configuration with custom logger
    cfg := wordsubgen.DefaultConfig()
    cfg.Logger = wordsubgen.NewDefaultLogger() // or your custom logger
    
    // Generate subtitles
    lines := []string{"Hello world", "This is a test"}
    content, err := wordsubgen.GenerateASS(cfg, lines)
    if err != nil {
        log.Fatal(err)
    }
    
    // Write to file
    err = wordsubgen.WriteASS("output.ass", content, cfg.Logger)
    if err != nil {
        log.Fatal(err)
    }
}

CLI with Help

# Show help with current default values
wordsubgen --help

This will display all available options with their current default values, which are dynamically generated from the DefaultConfig() function.

ASS Format Details

The generated ASS files follow the Advanced SubStation Alpha v4.00+ format with:

  • Script Info: Basic metadata and video resolution
  • V4+ Styles: Font, color, and styling definitions
  • Events: Dialogue lines with timing and effects

Word Fade Effect

Each word uses the \alpha tag for fade effects:

{\alpha&HFF&\t(start,end,\alpha&H00&)}word

Where:

  • \alpha&HFF&: Start invisible (fully transparent)
  • \t(start,end,\alpha&H00&): Transition to visible over time
  • start,end: Timing in milliseconds

Karaoke Effect

When karaoke mode is enabled, words also include \k tags:

{\k100}word

Where 100 is the duration in centiseconds.

Shadow Effect

When shadow is enabled, text includes \xshad and \yshad tags:

{\xshad3\yshad8}word

Where:

  • \xshad3: Horizontal shadow offset of 3 pixels
  • \yshad8: Vertical shadow offset of 8 pixels

This creates a diagonal shadow that's more downward than sideways, as requested in the original specification.

Key Benefits

  • 🎯 Simple Structure: Clean, flat project structure with all source files at the root
  • 🔌 Flexible Logging: Injectable logger interface supporting multiple logging libraries
  • ⚡ Zero Dependencies: Core library has no forced external dependencies
  • 📝 Structured Logging: Built-in support for structured logging with key-value fields
  • 🛠️ Easy Integration: Simple API that works with any Go project
  • 🎨 Rich Configuration: Extensive customization options for fonts, colors, timing, and styling
  • 📊 Performance: Optimized for performance with zero-allocation logging options
  • 🔧 Developer Friendly: Comprehensive CLI with verbose logging and helpful error messages
  • 🔄 Consistent Defaults: CLI flags automatically use configuration defaults, ensuring consistency
  • 🎨 Color Conversion: Built-in functions to convert between hex and ASS color formats
  • 🌍 Encoding Support: Built-in support for different text encodings (ANSI/Unicode)

License

This project is licensed under the MIT License - see the LICENSE file for details.

Contributing

We welcome contributions! Please follow these steps:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feat/amazing-feature)
  3. Make your changes
  4. Add tests if applicable
  5. Run the test suite (make test)
  6. Commit your changes (git commit -m 'feat: add some amazing feature')
  7. Push to the branch (git push origin feat/amazing-feature)
  8. Open a Pull Request

Development Guidelines

  • Follow Go best practices and conventions
  • Add tests for new functionality
  • Update documentation as needed
  • Use the provided Makefile targets for consistency
  • Ensure all tests pass before submitting

About

A Go library and CLI tool for generating ASS (Advanced SubStation Alpha) subtitle files with word-by-word fade-in effects. Perfect for creating dynamic subtitles where words appear sequentially with smooth fade transitions.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published