.add composer and fix multiple value tag

This commit is contained in:
afkarxyz
2026-04-13 21:35:55 +07:00
parent e79622751d
commit 7792a69d33
7 changed files with 359 additions and 51 deletions
+28 -22
View File
@@ -161,6 +161,7 @@ type DownloadRequest struct {
SpotifyTotalDiscs int `json:"spotify_total_discs,omitempty"` SpotifyTotalDiscs int `json:"spotify_total_discs,omitempty"`
Copyright string `json:"copyright,omitempty"` Copyright string `json:"copyright,omitempty"`
Publisher string `json:"publisher,omitempty"` Publisher string `json:"publisher,omitempty"`
Composer string `json:"composer,omitempty"`
PlaylistName string `json:"playlist_name,omitempty"` PlaylistName string `json:"playlist_name,omitempty"`
PlaylistOwner string `json:"playlist_owner,omitempty"` PlaylistOwner string `json:"playlist_owner,omitempty"`
AllowFallback bool `json:"allow_fallback"` AllowFallback bool `json:"allow_fallback"`
@@ -384,25 +385,26 @@ func (a *App) DownloadTrack(req DownloadRequest) (DownloadResponse, error) {
spotifyURL = fmt.Sprintf("https://open.spotify.com/track/%s", req.SpotifyID) spotifyURL = fmt.Sprintf("https://open.spotify.com/track/%s", req.SpotifyID)
} }
if req.SpotifyID != "" && (req.Copyright == "" || req.Publisher == "" || req.SpotifyTotalDiscs == 0 || req.ReleaseDate == "" || req.SpotifyTotalTracks == 0 || req.SpotifyTrackNumber == 0) { metadataSeparator := req.Separator
if metadataSeparator == "" {
metadataSeparator = ", "
metadataSettings, _ := a.LoadSettings()
if metadataSettings != nil {
if sep, ok := metadataSettings["separator"].(string); ok {
if sep == "semicolon" {
metadataSeparator = "; "
} else if sep == "comma" {
metadataSeparator = ", "
}
}
}
}
if req.SpotifyID != "" && (req.Copyright == "" || req.Publisher == "" || req.Composer == "" || req.SpotifyTotalDiscs == 0 || req.ReleaseDate == "" || req.SpotifyTotalTracks == 0 || req.SpotifyTrackNumber == 0) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel() defer cancel()
trackURL := fmt.Sprintf("https://open.spotify.com/track/%s", req.SpotifyID) trackURL := fmt.Sprintf("https://open.spotify.com/track/%s", req.SpotifyID)
metadataSeparator := req.Separator
if metadataSeparator == "" {
metadataSeparator = ", "
metadataSettings, _ := a.LoadSettings()
if metadataSettings != nil {
if sep, ok := metadataSettings["separator"].(string); ok {
if sep == "semicolon" {
metadataSeparator = "; "
} else if sep == "comma" {
metadataSeparator = ", "
}
}
}
}
trackData, err := backend.GetFilteredSpotifyData(ctx, trackURL, false, 0, metadataSeparator, nil) trackData, err := backend.GetFilteredSpotifyData(ctx, trackURL, false, 0, metadataSeparator, nil)
if err == nil { if err == nil {
@@ -410,6 +412,7 @@ func (a *App) DownloadTrack(req DownloadRequest) (DownloadResponse, error) {
Track struct { Track struct {
Copyright string `json:"copyright"` Copyright string `json:"copyright"`
Publisher string `json:"publisher"` Publisher string `json:"publisher"`
Composer string `json:"composer"`
TotalDiscs int `json:"total_discs"` TotalDiscs int `json:"total_discs"`
TotalTracks int `json:"total_tracks"` TotalTracks int `json:"total_tracks"`
TrackNumber int `json:"track_number"` TrackNumber int `json:"track_number"`
@@ -425,6 +428,9 @@ func (a *App) DownloadTrack(req DownloadRequest) (DownloadResponse, error) {
if req.Publisher == "" && trackResp.Track.Publisher != "" { if req.Publisher == "" && trackResp.Track.Publisher != "" {
req.Publisher = trackResp.Track.Publisher req.Publisher = trackResp.Track.Publisher
} }
if req.Composer == "" && trackResp.Track.Composer != "" {
req.Composer = trackResp.Track.Composer
}
if req.SpotifyTotalDiscs == 0 && trackResp.Track.TotalDiscs > 0 { if req.SpotifyTotalDiscs == 0 && trackResp.Track.TotalDiscs > 0 {
req.SpotifyTotalDiscs = trackResp.Track.TotalDiscs req.SpotifyTotalDiscs = trackResp.Track.TotalDiscs
} }
@@ -500,25 +506,25 @@ func (a *App) DownloadTrack(req DownloadRequest) (DownloadResponse, error) {
downloader := backend.NewAmazonDownloader() downloader := backend.NewAmazonDownloader()
if req.ServiceURL != "" { if req.ServiceURL != "" {
filename, err = downloader.DownloadByURL(req.ServiceURL, req.OutputDir, req.AudioFormat, req.FilenameFormat, req.PlaylistName, req.PlaylistOwner, req.TrackNumber, req.Position, req.TrackName, req.ArtistName, req.AlbumName, req.AlbumArtist, req.ReleaseDate, req.CoverURL, req.SpotifyTrackNumber, req.SpotifyDiscNumber, req.SpotifyTotalTracks, req.EmbedMaxQualityCover, req.SpotifyTotalDiscs, req.Copyright, req.Publisher, spotifyURL, req.UseFirstArtistOnly, req.UseSingleGenre, req.EmbedGenre) filename, err = downloader.DownloadByURL(req.ServiceURL, req.OutputDir, req.AudioFormat, req.FilenameFormat, req.PlaylistName, req.PlaylistOwner, req.TrackNumber, req.Position, req.TrackName, req.ArtistName, req.AlbumName, req.AlbumArtist, req.ReleaseDate, req.CoverURL, req.SpotifyTrackNumber, req.SpotifyDiscNumber, req.SpotifyTotalTracks, req.EmbedMaxQualityCover, req.SpotifyTotalDiscs, req.Copyright, req.Publisher, req.Composer, metadataSeparator, spotifyURL, req.UseFirstArtistOnly, req.UseSingleGenre, req.EmbedGenre)
} else { } else {
filename, err = downloader.DownloadBySpotifyID(req.SpotifyID, req.OutputDir, req.AudioFormat, req.FilenameFormat, req.PlaylistName, req.PlaylistOwner, req.TrackNumber, req.Position, req.TrackName, req.ArtistName, req.AlbumName, req.AlbumArtist, req.ReleaseDate, req.CoverURL, req.SpotifyTrackNumber, req.SpotifyDiscNumber, req.SpotifyTotalTracks, req.EmbedMaxQualityCover, req.SpotifyTotalDiscs, req.Copyright, req.Publisher, spotifyURL, req.UseFirstArtistOnly, req.UseSingleGenre, req.EmbedGenre) filename, err = downloader.DownloadBySpotifyID(req.SpotifyID, req.OutputDir, req.AudioFormat, req.FilenameFormat, req.PlaylistName, req.PlaylistOwner, req.TrackNumber, req.Position, req.TrackName, req.ArtistName, req.AlbumName, req.AlbumArtist, req.ReleaseDate, req.CoverURL, req.SpotifyTrackNumber, req.SpotifyDiscNumber, req.SpotifyTotalTracks, req.EmbedMaxQualityCover, req.SpotifyTotalDiscs, req.Copyright, req.Publisher, req.Composer, metadataSeparator, spotifyURL, req.UseFirstArtistOnly, req.UseSingleGenre, req.EmbedGenre)
} }
case "tidal": case "tidal":
if req.ApiURL == "" || req.ApiURL == "auto" { if req.ApiURL == "" || req.ApiURL == "auto" {
downloader := backend.NewTidalDownloader("") downloader := backend.NewTidalDownloader("")
if req.ServiceURL != "" { if req.ServiceURL != "" {
filename, err = downloader.DownloadByURLWithFallback(req.ServiceURL, req.OutputDir, req.AudioFormat, req.FilenameFormat, req.TrackNumber, req.Position, req.TrackName, req.ArtistName, req.AlbumName, req.AlbumArtist, req.ReleaseDate, req.UseAlbumTrackNumber, req.CoverURL, req.EmbedMaxQualityCover, req.SpotifyTrackNumber, req.SpotifyDiscNumber, req.SpotifyTotalTracks, req.SpotifyTotalDiscs, req.Copyright, req.Publisher, spotifyURL, req.AllowFallback, req.UseFirstArtistOnly, req.UseSingleGenre, req.EmbedGenre) filename, err = downloader.DownloadByURLWithFallback(req.ServiceURL, req.OutputDir, req.AudioFormat, req.FilenameFormat, req.TrackNumber, req.Position, req.TrackName, req.ArtistName, req.AlbumName, req.AlbumArtist, req.ReleaseDate, req.UseAlbumTrackNumber, req.CoverURL, req.EmbedMaxQualityCover, req.SpotifyTrackNumber, req.SpotifyDiscNumber, req.SpotifyTotalTracks, req.SpotifyTotalDiscs, req.Copyright, req.Publisher, req.Composer, metadataSeparator, spotifyURL, req.AllowFallback, req.UseFirstArtistOnly, req.UseSingleGenre, req.EmbedGenre)
} else { } else {
filename, err = downloader.Download(req.SpotifyID, req.OutputDir, req.AudioFormat, req.FilenameFormat, req.TrackNumber, req.Position, req.TrackName, req.ArtistName, req.AlbumName, req.AlbumArtist, req.ReleaseDate, req.UseAlbumTrackNumber, req.CoverURL, req.EmbedMaxQualityCover, req.SpotifyTrackNumber, req.SpotifyDiscNumber, req.SpotifyTotalTracks, req.SpotifyTotalDiscs, req.Copyright, req.Publisher, spotifyURL, req.AllowFallback, req.UseFirstArtistOnly, req.UseSingleGenre, req.EmbedGenre) filename, err = downloader.Download(req.SpotifyID, req.OutputDir, req.AudioFormat, req.FilenameFormat, req.TrackNumber, req.Position, req.TrackName, req.ArtistName, req.AlbumName, req.AlbumArtist, req.ReleaseDate, req.UseAlbumTrackNumber, req.CoverURL, req.EmbedMaxQualityCover, req.SpotifyTrackNumber, req.SpotifyDiscNumber, req.SpotifyTotalTracks, req.SpotifyTotalDiscs, req.Copyright, req.Publisher, req.Composer, metadataSeparator, spotifyURL, req.AllowFallback, req.UseFirstArtistOnly, req.UseSingleGenre, req.EmbedGenre)
} }
} else { } else {
downloader := backend.NewTidalDownloader(req.ApiURL) downloader := backend.NewTidalDownloader(req.ApiURL)
if req.ServiceURL != "" { if req.ServiceURL != "" {
filename, err = downloader.DownloadByURL(req.ServiceURL, req.OutputDir, req.AudioFormat, req.FilenameFormat, req.TrackNumber, req.Position, req.TrackName, req.ArtistName, req.AlbumName, req.AlbumArtist, req.ReleaseDate, req.UseAlbumTrackNumber, req.CoverURL, req.EmbedMaxQualityCover, req.SpotifyTrackNumber, req.SpotifyDiscNumber, req.SpotifyTotalTracks, req.SpotifyTotalDiscs, req.Copyright, req.Publisher, spotifyURL, req.AllowFallback, req.UseFirstArtistOnly, req.UseSingleGenre, req.EmbedGenre) filename, err = downloader.DownloadByURL(req.ServiceURL, req.OutputDir, req.AudioFormat, req.FilenameFormat, req.TrackNumber, req.Position, req.TrackName, req.ArtistName, req.AlbumName, req.AlbumArtist, req.ReleaseDate, req.UseAlbumTrackNumber, req.CoverURL, req.EmbedMaxQualityCover, req.SpotifyTrackNumber, req.SpotifyDiscNumber, req.SpotifyTotalTracks, req.SpotifyTotalDiscs, req.Copyright, req.Publisher, req.Composer, metadataSeparator, spotifyURL, req.AllowFallback, req.UseFirstArtistOnly, req.UseSingleGenre, req.EmbedGenre)
} else { } else {
filename, err = downloader.Download(req.SpotifyID, req.OutputDir, req.AudioFormat, req.FilenameFormat, req.TrackNumber, req.Position, req.TrackName, req.ArtistName, req.AlbumName, req.AlbumArtist, req.ReleaseDate, req.UseAlbumTrackNumber, req.CoverURL, req.EmbedMaxQualityCover, req.SpotifyTrackNumber, req.SpotifyDiscNumber, req.SpotifyTotalTracks, req.SpotifyTotalDiscs, req.Copyright, req.Publisher, spotifyURL, req.AllowFallback, req.UseFirstArtistOnly, req.UseSingleGenre, req.EmbedGenre) filename, err = downloader.Download(req.SpotifyID, req.OutputDir, req.AudioFormat, req.FilenameFormat, req.TrackNumber, req.Position, req.TrackName, req.ArtistName, req.AlbumName, req.AlbumArtist, req.ReleaseDate, req.UseAlbumTrackNumber, req.CoverURL, req.EmbedMaxQualityCover, req.SpotifyTrackNumber, req.SpotifyDiscNumber, req.SpotifyTotalTracks, req.SpotifyTotalDiscs, req.Copyright, req.Publisher, req.Composer, metadataSeparator, spotifyURL, req.AllowFallback, req.UseFirstArtistOnly, req.UseSingleGenre, req.EmbedGenre)
} }
} }
@@ -531,7 +537,7 @@ func (a *App) DownloadTrack(req DownloadRequest) (DownloadResponse, error) {
if quality == "" { if quality == "" {
quality = "6" quality = "6"
} }
filename, err = downloader.DownloadTrackWithISRC(isrc, req.OutputDir, quality, req.FilenameFormat, req.TrackNumber, req.Position, req.TrackName, req.ArtistName, req.AlbumName, req.AlbumArtist, req.ReleaseDate, req.UseAlbumTrackNumber, req.CoverURL, req.EmbedMaxQualityCover, req.SpotifyTrackNumber, req.SpotifyDiscNumber, req.SpotifyTotalTracks, req.SpotifyTotalDiscs, req.Copyright, req.Publisher, spotifyURL, req.AllowFallback, req.UseFirstArtistOnly, req.UseSingleGenre, req.EmbedGenre) filename, err = downloader.DownloadTrackWithISRC(isrc, req.OutputDir, quality, req.FilenameFormat, req.TrackNumber, req.Position, req.TrackName, req.ArtistName, req.AlbumName, req.AlbumArtist, req.ReleaseDate, req.UseAlbumTrackNumber, req.CoverURL, req.EmbedMaxQualityCover, req.SpotifyTrackNumber, req.SpotifyDiscNumber, req.SpotifyTotalTracks, req.SpotifyTotalDiscs, req.Copyright, req.Publisher, req.Composer, metadataSeparator, spotifyURL, req.AllowFallback, req.UseFirstArtistOnly, req.UseSingleGenre, req.EmbedGenre)
default: default:
return DownloadResponse{ return DownloadResponse{
+5 -3
View File
@@ -204,7 +204,7 @@ func (a *AmazonDownloader) DownloadFromService(amazonURL, outputDir, quality str
return a.DownloadFromAfkarXYZ(amazonURL, outputDir, quality) return a.DownloadFromAfkarXYZ(amazonURL, outputDir, quality)
} }
func (a *AmazonDownloader) DownloadByURL(amazonURL, outputDir, quality, filenameFormat, playlistName, playlistOwner string, includeTrackNumber bool, position int, spotifyTrackName, spotifyArtistName, spotifyAlbumName, spotifyAlbumArtist, spotifyReleaseDate, spotifyCoverURL string, spotifyTrackNumber, spotifyDiscNumber, spotifyTotalTracks int, embedMaxQualityCover bool, spotifyTotalDiscs int, spotifyCopyright, spotifyPublisher, spotifyURL string, useFirstArtistOnly bool, useSingleGenre bool, embedGenre bool) (string, error) { func (a *AmazonDownloader) DownloadByURL(amazonURL, outputDir, quality, filenameFormat, playlistName, playlistOwner string, includeTrackNumber bool, position int, spotifyTrackName, spotifyArtistName, spotifyAlbumName, spotifyAlbumArtist, spotifyReleaseDate, spotifyCoverURL string, spotifyTrackNumber, spotifyDiscNumber, spotifyTotalTracks int, embedMaxQualityCover bool, spotifyTotalDiscs int, spotifyCopyright, spotifyPublisher, spotifyComposer, metadataSeparator, spotifyURL string, useFirstArtistOnly bool, useSingleGenre bool, embedGenre bool) (string, error) {
if outputDir != "." { if outputDir != "." {
if err := os.MkdirAll(outputDir, 0755); err != nil { if err := os.MkdirAll(outputDir, 0755); err != nil {
@@ -390,6 +390,8 @@ func (a *AmazonDownloader) DownloadByURL(amazonURL, outputDir, quality, filename
Comment: spotifyURL, Comment: spotifyURL,
Copyright: spotifyCopyright, Copyright: spotifyCopyright,
Publisher: spotifyPublisher, Publisher: spotifyPublisher,
Composer: spotifyComposer,
Separator: metadataSeparator,
Description: "https://github.com/afkarxyz/SpotiFLAC", Description: "https://github.com/afkarxyz/SpotiFLAC",
ISRC: isrc, ISRC: isrc,
Genre: mbMeta.Genre, Genre: mbMeta.Genre,
@@ -418,7 +420,7 @@ func (a *AmazonDownloader) DownloadByURL(amazonURL, outputDir, quality, filename
return filePath, nil return filePath, nil
} }
func (a *AmazonDownloader) DownloadBySpotifyID(spotifyTrackID, outputDir, quality, filenameFormat, playlistName, playlistOwner string, includeTrackNumber bool, position int, spotifyTrackName, spotifyArtistName, spotifyAlbumName, spotifyAlbumArtist, spotifyReleaseDate, spotifyCoverURL string, spotifyTrackNumber, spotifyDiscNumber, spotifyTotalTracks int, embedMaxQualityCover bool, spotifyTotalDiscs int, spotifyCopyright, spotifyPublisher, spotifyURL string, func (a *AmazonDownloader) DownloadBySpotifyID(spotifyTrackID, outputDir, quality, filenameFormat, playlistName, playlistOwner string, includeTrackNumber bool, position int, spotifyTrackName, spotifyArtistName, spotifyAlbumName, spotifyAlbumArtist, spotifyReleaseDate, spotifyCoverURL string, spotifyTrackNumber, spotifyDiscNumber, spotifyTotalTracks int, embedMaxQualityCover bool, spotifyTotalDiscs int, spotifyCopyright, spotifyPublisher, spotifyComposer, metadataSeparator, spotifyURL string,
useFirstArtistOnly bool, useSingleGenre bool, embedGenre bool, useFirstArtistOnly bool, useSingleGenre bool, embedGenre bool,
) (string, error) { ) (string, error) {
@@ -427,5 +429,5 @@ func (a *AmazonDownloader) DownloadBySpotifyID(spotifyTrackID, outputDir, qualit
return "", err return "", err
} }
return a.DownloadByURL(amazonURL, outputDir, quality, filenameFormat, playlistName, playlistOwner, includeTrackNumber, position, spotifyTrackName, spotifyArtistName, spotifyAlbumName, spotifyAlbumArtist, spotifyReleaseDate, spotifyCoverURL, spotifyTrackNumber, spotifyDiscNumber, spotifyTotalTracks, embedMaxQualityCover, spotifyTotalDiscs, spotifyCopyright, spotifyPublisher, spotifyURL, useFirstArtistOnly, useSingleGenre, embedGenre) return a.DownloadByURL(amazonURL, outputDir, quality, filenameFormat, playlistName, playlistOwner, includeTrackNumber, position, spotifyTrackName, spotifyArtistName, spotifyAlbumName, spotifyAlbumArtist, spotifyReleaseDate, spotifyCoverURL, spotifyTrackNumber, spotifyDiscNumber, spotifyTotalTracks, embedMaxQualityCover, spotifyTotalDiscs, spotifyCopyright, spotifyPublisher, spotifyComposer, metadataSeparator, spotifyURL, useFirstArtistOnly, useSingleGenre, embedGenre)
} }
+90
View File
@@ -0,0 +1,90 @@
package backend
import "strings"
func normalizeArtistSeparator(separator string) string {
separator = strings.TrimSpace(separator)
if separator == "," || separator == ";" {
return separator
}
return ""
}
func splitArtistSegment(segment string, separator string) []string {
segment = strings.TrimSpace(segment)
if segment == "" {
return nil
}
if strings.Contains(segment, "|||SEP|||") {
return strings.Split(segment, "|||SEP|||")
}
parts := []string{segment}
if separator = normalizeArtistSeparator(separator); separator != "" {
var separated []string
for _, part := range parts {
for _, item := range strings.Split(part, separator) {
separated = append(separated, item)
}
}
parts = separated
} else if strings.Contains(segment, ";") {
var separated []string
for _, part := range parts {
for _, item := range strings.Split(part, ";") {
separated = append(separated, item)
}
}
parts = separated
}
return parts
}
func SplitArtistCredits(artistStr, separator string) []string {
rawParts := splitArtistSegment(artistStr, separator)
if len(rawParts) == 0 {
return nil
}
seen := make(map[string]struct{}, len(rawParts))
result := make([]string, 0, len(rawParts))
for _, part := range rawParts {
part = strings.TrimSpace(part)
if part == "" {
continue
}
if _, exists := seen[part]; exists {
continue
}
seen[part] = struct{}{}
result = append(result, part)
}
return result
}
func SplitMetadataValues(value, separator string) []string {
rawParts := splitArtistSegment(value, separator)
if len(rawParts) == 0 {
return nil
}
seen := make(map[string]struct{}, len(rawParts))
result := make([]string, 0, len(rawParts))
for _, part := range rawParts {
part = strings.TrimSpace(part)
if part == "" {
continue
}
if _, exists := seen[part]; exists {
continue
}
seen[part] = struct{}{}
result = append(result, part)
}
return result
}
+133 -19
View File
@@ -21,6 +21,7 @@ type Metadata struct {
Artist string Artist string
Album string Album string
AlbumArtist string AlbumArtist string
Separator string
Date string Date string
ReleaseDate string ReleaseDate string
TrackNumber int TrackNumber int
@@ -31,12 +32,72 @@ type Metadata struct {
Comment string Comment string
Copyright string Copyright string
Publisher string Publisher string
Composer string
Lyrics string Lyrics string
Description string Description string
ISRC string ISRC string
Genre string Genre string
} }
func resolveMetadataSeparator(separator string) string {
if normalized := normalizeArtistSeparator(separator); normalized != "" {
return normalized
}
return normalizeArtistSeparator(GetSeparator())
}
func displayMetadataSeparator(separator string) string {
if resolved := resolveMetadataSeparator(separator); resolved != "" {
return resolved + " "
}
return "; "
}
func addVorbisTagValues(cmt *flacvorbis.MetaDataBlockVorbisComment, key string, values []string) {
for _, value := range values {
value = strings.TrimSpace(value)
if value == "" {
continue
}
_ = cmt.Add(key, value)
}
}
func addMP3TextFrame(tag *id3v2.Tag, frameID string, value string) {
tag.DeleteFrames(frameID)
value = strings.TrimSpace(value)
if value == "" {
return
}
tag.AddTextFrame(frameID, id3v2.EncodingUTF8, value)
}
func joinMultiValueText(values []string, separator string, nullSeparated bool) string {
cleaned := make([]string, 0, len(values))
for _, value := range values {
value = strings.TrimSpace(value)
if value != "" {
cleaned = append(cleaned, value)
}
}
if len(cleaned) == 0 {
return ""
}
if len(cleaned) == 1 {
return cleaned[0]
}
if nullSeparated {
return strings.Join(cleaned, "\x00")
}
return strings.Join(cleaned, displayMetadataSeparator(separator))
}
func EmbedMetadata(filepath string, metadata Metadata, coverPath string) error { func EmbedMetadata(filepath string, metadata Metadata, coverPath string) error {
f, err := flac.ParseFile(filepath) f, err := flac.ParseFile(filepath)
if err != nil { if err != nil {
@@ -52,17 +113,22 @@ func EmbedMetadata(filepath string, metadata Metadata, coverPath string) error {
} }
cmt := flacvorbis.New() cmt := flacvorbis.New()
separator := resolveMetadataSeparator(metadata.Separator)
if metadata.Title != "" { if metadata.Title != "" {
_ = cmt.Add(flacvorbis.FIELD_TITLE, metadata.Title) _ = cmt.Add(flacvorbis.FIELD_TITLE, metadata.Title)
} }
if metadata.Artist != "" { if artistValues := SplitArtistCredits(metadata.Artist, separator); len(artistValues) > 0 {
addVorbisTagValues(cmt, flacvorbis.FIELD_ARTIST, artistValues)
} else if metadata.Artist != "" {
_ = cmt.Add(flacvorbis.FIELD_ARTIST, metadata.Artist) _ = cmt.Add(flacvorbis.FIELD_ARTIST, metadata.Artist)
} }
if metadata.Album != "" { if metadata.Album != "" {
_ = cmt.Add(flacvorbis.FIELD_ALBUM, metadata.Album) _ = cmt.Add(flacvorbis.FIELD_ALBUM, metadata.Album)
} }
if metadata.AlbumArtist != "" { if albumArtistValues := SplitArtistCredits(metadata.AlbumArtist, separator); len(albumArtistValues) > 0 {
addVorbisTagValues(cmt, "ALBUMARTIST", albumArtistValues)
} else if metadata.AlbumArtist != "" {
_ = cmt.Add("ALBUMARTIST", metadata.AlbumArtist) _ = cmt.Add("ALBUMARTIST", metadata.AlbumArtist)
} }
if metadata.Date != "" { if metadata.Date != "" {
@@ -86,6 +152,11 @@ func EmbedMetadata(filepath string, metadata Metadata, coverPath string) error {
if metadata.Publisher != "" { if metadata.Publisher != "" {
_ = cmt.Add("PUBLISHER", metadata.Publisher) _ = cmt.Add("PUBLISHER", metadata.Publisher)
} }
if composerValues := SplitArtistCredits(metadata.Composer, separator); len(composerValues) > 0 {
addVorbisTagValues(cmt, "COMPOSER", composerValues)
} else if metadata.Composer != "" {
_ = cmt.Add("COMPOSER", metadata.Composer)
}
if metadata.Description != "" { if metadata.Description != "" {
_ = cmt.Add("DESCRIPTION", metadata.Description) _ = cmt.Add("DESCRIPTION", metadata.Description)
} }
@@ -97,7 +168,9 @@ func EmbedMetadata(filepath string, metadata Metadata, coverPath string) error {
_ = cmt.Add("ISRC", metadata.ISRC) _ = cmt.Add("ISRC", metadata.ISRC)
} }
if metadata.Genre != "" { if genreValues := SplitMetadataValues(metadata.Genre, separator); len(genreValues) > 0 {
addVorbisTagValues(cmt, "GENRE", genreValues)
} else if metadata.Genre != "" {
_ = cmt.Add("GENRE", metadata.Genre) _ = cmt.Add("GENRE", metadata.Genre)
} }
@@ -901,6 +974,10 @@ func ExtractFullMetadataFromFile(filePath string) (Metadata, error) {
metadata.Copyright = value metadata.Copyright = value
case "publisher", "tpub", "label": case "publisher", "tpub", "label":
metadata.Publisher = value metadata.Publisher = value
case "composer", "writer", "wm/composer", "©wrt":
metadata.Composer = value
case "genre", "tcon":
metadata.Genre = value
case "url": case "url":
metadata.URL = value metadata.URL = value
case "comment", "comments": case "comment", "comments":
@@ -940,15 +1017,13 @@ func embedMetadataToMP3(filePath string, metadata Metadata, coverPath string) er
return fmt.Errorf("failed to open MP3 file: %w", err) return fmt.Errorf("failed to open MP3 file: %w", err)
} }
defer tag.Close() defer tag.Close()
separator := resolveMetadataSeparator(metadata.Separator)
tag.DeleteFrames("TXXX") tag.DeleteFrames("TXXX")
if metadata.Title != "" { if metadata.Title != "" {
tag.SetTitle(metadata.Title) tag.SetTitle(metadata.Title)
} }
if metadata.Artist != "" {
tag.SetArtist(metadata.Artist)
}
if metadata.Album != "" { if metadata.Album != "" {
tag.SetAlbum(metadata.Album) tag.SetAlbum(metadata.Album)
} }
@@ -960,10 +1035,17 @@ func embedMetadataToMP3(filePath string, metadata Metadata, coverPath string) er
tag.SetYear(year) tag.SetYear(year)
} }
if metadata.AlbumArtist != "" { artistText := joinMultiValueText(SplitArtistCredits(metadata.Artist, separator), separator, true)
tag.DeleteFrames("TPE2") if artistText == "" {
tag.AddTextFrame("TPE2", id3v2.EncodingUTF8, metadata.AlbumArtist) artistText = strings.TrimSpace(metadata.Artist)
} }
addMP3TextFrame(tag, "TPE1", artistText)
albumArtistText := joinMultiValueText(SplitArtistCredits(metadata.AlbumArtist, separator), separator, true)
if albumArtistText == "" {
albumArtistText = strings.TrimSpace(metadata.AlbumArtist)
}
addMP3TextFrame(tag, "TPE2", albumArtistText)
if metadata.TrackNumber > 0 { if metadata.TrackNumber > 0 {
tag.DeleteFrames(tag.CommonID("Track number/Position in set")) tag.DeleteFrames(tag.CommonID("Track number/Position in set"))
@@ -984,18 +1066,21 @@ func embedMetadataToMP3(filePath string, metadata Metadata, coverPath string) er
} }
if metadata.Copyright != "" { if metadata.Copyright != "" {
tag.DeleteFrames("TCOP") addMP3TextFrame(tag, "TCOP", metadata.Copyright)
tag.AddTextFrame("TCOP", id3v2.EncodingUTF8, metadata.Copyright)
} }
if metadata.Publisher != "" { if metadata.Publisher != "" {
tag.DeleteFrames("TPUB") addMP3TextFrame(tag, "TPUB", metadata.Publisher)
tag.AddTextFrame("TPUB", id3v2.EncodingUTF8, metadata.Publisher)
} }
composerText := joinMultiValueText(SplitArtistCredits(metadata.Composer, separator), separator, true)
if composerText == "" {
composerText = strings.TrimSpace(metadata.Composer)
}
addMP3TextFrame(tag, "TCOM", composerText)
if metadata.ISRC != "" { if metadata.ISRC != "" {
tag.DeleteFrames("TSRC") addMP3TextFrame(tag, "TSRC", metadata.ISRC)
tag.AddTextFrame("TSRC", id3v2.EncodingUTF8, metadata.ISRC)
} }
if comment := resolveMetadataComment(metadata); comment != "" { if comment := resolveMetadataComment(metadata); comment != "" {
@@ -1027,6 +1112,12 @@ func embedMetadataToMP3(filePath string, metadata Metadata, coverPath string) er
} }
} }
genreText := joinMultiValueText(SplitMetadataValues(metadata.Genre, separator), separator, true)
if genreText == "" {
genreText = strings.TrimSpace(metadata.Genre)
}
addMP3TextFrame(tag, "TCON", genreText)
if err := tag.Save(); err != nil { if err := tag.Save(); err != nil {
return fmt.Errorf("failed to save MP3 tags: %w", err) return fmt.Errorf("failed to save MP3 tags: %w", err)
} }
@@ -1048,6 +1139,7 @@ func embedMetadataToM4A(filePath string, metadata Metadata, coverPath string) er
"-i", filePath, "-i", filePath,
"-y", "-y",
} }
separator := resolveMetadataSeparator(metadata.Separator)
if coverPath != "" && fileExists(coverPath) { if coverPath != "" && fileExists(coverPath) {
args = append(args, "-i", coverPath) args = append(args, "-i", coverPath)
@@ -1059,14 +1151,22 @@ func embedMetadataToM4A(filePath string, metadata Metadata, coverPath string) er
if metadata.Title != "" { if metadata.Title != "" {
args = append(args, "-metadata", "title="+metadata.Title) args = append(args, "-metadata", "title="+metadata.Title)
} }
if metadata.Artist != "" { artistText := joinMultiValueText(SplitArtistCredits(metadata.Artist, separator), separator, false)
args = append(args, "-metadata", "artist="+metadata.Artist) if artistText == "" {
artistText = strings.TrimSpace(metadata.Artist)
}
if artistText != "" {
args = append(args, "-metadata", "artist="+artistText)
} }
if metadata.Album != "" { if metadata.Album != "" {
args = append(args, "-metadata", "album="+metadata.Album) args = append(args, "-metadata", "album="+metadata.Album)
} }
if metadata.AlbumArtist != "" { albumArtistText := joinMultiValueText(SplitArtistCredits(metadata.AlbumArtist, separator), separator, false)
args = append(args, "-metadata", "album_artist="+metadata.AlbumArtist) if albumArtistText == "" {
albumArtistText = strings.TrimSpace(metadata.AlbumArtist)
}
if albumArtistText != "" {
args = append(args, "-metadata", "album_artist="+albumArtistText)
} }
if metadata.Date != "" { if metadata.Date != "" {
args = append(args, "-metadata", "date="+metadata.Date) args = append(args, "-metadata", "date="+metadata.Date)
@@ -1091,9 +1191,23 @@ func embedMetadataToM4A(filePath string, metadata Metadata, coverPath string) er
if metadata.Publisher != "" { if metadata.Publisher != "" {
args = append(args, "-metadata", "publisher="+metadata.Publisher) args = append(args, "-metadata", "publisher="+metadata.Publisher)
} }
composerText := joinMultiValueText(SplitArtistCredits(metadata.Composer, separator), separator, false)
if composerText == "" {
composerText = strings.TrimSpace(metadata.Composer)
}
if composerText != "" {
args = append(args, "-metadata", "composer="+composerText)
}
if metadata.ISRC != "" { if metadata.ISRC != "" {
args = append(args, "-metadata", "isrc="+metadata.ISRC) args = append(args, "-metadata", "isrc="+metadata.ISRC)
} }
genreText := joinMultiValueText(SplitMetadataValues(metadata.Genre, separator), separator, false)
if genreText == "" {
genreText = strings.TrimSpace(metadata.Genre)
}
if genreText != "" {
args = append(args, "-metadata", "genre="+genreText)
}
if comment := resolveMetadataComment(metadata); comment != "" { if comment := resolveMetadataComment(metadata); comment != "" {
args = append(args, "-metadata", "comment="+comment) args = append(args, "-metadata", "comment="+comment)
} }
+5 -3
View File
@@ -381,7 +381,7 @@ func buildQobuzFilename(title, artist, album, albumArtist, releaseDate string, t
return filename + ".flac" return filename + ".flac"
} }
func (q *QobuzDownloader) DownloadTrack(spotifyID, outputDir, quality, filenameFormat string, includeTrackNumber bool, position int, spotifyTrackName, spotifyArtistName, spotifyAlbumName, spotifyAlbumArtist, spotifyReleaseDate string, useAlbumTrackNumber bool, spotifyCoverURL string, embedMaxQualityCover bool, spotifyTrackNumber, spotifyDiscNumber, spotifyTotalTracks int, spotifyTotalDiscs int, spotifyCopyright, spotifyPublisher, spotifyURL string, allowFallback bool, useFirstArtistOnly bool, useSingleGenre bool, embedGenre bool) (string, error) { func (q *QobuzDownloader) DownloadTrack(spotifyID, outputDir, quality, filenameFormat string, includeTrackNumber bool, position int, spotifyTrackName, spotifyArtistName, spotifyAlbumName, spotifyAlbumArtist, spotifyReleaseDate string, useAlbumTrackNumber bool, spotifyCoverURL string, embedMaxQualityCover bool, spotifyTrackNumber, spotifyDiscNumber, spotifyTotalTracks int, spotifyTotalDiscs int, spotifyCopyright, spotifyPublisher, spotifyComposer, metadataSeparator, spotifyURL string, allowFallback bool, useFirstArtistOnly bool, useSingleGenre bool, embedGenre bool) (string, error) {
var isrc string var isrc string
if spotifyID != "" { if spotifyID != "" {
linkClient := NewSongLinkClient() linkClient := NewSongLinkClient()
@@ -394,10 +394,10 @@ func (q *QobuzDownloader) DownloadTrack(spotifyID, outputDir, quality, filenameF
return "", fmt.Errorf("spotify ID is required for Qobuz download") return "", fmt.Errorf("spotify ID is required for Qobuz download")
} }
return q.DownloadTrackWithISRC(isrc, outputDir, quality, filenameFormat, includeTrackNumber, position, spotifyTrackName, spotifyArtistName, spotifyAlbumName, spotifyAlbumArtist, spotifyReleaseDate, useAlbumTrackNumber, spotifyCoverURL, embedMaxQualityCover, spotifyTrackNumber, spotifyDiscNumber, spotifyTotalTracks, spotifyTotalDiscs, spotifyCopyright, spotifyPublisher, spotifyURL, allowFallback, useFirstArtistOnly, useSingleGenre, embedGenre) return q.DownloadTrackWithISRC(isrc, outputDir, quality, filenameFormat, includeTrackNumber, position, spotifyTrackName, spotifyArtistName, spotifyAlbumName, spotifyAlbumArtist, spotifyReleaseDate, useAlbumTrackNumber, spotifyCoverURL, embedMaxQualityCover, spotifyTrackNumber, spotifyDiscNumber, spotifyTotalTracks, spotifyTotalDiscs, spotifyCopyright, spotifyPublisher, spotifyComposer, metadataSeparator, spotifyURL, allowFallback, useFirstArtistOnly, useSingleGenre, embedGenre)
} }
func (q *QobuzDownloader) DownloadTrackWithISRC(isrc, outputDir, quality, filenameFormat string, includeTrackNumber bool, position int, spotifyTrackName, spotifyArtistName, spotifyAlbumName, spotifyAlbumArtist, spotifyReleaseDate string, useAlbumTrackNumber bool, spotifyCoverURL string, embedMaxQualityCover bool, spotifyTrackNumber, spotifyDiscNumber, spotifyTotalTracks int, spotifyTotalDiscs int, spotifyCopyright, spotifyPublisher, spotifyURL string, allowFallback bool, useFirstArtistOnly bool, useSingleGenre bool, embedGenre bool) (string, error) { func (q *QobuzDownloader) DownloadTrackWithISRC(isrc, outputDir, quality, filenameFormat string, includeTrackNumber bool, position int, spotifyTrackName, spotifyArtistName, spotifyAlbumName, spotifyAlbumArtist, spotifyReleaseDate string, useAlbumTrackNumber bool, spotifyCoverURL string, embedMaxQualityCover bool, spotifyTrackNumber, spotifyDiscNumber, spotifyTotalTracks int, spotifyTotalDiscs int, spotifyCopyright, spotifyPublisher, spotifyComposer, metadataSeparator, spotifyURL string, allowFallback bool, useFirstArtistOnly bool, useSingleGenre bool, embedGenre bool) (string, error) {
fmt.Printf("Fetching track info for ISRC: %s\n", isrc) fmt.Printf("Fetching track info for ISRC: %s\n", isrc)
metaChan := make(chan Metadata, 1) metaChan := make(chan Metadata, 1)
@@ -522,6 +522,8 @@ func (q *QobuzDownloader) DownloadTrackWithISRC(isrc, outputDir, quality, filena
Comment: spotifyURL, Comment: spotifyURL,
Copyright: spotifyCopyright, Copyright: spotifyCopyright,
Publisher: spotifyPublisher, Publisher: spotifyPublisher,
Composer: spotifyComposer,
Separator: metadataSeparator,
Description: "https://github.com/afkarxyz/SpotiFLAC", Description: "https://github.com/afkarxyz/SpotiFLAC",
ISRC: isrc, ISRC: isrc,
Genre: mbMeta.Genre, Genre: mbMeta.Genre,
+90
View File
@@ -53,6 +53,7 @@ type TrackMetadata struct {
ArtistsData []ArtistSimple `json:"artists_data,omitempty"` ArtistsData []ArtistSimple `json:"artists_data,omitempty"`
Copyright string `json:"copyright,omitempty"` Copyright string `json:"copyright,omitempty"`
Publisher string `json:"publisher,omitempty"` Publisher string `json:"publisher,omitempty"`
Composer string `json:"composer,omitempty"`
Plays string `json:"plays,omitempty"` Plays string `json:"plays,omitempty"`
PreviewURL string `json:"preview_url,omitempty"` PreviewURL string `json:"preview_url,omitempty"`
IsExplicit bool `json:"is_explicit,omitempty"` IsExplicit bool `json:"is_explicit,omitempty"`
@@ -193,6 +194,7 @@ type apiTrackResponse struct {
Disc int `json:"disc"` Disc int `json:"disc"`
Discs int `json:"discs"` Discs int `json:"discs"`
Copyright string `json:"copyright"` Copyright string `json:"copyright"`
Composer string `json:"composer,omitempty"`
Plays string `json:"plays"` Plays string `json:"plays"`
Album struct { Album struct {
ID string `json:"id"` ID string `json:"id"`
@@ -496,6 +498,10 @@ func (c *SpotifyMetadataClient) fetchTrack(ctx context.Context, trackID string)
} }
filteredData := FilterTrack(data, c.Separator, albumFetchData) filteredData := FilterTrack(data, c.Separator, albumFetchData)
composer, composerErr := c.fetchTrackComposerWithClient(ctx, client, trackID)
if composerErr == nil && composer != "" {
filteredData["composer"] = composer
}
jsonData, err := json.Marshal(filteredData) jsonData, err := json.Marshal(filteredData)
if err != nil { if err != nil {
@@ -510,6 +516,89 @@ func (c *SpotifyMetadataClient) fetchTrack(ctx context.Context, trackID string)
return &result, nil return &result, nil
} }
func collectTrackCreditNamesByRole(items []interface{}, role string) []string {
role = strings.TrimSpace(role)
if role == "" || len(items) == 0 {
return nil
}
seen := make(map[string]struct{}, len(items))
names := make([]string, 0, len(items))
for _, item := range items {
itemMap, ok := item.(map[string]interface{})
if !ok {
continue
}
if !strings.EqualFold(strings.TrimSpace(getString(itemMap, "role")), role) {
continue
}
name := strings.TrimSpace(getString(itemMap, "name"))
if name == "" {
continue
}
if _, exists := seen[name]; exists {
continue
}
seen[name] = struct{}{}
names = append(names, name)
}
return names
}
func (c *SpotifyMetadataClient) fetchTrackComposerWithClient(ctx context.Context, client *SpotifyClient, trackID string) (string, error) {
_ = ctx
payload := map[string]interface{}{
"variables": map[string]interface{}{
"trackUri": fmt.Sprintf("spotify:track:%s", trackID),
"contributorsLimit": 100,
"contributorsOffset": 0,
},
"operationName": "queryTrackCreditsModal",
"extensions": map[string]interface{}{
"persistedQuery": map[string]interface{}{
"version": 1,
"sha256Hash": "e2ca40d46cf1fde36562261ccec754f23fb31b561877252e9fe0d6834aabb84b",
},
},
}
data, err := client.Query(payload)
if err != nil {
return "", fmt.Errorf("failed to query track credits: %w", err)
}
creditItems := getSlice(
getMap(
getMap(
getMap(
getMap(data, "data"),
"trackUnion",
),
"creditsTrait",
),
"contributors",
),
"items",
)
composerNames := collectTrackCreditNamesByRole(creditItems, "Composer")
if len(composerNames) == 0 {
return "", nil
}
separator := strings.TrimSpace(c.Separator)
if separator == "" {
separator = ", "
}
return strings.Join(composerNames, separator), nil
}
func (c *SpotifyMetadataClient) fetchAlbum(ctx context.Context, albumID string, callback MetadataCallback) (*apiAlbumResponse, error) { func (c *SpotifyMetadataClient) fetchAlbum(ctx context.Context, albumID string, callback MetadataCallback) (*apiAlbumResponse, error) {
client := NewSpotifyClient() client := NewSpotifyClient()
if err := client.Initialize(); err != nil { if err := client.Initialize(); err != nil {
@@ -963,6 +1052,7 @@ func (c *SpotifyMetadataClient) formatTrackData(raw *apiTrackResponse) TrackResp
ArtistsData: artistsData, ArtistsData: artistsData,
Copyright: raw.Copyright, Copyright: raw.Copyright,
Publisher: raw.Album.Label, Publisher: raw.Album.Label,
Composer: raw.Composer,
Plays: raw.Plays, Plays: raw.Plays,
IsExplicit: raw.IsExplicit, IsExplicit: raw.IsExplicit,
} }
+8 -4
View File
@@ -416,7 +416,7 @@ func (t *TidalDownloader) DownloadFromManifest(manifestB64, outputPath string) e
return nil return nil
} }
func (t *TidalDownloader) DownloadByURL(tidalURL, outputDir, quality, filenameFormat string, includeTrackNumber bool, position int, spotifyTrackName, spotifyArtistName, spotifyAlbumName, spotifyAlbumArtist, spotifyReleaseDate string, useAlbumTrackNumber bool, spotifyCoverURL string, embedMaxQualityCover bool, spotifyTrackNumber, spotifyDiscNumber, spotifyTotalTracks int, spotifyTotalDiscs int, spotifyCopyright, spotifyPublisher, spotifyURL string, allowFallback bool, useFirstArtistOnly bool, useSingleGenre bool, embedGenre bool) (string, error) { func (t *TidalDownloader) DownloadByURL(tidalURL, outputDir, quality, filenameFormat string, includeTrackNumber bool, position int, spotifyTrackName, spotifyArtistName, spotifyAlbumName, spotifyAlbumArtist, spotifyReleaseDate string, useAlbumTrackNumber bool, spotifyCoverURL string, embedMaxQualityCover bool, spotifyTrackNumber, spotifyDiscNumber, spotifyTotalTracks int, spotifyTotalDiscs int, spotifyCopyright, spotifyPublisher, spotifyComposer, metadataSeparator, spotifyURL string, allowFallback bool, useFirstArtistOnly bool, useSingleGenre bool, embedGenre bool) (string, error) {
if outputDir != "." { if outputDir != "." {
if err := os.MkdirAll(outputDir, 0755); err != nil { if err := os.MkdirAll(outputDir, 0755); err != nil {
return "", fmt.Errorf("directory error: %w", err) return "", fmt.Errorf("directory error: %w", err)
@@ -554,6 +554,8 @@ func (t *TidalDownloader) DownloadByURL(tidalURL, outputDir, quality, filenameFo
Comment: spotifyURL, Comment: spotifyURL,
Copyright: spotifyCopyright, Copyright: spotifyCopyright,
Publisher: spotifyPublisher, Publisher: spotifyPublisher,
Composer: spotifyComposer,
Separator: metadataSeparator,
Description: "https://github.com/afkarxyz/SpotiFLAC", Description: "https://github.com/afkarxyz/SpotiFLAC",
ISRC: isrc, ISRC: isrc,
Genre: mbMeta.Genre, Genre: mbMeta.Genre,
@@ -570,7 +572,7 @@ func (t *TidalDownloader) DownloadByURL(tidalURL, outputDir, quality, filenameFo
return outputFilename, nil return outputFilename, nil
} }
func (t *TidalDownloader) DownloadByURLWithFallback(tidalURL, outputDir, quality, filenameFormat string, includeTrackNumber bool, position int, spotifyTrackName, spotifyArtistName, spotifyAlbumName, spotifyAlbumArtist, spotifyReleaseDate string, useAlbumTrackNumber bool, spotifyCoverURL string, embedMaxQualityCover bool, spotifyTrackNumber, spotifyDiscNumber, spotifyTotalTracks int, spotifyTotalDiscs int, spotifyCopyright, spotifyPublisher, spotifyURL string, allowFallback bool, useFirstArtistOnly bool, useSingleGenre bool, embedGenre bool) (string, error) { func (t *TidalDownloader) DownloadByURLWithFallback(tidalURL, outputDir, quality, filenameFormat string, includeTrackNumber bool, position int, spotifyTrackName, spotifyArtistName, spotifyAlbumName, spotifyAlbumArtist, spotifyReleaseDate string, useAlbumTrackNumber bool, spotifyCoverURL string, embedMaxQualityCover bool, spotifyTrackNumber, spotifyDiscNumber, spotifyTotalTracks int, spotifyTotalDiscs int, spotifyCopyright, spotifyPublisher, spotifyComposer, metadataSeparator, spotifyURL string, allowFallback bool, useFirstArtistOnly bool, useSingleGenre bool, embedGenre bool) (string, error) {
apis, err := t.GetAvailableAPIs() apis, err := t.GetAvailableAPIs()
if err != nil { if err != nil {
return "", fmt.Errorf("no APIs available for fallback: %w", err) return "", fmt.Errorf("no APIs available for fallback: %w", err)
@@ -714,6 +716,8 @@ func (t *TidalDownloader) DownloadByURLWithFallback(tidalURL, outputDir, quality
Comment: spotifyURL, Comment: spotifyURL,
Copyright: spotifyCopyright, Copyright: spotifyCopyright,
Publisher: spotifyPublisher, Publisher: spotifyPublisher,
Composer: spotifyComposer,
Separator: metadataSeparator,
Description: "https://github.com/afkarxyz/SpotiFLAC", Description: "https://github.com/afkarxyz/SpotiFLAC",
ISRC: isrc, ISRC: isrc,
Genre: mbMeta.Genre, Genre: mbMeta.Genre,
@@ -730,14 +734,14 @@ func (t *TidalDownloader) DownloadByURLWithFallback(tidalURL, outputDir, quality
return outputFilename, nil return outputFilename, nil
} }
func (t *TidalDownloader) Download(spotifyTrackID, outputDir, quality, filenameFormat string, includeTrackNumber bool, position int, spotifyTrackName, spotifyArtistName, spotifyAlbumName, spotifyAlbumArtist, spotifyReleaseDate string, useAlbumTrackNumber bool, spotifyCoverURL string, embedMaxQualityCover bool, spotifyTrackNumber, spotifyDiscNumber, spotifyTotalTracks int, spotifyTotalDiscs int, spotifyCopyright, spotifyPublisher, spotifyURL string, allowFallback bool, useFirstArtistOnly bool, useSingleGenre bool, embedGenre bool) (string, error) { func (t *TidalDownloader) Download(spotifyTrackID, outputDir, quality, filenameFormat string, includeTrackNumber bool, position int, spotifyTrackName, spotifyArtistName, spotifyAlbumName, spotifyAlbumArtist, spotifyReleaseDate string, useAlbumTrackNumber bool, spotifyCoverURL string, embedMaxQualityCover bool, spotifyTrackNumber, spotifyDiscNumber, spotifyTotalTracks int, spotifyTotalDiscs int, spotifyCopyright, spotifyPublisher, spotifyComposer, metadataSeparator, spotifyURL string, allowFallback bool, useFirstArtistOnly bool, useSingleGenre bool, embedGenre bool) (string, error) {
tidalURL, err := t.GetTidalURLFromSpotify(spotifyTrackID) tidalURL, err := t.GetTidalURLFromSpotify(spotifyTrackID)
if err != nil { if err != nil {
return "", fmt.Errorf("songlink couldn't find Tidal URL: %w", err) return "", fmt.Errorf("songlink couldn't find Tidal URL: %w", err)
} }
return t.DownloadByURLWithFallback(tidalURL, outputDir, quality, filenameFormat, includeTrackNumber, position, spotifyTrackName, spotifyArtistName, spotifyAlbumName, spotifyAlbumArtist, spotifyReleaseDate, useAlbumTrackNumber, spotifyCoverURL, embedMaxQualityCover, spotifyTrackNumber, spotifyDiscNumber, spotifyTotalTracks, spotifyTotalDiscs, spotifyCopyright, spotifyPublisher, spotifyURL, allowFallback, useFirstArtistOnly, useSingleGenre, embedGenre) return t.DownloadByURLWithFallback(tidalURL, outputDir, quality, filenameFormat, includeTrackNumber, position, spotifyTrackName, spotifyArtistName, spotifyAlbumName, spotifyAlbumArtist, spotifyReleaseDate, useAlbumTrackNumber, spotifyCoverURL, embedMaxQualityCover, spotifyTrackNumber, spotifyDiscNumber, spotifyTotalTracks, spotifyTotalDiscs, spotifyCopyright, spotifyPublisher, spotifyComposer, metadataSeparator, spotifyURL, allowFallback, useFirstArtistOnly, useSingleGenre, embedGenre)
} }
type SegmentTemplate struct { type SegmentTemplate struct {