fix
This commit is contained in:
parent
42ca991329
commit
ac937f8076
64
main.go
64
main.go
@ -22,16 +22,15 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"main/utils/runv2"
|
"main/utils/runv2"
|
||||||
"main/utils/structs"
|
|
||||||
"main/utils/runv3"
|
"main/utils/runv3"
|
||||||
|
"main/utils/structs"
|
||||||
|
|
||||||
"github.com/spf13/pflag"
|
|
||||||
"github.com/zhaarey/go-mp4tag"
|
|
||||||
"gopkg.in/yaml.v2"
|
|
||||||
"github.com/beevik/etree"
|
"github.com/beevik/etree"
|
||||||
"github.com/grafov/m3u8"
|
"github.com/grafov/m3u8"
|
||||||
"github.com/olekukonko/tablewriter"
|
"github.com/olekukonko/tablewriter"
|
||||||
|
"github.com/spf13/pflag"
|
||||||
|
"github.com/zhaarey/go-mp4tag"
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -228,7 +227,7 @@ func checkArtist(artistUrl string, token string, relationship string) ([]string,
|
|||||||
table := tablewriter.NewWriter(os.Stdout)
|
table := tablewriter.NewWriter(os.Stdout)
|
||||||
if relationship == "albums" {
|
if relationship == "albums" {
|
||||||
table.SetHeader([]string{"", "Album Name", "Date", "Album ID"})
|
table.SetHeader([]string{"", "Album Name", "Date", "Album ID"})
|
||||||
}else if relationship == "music-videos" {
|
} else if relationship == "music-videos" {
|
||||||
table.SetHeader([]string{"", "MV Name", "Date", "MV ID"})
|
table.SetHeader([]string{"", "MV Name", "Date", "MV ID"})
|
||||||
}
|
}
|
||||||
//table.SetFooter([]string{"", "", "Total", "$146.93"})
|
//table.SetFooter([]string{"", "", "Total", "$146.93"})
|
||||||
@ -744,8 +743,8 @@ func rip(albumId string, token string, storefront string, mediaUserToken string,
|
|||||||
}
|
}
|
||||||
if needCheck {
|
if needCheck {
|
||||||
fullM3u8Url, err := checkM3u8(track.ID, "song")
|
fullM3u8Url, err := checkM3u8(track.ID, "song")
|
||||||
if err == nil && strings.HasSuffix(fullM3u8Url, ".m3u8"){
|
if err == nil && strings.HasSuffix(fullM3u8Url, ".m3u8") {
|
||||||
m3u8Url = fullM3u8Url
|
m3u8Url = fullM3u8Url
|
||||||
} else {
|
} else {
|
||||||
fmt.Println("Failed to get best quality m3u8 from device m3u8 port, will use m3u8 from Web API")
|
fmt.Println("Failed to get best quality m3u8 from device m3u8 port, will use m3u8 from Web API")
|
||||||
}
|
}
|
||||||
@ -1001,7 +1000,7 @@ func rip(albumId string, token string, storefront string, mediaUserToken string,
|
|||||||
//table.SetFooter([]string{"", "", "Footer", "Footer4"})
|
//table.SetFooter([]string{"", "", "Footer", "Footer4"})
|
||||||
table.SetRowLine(false)
|
table.SetRowLine(false)
|
||||||
//table.SetAutoMergeCells(true)
|
//table.SetAutoMergeCells(true)
|
||||||
table.SetCaption(meta.Data[0].Type == "albums", fmt.Sprintf("Storefront: %s, %d tracks missing", strings.ToUpper(storefront), meta.Data[0].Attributes.TrackCount -trackTotal))
|
table.SetCaption(meta.Data[0].Type == "albums", fmt.Sprintf("Storefront: %s, %d tracks missing", strings.ToUpper(storefront), meta.Data[0].Attributes.TrackCount-trackTotal))
|
||||||
table.SetHeaderColor(tablewriter.Colors{tablewriter.Bold, tablewriter.BgGreenColor},
|
table.SetHeaderColor(tablewriter.Colors{tablewriter.Bold, tablewriter.BgGreenColor},
|
||||||
tablewriter.Colors{tablewriter.FgHiRedColor, tablewriter.Bold, tablewriter.BgBlackColor},
|
tablewriter.Colors{tablewriter.FgHiRedColor, tablewriter.Bold, tablewriter.BgBlackColor},
|
||||||
tablewriter.Colors{tablewriter.BgRedColor, tablewriter.FgWhiteColor},
|
tablewriter.Colors{tablewriter.BgRedColor, tablewriter.FgWhiteColor},
|
||||||
@ -1369,6 +1368,9 @@ 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 == "" {
|
||||||
|
return errors.New("media-user-token may wrong or expired.")
|
||||||
|
}
|
||||||
|
|
||||||
os.MkdirAll(saveDir, os.ModePerm)
|
os.MkdirAll(saveDir, os.ModePerm)
|
||||||
//video
|
//video
|
||||||
@ -1428,6 +1430,26 @@ func mvDownloader(adamID string, saveDir string, token string, storefront string
|
|||||||
//tags = append(tags, fmt.Sprintf("album_artist=%s", MVInfo.Data[0].Attributes.ArtistName))
|
//tags = append(tags, fmt.Sprintf("album_artist=%s", MVInfo.Data[0].Attributes.ArtistName))
|
||||||
tags = append(tags, fmt.Sprintf("performer=%s", MVInfo.Data[0].Attributes.ArtistName))
|
tags = append(tags, fmt.Sprintf("performer=%s", MVInfo.Data[0].Attributes.ArtistName))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Extract and save thumbnail if enabled
|
||||||
|
if Config.SaveThumbnailImage {
|
||||||
|
// Get the highest quality thumbnail URL from the MV info
|
||||||
|
thumbURL := MVInfo.Data[0].Attributes.Artwork.URL
|
||||||
|
thumbURL = strings.Replace(thumbURL, "{w}x{h}", Config.CoverSize, 1)
|
||||||
|
|
||||||
|
// Generate base name without extension
|
||||||
|
baseThumbName := forbiddenNames.ReplaceAllString(mvSaveName, "_") + "_thumbnail"
|
||||||
|
|
||||||
|
// Download and save thumbnail
|
||||||
|
err = writeCover(saveDir, baseThumbName, thumbURL)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Failed to save MV thumbnail:", err)
|
||||||
|
} else {
|
||||||
|
fmt.Println("MV thumbnail saved successfully")
|
||||||
|
tags = append(tags, fmt.Sprintf("cover=%s/%s.%s", saveDir, baseThumbName, Config.CoverFormat))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//mux and add tag
|
//mux and add tag
|
||||||
tagsString := strings.Join(tags, ":")
|
tagsString := strings.Join(tags, ":")
|
||||||
muxCmd := exec.Command("MP4Box", "-itags", tagsString, "-quiet", "-add", vidPath, "-add", audPath, "-keep-utc", "-new", mvOutPath)
|
muxCmd := exec.Command("MP4Box", "-itags", tagsString, "-quiet", "-add", vidPath, "-add", audPath, "-keep-utc", "-new", mvOutPath)
|
||||||
@ -1440,24 +1462,6 @@ func mvDownloader(adamID string, saveDir string, token string, storefront string
|
|||||||
defer os.Remove(vidPath)
|
defer os.Remove(vidPath)
|
||||||
defer os.Remove(audPath)
|
defer os.Remove(audPath)
|
||||||
|
|
||||||
// Extract and save thumbnail if enabled
|
|
||||||
if Config.SaveThumbnailImage {
|
|
||||||
// Get the highest quality thumbnail URL from the MV info
|
|
||||||
thumbURL := MVInfo.Data[0].Attributes.Artwork.URL
|
|
||||||
thumbURL = strings.Replace(thumbURL, "{w}x{h}", Config.CoverSize, 1)
|
|
||||||
|
|
||||||
// Generate base name without extension
|
|
||||||
baseThumbName := forbiddenNames.ReplaceAllString(mvSaveName, "_") + "_thumbnail"
|
|
||||||
|
|
||||||
// Download and save thumbnail
|
|
||||||
err = writeCover(saveDir, baseThumbName, thumbURL)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Failed to save MV thumbnail:", err)
|
|
||||||
} else {
|
|
||||||
fmt.Println("MV thumbnail saved successfully")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1785,7 +1789,7 @@ func extractMedia(b string, more_mode bool) (string, string, error) {
|
|||||||
}
|
}
|
||||||
resp, err := http.Get(b)
|
resp, err := http.Get(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "",err
|
return "", "", err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
@ -1809,9 +1813,9 @@ func extractMedia(b string, more_mode bool) (string, string, error) {
|
|||||||
fmt.Println("\nDebug: All Available Variants:")
|
fmt.Println("\nDebug: All Available Variants:")
|
||||||
var data [][]string
|
var data [][]string
|
||||||
for _, variant := range master.Variants {
|
for _, variant := range master.Variants {
|
||||||
data = append(data, []string{variant.Codecs, variant.Audio, fmt.Sprint(variant.Bandwidth)})
|
data = append(data, []string{variant.Codecs, variant.Audio, fmt.Sprint(variant.Bandwidth)})
|
||||||
//fmt.Printf("Codec: %s, Audio: %s, Bandwidth: %d\n",
|
//fmt.Printf("Codec: %s, Audio: %s, Bandwidth: %d\n",
|
||||||
//variant.Codecs, variant.Audio, variant.Bandwidth)
|
//variant.Codecs, variant.Audio, variant.Bandwidth)
|
||||||
}
|
}
|
||||||
table := tablewriter.NewWriter(os.Stdout)
|
table := tablewriter.NewWriter(os.Stdout)
|
||||||
table.SetHeader([]string{"Codec", "Audio", "Bandwidth"})
|
table.SetHeader([]string{"Codec", "Audio", "Bandwidth"})
|
||||||
|
@ -4,25 +4,29 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/gospider007/requests"
|
"github.com/gospider007/requests"
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
|
|
||||||
//"log/slog"
|
//"log/slog"
|
||||||
"os"
|
|
||||||
cdm "main/utils/runv3/cdm"
|
cdm "main/utils/runv3/cdm"
|
||||||
key "main/utils/runv3/key"
|
key "main/utils/runv3/key"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
|
||||||
"github.com/Eyevinn/mp4ff/mp4"
|
"github.com/Eyevinn/mp4ff/mp4"
|
||||||
"bytes"
|
|
||||||
"io"
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
//"io/ioutil"
|
//"io/ioutil"
|
||||||
"net/http"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/grafov/m3u8"
|
"net/http"
|
||||||
"strings"
|
|
||||||
"github.com/schollz/progressbar/v3"
|
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/grafov/m3u8"
|
||||||
|
"github.com/schollz/progressbar/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PlaybackLicense struct {
|
type PlaybackLicense struct {
|
||||||
@ -32,24 +36,24 @@ type PlaybackLicense struct {
|
|||||||
Status int `json:"status"`
|
Status int `json:"status"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// func log() {
|
// func log() {
|
||||||
// f, err := os.OpenFile("log.txt", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
// f, err := os.OpenFile("log.txt", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
||||||
// if err != nil {
|
// if err != nil {
|
||||||
// slog.Error("error opening file: %s", err)
|
// slog.Error("error opening file: %s", err)
|
||||||
// }
|
// }
|
||||||
// defer func(f *os.File) {
|
// defer func(f *os.File) {
|
||||||
// err := f.Close()
|
// err := f.Close()
|
||||||
// if err != nil {
|
// if err != nil {
|
||||||
// slog.Error("error closing file: %s", err)
|
// slog.Error("error closing file: %s", err)
|
||||||
// }
|
// }
|
||||||
// }(f)
|
// }(f)
|
||||||
// opts := slog.HandlerOptions{
|
// opts := slog.HandlerOptions{
|
||||||
// AddSource: true,
|
// AddSource: true,
|
||||||
// Level: slog.LevelDebug,
|
// Level: slog.LevelDebug,
|
||||||
// }
|
// }
|
||||||
// logger := slog.New(slog.NewJSONHandler(os.Stdout, &opts))
|
// logger := slog.New(slog.NewJSONHandler(os.Stdout, &opts))
|
||||||
// slog.SetDefault(logger)
|
// slog.SetDefault(logger)
|
||||||
//}
|
// }
|
||||||
func getPSSH(contentId string, kidBase64 string) (string, error) {
|
func getPSSH(contentId string, kidBase64 string) (string, error) {
|
||||||
kidBytes, err := base64.StdEncoding.DecodeString(kidBase64)
|
kidBytes, err := base64.StdEncoding.DecodeString(kidBase64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -145,10 +149,10 @@ func GetWebplayback(adamId string, authtoken string, mutoken string, mvmode bool
|
|||||||
fmt.Println("json err:", err)
|
fmt.Println("json err:", err)
|
||||||
return "", "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
if mvmode {
|
|
||||||
return obj.List[0].HlsPlaylistUrl, "", nil
|
|
||||||
}
|
|
||||||
if len(obj.List) > 0 {
|
if len(obj.List) > 0 {
|
||||||
|
if mvmode {
|
||||||
|
return obj.List[0].HlsPlaylistUrl, "", nil
|
||||||
|
}
|
||||||
// 遍历 Assets
|
// 遍历 Assets
|
||||||
for i, _ := range obj.List[0].Assets {
|
for i, _ := range obj.List[0].Assets {
|
||||||
if obj.List[0].Assets[i].Flavor == "28:ctrp256" {
|
if obj.List[0].Assets[i].Flavor == "28:ctrp256" {
|
||||||
@ -163,15 +167,16 @@ func GetWebplayback(adamId string, authtoken string, mutoken string, mvmode bool
|
|||||||
}
|
}
|
||||||
return "", "", nil
|
return "", "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type Songlist struct {
|
type Songlist struct {
|
||||||
List []struct {
|
List []struct {
|
||||||
Hlsurl string `json:"hls-key-cert-url"`
|
Hlsurl string `json:"hls-key-cert-url"`
|
||||||
HlsPlaylistUrl string `json:"hls-playlist-url"`
|
HlsPlaylistUrl string `json:"hls-playlist-url"`
|
||||||
Assets []struct {
|
Assets []struct {
|
||||||
Flavor string `json:"flavor"`
|
Flavor string `json:"flavor"`
|
||||||
URL string `json:"URL"`
|
URL string `json:"URL"`
|
||||||
}`json:"assets"`
|
} `json:"assets"`
|
||||||
}`json:"songList"`
|
} `json:"songList"`
|
||||||
Status int `json:"status"`
|
Status int `json:"status"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,9 +207,9 @@ func extractKidBase64(b string, mvmode bool) (string, string, error) {
|
|||||||
kidbase64 = split[1]
|
kidbase64 = split[1]
|
||||||
lastSlashIndex := strings.LastIndex(b, "/")
|
lastSlashIndex := strings.LastIndex(b, "/")
|
||||||
// 截取最后一个斜杠之前的部分
|
// 截取最后一个斜杠之前的部分
|
||||||
urlBuilder.WriteString(b[:lastSlashIndex])
|
urlBuilder.WriteString(b[:lastSlashIndex])
|
||||||
urlBuilder.WriteString("/")
|
urlBuilder.WriteString("/")
|
||||||
urlBuilder.WriteString(mediaPlaylist.Map.URI)
|
urlBuilder.WriteString(mediaPlaylist.Map.URI)
|
||||||
//fileurl = b[:lastSlashIndex] + "/" + mediaPlaylist.Map.URI
|
//fileurl = b[:lastSlashIndex] + "/" + mediaPlaylist.Map.URI
|
||||||
//fmt.Println("Extracted URI:", mediaPlaylist.Map.URI)
|
//fmt.Println("Extracted URI:", mediaPlaylist.Map.URI)
|
||||||
if mvmode {
|
if mvmode {
|
||||||
@ -212,9 +217,9 @@ func extractKidBase64(b string, mvmode bool) (string, string, error) {
|
|||||||
if segment != nil {
|
if segment != nil {
|
||||||
//fmt.Println("Extracted URI:", segment.URI)
|
//fmt.Println("Extracted URI:", segment.URI)
|
||||||
urlBuilder.WriteString(";")
|
urlBuilder.WriteString(";")
|
||||||
urlBuilder.WriteString(b[:lastSlashIndex])
|
urlBuilder.WriteString(b[:lastSlashIndex])
|
||||||
urlBuilder.WriteString("/")
|
urlBuilder.WriteString("/")
|
||||||
urlBuilder.WriteString(segment.URI)
|
urlBuilder.WriteString(segment.URI)
|
||||||
//fileurl = fileurl + ";" + b[:lastSlashIndex] + "/" + segment.URI
|
//fileurl = fileurl + ";" + b[:lastSlashIndex] + "/" + segment.URI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -227,7 +232,7 @@ func extractKidBase64(b string, mvmode bool) (string, string, error) {
|
|||||||
}
|
}
|
||||||
return kidbase64, urlBuilder.String(), nil
|
return kidbase64, urlBuilder.String(), nil
|
||||||
}
|
}
|
||||||
func extsong(b string)(bytes.Buffer){
|
func extsong(b string) bytes.Buffer {
|
||||||
resp, err := http.Get(b)
|
resp, err := http.Get(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("下载文件失败: %v\n", err)
|
fmt.Printf("下载文件失败: %v\n", err)
|
||||||
@ -255,8 +260,8 @@ func extsong(b string)(bytes.Buffer){
|
|||||||
io.Copy(io.MultiWriter(&buffer, bar), resp.Body)
|
io.Copy(io.MultiWriter(&buffer, bar), resp.Body)
|
||||||
return buffer
|
return buffer
|
||||||
}
|
}
|
||||||
func Run(adamId string, trackpath string, authtoken string, mutoken string, mvmode bool)(string, error) {
|
func Run(adamId string, trackpath string, authtoken string, mutoken string, mvmode bool) (string, error) {
|
||||||
var keystr string //for mv key
|
var keystr string //for mv key
|
||||||
var fileurl string
|
var fileurl string
|
||||||
var kidBase64 string
|
var kidBase64 string
|
||||||
var err error
|
var err error
|
||||||
@ -281,7 +286,7 @@ func Run(adamId string, trackpath string, authtoken string, mutoken string, mvmo
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
headers := map[string]interface{}{
|
headers := map[string]interface{}{
|
||||||
"authorization": "Bearer " + authtoken,
|
"authorization": "Bearer " + authtoken,
|
||||||
"x-apple-music-user-token": mutoken,
|
"x-apple-music-user-token": mutoken,
|
||||||
}
|
}
|
||||||
client, _ := requests.NewClient(nil, requests.ClientOption{
|
client, _ := requests.NewClient(nil, requests.ClientOption{
|
||||||
@ -330,7 +335,7 @@ func Run(adamId string, trackpath string, authtoken string, mutoken string, mvmo
|
|||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExtMvData (keyAndUrls string, savePath string)(error) {
|
func ExtMvData(keyAndUrls string, savePath string) error {
|
||||||
segments := strings.Split(keyAndUrls, ";")
|
segments := strings.Split(keyAndUrls, ";")
|
||||||
key := segments[0]
|
key := segments[0]
|
||||||
//fmt.Println(key)
|
//fmt.Println(key)
|
||||||
@ -345,7 +350,7 @@ func ExtMvData (keyAndUrls string, savePath string)(error) {
|
|||||||
|
|
||||||
// 依次下载每个链接并写入文件
|
// 依次下载每个链接并写入文件
|
||||||
bar := progressbar.DefaultBytes(
|
bar := progressbar.DefaultBytes(
|
||||||
-1,
|
-1,
|
||||||
"Downloading...",
|
"Downloading...",
|
||||||
)
|
)
|
||||||
barWriter := io.MultiWriter(tempFile, bar)
|
barWriter := io.MultiWriter(tempFile, bar)
|
||||||
@ -384,7 +389,6 @@ func ExtMvData (keyAndUrls string, savePath string)(error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// DecryptMP4 decrypts a fragmented MP4 file with keys from widevice license. Supports CENC and CBCS schemes.
|
// DecryptMP4 decrypts a fragmented MP4 file with keys from widevice license. Supports CENC and CBCS schemes.
|
||||||
func DecryptMP4(r io.Reader, key []byte, w io.Writer) error {
|
func DecryptMP4(r io.Reader, key []byte, w io.Writer) error {
|
||||||
// Initialization
|
// Initialization
|
||||||
|
Loading…
x
Reference in New Issue
Block a user