This commit is contained in:
itouakirai 2025-02-18 12:23:21 +08:00
parent 0de01fd251
commit 1ccc4932b7

67
main.go
View File

@ -7,7 +7,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"log" "log"
"net" "net"
"net/http" "net/http"
@ -26,12 +25,12 @@ import (
"main/utils/structs" "main/utils/structs"
"github.com/beevik/etree" "github.com/beevik/etree"
"github.com/fatih/color"
"github.com/grafov/m3u8" "github.com/grafov/m3u8"
"github.com/olekukonko/tablewriter" "github.com/olekukonko/tablewriter"
"github.com/spf13/pflag" "github.com/spf13/pflag"
"github.com/zhaarey/go-mp4tag" "github.com/zhaarey/go-mp4tag"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
"github.com/fatih/color"
) )
var ( var (
@ -54,7 +53,7 @@ var (
func loadConfig() error { func loadConfig() error {
// 读取config.yaml文件内容 // 读取config.yaml文件内容
data, err := ioutil.ReadFile("config.yaml") data, err := os.ReadFile("config.yaml")
if err != nil { if err != nil {
return err return err
} }
@ -412,14 +411,14 @@ func getSongLyrics(songId string, storefront string, token string, userToken str
defer do.Body.Close() defer do.Body.Close()
obj := new(structs.SongLyrics) obj := new(structs.SongLyrics)
err = json.NewDecoder(do.Body).Decode(&obj) err = json.NewDecoder(do.Body).Decode(&obj)
if obj.Data != nil { if err != nil {
return obj.Data[0].Attributes.Ttml, nil
} else {
return "", errors.New("failed to get lyrics") return "", errors.New("failed to get lyrics")
} else {
return obj.Data[0].Attributes.Ttml, nil
} }
} }
func writeCover(sanAlbumFolder, name string, url string) error { func writeCover(sanAlbumFolder, name string, url string) (string, error) {
covPath := filepath.Join(sanAlbumFolder, name+"."+Config.CoverFormat) covPath := filepath.Join(sanAlbumFolder, name+"."+Config.CoverFormat)
if Config.CoverFormat == "original" { if Config.CoverFormat == "original" {
ext := strings.Split(url, "/")[len(strings.Split(url, "/"))-2] ext := strings.Split(url, "/")[len(strings.Split(url, "/"))-2]
@ -429,7 +428,7 @@ func writeCover(sanAlbumFolder, name string, url string) error {
exists, err := fileExists(covPath) exists, err := fileExists(covPath)
if err != nil { if err != nil {
fmt.Println("Failed to check if cover exists.") fmt.Println("Failed to check if cover exists.")
return err return "", err
} }
if exists { if exists {
_ = os.Remove(covPath) _ = os.Remove(covPath)
@ -446,27 +445,27 @@ func writeCover(sanAlbumFolder, name string, url string) error {
} }
req, err := http.NewRequest("GET", url, nil) req, err := http.NewRequest("GET", url, nil)
if err != nil { if err != nil {
return err return "", err
} }
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36") req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36")
do, err := http.DefaultClient.Do(req) do, err := http.DefaultClient.Do(req)
if err != nil { if err != nil {
return err return "", err
} }
defer do.Body.Close() defer do.Body.Close()
if do.StatusCode != http.StatusOK { if do.StatusCode != http.StatusOK {
errors.New(do.Status) return "", errors.New(do.Status)
} }
f, err := os.Create(covPath) f, err := os.Create(covPath)
if err != nil { if err != nil {
return err return "", err
} }
defer f.Close() defer f.Close()
_, err = io.Copy(f, do.Body) _, err = io.Copy(f, do.Body)
if err != nil { if err != nil {
return err return "", err
} }
return nil return covPath, nil
} }
func writeLyrics(sanAlbumFolder, filename string, lrc string) error { func writeLyrics(sanAlbumFolder, filename string, lrc string) error {
@ -493,7 +492,7 @@ func contains(slice []string, item string) bool {
} }
// 下载单曲逻辑 // 下载单曲逻辑
func downloadTrack(trackNum int, trackTotal int, meta *structs.AutoGenerated, track structs.TrackData, albumId, token, storefront, mediaUserToken, sanAlbumFolder, Codec string, counter *structs.Counter) { func downloadTrack(trackNum int, trackTotal int, meta *structs.AutoGenerated, track structs.TrackData, albumId, token, storefront, mediaUserToken, sanAlbumFolder, Codec string, covPath string) {
counter.Total++ counter.Total++
fmt.Printf("Track %d of %d:\n", trackNum, trackTotal) fmt.Printf("Track %d of %d:\n", trackNum, trackTotal)
@ -547,7 +546,7 @@ func downloadTrack(trackNum int, trackTotal int, meta *structs.AutoGenerated, tr
} }
var EnhancedHls_m3u8 string var EnhancedHls_m3u8 string
if needCheck && !needDlAacLc { if needCheck && !needDlAacLc {
EnhancedHls_m3u8, err = checkM3u8(track.ID, "song") EnhancedHls_m3u8, _ = checkM3u8(track.ID, "song")
if strings.HasSuffix(EnhancedHls_m3u8, ".m3u8") { if strings.HasSuffix(EnhancedHls_m3u8, ".m3u8") {
manifest.Attributes.ExtendedAssetUrls.EnhancedHls = EnhancedHls_m3u8 manifest.Attributes.ExtendedAssetUrls.EnhancedHls = EnhancedHls_m3u8
} }
@ -557,7 +556,7 @@ func downloadTrack(trackNum int, trackTotal int, meta *structs.AutoGenerated, tr
if dl_atmos { if dl_atmos {
Quality = fmt.Sprintf("%dkbps", Config.AtmosMax-2000) Quality = fmt.Sprintf("%dkbps", Config.AtmosMax-2000)
} else if needDlAacLc { } else if needDlAacLc {
Quality = fmt.Sprintf("256kbps") Quality = "256kbps"
} else { } else {
_, Quality, err = extractMedia(manifest.Attributes.ExtendedAssetUrls.EnhancedHls, true) _, Quality, err = extractMedia(manifest.Attributes.ExtendedAssetUrls.EnhancedHls, true)
if err != nil { if err != nil {
@ -674,15 +673,16 @@ func downloadTrack(trackNum int, trackTotal int, meta *structs.AutoGenerated, tr
fmt.Sprintf("artist=%s", meta.Data[0].Attributes.ArtistName), fmt.Sprintf("artist=%s", meta.Data[0].Attributes.ArtistName),
fmt.Sprintf("lyrics=%s", lrc), fmt.Sprintf("lyrics=%s", lrc),
} }
var trackCovPath string
if Config.EmbedCover { if Config.EmbedCover {
if strings.Contains(albumId, "pl.") && Config.DlAlbumcoverForPlaylist { if strings.Contains(albumId, "pl.") && Config.DlAlbumcoverForPlaylist {
err = writeCover(sanAlbumFolder, track.ID, track.Attributes.Artwork.URL) trackCovPath, err = writeCover(sanAlbumFolder, track.ID, track.Attributes.Artwork.URL)
if err != nil { if err != nil {
fmt.Println("Failed to write cover.") fmt.Println("Failed to write cover.")
} }
tags = append(tags, fmt.Sprintf("cover=%s/%s.%s", sanAlbumFolder, track.ID, Config.CoverFormat)) tags = append(tags, fmt.Sprintf("cover=%s", trackCovPath))
} else { } else {
tags = append(tags, fmt.Sprintf("cover=%s/%s.%s", sanAlbumFolder, "cover", Config.CoverFormat)) tags = append(tags, fmt.Sprintf("cover=%s", covPath))
} }
} }
tagsString := strings.Join(tags, ":") tagsString := strings.Join(tags, ":")
@ -692,9 +692,9 @@ func downloadTrack(trackNum int, trackTotal int, meta *structs.AutoGenerated, tr
counter.Error++ counter.Error++
return return
} }
if strings.Contains(albumId, "pl.") && Config.DlAlbumcoverForPlaylist { if strings.Contains(albumId, "pl.") && Config.DlAlbumcoverForPlaylist && trackCovPath != "" {
if err := os.Remove(fmt.Sprintf("%s/%s.%s", sanAlbumFolder, track.ID, Config.CoverFormat)); err != nil { if err := os.Remove(trackCovPath); err != nil {
fmt.Printf("Error deleting file: %s/%s.%s\n", sanAlbumFolder, track.ID, Config.CoverFormat) fmt.Printf("Error deleting file: %s\n", trackCovPath)
counter.Error++ counter.Error++
return return
} }
@ -824,7 +824,7 @@ func rip(albumId string, token string, storefront string, mediaUserToken string,
} }
var EnhancedHls_m3u8 string var EnhancedHls_m3u8 string
if needCheck { if needCheck {
EnhancedHls_m3u8, err = checkM3u8(meta.Data[0].Relationships.Tracks.Data[0].ID, "album") EnhancedHls_m3u8, _ = checkM3u8(meta.Data[0].Relationships.Tracks.Data[0].ID, "album")
if strings.HasSuffix(EnhancedHls_m3u8, ".m3u8") { if strings.HasSuffix(EnhancedHls_m3u8, ".m3u8") {
manifest1.Attributes.ExtendedAssetUrls.EnhancedHls = EnhancedHls_m3u8 manifest1.Attributes.ExtendedAssetUrls.EnhancedHls = EnhancedHls_m3u8
} }
@ -889,14 +889,14 @@ func rip(albumId string, token string, storefront string, mediaUserToken string,
//get artist cover //get artist cover
if Config.SaveArtistCover && !(strings.Contains(albumId, "pl.")) { if Config.SaveArtistCover && !(strings.Contains(albumId, "pl.")) {
if len(meta.Data[0].Relationships.Artists.Data) > 0 { if len(meta.Data[0].Relationships.Artists.Data) > 0 {
err = writeCover(singerFolder, "folder", 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 { if err != nil {
fmt.Println("Failed to write artist cover.") fmt.Println("Failed to write artist cover.")
} }
} }
} }
//get album cover //get album cover
err = writeCover(sanAlbumFolder, "cover", meta.Data[0].Attributes.Artwork.URL) covPath, err := writeCover(sanAlbumFolder, "cover", meta.Data[0].Attributes.Artwork.URL)
if err != nil { if err != nil {
fmt.Println("Failed to write cover.") fmt.Println("Failed to write cover.")
} }
@ -971,7 +971,7 @@ func rip(albumId string, token string, storefront string, mediaUserToken string,
for trackNum, track := range meta.Data[0].Relationships.Tracks.Data { for trackNum, track := range meta.Data[0].Relationships.Tracks.Data {
trackNum++ trackNum++
if urlArg_i == track.ID { if urlArg_i == track.ID {
downloadTrack(trackNum, trackTotal, meta, track, albumId, token, storefront, mediaUserToken, sanAlbumFolder, Codec, &counter) downloadTrack(trackNum, trackTotal, meta, track, albumId, token, storefront, mediaUserToken, sanAlbumFolder, Codec, covPath)
return nil return nil
} }
} }
@ -1097,7 +1097,7 @@ func rip(albumId string, token string, storefront string, mediaUserToken string,
continue continue
} }
if isInArray(selected, trackNum) { if isInArray(selected, trackNum) {
downloadTrack(trackNum, trackTotal, meta, track, albumId, token, storefront, mediaUserToken, sanAlbumFolder, Codec, &counter) downloadTrack(trackNum, trackTotal, meta, track, albumId, token, storefront, mediaUserToken, sanAlbumFolder, Codec, covPath)
} }
} }
return nil return nil
@ -1356,7 +1356,7 @@ func mvDownloader(adamID string, saveDir string, token string, storefront string
vidPath := filepath.Join(saveDir, fmt.Sprintf("%s_vid.mp4", adamID)) vidPath := filepath.Join(saveDir, fmt.Sprintf("%s_vid.mp4", adamID))
audPath := filepath.Join(saveDir, fmt.Sprintf("%s_aud.mp4", adamID)) audPath := filepath.Join(saveDir, fmt.Sprintf("%s_aud.mp4", adamID))
mvSaveName := MVInfo.Data[0].Attributes.Name mvSaveName := fmt.Sprintf("%s (%s)", MVInfo.Data[0].Attributes.Name, adamID)
if meta != nil { if meta != nil {
mvSaveName = fmt.Sprintf("%02d. %s", trackNum, MVInfo.Data[0].Attributes.Name) mvSaveName = fmt.Sprintf("%02d. %s", trackNum, MVInfo.Data[0].Attributes.Name)
} }
@ -1372,7 +1372,7 @@ func mvDownloader(adamID string, saveDir string, token string, storefront string
mvm3u8url, _, _ := runv3.GetWebplayback(adamID, token, mediaUserToken, true) mvm3u8url, _, _ := runv3.GetWebplayback(adamID, token, mediaUserToken, true)
if mvm3u8url == "" { if mvm3u8url == "" {
return errors.New("media-user-token may wrong or expired.") return errors.New("media-user-token may wrong or expired")
} }
os.MkdirAll(saveDir, os.ModePerm) os.MkdirAll(saveDir, os.ModePerm)
@ -1435,21 +1435,21 @@ func mvDownloader(adamID string, saveDir string, token string, storefront string
} }
// Extract and save thumbnail if enabled // Extract and save thumbnail if enabled
var covPath string
if Config.SaveThumbnailImage { if Config.SaveThumbnailImage {
// Get the highest quality thumbnail URL from the MV info // Get the highest quality thumbnail URL from the MV info
thumbURL := MVInfo.Data[0].Attributes.Artwork.URL thumbURL := MVInfo.Data[0].Attributes.Artwork.URL
thumbURL = strings.Replace(thumbURL, "{w}x{h}", Config.CoverSize, 1)
// Generate base name without extension // Generate base name without extension
baseThumbName := forbiddenNames.ReplaceAllString(mvSaveName, "_") + "_thumbnail" baseThumbName := forbiddenNames.ReplaceAllString(mvSaveName, "_") + "_thumbnail"
// Download and save thumbnail // Download and save thumbnail
err = writeCover(saveDir, baseThumbName, thumbURL) covPath, err = writeCover(saveDir, baseThumbName, thumbURL)
if err != nil { if err != nil {
fmt.Println("Failed to save MV thumbnail:", err) fmt.Println("Failed to save MV thumbnail:", err)
} else { } else {
fmt.Println("MV thumbnail saved successfully") fmt.Println("MV thumbnail saved successfully")
tags = append(tags, fmt.Sprintf("cover=%s/%s.%s", saveDir, baseThumbName, Config.CoverFormat)) tags = append(tags, fmt.Sprintf("cover=%s", covPath))
} }
} }
@ -1464,6 +1464,7 @@ func mvDownloader(adamID string, saveDir string, token string, storefront string
fmt.Printf("\rMV Remuxed. \n") fmt.Printf("\rMV Remuxed. \n")
defer os.Remove(vidPath) defer os.Remove(vidPath)
defer os.Remove(audPath) defer os.Remove(audPath)
defer os.Remove(covPath)
return nil return nil
} }