commit
ce4d5f8af0
@ -6,7 +6,7 @@ lrc-format: "lrc" #lrc or ttml
|
|||||||
embed-lrc: true #Unable to embed ttml lyrics
|
embed-lrc: true #Unable to embed ttml lyrics
|
||||||
save-lrc-file: false
|
save-lrc-file: false
|
||||||
save-artist-cover: false
|
save-artist-cover: false
|
||||||
save-animated-artwork: false # If enabled, requires ffmpeg
|
save-animated-artwork: true # If enabled, requires ffmpeg
|
||||||
emby-animated-artwork: false # If enabled, requires ffmpeg
|
emby-animated-artwork: false # If enabled, requires ffmpeg
|
||||||
embed-cover: true
|
embed-cover: true
|
||||||
cover-size: 5000x5000
|
cover-size: 5000x5000
|
||||||
@ -33,7 +33,7 @@ playlist-folder-format: "{PlaylistName}"
|
|||||||
song-file-format: "{SongNumer}. {SongName}"
|
song-file-format: "{SongNumer}. {SongName}"
|
||||||
#{ArtistId} {ArtistName}/{UrlArtistName}
|
#{ArtistId} {ArtistName}/{UrlArtistName}
|
||||||
#if artist-folder-format set "",will not make artist folder
|
#if artist-folder-format set "",will not make artist folder
|
||||||
artist-folder-format: "{ArtistName}"
|
artist-folder-format: "{UrlArtistName}"
|
||||||
#if set "" will not add tag
|
#if set "" will not add tag
|
||||||
explicit-choice : "[E]"
|
explicit-choice : "[E]"
|
||||||
clean-choice : "[C]"
|
clean-choice : "[C]"
|
||||||
|
2
go.mod
2
go.mod
@ -51,6 +51,7 @@ require (
|
|||||||
github.com/klauspost/pgzip v1.2.6 // indirect
|
github.com/klauspost/pgzip v1.2.6 // indirect
|
||||||
github.com/kr/pretty v0.2.1 // indirect
|
github.com/kr/pretty v0.2.1 // indirect
|
||||||
github.com/libdns/libdns v0.2.2 // indirect
|
github.com/libdns/libdns v0.2.2 // indirect
|
||||||
|
github.com/mattn/go-runewidth v0.0.9 // indirect
|
||||||
github.com/mholt/acmez/v3 v3.0.0 // indirect
|
github.com/mholt/acmez/v3 v3.0.0 // indirect
|
||||||
github.com/mholt/archives v0.1.0 // indirect
|
github.com/mholt/archives v0.1.0 // indirect
|
||||||
github.com/miekg/dns v1.1.62 // indirect
|
github.com/miekg/dns v1.1.62 // indirect
|
||||||
@ -93,6 +94,7 @@ require (
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/beevik/etree v1.3.0
|
github.com/beevik/etree v1.3.0
|
||||||
|
github.com/olekukonko/tablewriter v0.0.5
|
||||||
github.com/zhaarey/go-mp4tag v0.0.0-20250210094042-22578afc09bf
|
github.com/zhaarey/go-mp4tag v0.0.0-20250210094042-22578afc09bf
|
||||||
gopkg.in/yaml.v2 v2.2.8
|
gopkg.in/yaml.v2 v2.2.8
|
||||||
)
|
)
|
||||||
|
4
go.sum
4
go.sum
@ -166,6 +166,8 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
|||||||
github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s=
|
github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s=
|
||||||
github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
|
github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||||
|
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||||
github.com/mholt/acmez/v3 v3.0.0 h1:r1NcjuWR0VaKP2BTjDK9LRFBw/WvURx3jlaEUl9Ht8E=
|
github.com/mholt/acmez/v3 v3.0.0 h1:r1NcjuWR0VaKP2BTjDK9LRFBw/WvURx3jlaEUl9Ht8E=
|
||||||
github.com/mholt/acmez/v3 v3.0.0/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ=
|
github.com/mholt/acmez/v3 v3.0.0/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ=
|
||||||
github.com/mholt/archives v0.1.0 h1:FacgJyrjiuyomTuNA92X5GyRBRZjE43Y/lrzKIlF35Q=
|
github.com/mholt/archives v0.1.0 h1:FacgJyrjiuyomTuNA92X5GyRBRZjE43Y/lrzKIlF35Q=
|
||||||
@ -181,6 +183,8 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
|
|||||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
github.com/nwaples/rardecode/v2 v2.0.1 h1:3MN6/R+Y4c7e+21U3yhWuUcf72sYmcmr6jtiuAVSH1A=
|
github.com/nwaples/rardecode/v2 v2.0.1 h1:3MN6/R+Y4c7e+21U3yhWuUcf72sYmcmr6jtiuAVSH1A=
|
||||||
github.com/nwaples/rardecode/v2 v2.0.1/go.mod h1:yntwv/HfMc/Hbvtq9I19D1n58te3h6KsqCf3GxyfBGY=
|
github.com/nwaples/rardecode/v2 v2.0.1/go.mod h1:yntwv/HfMc/Hbvtq9I19D1n58te3h6KsqCf3GxyfBGY=
|
||||||
|
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||||
|
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||||
github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU=
|
github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU=
|
||||||
github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk=
|
github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk=
|
||||||
github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=
|
github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=
|
||||||
|
120
main.go
120
main.go
@ -19,19 +19,19 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
"github.com/spf13/pflag"
|
|
||||||
"github.com/zhaarey/go-mp4tag"
|
|
||||||
|
|
||||||
"gopkg.in/yaml.v2"
|
|
||||||
|
|
||||||
"github.com/beevik/etree"
|
|
||||||
"github.com/grafov/m3u8"
|
|
||||||
|
|
||||||
"main/utils/runv2"
|
"main/utils/runv2"
|
||||||
"main/utils/structs"
|
"main/utils/structs"
|
||||||
|
|
||||||
"main/utils/runv3"
|
"main/utils/runv3"
|
||||||
|
|
||||||
|
"github.com/spf13/pflag"
|
||||||
|
"github.com/zhaarey/go-mp4tag"
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
"github.com/beevik/etree"
|
||||||
|
"github.com/grafov/m3u8"
|
||||||
|
"github.com/olekukonko/tablewriter"
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -185,10 +185,10 @@ func getUrlArtistName(artistUrl string, token string) (string, string, error) {
|
|||||||
func checkArtist(artistUrl string, token string, relationship string) ([]string, error) {
|
func checkArtist(artistUrl string, token string, relationship string) ([]string, error) {
|
||||||
storefront, artistId := checkUrlArtist(artistUrl)
|
storefront, artistId := checkUrlArtist(artistUrl)
|
||||||
Num := 0
|
Num := 0
|
||||||
|
//id := 1
|
||||||
var args []string
|
var args []string
|
||||||
var urls []string
|
var urls []string
|
||||||
var options []string
|
var options [][]string
|
||||||
for {
|
for {
|
||||||
req, err := http.NewRequest("GET", fmt.Sprintf("https://amp-api.music.apple.com/v1/catalog/%s/artists/%s/%s?limit=100&offset=%d&l=%s", storefront, artistId, relationship, Num, Config.Language), nil)
|
req, err := http.NewRequest("GET", fmt.Sprintf("https://amp-api.music.apple.com/v1/catalog/%s/artists/%s/%s?limit=100&offset=%d&l=%s", storefront, artistId, relationship, Num, Config.Language), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -211,23 +211,52 @@ func checkArtist(artistUrl string, token string, relationship string) ([]string,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for _, album := range obj.Data {
|
for _, album := range obj.Data {
|
||||||
urls = append(urls, album.Attributes.URL)
|
options = append(options, []string{album.Attributes.Name, album.Attributes.ReleaseDate, album.ID, album.Attributes.URL})
|
||||||
options = append(options, fmt.Sprintf("%s(%s)", album.Attributes.Name, album.ID))
|
|
||||||
}
|
}
|
||||||
Num = Num + 100
|
Num = Num + 100
|
||||||
if len(obj.Next) == 0 {
|
if len(obj.Next) == 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i, option := range options {
|
sort.Slice(options, func(i, j int) bool {
|
||||||
fmt.Printf("%02d: %s\n", i+1, option)
|
// 将日期字符串解析为 time.Time 类型进行比较
|
||||||
|
dateI, _ := time.Parse("2006-01-02", options[i][1])
|
||||||
|
dateJ, _ := time.Parse("2006-01-02", options[j][1])
|
||||||
|
return dateI.Before(dateJ) // 返回 true 表示 i 在 j 前面
|
||||||
|
})
|
||||||
|
|
||||||
|
table := tablewriter.NewWriter(os.Stdout)
|
||||||
|
if relationship == "albums" {
|
||||||
|
table.SetHeader([]string{"", "Album Name", "Date", "Album ID"})
|
||||||
|
}else if relationship == "music-videos" {
|
||||||
|
table.SetHeader([]string{"", "MV Name", "Date", "MV ID"})
|
||||||
}
|
}
|
||||||
|
//table.SetFooter([]string{"", "", "Total", "$146.93"})
|
||||||
|
//table.SetAutoMergeCells(true)
|
||||||
|
//table.SetAutoMergeCellsByColumnIndex([]int{1,2,3})
|
||||||
|
table.SetRowLine(true)
|
||||||
|
//table.AppendBulk(options)
|
||||||
|
table.SetHeaderColor(tablewriter.Colors{tablewriter.Bold, tablewriter.BgGreenColor},
|
||||||
|
tablewriter.Colors{tablewriter.FgHiRedColor, tablewriter.Bold, tablewriter.BgBlackColor},
|
||||||
|
tablewriter.Colors{tablewriter.BgRedColor, tablewriter.FgWhiteColor},
|
||||||
|
tablewriter.Colors{tablewriter.BgCyanColor, tablewriter.FgWhiteColor})
|
||||||
|
|
||||||
|
table.SetColumnColor(tablewriter.Colors{tablewriter.Bold, tablewriter.FgHiBlackColor},
|
||||||
|
tablewriter.Colors{tablewriter.Bold, tablewriter.FgHiRedColor},
|
||||||
|
tablewriter.Colors{tablewriter.Bold, tablewriter.FgHiBlackColor},
|
||||||
|
tablewriter.Colors{tablewriter.Bold, tablewriter.FgBlackColor})
|
||||||
|
for i, v := range options {
|
||||||
|
urls = append(urls, v[3])
|
||||||
|
options[i] = append([]string{fmt.Sprint(i + 1)}, v[:3]...)
|
||||||
|
table.Append(options[i])
|
||||||
|
}
|
||||||
|
table.Render()
|
||||||
if artist_select {
|
if artist_select {
|
||||||
fmt.Println("You have selected all options:")
|
fmt.Println("You have selected all options:")
|
||||||
return urls, nil
|
return urls, nil
|
||||||
}
|
}
|
||||||
reader := bufio.NewReader(os.Stdin)
|
reader := bufio.NewReader(os.Stdin)
|
||||||
fmt.Println("Please select from the following " + relationship + " options (multiple options separated by commas, ranges supported, or type 'all' to select all)")
|
fmt.Println("Please select from the " + relationship + " options above (multiple options separated by commas, ranges supported, or type 'all' to select all)")
|
||||||
fmt.Print("Enter your choice: ")
|
fmt.Print("Enter your choice: ")
|
||||||
input, _ := reader.ReadString('\n')
|
input, _ := reader.ReadString('\n')
|
||||||
|
|
||||||
@ -714,7 +743,12 @@ func rip(albumId string, token string, storefront string, mediaUserToken string,
|
|||||||
needCheck = true
|
needCheck = true
|
||||||
}
|
}
|
||||||
if needCheck {
|
if needCheck {
|
||||||
m3u8Url, err = checkM3u8(track.ID, "song")
|
fullM3u8Url, err := checkM3u8(track.ID, "song")
|
||||||
|
if err == nil && strings.HasSuffix(fullM3u8Url, ".m3u8"){
|
||||||
|
m3u8Url = fullM3u8Url
|
||||||
|
} else {
|
||||||
|
fmt.Println("Failed to get best quality m3u8 from device m3u8 port, will use m3u8 from Web API")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, err = extractMedia(m3u8Url, true)
|
_, _, err = extractMedia(m3u8Url, true)
|
||||||
@ -1231,9 +1265,28 @@ func mvDownloader(adamID string, saveDir string, token string, storefront string
|
|||||||
fmt.Println("\u26A0 Failed to get MV manifest:", err)
|
fmt.Println("\u26A0 Failed to get MV manifest:", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//获取传入的专辑信息当中该mv所在的位置
|
||||||
|
var trackTotal int
|
||||||
|
var trackNum int
|
||||||
|
var index int
|
||||||
|
if meta != nil {
|
||||||
|
trackTotal = len(meta.Data[0].Relationships.Tracks.Data)
|
||||||
|
for i, track := range meta.Data[0].Relationships.Tracks.Data {
|
||||||
|
if adamID == track.ID {
|
||||||
|
index = i
|
||||||
|
trackNum = i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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))
|
||||||
mvOutPath := filepath.Join(saveDir, fmt.Sprintf("%s.mp4", forbiddenNames.ReplaceAllString(MVInfo.Data[0].Attributes.Name, "_")))
|
mvSaveName := MVInfo.Data[0].Attributes.Name
|
||||||
|
if meta != nil {
|
||||||
|
mvSaveName = fmt.Sprintf("%02d. %s", trackNum, MVInfo.Data[0].Attributes.Name)
|
||||||
|
}
|
||||||
|
mvOutPath := filepath.Join(saveDir, fmt.Sprintf("%s.mp4", forbiddenNames.ReplaceAllString(mvSaveName, "_")))
|
||||||
|
|
||||||
fmt.Println(MVInfo.Data[0].Attributes.Name)
|
fmt.Println(MVInfo.Data[0].Attributes.Name)
|
||||||
|
|
||||||
@ -1274,19 +1327,6 @@ func mvDownloader(adamID string, saveDir string, token string, storefront string
|
|||||||
tags = append(tags, "rating=0")
|
tags = append(tags, "rating=0")
|
||||||
}
|
}
|
||||||
|
|
||||||
//获取传入的专辑信息当中该mv所在的位置
|
|
||||||
var trackTotal int
|
|
||||||
var trackNum int
|
|
||||||
var index int
|
|
||||||
if meta != nil {
|
|
||||||
trackTotal = len(meta.Data[0].Relationships.Tracks.Data)
|
|
||||||
for i, track := range meta.Data[0].Relationships.Tracks.Data {
|
|
||||||
if adamID == track.ID {
|
|
||||||
index = i
|
|
||||||
trackNum = i + 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//根据情况额外添加可使用的tags
|
//根据情况额外添加可使用的tags
|
||||||
if meta != nil {
|
if meta != nil {
|
||||||
if meta.Data[0].Type == "playlists" && !Config.UseSongInfoForPlaylist {
|
if meta.Data[0].Type == "playlists" && !Config.UseSongInfoForPlaylist {
|
||||||
@ -1302,7 +1342,7 @@ func mvDownloader(adamID string, saveDir string, token string, storefront string
|
|||||||
tags = append(tags, fmt.Sprintf("album=%s", meta.Data[0].Relationships.Tracks.Data[index].Attributes.AlbumName))
|
tags = append(tags, fmt.Sprintf("album=%s", meta.Data[0].Relationships.Tracks.Data[index].Attributes.AlbumName))
|
||||||
tags = append(tags, fmt.Sprintf("disk=%d/%d", meta.Data[0].Relationships.Tracks.Data[index].Attributes.DiscNumber, meta.Data[0].Relationships.Tracks.Data[trackTotal-1].Attributes.DiscNumber))
|
tags = append(tags, fmt.Sprintf("disk=%d/%d", meta.Data[0].Relationships.Tracks.Data[index].Attributes.DiscNumber, meta.Data[0].Relationships.Tracks.Data[trackTotal-1].Attributes.DiscNumber))
|
||||||
tags = append(tags, fmt.Sprintf("track=%d", meta.Data[0].Relationships.Tracks.Data[index].Attributes.TrackNumber))
|
tags = append(tags, fmt.Sprintf("track=%d", meta.Data[0].Relationships.Tracks.Data[index].Attributes.TrackNumber))
|
||||||
tags = append(tags, fmt.Sprintf("tracknum=%d/%d", meta.Data[0].Relationships.Tracks.Data[index].Attributes.TrackNumber, trackTotal))
|
tags = append(tags, fmt.Sprintf("tracknum=%d/%d", meta.Data[0].Relationships.Tracks.Data[index].Attributes.TrackNumber, meta.Data[0].Attributes.TrackCount))
|
||||||
tags = append(tags, fmt.Sprintf("album_artist=%s", meta.Data[0].Attributes.ArtistName))
|
tags = append(tags, fmt.Sprintf("album_artist=%s", meta.Data[0].Attributes.ArtistName))
|
||||||
tags = append(tags, fmt.Sprintf("performer=%s", meta.Data[0].Relationships.Tracks.Data[index].Attributes.ArtistName))
|
tags = append(tags, fmt.Sprintf("performer=%s", meta.Data[0].Relationships.Tracks.Data[index].Attributes.ArtistName))
|
||||||
tags = append(tags, fmt.Sprintf("copyright=%s", meta.Data[0].Attributes.Copyright))
|
tags = append(tags, fmt.Sprintf("copyright=%s", meta.Data[0].Attributes.Copyright))
|
||||||
@ -1677,12 +1717,20 @@ func extractMedia(b string, more_mode bool) (string, string, error) {
|
|||||||
})
|
})
|
||||||
if debug_mode && more_mode {
|
if debug_mode && more_mode {
|
||||||
fmt.Println("\nDebug: All Available Variants:")
|
fmt.Println("\nDebug: All Available Variants:")
|
||||||
fmt.Println("-----------------------------")
|
var data [][]string
|
||||||
for _, variant := range master.Variants {
|
for _, variant := range master.Variants {
|
||||||
fmt.Printf("Codec: %s, Audio: %s, Bandwidth: %d\n",
|
data = append(data, []string{variant.Codecs, variant.Audio, fmt.Sprint(variant.Bandwidth)})
|
||||||
variant.Codecs, variant.Audio, variant.Bandwidth)
|
//fmt.Printf("Codec: %s, Audio: %s, Bandwidth: %d\n",
|
||||||
|
//variant.Codecs, variant.Audio, variant.Bandwidth)
|
||||||
}
|
}
|
||||||
fmt.Println("-----------------------------")
|
table := tablewriter.NewWriter(os.Stdout)
|
||||||
|
table.SetHeader([]string{"Codec", "Audio", "Bandwidth"})
|
||||||
|
//table.SetFooter([]string{"", "", "Total", "$146.93"})
|
||||||
|
table.SetAutoMergeCells(true)
|
||||||
|
//table.SetAutoMergeCellsByColumnIndex([]int{1,2,3})
|
||||||
|
table.SetRowLine(true)
|
||||||
|
table.AppendBulk(data)
|
||||||
|
table.Render()
|
||||||
|
|
||||||
var hasAAC, hasLossless, hasHiRes, hasAtmos, hasDolbyAudio bool
|
var hasAAC, hasLossless, hasHiRes, hasAtmos, hasDolbyAudio bool
|
||||||
var aacQuality, losslessQuality, hiResQuality, atmosQuality, dolbyAudioQuality string
|
var aacQuality, losslessQuality, hiResQuality, atmosQuality, dolbyAudioQuality string
|
||||||
|
Loading…
x
Reference in New Issue
Block a user