An open API service indexing awesome lists of open source software.

https://github.com/kperreau/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.
https://github.com/kperreau/wordsubgen

advanced-substation-alpha ass cli fade-in go golang karaoke ssa subtitles text-animation word-by-word

Last synced: 14 days ago
JSON representation

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.

Awesome Lists containing this project

README

          

# 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
- **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](assets/example.gif)

This demo was generated using the following command:

```bash
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:

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

### Package Installation (for Go projects)

To use wordsubgen as a library in your Go project:

```bash
go get github.com/kperreau/wordsubgen@latest
```

Then import it in your Go code:
```go
import "github.com/kperreau/wordsubgen"
```

### Development Installation

For development work on the wordsubgen project itself:

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

Or install the CLI to your GOPATH/bin:
```bash
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:

```bash
# 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

# Show help with current default values
wordsubgen --help
```

### Library Usage

```go
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)
}
}
```

## 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

```go
// 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

```go
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 |
|--------|------|---------|-------------|
| `PerWordDelay` | int | 300 | Delay between words (milliseconds) |
| `FadeDuration` | int | 140 | Fade duration (milliseconds) |
| `LineHold` | int | 2000 | Line hold duration (milliseconds) |
| `LineGap` | int | 0 | Gap between lines (milliseconds) |

### 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

- `--lines string`: Input lines separated by `|` (e.g., 'Hello world|Second line')
- `--file string`: Input file with lines (one per line)

### 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

- `--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

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

### Custom Styling

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

### Karaoke Mode

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

### From File

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

### Shadow Effects

```bash
# 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
```

### Quick Demo with Makefile

```bash
# 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

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

### Testing

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

### Running Examples

```bash
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

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

### Development Workflow

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

### Available Make Targets

```bash
make help # Show all available targets
```

### Testing

The project includes test coverage:

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

Run tests with:
```bash
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

```go
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

```bash
# 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](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