Skip to content

Instantly share code, notes, and snippets.

@nimatrueway
Last active September 6, 2025 06:11
Show Gist options
  • Select an option

  • Save nimatrueway/4589700f49c691e5413c5b2df4d02f4f to your computer and use it in GitHub Desktop.

Select an option

Save nimatrueway/4589700f49c691e5413c5b2df4d02f4f to your computer and use it in GitHub Desktop.

Revisions

  1. nimatrueway revised this gist Mar 22, 2018. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion subtitle-overlap-fixer.go
    Original file line number Diff line number Diff line change
    @@ -113,7 +113,7 @@ func main() {
    }
    // skip over super-short subtitles that basically contain what their previous subtitle contains, and just prolong previous subtitle
    if subtitle.toTime - subtitle.fromTime < time.Millisecond * 150 &&
    strings.Contains(strings.Trim(lastSubtitle.text, "\n "), strings.Trim(subtitle.text, "\n ")) {
    strings.Contains(lastSubtitle.text, subtitle.text) {
    lastSubtitle.toTime = subtitle.toTime
    continue
    }
  2. nimatrueway revised this gist Mar 22, 2018. No changes.
  3. nimatrueway revised this gist Mar 22, 2018. No changes.
  4. nimatrueway revised this gist Mar 22, 2018. No changes.
  5. nimatrueway revised this gist Mar 22, 2018. 1 changed file with 23 additions and 3 deletions.
    26 changes: 23 additions & 3 deletions subtitle-overlap-fixer.go
    Original file line number Diff line number Diff line change
    @@ -9,6 +9,7 @@ import (
    "os"
    "errors"
    "io"
    "strings"
    )

    type Subtitle struct {
    @@ -105,8 +106,27 @@ func main() {
    for {
    subtitle, err := readOneSubtitle(scanner)
    if lastSubtitle != nil {
    if subtitle != nil && subtitle.fromTime < lastSubtitle.toTime {
    lastSubtitle.toTime = subtitle.fromTime - time.Millisecond
    if subtitle != nil {
    subtitle.text = strings.Trim(subtitle.text, "\n ")
    if len(subtitle.text) == 0 { // skip over empty subtitles
    continue
    }
    // skip over super-short subtitles that basically contain what their previous subtitle contains, and just prolong previous subtitle
    if subtitle.toTime - subtitle.fromTime < time.Millisecond * 150 &&
    strings.Contains(strings.Trim(lastSubtitle.text, "\n "), strings.Trim(subtitle.text, "\n ")) {
    lastSubtitle.toTime = subtitle.toTime
    continue
    }
    // if first-line of current subtitle is repeating last-line of previous-subtitle remove it
    currentLines := strings.Split(subtitle.text, "\n")
    lastLines := strings.Split(lastSubtitle.text, "\n")
    if currentLines[0] == lastLines[len(lastLines)-1] {
    subtitle.text = strings.Join(currentLines[1:], "\n")
    }
    // if first-line of current subtitle is repeating last-line of previous-subtitle remove it
    if subtitle.fromTime < lastSubtitle.toTime {
    lastSubtitle.toTime = subtitle.fromTime - time.Millisecond
    }
    }
    writeOneSubtitle(newFile, lastSubtitle, &newIdx)
    }
    @@ -121,4 +141,4 @@ func main() {

    os.Rename(filePath, filePath + ".bak")
    os.Rename(newFilePath, filePath)
    }
    }
  6. nimatrueway revised this gist Mar 2, 2018. 1 changed file with 9 additions and 5 deletions.
    14 changes: 9 additions & 5 deletions subtitle-overlap-fixer.go
    Original file line number Diff line number Diff line change
    @@ -47,7 +47,8 @@ func readOneSubtitle(scanner *bufio.Scanner) (*Subtitle, error) {
    if !scanner.Scan() {
    return nil, nil
    }
    idx, err := strconv.Atoi(scanner.Text())
    idxRaw := scanner.Text()
    idx, err := strconv.Atoi(idxRaw)
    if err != nil {
    return nil, errors.New("invalid subtitle index")
    }
    @@ -74,11 +75,13 @@ func readOneSubtitle(scanner *bufio.Scanner) (*Subtitle, error) {
    return subtitle, nil
    }

    func writeOneSubtitle(file io.Writer, subtitle *Subtitle) (int, error) {
    return fmt.Fprint(file,
    subtitle.idx, "\n",
    func writeOneSubtitle(file io.Writer, subtitle *Subtitle, idx *int) error {
    _, err := fmt.Fprint(file,
    *idx, "\n",
    printDuration(subtitle.fromTime), " --> ", printDuration(subtitle.toTime), "\n",
    subtitle.text, "\n\n")
    *idx++
    return err
    }

    func main() {
    @@ -96,6 +99,7 @@ func main() {
    defer newFile.Close()

    scanner := bufio.NewScanner(file)
    var newIdx = 1
    var lastSubtitle *Subtitle = nil

    for {
    @@ -104,7 +108,7 @@ func main() {
    if subtitle != nil && subtitle.fromTime < lastSubtitle.toTime {
    lastSubtitle.toTime = subtitle.fromTime - time.Millisecond
    }
    writeOneSubtitle(newFile, lastSubtitle)
    writeOneSubtitle(newFile, lastSubtitle, &newIdx)
    }
    if subtitle == nil {
    break
  7. nimatrueway created this gist Mar 2, 2018.
    120 changes: 120 additions & 0 deletions subtitle-overlap-fixer.go
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,120 @@
    package main

    import (
    "time"
    "regexp"
    "bufio"
    "strconv"
    "fmt"
    "os"
    "errors"
    "io"
    )

    type Subtitle struct {
    idx int
    fromTime time.Duration
    toTime time.Duration
    text string
    }

    var timeFramePattern, _ = regexp.Compile(`(\d+):(\d+):(\d+),(\d+) --> (\d+):(\d+):(\d+),(\d+)`)

    func getDuration(parts []string) time.Duration {
    hour, _ := strconv.Atoi(parts[0])
    minute, _ := strconv.Atoi(parts[1])
    second, _ := strconv.Atoi(parts[2])
    millisecond, _ := strconv.Atoi(parts[3])
    return time.Millisecond * time.Duration(millisecond) +
    time.Second * time.Duration(second) +
    time.Minute * time.Duration(minute) +
    time.Hour * time.Duration(hour)
    }

    func printDuration(duration time.Duration) string {
    hour := duration / time.Hour
    duration -= hour * time.Hour
    minute := duration / time.Minute
    duration -= minute * time.Minute
    second := duration / time.Second
    duration -= second * time.Second
    millisecond := duration / time.Millisecond
    return fmt.Sprintf(`%02d:%02d:%02d,%03d`, hour, minute, second, millisecond)
    }

    func readOneSubtitle(scanner *bufio.Scanner) (*Subtitle, error) {
    // read idx
    if !scanner.Scan() {
    return nil, nil
    }
    idx, err := strconv.Atoi(scanner.Text())
    if err != nil {
    return nil, errors.New("invalid subtitle index")
    }
    // read timing
    if !scanner.Scan() {
    return nil, errors.New("could not find subtitle timing")
    }
    timing := timeFramePattern.FindStringSubmatch(scanner.Text())
    if timing == nil {
    return nil, errors.New("invalid subtitle timing")
    }
    fromTime := getDuration(timing[1:5])
    toTime := getDuration(timing[5:9])
    // read content
    if !scanner.Scan() {
    return nil, errors.New("could not find subtitle text")
    }
    content := scanner.Text()
    for scanner.Scan() && scanner.Text() != "" {
    content += "\n"
    content += scanner.Text()
    }
    subtitle := &Subtitle{idx, fromTime, toTime, content}
    return subtitle, nil
    }

    func writeOneSubtitle(file io.Writer, subtitle *Subtitle) (int, error) {
    return fmt.Fprint(file,
    subtitle.idx, "\n",
    printDuration(subtitle.fromTime), " --> ", printDuration(subtitle.toTime), "\n",
    subtitle.text, "\n\n")
    }

    func main() {
    if len(os.Args) < 2 {
    println("Provide a subtitle file to fix.\ne.g. subtitle-fixer mysubtitle.srt")
    return
    }

    filePath := os.Args[1]
    newFilePath := filePath + ".fixed"

    file, _ := os.Open(filePath)
    newFile, _ := os.Create(newFilePath)
    defer file.Close()
    defer newFile.Close()

    scanner := bufio.NewScanner(file)
    var lastSubtitle *Subtitle = nil

    for {
    subtitle, err := readOneSubtitle(scanner)
    if lastSubtitle != nil {
    if subtitle != nil && subtitle.fromTime < lastSubtitle.toTime {
    lastSubtitle.toTime = subtitle.fromTime - time.Millisecond
    }
    writeOneSubtitle(newFile, lastSubtitle)
    }
    if subtitle == nil {
    break
    }
    if err != nil {
    panic(err)
    }
    lastSubtitle = subtitle
    }

    os.Rename(filePath, filePath + ".bak")
    os.Rename(newFilePath, filePath)
    }