187 lines
5.2 KiB
Go
187 lines
5.2 KiB
Go
package media
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
p "path/filepath"
|
|
"strconv"
|
|
s "strings"
|
|
|
|
"codeberg.org/danjones000/strip-beats/config"
|
|
t "codeberg.org/danjones000/strip-beats/media/tags"
|
|
"codeberg.org/danjones000/strip-beats/utils"
|
|
"github.com/google/uuid"
|
|
ffmpeg "github.com/u2takey/ffmpeg-go"
|
|
)
|
|
|
|
func ConvertAndTag(in Probe, tags t.Tags) (string, error) {
|
|
st := in.GetFirstAcceptableAudio()
|
|
if st == nil {
|
|
st = in.GetFirstAudio()
|
|
}
|
|
if st == nil {
|
|
return "", errors.New("Can't find an audio stream")
|
|
}
|
|
|
|
conf := config.GetConfig()
|
|
codec := utils.Tern(st.isAcceptableCodec(), "copy", conf.FfEncoder)
|
|
target := utils.Tern(st.isAcceptableCodec(), st.CodecName, conf.Codec)
|
|
|
|
base := p.Base(in.Format.Path)
|
|
ext := p.Ext(base)
|
|
base = s.TrimSuffix(base, ext)
|
|
ext = conf.CodecExt[target]
|
|
|
|
out := p.Join(conf.SavePath, base+"."+ext)
|
|
|
|
input := ffmpeg.Input(in.Format.Path).Get(strconv.Itoa(st.Index))
|
|
args := ffmpeg.KwArgs{"c:a": codec}
|
|
output := input.Output(out, args, getMetadataArgs(ext, tags)).GlobalArgs("-y")
|
|
|
|
return out, output.Run()
|
|
}
|
|
|
|
func getMetadataArgs(ext string, tags t.Tags) ffmpeg.KwArgs {
|
|
var meta []string
|
|
args := ffmpeg.KwArgs{}
|
|
switch ext {
|
|
case "opus":
|
|
fallthrough
|
|
case "flac":
|
|
fallthrough
|
|
case "ogg":
|
|
meta = append(meta, mapMetaKeys(getOggMeta(tags))...)
|
|
case "m4a":
|
|
fallthrough
|
|
case "mp4":
|
|
meta = append(meta, mapMetaKeys(getMp4Meta(tags))...)
|
|
case "mp3":
|
|
// @todo meta = append(meta, mapMetaKeys(getMp3Meta(tags))...)
|
|
}
|
|
|
|
meta = append(meta, "comment=Processed by "+config.UserAgent)
|
|
args["metadata"] = meta
|
|
return args
|
|
}
|
|
|
|
func mapMetaKeys(meta map[string]string) []string {
|
|
out := []string{}
|
|
for k, v := range meta {
|
|
if v != "" {
|
|
out = append(out, fmt.Sprintf("%s=%s", k, v))
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
func getOggMeta(tags t.Tags) map[string]string {
|
|
meta := map[string]string{}
|
|
meta["TITLE"] = tags.Title
|
|
meta["ARTIST"] = tags.Artist
|
|
meta["ALBUM_ARTIST"] = tags.AlbumArtist
|
|
meta["ALBUMARTIST"] = tags.AlbumArtist
|
|
meta["ALBUM"] = tags.Album
|
|
meta["DATE"] = tags.Date
|
|
year, _, _ := s.Cut(tags.Date, "-")
|
|
meta["YEAR"] = year
|
|
meta["URL"] = tags.Url
|
|
meta["PURL"] = tags.Url
|
|
meta["URI"] = tags.Url
|
|
meta["ARTISTSORT"] = tags.ArtistSort
|
|
meta["ALBUMARTISTSORT"] = tags.AlbumArtistSort
|
|
meta["RELEASECOUNTRY"] = tags.ReleaseCountry
|
|
meta["LABEL"] = tags.Label
|
|
meta["GENRE"] = tags.Genre
|
|
meta["DISC"] = utils.Tern(tags.Disc > 0, strconv.Itoa(tags.Disc), "")
|
|
meta["DISCTOTAL"] = utils.Tern(tags.DiscCount > 0, strconv.Itoa(tags.DiscCount), "")
|
|
meta["TRACK"] = utils.Tern(tags.Track > 0, strconv.Itoa(tags.Track), "")
|
|
meta["TRACKTOTAL"] = utils.Tern(tags.TrackCount > 0, strconv.Itoa(tags.TrackCount), "")
|
|
if tags.AcoustidId != uuid.Nil {
|
|
meta["ACOUSTID_ID"] = tags.AcoustidId.String()
|
|
}
|
|
if tags.MusicbrainzReleaseGroupId != uuid.Nil {
|
|
meta["MUSICBRAINZ_RELEASEGROUPID"] = tags.MusicbrainzReleaseGroupId.String()
|
|
}
|
|
if tags.MusicbrainzAlbumId != uuid.Nil {
|
|
meta["MUSICBRAINZ_ALBUMID"] = tags.MusicbrainzAlbumId.String()
|
|
}
|
|
if tags.MusicbrainzAlbumArtistId != uuid.Nil {
|
|
meta["MUSICBRAINZ_ALBUMARTISTID"] = tags.MusicbrainzAlbumArtistId.String()
|
|
}
|
|
if tags.MusicbrainzRecordingId != uuid.Nil {
|
|
meta["MUSICBRAINZ_RECORDINGID"] = tags.MusicbrainzRecordingId.String()
|
|
}
|
|
if tags.MusicBrainzLabelId != uuid.Nil {
|
|
meta["MUSICBRAINZ_LABELID"] = tags.MusicBrainzLabelId.String()
|
|
}
|
|
|
|
return meta
|
|
}
|
|
|
|
func getMp4Meta(tags t.Tags) map[string]string {
|
|
meta := map[string]string{}
|
|
meta["title"] = tags.Title
|
|
meta["artist"] = tags.Artist
|
|
meta["album_artist"] = tags.AlbumArtist
|
|
meta["album"] = tags.Album
|
|
year, _, _ := s.Cut(tags.Date, "-")
|
|
meta["year"] = year
|
|
meta["description"] = tags.Url
|
|
meta["genre"] = tags.Genre
|
|
meta["network"] = tags.Label
|
|
if tags.Disc > 0 {
|
|
meta["disc"] = fmt.Sprintf("%d", tags.Disc) + utils.Tern(tags.DiscCount > 0, fmt.Sprintf("/%d", tags.DiscCount), "")
|
|
}
|
|
if tags.Track > 0 {
|
|
meta["track"] = fmt.Sprintf("%d", tags.Track) + utils.Tern(tags.TrackCount > 0, fmt.Sprintf("/%d", tags.TrackCount), "")
|
|
}
|
|
|
|
return meta
|
|
}
|
|
|
|
func TrimWithFade(in Probe, out string, start, stop, up, down float64) error {
|
|
// -ss (start) -t (end) -af afade=t=in:st=(start):d=(up),afade=t=out:st=(downstart):d=(down)
|
|
st := in.GetFirstAcceptableAudio()
|
|
if st == nil {
|
|
return errors.New("Can't find an audio stream")
|
|
}
|
|
args := ffmpeg.KwArgs{"c:a": "copy"}
|
|
if start != 0 {
|
|
args["ss"] = start
|
|
}
|
|
if stop != 0 {
|
|
end := stop - start
|
|
args["t"] = end
|
|
} else {
|
|
stop = st.Duration
|
|
}
|
|
|
|
input := ffmpeg.Input(in.Format.Path).Get(strconv.Itoa(st.Index))
|
|
if up != 0 {
|
|
args["c:a"] = config.GetConfig().FfEncoder
|
|
input = input.Filter("afade", ffmpeg.Args{
|
|
"t=in",
|
|
fmt.Sprintf("st=%v", start),
|
|
fmt.Sprintf("d=%v", up)})
|
|
}
|
|
|
|
if down != 0 {
|
|
args["c:a"] = config.GetConfig().FfEncoder
|
|
downstart := stop - down
|
|
input = input.Filter("afade", ffmpeg.Args{
|
|
"t=out",
|
|
fmt.Sprintf("st=%v", downstart),
|
|
fmt.Sprintf("d=%v", down)})
|
|
}
|
|
output := input.Output(out, args).GlobalArgs("-y")
|
|
// @todo out should be stdout
|
|
return output.Run()
|
|
}
|
|
|
|
func Trim(in Probe, out string, start, stop float64) error {
|
|
return TrimWithFade(in, out, start, stop, 0, 0)
|
|
}
|
|
|
|
func Fade(in Probe, out string, up, down float64) error {
|
|
return TrimWithFade(in, out, 0, 0, up, down)
|
|
}
|