mirror of
https://github.com/Superredstone/spotiflac-cli.git
synced 2026-03-07 20:18:07 +01:00
feat: fix metadata embedding
This commit is contained in:
@@ -4,12 +4,13 @@ type App struct {
|
||||
UserAgent string // User agent used for scraping requests
|
||||
SelectedTidalApiUrl string
|
||||
Verbose bool
|
||||
SpotifyClient *SpotifyClient
|
||||
}
|
||||
|
||||
func NewApp() App {
|
||||
return App{
|
||||
UserAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36",
|
||||
Verbose: false,
|
||||
Verbose: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,5 +20,9 @@ func (app *App) Init() error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := app.InitSpotifyClient(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package lib
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -38,9 +39,18 @@ func (app *App) Download(url string, outputFile string, service string, quality
|
||||
if err := app.DownloadTrack(url, outputFile, service, quality); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
case UrlTypePlaylist:
|
||||
_, err := app.GetPlaylistMetadata(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
return errors.New("Invalid URL type.")
|
||||
}
|
||||
|
||||
func (app *App) DownloadTrack(url string, outputFile, service string, quality string) error {
|
||||
|
||||
@@ -3,21 +3,47 @@ package lib
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
id3v2 "github.com/bogem/id3v2/v2"
|
||||
"github.com/go-flac/flacpicture/v2"
|
||||
"github.com/go-flac/flacvorbis/v2"
|
||||
"github.com/go-flac/go-flac/v2"
|
||||
)
|
||||
|
||||
func (app *App) GetTrackMetadata(url string) (TrackMetadata, error) {
|
||||
app.log("Getting metadata for " + url)
|
||||
func (app *App) GetPlaylistMetadata(url string) (PlaylistMetadata, error) {
|
||||
app.log("Fetching playlist metadata")
|
||||
|
||||
client := NewSpotifyClient()
|
||||
var result TrackMetadata
|
||||
|
||||
err := client.Initialize()
|
||||
var result PlaylistMetadata
|
||||
playlistId, err := ParseTrackId(url)
|
||||
if err != nil {
|
||||
return result, errors.New("Unable to fetch Spotify metadata.")
|
||||
return result, err
|
||||
}
|
||||
|
||||
payload := BuildSpotifyReqPayloadPlaylist(playlistId)
|
||||
|
||||
rawMetadata, err := app.SpotifyClient.Query(payload)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
byteMetadata, err := json.Marshal(rawMetadata)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(byteMetadata, &result); err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (app *App) GetTrackMetadata(url string) (TrackMetadata, error) {
|
||||
app.log("Fetching metadata for " + url)
|
||||
|
||||
var result TrackMetadata
|
||||
|
||||
trackId, err := ParseTrackId(url)
|
||||
if err != nil {
|
||||
return result, err
|
||||
@@ -25,7 +51,7 @@ func (app *App) GetTrackMetadata(url string) (TrackMetadata, error) {
|
||||
|
||||
payload := BuildSpotifyReqPayloadTrack(trackId)
|
||||
|
||||
rawMetadata, err := client.Query(payload)
|
||||
rawMetadata, err := app.SpotifyClient.Query(payload)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
@@ -43,10 +69,10 @@ func (app *App) PrintMetadata(url string) error {
|
||||
return errors.New("Unimplemented.")
|
||||
}
|
||||
|
||||
func (app *App) EmbedMetadata(file string, metadata TrackMetadata) error {
|
||||
func (app *App) EmbedMetadata(fileName string, metadata TrackMetadata) error {
|
||||
app.log("Embedding metadata")
|
||||
|
||||
tag, err := id3v2.Open(file, id3v2.Options{Parse: true})
|
||||
file, err := flac.ParseFile(fileName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -56,14 +82,46 @@ func (app *App) EmbedMetadata(file string, metadata TrackMetadata) error {
|
||||
return err
|
||||
}
|
||||
|
||||
tag.SetArtist(artists)
|
||||
tag.SetTitle(metadata.Data.TrackUnion.Name)
|
||||
tag.SetYear(string(metadata.Data.TrackUnion.AlbumOfTrack.Date.Year))
|
||||
tag.SetAlbum(metadata.Data.TrackUnion.AlbumOfTrack.Name)
|
||||
cmt := flacvorbis.New()
|
||||
cmt.Add(flacvorbis.FIELD_ALBUM, metadata.Data.TrackUnion.AlbumOfTrack.Name)
|
||||
cmt.Add(flacvorbis.FIELD_DATE, string(metadata.Data.TrackUnion.AlbumOfTrack.Date.IsoString.Year()))
|
||||
cmt.Add(flacvorbis.FIELD_ARTIST, artists)
|
||||
cmt.Add(flacvorbis.FIELD_TITLE, metadata.Data.TrackUnion.Name)
|
||||
cmtBlock := cmt.Marshal()
|
||||
file.Meta = append(file.Meta, &cmtBlock)
|
||||
|
||||
if err = tag.Save(); err != nil {
|
||||
cover, err := app.GetAlbumCover(metadata)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
picture, err := flacpicture.NewFromImageData(
|
||||
flacpicture.PictureTypeFrontCover, "Front cover", cover, "image/jpeg")
|
||||
|
||||
pictureMeta := picture.Marshal()
|
||||
file.Meta = append(file.Meta, &pictureMeta)
|
||||
file.Save(fileName)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (app *App) GetAlbumCover(metadata TrackMetadata) ([]byte, error) {
|
||||
app.log("Embedding cover")
|
||||
|
||||
for _, source := range metadata.Data.TrackUnion.AlbumOfTrack.CoverArt.Sources {
|
||||
rawResponse, err := http.Get(source.Url)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
defer rawResponse.Body.Close()
|
||||
|
||||
response, err := io.ReadAll(rawResponse.Body)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
return []byte{}, errors.New("Unable to download album cover for " + metadata.Data.TrackUnion.Name + ".")
|
||||
}
|
||||
|
||||
@@ -1835,3 +1835,43 @@ func BuildSpotifyReqPayloadTrack(trackId string) SpotifyPayload {
|
||||
|
||||
return payload
|
||||
}
|
||||
|
||||
func BuildSpotifyReqPayloadPlaylist(playlistId string) SpotifyPayload {
|
||||
payload := map[string]interface{}{
|
||||
"variables": map[string]interface{}{
|
||||
"uri": fmt.Sprintf("spotify:playlist:%s", playlistId),
|
||||
"offset": 0, // No one wants to download from their playlists starting from song 158th, right?
|
||||
"limit": 5000, // Hope that this does not limit anyone
|
||||
"enableWatchFeedEntrypoint": false,
|
||||
},
|
||||
"operationName": "fetchPlaylist",
|
||||
"extensions": map[string]interface{}{
|
||||
"persistedQuery": map[string]interface{}{
|
||||
"version": 1,
|
||||
"sha256Hash": "bb67e0af06e8d6f52b531f97468ee4acd44cd0f82b988e15c2ea47b1148efc77",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return payload
|
||||
}
|
||||
|
||||
func BuildSpotifyReqPayloadAlbum(albumId string) SpotifyPayload {
|
||||
payload := map[string]interface{}{
|
||||
"variables": map[string]interface{}{
|
||||
"uri": fmt.Sprintf("spotify:album:%s", albumId),
|
||||
"locale": "",
|
||||
"offset": 0, // No one wants to download from an album from song number 9
|
||||
"limit": 5000, // No album will ever have more than 5000 songs, i hope
|
||||
},
|
||||
"operationName": "getAlbum",
|
||||
"extensions": map[string]interface{}{
|
||||
"persistedQuery": map[string]interface{}{
|
||||
"version": 1,
|
||||
"sha256Hash": "b9bfabef66ed756e5e13f68a942deb60bd4125ec1f1be8cc42769dc0259b4b10",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return payload
|
||||
}
|
||||
|
||||
16
lib/types.go
16
lib/types.go
@@ -16,8 +16,12 @@ type ExtractedColors struct {
|
||||
}
|
||||
|
||||
type CoverArt struct {
|
||||
ExtractedColors ExtractedColors `json:"extractedColors"`
|
||||
Sources []map[string]interface{} `json:"sources"`
|
||||
ExtractedColors ExtractedColors `json:"extractedColors"`
|
||||
Sources []struct {
|
||||
Height int `json:"height"`
|
||||
Width int `json:"width"`
|
||||
Url string `json:"url"`
|
||||
} `json:"sources"`
|
||||
}
|
||||
|
||||
type Date struct {
|
||||
@@ -119,3 +123,11 @@ type Data struct {
|
||||
type TrackMetadata struct {
|
||||
Data Data `json:"data"`
|
||||
}
|
||||
|
||||
type PlaylistMetadata struct {
|
||||
Data struct {
|
||||
Playlist struct {
|
||||
Name string `json:"name"`
|
||||
} `json:"playlistV2"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
10
lib/utils.go
10
lib/utils.go
@@ -121,3 +121,13 @@ func FileExists(file string) (bool, error) {
|
||||
|
||||
return false, err
|
||||
}
|
||||
|
||||
func (app *App) InitSpotifyClient() error {
|
||||
app.SpotifyClient = NewSpotifyClient()
|
||||
|
||||
if err := app.SpotifyClient.Initialize(); err != nil {
|
||||
return errors.New("Unable to fetch Spotify metadata.")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user