show-rss/src/asses/deport.go

226 lines
4.6 KiB
Go

package asses
import (
"bytes"
"context"
"fmt"
"log"
"os"
"os/exec"
"path"
"path/filepath"
"regexp"
"slices"
"strings"
)
func Entrypoint(ctx context.Context, p string) error {
return deport(ctx, p)
}
func deport(ctx context.Context, p string) error {
if os.Getenv("NO_DEPORT") != "" {
log.Printf("would deport %s", p)
return nil
}
assStreams, err := assStreams(ctx, p)
if err != nil {
return err
}
assStreamIDs := make([]string, len(assStreams))
for i, stream := range assStreams {
assStreamIDs[i] = stream.id
assF := path.Join(
path.Dir(p),
fmt.Sprintf(
".%s.%s.%s.ass",
path.Base(p),
stream.id,
stream.title,
),
)
if err := ffmpeg(ctx, "-y", "-i", p, "-map", stream.id, assF); err != nil {
return fmt.Errorf("failed to pull %s from %s: %w", stream.id, p, err)
}
}
if err := BestAssToSRT(ctx, p); err != nil {
return err
}
base := path.Base(p)
withoutExt := strings.TrimSuffix(base, path.Ext(base))
p2 := path.Join(path.Dir(p), fmt.Sprintf("%s.subless.mkv", withoutExt))
args := []string{
"-i", p,
"-map", "0",
}
for _, assStream := range assStreams {
args = append(args, "-map", "-"+assStream.id)
}
args = append(args,
"-c", "copy",
p2,
)
if err := ffmpeg(ctx, args...); err != nil {
return err
} else if err := os.Rename(p2, p); err != nil {
return err
}
return nil
}
type stream struct {
id string
title string
}
func assStreams(ctx context.Context, p string) ([]stream, error) {
output, err := ffprobe(ctx, "-i", p)
if err != nil {
return nil, err
}
result := []stream{}
for _, line := range strings.Split(output, "\n") {
fields := strings.Fields(line)
if len(fields) < 3 {
continue
} else if fields[0] != "Stream" {
continue
} else if !strings.Contains(fields[1], "(") {
continue
} else if fields[2] != "Subtitle:" {
continue
} else if fields[3] != "ass" {
continue
}
field1 := fields[1]
id := strings.Trim(strings.Split(field1, "(")[0], "#")
title := strings.Trim(strings.Split(field1, "(")[1], "):")
result = append(result, stream{
id: id,
title: title,
})
}
return result, nil
}
func ffprobe(ctx context.Context, args ...string) (string, error) {
return execc(ctx, "ffprobe", args...)
}
func ffmpeg(ctx context.Context, args ...string) error {
std, err := execc(ctx, "ffmpeg", args...)
if err != nil {
return fmt.Errorf("(%w) %s", err, std)
}
return nil
}
func execc(ctx context.Context, bin string, args ...string) (string, error) {
stdout := bytes.NewBuffer(nil)
cmd := exec.CommandContext(ctx, bin, args...)
cmd.Stdin = nil
cmd.Stderr = stdout
cmd.Stdout = stdout
err := cmd.Run()
return string(stdout.Bytes()), err
}
func BestAssToSRT(ctx context.Context, p string) error {
asses, err := filepath.Glob(path.Join(
path.Dir(p),
fmt.Sprintf(".%s.*.ass", path.Base(p)),
))
if err != nil {
return err
}
srts := []string{}
for _, ass := range asses {
srt, err := assToSRT(ctx, ass)
if err != nil {
return err
}
srts = append(srts, srt)
}
srts = SRTsByGoodness(srts)
for i := range srts {
if i == 0 {
base := path.Base(p)
withoutExt := strings.TrimSuffix(base, path.Ext(base))
srt := path.Join(path.Dir(p), fmt.Sprintf("%s.srt", withoutExt))
if err := os.Rename(srts[i], srt); err != nil {
return err
}
} else {
os.Remove(srts[i])
}
}
return nil
}
func assToSRT(ctx context.Context, ass string) (string, error) {
srt := fmt.Sprintf("%s.srt", strings.TrimSuffix(ass, ".ass"))
if _, err := os.Stat(srt); err == nil {
return srt, nil
}
if err := ffmpeg(ctx, "-y", "-i", ass, srt); err != nil {
if ctx.Err() == nil {
log.Printf("ffmpeg failed to process %s; removing", ass)
os.Remove(ass)
}
return srt, err
}
b, err := os.ReadFile(srt)
if err != nil {
return srt, err
}
before := len(b)
b = regexp.MustCompile(`size="[^"]*"`).ReplaceAll(b, []byte{})
if after := len(b); before == after {
} else if err := os.WriteFile(srt, b, os.ModePerm); err != nil {
return srt, err
}
return srt, nil
}
func SRTsByGoodness(srts []string) []string {
/*
1 lat.*amer Lat.*Amer \
2 signs \
3 rus Rus \
4 por Por \
5 ita Ita \
6 fre Fre \
7 spa Spa \
8 ger Ger \
9 ara Ara \
10 jpn Jpn \
11 Europ \
12 Brazil \
13 Deu \
*/
panic("NOT IMPL")
srts = slices.Clone(srts)
slices.SortFunc(srts, func(a, b string) int {
// if skip a { return 1 }
// if skip b { return -1 }
// return -1 * (wc(a) - wc(b))
return strings.Compare(a, b)
})
return srts
}