Skip to content

Commit

Permalink
streamer/audio: ProbeText now also returns the 'comment' tag
Browse files Browse the repository at this point in the history
Also implemented a WriteMetadata that writes the given songs metadata
to the file given and returns the new file in-memory.
  • Loading branch information
Wessie committed Jun 21, 2024
1 parent fe8f7f1 commit 17de34c
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 1 deletion.
21 changes: 20 additions & 1 deletion streamer/audio/ffprobe.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"bytes"
"context"
"io"
"os"
"os/exec"
"regexp"
"strconv"
Expand Down Expand Up @@ -53,13 +54,14 @@ type Info struct {
Title string
Artist string
Album string
Comment string
Bitrate int
}

var ffprobeTextArgs = []string{
"-loglevel", "fatal",
"-hide_banner",
"-show_entries", "format_tags=title,artist,album:stream_tags=title,artist,album:stream=duration,bit_rate:format=format_name,bit_rate",
"-show_entries", "format_tags=title,artist,album,comment:stream_tags=title,artist,album,comment:stream=duration,bit_rate:format=format_name,bit_rate",
"-of", "default=noprint_wrappers=1",
}

Expand All @@ -77,6 +79,21 @@ func ProbeText(ctx context.Context, filename string) (*Info, error) {
return parseProbeText(ctx, bytes.NewReader(out))
}

func probeText(ctx context.Context, file *os.File) (*Info, error) {
const op errors.Op = "streamer/audio.Probe"

cmd := exec.CommandContext(ctx, "ffprobe",
append(ffprobeTextArgs, "-i", "-")...)
cmd.Stdin = file

out, err := cmd.Output()
if err != nil {
return nil, errors.E(op, err, errors.Info(cmd.String()))
}

return parseProbeText(ctx, bytes.NewReader(out))
}

func parseProbeText(ctx context.Context, out io.Reader) (*Info, error) {
const op errors.Op = "streamer/audio.parseProbeText"
var err error
Expand Down Expand Up @@ -107,6 +124,8 @@ func parseProbeText(ctx context.Context, out io.Reader) (*Info, error) {
info.Artist = value
case "album":
info.Album = value
case "comment":
info.Comment = value
case "bit_rate":
if value != "N/A" { // could not exist
info.Bitrate, err = strconv.Atoi(value)
Expand Down
71 changes: 71 additions & 0 deletions streamer/audio/metadata.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package audio

import (
"context"
"io"
"os/exec"
"path/filepath"
"strings"

radio "github.com/R-a-dio/valkyrie"
"github.com/R-a-dio/valkyrie/errors"
"github.com/spf13/afero"
)

// DeleteID3Tags runs `id3v2 --delete-all <filename>` this removes any id3 tags
// in the file.
func DeleteID3Tags(ctx context.Context, filename string) {
exec.CommandContext(ctx, "id3v2", "--delete-all", filename)
}

// WithMetadata puts the metadata of song into the file given by filename and returns
// it as an in-memory file.
func WriteMetadata(ctx context.Context, f afero.File, song radio.Song) (*MemoryBuffer, error) {
const op errors.Op = "streamer/audio.WriteMetadata"

args := []string{
"-hide_banner",
"-loglevel", "error",
"-i", "-",
"-c:a", "copy", // copy audio stream
"-id3v2_version", "3", // windows apparently doesn't support v2.4
}
if song.Title != "" {
args = append(args, "-metadata", "title="+song.Title)
}
if song.Artist != "" {
args = append(args, "-metadata", "artist="+song.Artist)
}
if song.Album != "" {
args = append(args, "-metadata", "album="+song.Album)
}
if song.Tags != "" {
args = append(args, "-metadata", "comment="+song.Tags)
}

switch strings.ToLower(filepath.Ext(f.Name())) {
case ".flac":
args = append(args, "-f", "flac")
case ".mp3":
args = append(args, "-f", "mp3")
case ".ogg":
args = append(args, "-f", "ogg")
default:
return nil, errors.E(op, errors.InvalidArgument)
}
args = append(args, "-")

ff, err := newFFmpegCmd("metadata-"+song.TrackID.String(), args)
if err != nil {
return nil, errors.E(op, err)
}
ff.Cmd.Stdin = f

out, err := ff.Run()
if err != nil {
return nil, errors.E(op, err)
}
// seek the output to the start
_, _ = out.Seek(0, io.SeekStart)
return out, nil
}
38 changes: 38 additions & 0 deletions streamer/audio/metadata_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package audio

import (
"context"
"testing"

radio "github.com/R-a-dio/valkyrie"
"github.com/spf13/afero"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestWriteMetadata(t *testing.T) {
fsys := afero.NewOsFs()
f, err := fsys.Open("testdata/MP3_2MG.mp3")
require.NoError(t, err)

song := radio.Song{
DatabaseTrack: &radio.DatabaseTrack{
Title: "test",
Artist: "some kind of artist",
Album: "a kind of album",
Tags: "test effective very",
},
}

out, err := WriteMetadata(context.Background(), f, song)
require.NoError(t, err)

// probe the output file
info, err := probeText(context.Background(), out.Memfd.File)
require.NoError(t, err)

assert.Equal(t, song.Title, info.Title)
assert.Equal(t, song.Artist, info.Artist)
assert.Equal(t, song.Album, info.Album)
assert.Equal(t, song.Tags, info.Comment)
}

0 comments on commit 17de34c

Please sign in to comment.