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) }