fix: preserve stream language metadata using ffprobe and -map_metadata -1
This commit is contained in:
@@ -30,6 +30,54 @@ type ProbeResult struct {
|
||||
Streams []StreamInfo `json:"streams"`
|
||||
}
|
||||
|
||||
type StreamMetadata struct {
|
||||
Index int `json:"index"`
|
||||
CodecType string `json:"codec_type"`
|
||||
CodecName string `json:"codec_name"`
|
||||
Language string `json:"tags"`
|
||||
Title string `json:"title"`
|
||||
}
|
||||
|
||||
func (e *Encoder) GetStreamLanguages(path string) ([]StreamMetadata, error) {
|
||||
cmd := exec.Command(e.ffprobePath, "-v", "error", "-show_streams", "-print_format", "json", path)
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ffprobe error: %w", err)
|
||||
}
|
||||
|
||||
var result struct {
|
||||
Streams []struct {
|
||||
Index int `json:"index"`
|
||||
CodecType string `json:"codec_type"`
|
||||
CodecName string `json:"codec_name"`
|
||||
Tags map[string]string `json:"tags"`
|
||||
} `json:"streams"`
|
||||
}
|
||||
if err := json.Unmarshal(output, &result); err != nil {
|
||||
return nil, fmt.Errorf("parsing ffprobe: %w", err)
|
||||
}
|
||||
|
||||
var streams []StreamMetadata
|
||||
for _, s := range result.Streams {
|
||||
lang := ""
|
||||
title := ""
|
||||
if s.Tags != nil {
|
||||
lang = s.Tags["language"]
|
||||
title = s.Tags["title"]
|
||||
}
|
||||
streams = append(streams, StreamMetadata{
|
||||
Index: s.Index,
|
||||
CodecType: s.CodecType,
|
||||
CodecName: s.CodecName,
|
||||
Language: lang,
|
||||
Title: title,
|
||||
})
|
||||
}
|
||||
|
||||
fmt.Printf("DEBUG stream languages: %+v\n", streams)
|
||||
return streams, nil
|
||||
}
|
||||
|
||||
func New() *Encoder {
|
||||
return &Encoder{
|
||||
ffmpegPath: "ffmpeg",
|
||||
@@ -131,7 +179,7 @@ func (e *Encoder) calculateZscaleWidth(path string, originalHeight int) (string,
|
||||
return zscale, newWidth, nil
|
||||
}
|
||||
|
||||
func (e *Encoder) Transcode(input string, job *types.Job, metadata *types.Metadata, interlaced bool) error {
|
||||
func (e *Encoder) Transcode(input string, job *types.Job, metadata *types.Metadata, interlaced bool, streamLangs []StreamMetadata) error {
|
||||
dir := filepath.Dir(input)
|
||||
audioWavs, err := e.extractAudio(input, dir)
|
||||
if err != nil {
|
||||
@@ -145,7 +193,7 @@ func (e *Encoder) Transcode(input string, job *types.Job, metadata *types.Metada
|
||||
}
|
||||
defer e.cleanupOpus(opusFiles)
|
||||
|
||||
if err := e.encodeVideo(input, opusFiles, job, metadata, interlaced); err != nil {
|
||||
if err := e.encodeVideo(input, opusFiles, job, metadata, interlaced, streamLangs); err != nil {
|
||||
return fmt.Errorf("encoding video: %w", err)
|
||||
}
|
||||
|
||||
@@ -177,7 +225,7 @@ func (e *Encoder) encodeOpus(wavs []string, dir string) ([]string, error) {
|
||||
return opusFiles, nil
|
||||
}
|
||||
|
||||
func (e *Encoder) encodeVideo(input string, opusFiles []string, job *types.Job, metadata *types.Metadata, interlaced bool) error {
|
||||
func (e *Encoder) encodeVideo(input string, opusFiles []string, job *types.Job, metadata *types.Metadata, interlaced bool, streamLangs []StreamMetadata) error {
|
||||
dir := filepath.Dir(input)
|
||||
outFile := filepath.Join(dir, "output.mkv")
|
||||
|
||||
@@ -221,12 +269,30 @@ func (e *Encoder) encodeVideo(input string, opusFiles []string, job *types.Job,
|
||||
|
||||
args = append(args, "-c:a", "copy")
|
||||
args = append(args, "-c:s", "copy")
|
||||
args = append(args, "-map_metadata", "0")
|
||||
args = append(args, "-map_metadata", "-1")
|
||||
|
||||
args = append(args, "-metadata", fmt.Sprintf("TITLE=%s", metadata.Title))
|
||||
args = append(args, "-metadata", fmt.Sprintf("DATE_RELEASED=%s", metadata.DateReleased))
|
||||
args = append(args, "-metadata", fmt.Sprintf("IMDBID=%s", metadata.IMDBID))
|
||||
args = append(args, "-metadata", fmt.Sprintf("ORIGINAL_MEDIA_TYPE=%s", metadata.OriginalMedia))
|
||||
for _, stream := range streamLangs {
|
||||
if stream.CodecType == "audio" && stream.Language != "" {
|
||||
idx := 1
|
||||
for _, s := range streamLangs {
|
||||
if s.CodecType == "audio" && s.Index < stream.Index {
|
||||
idx++
|
||||
}
|
||||
}
|
||||
args = append(args, fmt.Sprintf("-metadata:s:a:%d", idx-1), fmt.Sprintf("language=%s", stream.Language))
|
||||
fmt.Printf("DEBUG setting audio language: stream %d -> language=%s\n", idx-1, stream.Language)
|
||||
}
|
||||
if stream.CodecType == "subtitle" && stream.Language != "" {
|
||||
idx := 0
|
||||
for _, s := range streamLangs {
|
||||
if s.CodecType == "subtitle" && s.Index < stream.Index {
|
||||
idx++
|
||||
}
|
||||
}
|
||||
args = append(args, fmt.Sprintf("-metadata:s:s:%d", idx), fmt.Sprintf("language=%s", stream.Language))
|
||||
fmt.Printf("DEBUG setting subtitle language: stream %d -> language=%s\n", idx, stream.Language)
|
||||
}
|
||||
}
|
||||
|
||||
if metadata.IsSeries {
|
||||
args = append(args, "-metadata", fmt.Sprintf("COLLECTION=%s", metadata.Collection))
|
||||
|
||||
Reference in New Issue
Block a user