From 825672986755fda5b5f6f72a32a041dc26f9cc17 Mon Sep 17 00:00:00 2001 From: itouakirai <85016486+itouakirai@users.noreply.github.com> Date: Fri, 28 Jun 2024 22:56:05 +0800 Subject: [PATCH 1/9] add animated-artwork download --- main_atmos.go | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/main_atmos.go b/main_atmos.go index 353f4e6..a384496 100644 --- a/main_atmos.go +++ b/main_atmos.go @@ -41,6 +41,8 @@ var ( type Config struct { MediaUserToken string `yaml:"media-user-token"` SaveLrcFile bool `yaml:"save-lrc-file"` + SaveAnimatedArtwork bool `yaml:"save-animated-artwork"` + EmbyAnimatedArtwork bool `yaml:"emby-animated-artwork"` EmbedLrc bool `yaml:"embed-lrc"` EmbedCover bool `yaml:"embed-cover"` CoverSize string `yaml:"cover-size"` @@ -1101,6 +1103,7 @@ func getMeta(albumId string, token string, storefront string) (*AutoGenerated, e query.Set("fields[artists]", "name") query.Set("fields[albums:albums]", "artistName,artwork,name,releaseDate,url") query.Set("fields[record-labels]", "name") + query.Set("extend", "editorialVideo") // query.Set("l", "en-gb") req.URL.RawQuery = query.Encode() do, err := http.DefaultClient.Do(req) @@ -1319,6 +1322,36 @@ func rip(albumId string, token string, storefront string, userToken string) erro if err != nil { fmt.Println("Failed to write cover.") } + //get animated artwork + if config.SaveAnimatedArtwork && meta.Data[0].Attributes.EditorialVideo.MotionDetailSquare.Video != "" { + fmt.Println("Found Animation Artwork.") + motionvideoUrl, err := extractVideo(meta.Data[0].Attributes.EditorialVideo.MotionDetailSquare.Video) + if err != nil { + fmt.Println("no motion video.\n", err) + } + exists, err := fileExists(filepath.Join(sanAlbumFolder, "animated_artwork.mp4")) + if err != nil { + fmt.Println("Failed to check if animated artwork exists.") + } + if exists { + fmt.Println("Animated artwork already exists locally.") + } else { + fmt.Println("Animation Artwork Downloading...") + cmd := exec.Command("ffmpeg", "-loglevel", "quiet", "-y", "-i", motionvideoUrl, "-c", "copy", filepath.Join(sanAlbumFolder, "animated_artwork.mp4")) + if err := cmd.Run(); err != nil { + fmt.Printf("animated artwork dl err: %v\n", err) + } else { + fmt.Println("Animation Artwork Downloaded") + } + if config.EmbyAnimatedArtwork { + cmd2 := exec.Command("ffmpeg", "-i", filepath.Join(sanAlbumFolder, "animated_artwork.mp4"), "-vf", "scale=440:-1", "-r", "24", "-f", "gif", filepath.Join(sanAlbumFolder, "folder.jpg")) + if err := cmd2.Run(); err != nil { + fmt.Printf("animated artwork to gif err: %v\n", err) + } + } + + } + } trackTotal := len(meta.Data[0].Relationships.Tracks.Data) for trackNum, track := range meta.Data[0].Relationships.Tracks.Data { trackNum++ @@ -1651,6 +1684,46 @@ func extractMedia(b string) (string, []string, error) { return streamUrl.String(), keys, nil } +func extractVideo(c string) (string, error) { + MediaUrl, err := url.Parse(c) + if err != nil { + return "", err + } + resp, err := http.Get(c) + if err != nil { + return "", err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return "", errors.New(resp.Status) + } + body, err := io.ReadAll(resp.Body) + if err != nil { + return "", err + } + videoString := string(body) + from, listType, err := m3u8.DecodeFrom(strings.NewReader(videoString), true) + if err != nil || listType != m3u8.MASTER { + return "", errors.New("m3u8 not of media type") + } + video := from.(*m3u8.MasterPlaylist) + var streamUrl *url.URL + sort.Slice(video.Variants, func(i, j int) bool { + return video.Variants[i].AverageBandwidth > video.Variants[j].AverageBandwidth + }) + if len(video.Variants) > 0 { + highestBandwidthVariant := video.Variants[0] + streamUrl, err = MediaUrl.Parse(highestBandwidthVariant.URI) + if err != nil { + return "", err + } + } + if streamUrl == nil { + return "", errors.New("no video codec found") + } + return streamUrl.String(), nil +} + func extractSong(url string) (*SongInfo, error) { fmt.Println("Downloading...") track, err := http.Get(url) @@ -2101,6 +2174,14 @@ type AutoGenerated struct { Kind string `json:"kind"` } `json:"playParams"` IsCompilation bool `json:"isCompilation"` + EditorialVideo struct { + MotionDetailSquare struct { + Video string `json:"video"` + } `json:"motionDetailSquare"` + MotionSquareVideo1x1 struct { + Video string `json:"video"` + } `json:"motionSquareVideo1x1"` + } `json:"editorialVideo"` } `json:"attributes"` Relationships struct { RecordLabels struct { From 5bcbf17bf9fc4568ec2a3c0c56d5a4258c3155dd Mon Sep 17 00:00:00 2001 From: itouakirai <85016486+itouakirai@users.noreply.github.com> Date: Fri, 28 Jun 2024 23:00:01 +0800 Subject: [PATCH 2/9] add animated artwork download --- main_select.go | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/main_select.go b/main_select.go index 2ceb885..08778eb 100644 --- a/main_select.go +++ b/main_select.go @@ -41,6 +41,8 @@ var ( type Config struct { MediaUserToken string `yaml:"media-user-token"` SaveLrcFile bool `yaml:"save-lrc-file"` + SaveAnimatedArtwork bool `yaml:"save-animated-artwork"` + EmbyAnimatedArtwork bool `yaml:"emby-animated-artwork"` EmbedLrc bool `yaml:"embed-lrc"` EmbedCover bool `yaml:"embed-cover"` CoverSize string `yaml:"cover-size"` @@ -1036,6 +1038,7 @@ func getMeta(albumId string, token string, storefront string) (*AutoGenerated, e query.Set("fields[artists]", "name") query.Set("fields[albums:albums]", "artistName,artwork,name,releaseDate,url") query.Set("fields[record-labels]", "name") + query.Set("extend", "editorialVideo") // query.Set("l", "en-gb") req.URL.RawQuery = query.Encode() do, err := http.DefaultClient.Do(req) @@ -1277,6 +1280,36 @@ func rip(albumId string, token string, storefront string, userToken string) erro if err != nil { fmt.Println("Failed to write cover.") } + //get animated artwork + if config.SaveAnimatedArtwork && meta.Data[0].Attributes.EditorialVideo.MotionDetailSquare.Video != "" { + fmt.Println("Found Animation Artwork.") + motionvideoUrl, err := extractVideo(meta.Data[0].Attributes.EditorialVideo.MotionDetailSquare.Video) + if err != nil { + fmt.Println("no motion video.\n", err) + } + exists, err := fileExists(filepath.Join(sanAlbumFolder, "animated_artwork.mp4")) + if err != nil { + fmt.Println("Failed to check if animated artwork exists.") + } + if exists { + fmt.Println("Animated artwork already exists locally.") + } else { + fmt.Println("Animation Artwork Downloading...") + cmd := exec.Command("ffmpeg", "-loglevel", "quiet", "-y", "-i", motionvideoUrl, "-c", "copy", filepath.Join(sanAlbumFolder, "animated_artwork.mp4")) + if err := cmd.Run(); err != nil { + fmt.Printf("animated artwork dl err: %v\n", err) + } else { + fmt.Println("Animation Artwork Downloaded") + } + if config.EmbyAnimatedArtwork { + cmd2 := exec.Command("ffmpeg", "-i", filepath.Join(sanAlbumFolder, "animated_artwork.mp4"), "-vf", "scale=440:-1", "-r", "24", "-f", "gif", filepath.Join(sanAlbumFolder, "folder.jpg")) + if err := cmd2.Run(); err != nil { + fmt.Printf("animated artwork to gif err: %v\n", err) + } + } + + } + } trackTotal := len(meta.Data[0].Relationships.Tracks.Data) arr := make([]int, trackTotal) for i := 0; i < trackTotal; i++ { @@ -1687,6 +1720,45 @@ func extractMedia(b string) (string, []string, error) { } return streamUrl.String(), keys, nil } +func extractVideo(c string) (string, error) { + MediaUrl, err := url.Parse(c) + if err != nil { + return "", err + } + resp, err := http.Get(c) + if err != nil { + return "", err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return "", errors.New(resp.Status) + } + body, err := io.ReadAll(resp.Body) + if err != nil { + return "", err + } + videoString := string(body) + from, listType, err := m3u8.DecodeFrom(strings.NewReader(videoString), true) + if err != nil || listType != m3u8.MASTER { + return "", errors.New("m3u8 not of media type") + } + video := from.(*m3u8.MasterPlaylist) + var streamUrl *url.URL + sort.Slice(video.Variants, func(i, j int) bool { + return video.Variants[i].AverageBandwidth > video.Variants[j].AverageBandwidth + }) + if len(video.Variants) > 0 { + highestBandwidthVariant := video.Variants[0] + streamUrl, err = MediaUrl.Parse(highestBandwidthVariant.URI) + if err != nil { + return "", err + } + } + if streamUrl == nil { + return "", errors.New("no video codec found") + } + return streamUrl.String(), nil +} func extractSong(url string) (*SongInfo, error) { fmt.Println("Downloading...") @@ -2137,6 +2209,14 @@ type AutoGenerated struct { Kind string `json:"kind"` } `json:"playParams"` IsCompilation bool `json:"isCompilation"` + EditorialVideo struct { + MotionDetailSquare struct { + Video string `json:"video"` + } `json:"motionDetailSquare"` + MotionSquareVideo1x1 struct { + Video string `json:"video"` + } `json:"motionSquareVideo1x1"` + } `json:"editorialVideo"` } `json:"attributes"` Relationships struct { RecordLabels struct { From eaf9cadd6a99a9a704ee591e23d8e301aaefeb27 Mon Sep 17 00:00:00 2001 From: itouakirai <85016486+itouakirai@users.noreply.github.com> Date: Sat, 29 Jun 2024 01:05:55 +0800 Subject: [PATCH 3/9] add artist cover download --- main.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/main.go b/main.go index 1e9e717..c912e4c 100644 --- a/main.go +++ b/main.go @@ -45,6 +45,7 @@ type Config struct { EmbyAnimatedArtwork bool `yaml:"emby-animated-artwork"` EmbedLrc bool `yaml:"embed-lrc"` EmbedCover bool `yaml:"embed-cover"` + SaveArtistCover bool `yaml:"save-artist-cover"` CoverSize string `yaml:"cover-size"` CoverFormat string `yaml:"cover-format"` AlacSaveFolder string `yaml:"alac-save-folder"` @@ -1154,7 +1155,7 @@ func getMeta(albumId string, token string, storefront string) (*AutoGenerated, e query.Set("omit[resource]", "autos") query.Set("include", "tracks,artists,record-labels") query.Set("include[songs]", "artists") - query.Set("fields[artists]", "name") + query.Set("fields[artists]", "name,artwork") query.Set("fields[albums:albums]", "artistName,artwork,name,releaseDate,url") query.Set("fields[record-labels]", "name") query.Set("extend", "editorialVideo") @@ -1390,6 +1391,14 @@ func rip(albumId string, token string, storefront string, userToken string) erro sanAlbumFolder := filepath.Join(singerFolder, forbiddenNames.ReplaceAllString(albumFolder, "_")) os.MkdirAll(sanAlbumFolder, os.ModePerm) fmt.Println(albumFolder) + //get artist cover + if config.SaveArtistCover && meta.Data[0].Relationships.Artists.Data[0].Attributes.Artwork.Url != "" { + err = writeCover(singerFolder, "folder", meta.Data[0].Relationships.Artists.Data[0].Attributes.Artwork.Url) + if err != nil { + fmt.Println("Failed to write artist cover.") + } + } + //get album cover err = writeCover(sanAlbumFolder, "cover", meta.Data[0].Attributes.Artwork.URL) if err != nil { fmt.Println("Failed to write cover.") @@ -2402,6 +2411,9 @@ type AutoGenerated struct { Href string `json:"href"` Attributes struct { Name string `json:"name"` + Artwork struct { + Url string `json:"url"` + } `json:"artwork"` } `json:"attributes"` } `json:"data"` } `json:"artists"` From 8ef251bbf649c7735dffcad5a79039c564425914 Mon Sep 17 00:00:00 2001 From: itouakirai <85016486+itouakirai@users.noreply.github.com> Date: Sat, 29 Jun 2024 01:10:25 +0800 Subject: [PATCH 4/9] add artist cover download --- main_atmos.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/main_atmos.go b/main_atmos.go index a384496..8b2d5bb 100644 --- a/main_atmos.go +++ b/main_atmos.go @@ -45,6 +45,7 @@ type Config struct { EmbyAnimatedArtwork bool `yaml:"emby-animated-artwork"` EmbedLrc bool `yaml:"embed-lrc"` EmbedCover bool `yaml:"embed-cover"` + SaveArtistCover bool `yaml:"save-artist-cover"` CoverSize string `yaml:"cover-size"` CoverFormat string `yaml:"cover-format"` AlacSaveFolder string `yaml:"alac-save-folder"` @@ -1100,7 +1101,7 @@ func getMeta(albumId string, token string, storefront string) (*AutoGenerated, e query.Set("omit[resource]", "autos") query.Set("include", "tracks,artists,record-labels") query.Set("include[songs]", "artists") - query.Set("fields[artists]", "name") + query.Set("fields[artists]", "name,artwork") query.Set("fields[albums:albums]", "artistName,artwork,name,releaseDate,url") query.Set("fields[record-labels]", "name") query.Set("extend", "editorialVideo") @@ -1318,6 +1319,14 @@ func rip(albumId string, token string, storefront string, userToken string) erro sanAlbumFolder := filepath.Join(singerFolder, forbiddenNames.ReplaceAllString(albumFolder, "_")) os.MkdirAll(sanAlbumFolder, os.ModePerm) fmt.Println(albumFolder) + //get artist cover + if config.SaveArtistCover && meta.Data[0].Relationships.Artists.Data[0].Attributes.Artwork.Url != "" { + err = writeCover(singerFolder, "folder", meta.Data[0].Relationships.Artists.Data[0].Attributes.Artwork.Url) + if err != nil { + fmt.Println("Failed to write artist cover.") + } + } + //get album cover err = writeCover(sanAlbumFolder, "cover", meta.Data[0].Attributes.Artwork.URL) if err != nil { fmt.Println("Failed to write cover.") @@ -2196,6 +2205,9 @@ type AutoGenerated struct { Href string `json:"href"` Attributes struct { Name string `json:"name"` + Artwork struct { + Url string `json:"url"` + } `json:"artwork"` } `json:"attributes"` } `json:"data"` } `json:"artists"` From e2b674b50ef15d3f0fa319ac296a585eed0427bd Mon Sep 17 00:00:00 2001 From: itouakirai <85016486+itouakirai@users.noreply.github.com> Date: Sat, 29 Jun 2024 01:13:58 +0800 Subject: [PATCH 5/9] add artist cover download --- main_select.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/main_select.go b/main_select.go index 08778eb..d3d79dd 100644 --- a/main_select.go +++ b/main_select.go @@ -45,6 +45,7 @@ type Config struct { EmbyAnimatedArtwork bool `yaml:"emby-animated-artwork"` EmbedLrc bool `yaml:"embed-lrc"` EmbedCover bool `yaml:"embed-cover"` + SaveArtistCover bool `yaml:"save-artist-cover"` CoverSize string `yaml:"cover-size"` CoverFormat string `yaml:"cover-format"` AlacSaveFolder string `yaml:"alac-save-folder"` @@ -1035,7 +1036,7 @@ func getMeta(albumId string, token string, storefront string) (*AutoGenerated, e query.Set("omit[resource]", "autos") query.Set("include", "tracks,artists,record-labels") query.Set("include[songs]", "artists") - query.Set("fields[artists]", "name") + query.Set("fields[artists]", "name,artwork") query.Set("fields[albums:albums]", "artistName,artwork,name,releaseDate,url") query.Set("fields[record-labels]", "name") query.Set("extend", "editorialVideo") @@ -1276,6 +1277,14 @@ func rip(albumId string, token string, storefront string, userToken string) erro sanAlbumFolder := filepath.Join(singerFolder, forbiddenNames.ReplaceAllString(albumFolder, "_")) os.MkdirAll(sanAlbumFolder, os.ModePerm) fmt.Println(albumFolder) + //get artist cover + if config.SaveArtistCover && meta.Data[0].Relationships.Artists.Data[0].Attributes.Artwork.Url != "" { + err = writeCover(singerFolder, "folder", meta.Data[0].Relationships.Artists.Data[0].Attributes.Artwork.Url) + if err != nil { + fmt.Println("Failed to write artist cover.") + } + } + //get album cover err = writeCover(sanAlbumFolder, "cover", meta.Data[0].Attributes.Artwork.URL) if err != nil { fmt.Println("Failed to write cover.") @@ -2231,6 +2240,9 @@ type AutoGenerated struct { Href string `json:"href"` Attributes struct { Name string `json:"name"` + Artwork struct { + Url string `json:"url"` + } `json:"artwork"` } `json:"attributes"` } `json:"data"` } `json:"artists"` From 2c8494c6b366754a1985c957a7e53a2a2c7e21da Mon Sep 17 00:00:00 2001 From: itouakirai <85016486+itouakirai@users.noreply.github.com> Date: Sat, 29 Jun 2024 01:17:58 +0800 Subject: [PATCH 6/9] add artist cover download config --- config.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/config.yaml b/config.yaml index 4fc5512..62d23ee 100644 --- a/config.yaml +++ b/config.yaml @@ -1,6 +1,7 @@ media-user-token: "your-media-user-token" embed-lrc: true save-lrc-file: false +save-artist-cover: false save-animated-artwork: false # If enabled, requires ffmpeg emby-animated-artwork: false # If enabled, requires ffmpeg embed-cover: true From 762d2518b1ebe07829820b98060263241c8d06b8 Mon Sep 17 00:00:00 2001 From: itouakirai <85016486+itouakirai@users.noreply.github.com> Date: Sat, 29 Jun 2024 02:39:05 +0800 Subject: [PATCH 7/9] fix playlist artist cover error --- main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.go b/main.go index c912e4c..b974136 100644 --- a/main.go +++ b/main.go @@ -1392,7 +1392,7 @@ func rip(albumId string, token string, storefront string, userToken string) erro os.MkdirAll(sanAlbumFolder, os.ModePerm) fmt.Println(albumFolder) //get artist cover - if config.SaveArtistCover && meta.Data[0].Relationships.Artists.Data[0].Attributes.Artwork.Url != "" { + if config.SaveArtistCover && !(strings.Contains(albumId, "pl.")) { err = writeCover(singerFolder, "folder", meta.Data[0].Relationships.Artists.Data[0].Attributes.Artwork.Url) if err != nil { fmt.Println("Failed to write artist cover.") From 3dd6bdceeab19ae7dab92ec23c7711f1456e3352 Mon Sep 17 00:00:00 2001 From: itouakirai <85016486+itouakirai@users.noreply.github.com> Date: Sat, 29 Jun 2024 02:39:42 +0800 Subject: [PATCH 8/9] Update main_atmos.go --- main_atmos.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main_atmos.go b/main_atmos.go index 8b2d5bb..f97ed7e 100644 --- a/main_atmos.go +++ b/main_atmos.go @@ -1320,7 +1320,7 @@ func rip(albumId string, token string, storefront string, userToken string) erro os.MkdirAll(sanAlbumFolder, os.ModePerm) fmt.Println(albumFolder) //get artist cover - if config.SaveArtistCover && meta.Data[0].Relationships.Artists.Data[0].Attributes.Artwork.Url != "" { + if config.SaveArtistCover && !(strings.Contains(albumId, "pl.")) { err = writeCover(singerFolder, "folder", meta.Data[0].Relationships.Artists.Data[0].Attributes.Artwork.Url) if err != nil { fmt.Println("Failed to write artist cover.") From 1e963f15670f186c4739e64932b642315f939968 Mon Sep 17 00:00:00 2001 From: itouakirai <85016486+itouakirai@users.noreply.github.com> Date: Sat, 29 Jun 2024 02:40:00 +0800 Subject: [PATCH 9/9] Update main_select.go --- main_select.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main_select.go b/main_select.go index d3d79dd..d46a240 100644 --- a/main_select.go +++ b/main_select.go @@ -1278,7 +1278,7 @@ func rip(albumId string, token string, storefront string, userToken string) erro os.MkdirAll(sanAlbumFolder, os.ModePerm) fmt.Println(albumFolder) //get artist cover - if config.SaveArtistCover && meta.Data[0].Relationships.Artists.Data[0].Attributes.Artwork.Url != "" { + if config.SaveArtistCover && !(strings.Contains(albumId, "pl.")) { err = writeCover(singerFolder, "folder", meta.Data[0].Relationships.Artists.Data[0].Attributes.Artwork.Url) if err != nil { fmt.Println("Failed to write artist cover.")