go-astisub

repository·master·Indexed 20 days ago

https://github.com/asticode/go-astisub

A Golang library and CLI tool for manipulating subtitle formats including SRT, TTML, VTT, STL, SSA/ASS, and Teletext. It provides functionality for parsing, writing, syncing, fragmenting, merging, and optimizing subtitles, as well as applying linear corrections to subtitle durations.

Tokens
7.7K
Snippets
28
Records
37
Agent score
67%

What's inside go-astisub

  1. Install the astisub library or CLI

    master

    You can install the library as a Go module for use in your projects, or install the standalone CLI tool for command-line subtitle manipulation.

    To install the library:

    go get github.com/asticode/go-astisub

    To install the CLI:

    go install github.com/asticode/go-astisub/cmd/astisub
    go get github.com/asticode/go-astisub
  2. Use the astisub library in your Go code

    master

    The library provides a programmatic way to parse, manipulate, and write various subtitle formats including srt, stl, ttml, ssa/ass, webvtt, and teletext.

    Note: The example below omits error handling for readability; you must handle errors in production code.

    // Open subtitles
    s1, _ := astisub.OpenFile("/path/to/example.ttml")
    s2, _ := astisub.ReadFromSRT(bytes.NewReader([]byte("1\n00:01:00.000 --> 00:02:00.000\nCredits")))
    
    // Add a duration to every subtitles (syncing)
    s1.Add(-2*time.Second)
    
    // Fragment the subtitles
    s1.Fragment(2*time.Second)
    
    // Merge subtitles
    s1.Merge(s2)
    
    // Optimize subtitles
    s1.Optimize()
    
    // Unfragment the subtitles
    s1.Unfragment()
    
    // Apply linear correction
    s1.ApplyLinearCorrection(1*time.Second, 2*time.Second, 5*time.Second, 7*time.Second)
    
    // Write subtitles
    s1.Write("/path/to/example.srt")
    var buf = &bytes.Buffer{}
    s2.WriteToTTML(buf)
  3. Understand STL metadata and character tables

    master

    STL files contain a GSI block that defines global metadata. go-astisub maps these to the Subtitles.Metadata field. Key metadata fields include:

    • Framerate: The video framerate.
    • STLCountryOfOrigin: The country code (e.g., stlCountryCodeFrance).
    • STLDisplayStandardCode: Indicates if the file uses Open Subtitling or Teletext (Level 1/2).
    • Language: Mapped from STL language codes (e.g., stlLanguageCodeEnglish).
    • STLTimecodeStartOfProgramme: The starting timecode offset.

    The parser uses specific character code tables (e.g., stlCharacterCodeTableNumberLatin) to decode text correctly based on the GSI block's specification.

  4. Handle WebVTT X-TIMESTAMP-MAP metadata

    master

    WebVTT supports an X-TIMESTAMP-MAP header used for syncing cue times with MPEG-TS streams. The library parses this into Metadata.WebVTTTimestampMap.

    You can use the Offset() method on a WebVTTTimestampMap to calculate the time offset between the local WebVTT time and the MPEG-TS time.

    // Example of calculating offset from a timestamp map
    if subtitles.Metadata != nil && subtitles.Metadata.WebVTTTimestampMap != nil {
    	offset := subtitles.Metadata.WebVTTTimestampMap.Offset()
    	fmt.Printf("Time offset: %v\n", offset)
    }
  5. How Subtitles, Items, and Lines are structured

    master

    The library uses a hierarchical model to represent subtitles:

    1. Subtitles: The top-level container. It holds a slice of Items, global Metadata, Regions, and Styles.
    2. Item: Represents a single subtitle cue shown between a StartAt and EndAt time. It contains Lines and can be associated with a Region or a Style.
    3. Line: A collection of LineItems. This allows for complex formatting within a single subtitle cue.
    4. LineItem: The smallest unit of text, which can have its own InlineStyle (e.g., a single word in a different color).
  6. How TTML duration formats work

    master

    The TTML parser supports several ways to represent time and duration within the begin and end attributes. The TTMLInDuration type implements UnmarshalText to handle these formats:

    1. Standard Clock Time: hh:mm:ss.mmm (e.g., 00:01:23.456)
    2. Clock Time with Frames: hh:mm:ss:fff where fff represents frames (e.g., 00:01:23:12)
    3. Offset Time: A numeric value followed by a metric:
      • h: hours
      • m: minutes
      • s: seconds
      • ms: milliseconds
      • f: frames
      • t: ticks (e.g., 100t)

    Note: When using frames (f) or ticks (t), the resulting duration calculation depends on the frameRate or tickRate attributes defined in the TTML root element.

  7. Configure STL parsing options

    master

    The STLOptions struct provides control over how the GSI block's timecode information is applied to the resulting subtitle items.

    • IgnoreTimecodeStartOfProgramme (bool): When true, the parser will not use the timecodeStartOfProgramme from the GSI block to offset the StartAt and EndAt times of the subtitle items. By default, this is false.
    opts := astisub.STLOptions{
    	IgnoreTimecodeStartOfProgramme: true,
    }
  8. Configure TeletextOptions

    master

    The TeletextOptions struct is used to control the behavior of the Teletext parser.

    FieldTypeDescription
    PageintThe specific Teletext page number to extract.
    PIDintThe Packet Identifier for the Teletext stream. If set to a value > 0, the library will use this PID instead of attempting to detect it from the PMT.
    type TeletextOptions struct {
    	Page int
    	PID  int
    }
  9. Use the astisub CLI

    master

    The astisub CLI allows you to perform common subtitle operations directly from your terminal. Supported operations include conversion, linear correction, fragmentation, merging, optimization, unfragmenting, and syncing.

    astisub convert -i example.srt -o example.ttml
    
    astisub apply-linear-correction -i example.srt -a1 1s -d1 2s -a2 5s -d2 7s -o example.out.srt
    
    astisub fragment -i example.srt -f 2s -o example.out.srt
    
    astisub merge -i example.srt -i example.ttml -o example.out.srt
    
    astisub optimize -i example.srt -o example.out.srt
    
    astisub unfragment -i example.srt -o example.out.srt
    
    astisub sync -i example.srt -s "-2s" -o example.out.srt
  10. Write subtitles to SRT format with WriteToSRT

    master

    Use the WriteToSRT method on a Subtitles instance to export the subtitle data to an io.Writer in .srt format.

    Note:

    • If the Subtitles object contains no items, it returns ErrNoSubtitlesToWrite.
    • The output includes a BOM header.
    • It preserves inline styles like bold, italics, underline, color, and position (using {\anX} tags).
    package main
    
    import (
    	"os"
    	"github.com/asticode/asticode/go-astisub"
    )
    
    func main() {
    	subtitles := astisub.NewSubtitles()
    	// ... populate subtitles ...
    
    	f, err := os.Create("output.srt")
    	if err != nil {
    		panic(err)
    	}
    	defer f.Close()
    
    	err = subtitles.WriteToSRT(f)
    	if err != nil {
    		panic(err)
    	}
    }
  11. Manipulate subtitle timing and fragments

    master

    The Subtitles type provides several methods to adjust the timing and structure of subtitle items:

    • Add(d time.Duration): Shifts all subtitle start and end times by the specified duration. If a shift results in a time $\le 0$, the item is removed or clamped to 0.
    • Fragment(f time.Duration): Breaks subtitles into smaller pieces of a fixed duration f. This is useful for creating segments for streaming or specific playback requirements.
    • Unfragment(): Recombines adjacent subtitle items that have the same text and overlapping/consecutive timing.
    • ApplyLinearCorrection(actual1, desired1, actual2, desired2 time.Duration): Performs a linear time shift (resync) based on two known reference points (e.g., mapping two actual timestamps to two desired timestamps).
    // Shift all subtitles forward by 2 seconds
    subs.Add(2 * time.Second)
    
    // Fragment subtitles into 5-second chunks
    subs.Fragment(5 * time.Second)
    
    // Resync subtitles using two points
    subs.ApplyLinearCorrection(time.Second*10, time.Second*12, time.Second*20, time.Second*22)
  12. Parse STL subtitles with ReadFromSTL

    master

    Use ReadFromSTL to parse an .stl file from an io.Reader. This function handles the GSI (General Subtitle Information) block and all subsequent TTI (Text and Timing Information) blocks. It populates a Subtitles object containing metadata and subtitle items.

    Options

    STLOptions allows you to configure the parser:

    • IgnoreTimecodeStartOfProgramme: If set to true, the STLTimecodeStartOfProgramme in the metadata will not be updated from the GSI block value.
    import (
    	"os"
    	"github.com/asticode/go-astisub"
    )
    
    func main() {
    	f, err := os.Open("subtitles.stl")
    	if err != nil {
    		return
    	}
    	defer f.Close()
    
    	opts := astisub.STLOptions{
    		IgnoreTimecodeStartOfProgramme: false,
    	}
    
    	subtitles, err := astisub.ReadFromSTL(f, opts)
    	if err != nil {
    		// handle error
    	}
    	// use subtitles
    }