From 6e97d1e96c7b9e573d2e230c3d9320f34e52cf2d Mon Sep 17 00:00:00 2001 From: itouakirai Date: Thu, 13 Feb 2025 02:38:45 +0800 Subject: [PATCH] =?UTF-8?q?fix:=E6=AD=8C=E6=89=8B=E9=93=BE=E6=8E=A5?= =?UTF-8?q?=E6=97=A0mv=E6=97=B6=E7=A8=8B=E5=BA=8F=E9=80=80=E5=87=BA;debug?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F=E6=9C=AA=E4=BD=BF=E7=94=A8=E6=BB=A1=E8=A1=80?= =?UTF-8?q?m3u8;Quality=E6=A0=87=E7=AD=BE=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.go | 266 ++++++++++++-------------------------------------------- 1 file changed, 58 insertions(+), 208 deletions(-) diff --git a/main.go b/main.go index d5436fc..7238023 100644 --- a/main.go +++ b/main.go @@ -40,7 +40,6 @@ var ( dl_aac bool dl_select bool dl_song bool - dl_cover bool artist_select bool debug_mode bool alac_max *int @@ -530,7 +529,7 @@ func downloadTrack(trackNum int, trackTotal int, meta *structs.AutoGenerated, tr } else if needDlAacLc { Quality = fmt.Sprintf("256kbps") } else { - Quality, err = extractMediaQuality(manifest.Attributes.ExtendedAssetUrls.EnhancedHls) + _, Quality, err = extractMedia(manifest.Attributes.ExtendedAssetUrls.EnhancedHls, true) if err != nil { fmt.Println("Failed to extract quality from manifest.\n", err) counter.Error++ @@ -626,7 +625,7 @@ func downloadTrack(trackNum int, trackTotal int, meta *structs.AutoGenerated, tr return } } else { - trackM3u8Url, err := extractMedia(manifest.Attributes.ExtendedAssetUrls.EnhancedHls) + trackM3u8Url, _, err := extractMedia(manifest.Attributes.ExtendedAssetUrls.EnhancedHls, false) if err != nil { fmt.Println("\u26A0 Failed to extract info from manifest:", err) counter.Unavailable++ @@ -703,25 +702,25 @@ func rip(albumId string, token string, storefront string, mediaUserToken string, } var m3u8Url string + //Web端m3u8 if manifest.Attributes.ExtendedAssetUrls.EnhancedHls != "" { m3u8Url = manifest.Attributes.ExtendedAssetUrls.EnhancedHls - } else if mediaUserToken != "" && len(mediaUserToken) > 10 { - // Try to get m3u8 from device if media-user-token is set + } + //设备端满血m3u8 + needCheck := false + if Config.GetM3u8Mode == "all" { + needCheck = true + } else if Config.GetM3u8Mode == "hires" && contains(track.Attributes.AudioTraits, "hi-res-lossless") { + needCheck = true + } + if needCheck { m3u8Url, err = checkM3u8(track.ID, "song") - if err != nil { - fmt.Printf("Failed to get m3u8 from device for track %d: %v\n", trackNum, err) - continue - } } - if m3u8Url != "" { - _, err = extractMediaQuality(m3u8Url) - if err != nil { - fmt.Printf("Failed to extract quality info for track %d: %v\n", trackNum, err) - continue - } - } else { - fmt.Println("\nNo audio formats available - valid media-user-token may be required") + _, _, err = extractMedia(m3u8Url, true) + if err != nil { + fmt.Printf("Failed to extract quality info for track %d: %v\n", trackNum, err) + continue } } return nil // Return directly without showing statistics @@ -795,7 +794,7 @@ func rip(albumId string, token string, storefront string, mediaUserToken string, manifest1.Attributes.ExtendedAssetUrls.EnhancedHls = EnhancedHls_m3u8 } } - Quality, err = extractMediaQuality(manifest1.Attributes.ExtendedAssetUrls.EnhancedHls) + _, Quality, err = extractMedia(manifest1.Attributes.ExtendedAssetUrls.EnhancedHls, true) if err != nil { fmt.Println("Failed to extract quality from manifest.\n", err) } @@ -852,95 +851,6 @@ func rip(albumId string, token string, storefront string, mediaUserToken string, sanAlbumFolder := filepath.Join(singerFolder, forbiddenNames.ReplaceAllString(albumFolder, "_")) os.MkdirAll(sanAlbumFolder, os.ModePerm) fmt.Println(albumFolder) - - // If cover-only mode is enabled, just download covers and animated artwork - if dl_cover { - fmt.Println("Cover only mode - downloading artwork") - counter.Total++ - - // Download album cover - err = writeCover(sanAlbumFolder, "cover", meta.Data[0].Attributes.Artwork.URL) - if err != nil { - fmt.Println("Failed to write cover.") - counter.Error++ - } else { - fmt.Println("Album cover downloaded successfully") - counter.Success++ - } - - // Download artist cover if enabled - if Config.SaveArtistCover && !(strings.Contains(albumId, "pl.")) { - if len(meta.Data[0].Relationships.Artists.Data) > 0 { - err = writeCover(singerFolder, "folder", meta.Data[0].Relationships.Artists.Data[0].Attributes.Artwork.Url) - if err != nil { - fmt.Println("Failed to write artist cover.") - counter.Error++ - } else { - fmt.Println("Artist cover downloaded successfully") - } - } - } - - // Download animated artwork if available and enabled - if Config.SaveAnimatedArtwork && meta.Data[0].Attributes.EditorialVideo.MotionDetailSquare.Video != "" { - fmt.Println("Found Animation Artwork.") - - // Download square version - motionvideoUrlSquare, err := extractVideo(meta.Data[0].Attributes.EditorialVideo.MotionDetailSquare.Video) - if err != nil { - fmt.Println("No motion video square:", err) - } else { - exists, err := fileExists(filepath.Join(sanAlbumFolder, "animated_artwork_square.mp4")) - if err != nil { - fmt.Println("Failed to check if animated artwork square exists.") - } - if exists { - fmt.Println("Animated artwork square already exists locally.") - } else { - fmt.Println("Animation Artwork Square Downloading...") - cmd := exec.Command("ffmpeg", "-loglevel", "quiet", "-y", "-i", motionvideoUrlSquare, "-c", "copy", filepath.Join(sanAlbumFolder, "animated_artwork_square.mp4")) - if err := cmd.Run(); err != nil { - fmt.Printf("Animated artwork square download failed: %v\n", err) - } else { - fmt.Println("Animation Artwork Square Downloaded") - } - } - } - - if Config.EmbyAnimatedArtwork { - // Convert square version to gif - cmd3 := exec.Command("ffmpeg", "-i", filepath.Join(sanAlbumFolder, "animated_artwork_square.mp4"), "-vf", "scale=440:-1", "-r", "24", "-f", "gif", filepath.Join(sanAlbumFolder, "folder.jpg")) - if err := cmd3.Run(); err != nil { - fmt.Printf("Animated artwork square to gif conversion failed: %v\n", err) - } - } - - // Download tall version - motionvideoUrlTall, err := extractVideo(meta.Data[0].Attributes.EditorialVideo.MotionDetailTall.Video) - if err != nil { - fmt.Println("No motion video tall:", err) - } else { - exists, err := fileExists(filepath.Join(sanAlbumFolder, "animated_artwork_tall.mp4")) - if err != nil { - fmt.Println("Failed to check if animated artwork tall exists.") - } - if exists { - fmt.Println("Animated artwork tall already exists locally.") - } else { - fmt.Println("Animation Artwork Tall Downloading...") - cmd := exec.Command("ffmpeg", "-loglevel", "quiet", "-y", "-i", motionvideoUrlTall, "-c", "copy", filepath.Join(sanAlbumFolder, "animated_artwork_tall.mp4")) - if err := cmd.Run(); err != nil { - fmt.Printf("Animated artwork tall download failed: %v\n", err) - } else { - fmt.Println("Animation Artwork Tall Downloaded") - } - } - } - } - - return nil - } - //get artist cover if Config.SaveArtistCover && !(strings.Contains(albumId, "pl.")) { if len(meta.Data[0].Relationships.Artists.Data) > 0 { @@ -1082,7 +992,6 @@ func rip(albumId string, token string, storefront string, mediaUserToken string, downloadTrack(trackNum, trackTotal, meta, track, albumId, token, storefront, mediaUserToken, sanAlbumFolder, Codec, &counter) } } - return nil } @@ -1187,11 +1096,10 @@ func main() { pflag.BoolVar(&dl_song, "song", false, "Enable single song download mode") pflag.BoolVar(&artist_select, "all-album", false, "Download all artist albums") pflag.BoolVar(&debug_mode, "debug", false, "Enable debug mode to show audio quality information") - pflag.BoolVar(&dl_cover, "cover", false, "Download only album covers and animated artwork") alac_max = pflag.Int("alac-max", Config.AlacMax, "Specify the max quality for download alac") atmos_max = pflag.Int("atmos-max", Config.AtmosMax, "Specify the max quality for download atmos") - aac_type = pflag.String("aac-type", Config.AacType, "Select AAC type: aac, aac-binaural, aac-downmix") - mv_audio_type = pflag.String("mv-audio-type", Config.MVAudioType, "Select MV audio type: atmos, ac3, aac") + aac_type = pflag.String("aac-type", Config.AacType, "Select AAC type, aac aac-binaural aac-downmix") + mv_audio_type = pflag.String("mv-audio-type", Config.MVAudioType, "Select MV audio type, atmos ac3 aac") mv_max = pflag.Int("mv-max", Config.MVMax, "Specify the max quality for download MV") // Custom usage message for help @@ -1235,7 +1143,7 @@ func main() { mvArgs, err := checkArtist(os.Args[0], token, "music-videos") if err != nil { fmt.Println("Failed to get artist music-videos.") - return + //return } os.Args = append(albumArgs, mvArgs...) } @@ -1733,29 +1641,41 @@ func checkM3u8(b string, f string) (string, error) { return EnhancedHls, nil } -func extractMediaQuality(b string) (string, error) { +func formatAvailability(available bool, quality string) string { + if !available { + return "Not Available" + } + return quality +} + +func extractMedia(b string, more_mode bool) (string, string, error) { + masterUrl, err := url.Parse(b) + if err != nil { + return "", "", err + } resp, err := http.Get(b) if err != nil { - return "", err + return "", "",err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { - return "", errors.New(resp.Status) + return "", "", errors.New(resp.Status) } body, err := io.ReadAll(resp.Body) if err != nil { - return "", err + return "", "", err } masterString := string(body) from, listType, err := m3u8.DecodeFrom(strings.NewReader(masterString), true) if err != nil || listType != m3u8.MASTER { - return "", errors.New("m3u8 not of master type") + return "", "", errors.New("m3u8 not of master type") } master := from.(*m3u8.MasterPlaylist) + var streamUrl *url.URL sort.Slice(master.Variants, func(i, j int) bool { return master.Variants[i].AverageBandwidth > master.Variants[j].AverageBandwidth }) - if debug_mode { + if debug_mode && more_mode { fmt.Println("\nDebug: All Available Variants:") fmt.Println("-----------------------------") for _, variant := range master.Variants { @@ -1836,90 +1756,13 @@ func extractMediaQuality(b string) (string, error) { fmt.Printf("Dolby Audio : %s\n", formatAvailability(hasDolbyAudio, dolbyAudioQuality)) fmt.Println("------------------------") - return "", nil + return "", "", nil } var Quality string for _, variant := range master.Variants { if dl_atmos { if variant.Codecs == "ec-3" && strings.Contains(variant.Audio, "atmos") { - split := strings.Split(variant.Audio, "-") - if len(split) > 0 { - Quality = fmt.Sprintf("%s kbps", split[len(split)-1]) - break - } - } else if variant.Codecs == "ac-3" { // Add Dolby Audio support for --atmos flag - split := strings.Split(variant.Audio, "-") - if len(split) > 0 { - Quality = fmt.Sprintf("%s kbps", split[len(split)-1]) - break - } - } - } else if dl_aac { - if variant.Codecs == "mp4a.40.2" { - split := strings.Split(variant.Audio, "-") - if len(split) >= 3 { - Quality = fmt.Sprintf("%s kbps", split[2]) - break - } - } - } else { - if variant.Codecs == "alac" { - split := strings.Split(variant.Audio, "-") - if len(split) >= 3 { - bitDepth := split[len(split)-1] - sampleRate := split[len(split)-2] - HZ, err := strconv.Atoi(sampleRate) - if err != nil { - fmt.Println(err) - } - KHZ := float64(HZ) / 1000.0 - Quality = fmt.Sprintf("%sB-%.1fkHz", bitDepth, KHZ) - break - } - } - } - } - return Quality, nil -} - -func formatAvailability(available bool, quality string) string { - if !available { - return "Not Available" - } - return quality -} - -func extractMedia(b string) (string, error) { - masterUrl, err := url.Parse(b) - if err != nil { - return "", err - } - resp, err := http.Get(b) - 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 - } - masterString := string(body) - from, listType, err := m3u8.DecodeFrom(strings.NewReader(masterString), true) - if err != nil || listType != m3u8.MASTER { - return "", errors.New("m3u8 not of master type") - } - master := from.(*m3u8.MasterPlaylist) - var streamUrl *url.URL - sort.Slice(master.Variants, func(i, j int) bool { - return master.Variants[i].AverageBandwidth > master.Variants[j].AverageBandwidth - }) - for _, variant := range master.Variants { - if dl_atmos { - if variant.Codecs == "ec-3" && strings.Contains(variant.Audio, "atmos") { - if debug_mode { + if debug_mode && !more_mode { fmt.Printf("Debug: Found Dolby Atmos variant - %s (Bitrate: %d kbps)\n", variant.Audio, variant.Bandwidth/1000) } @@ -1927,40 +1770,43 @@ func extractMedia(b string) (string, error) { length := len(split) length_int, err := strconv.Atoi(split[length-1]) if err != nil { - return "", err + return "", "", err } if length_int <= Config.AtmosMax { - if !debug_mode { + if !debug_mode && !more_mode { fmt.Printf("%s\n", variant.Audio) } streamUrlTemp, err := masterUrl.Parse(variant.URI) if err != nil { - return "", err + return "", "", err } streamUrl = streamUrlTemp + Quality = fmt.Sprintf("%s kbps", split[len(split)-1]) break } } else if variant.Codecs == "ac-3" { // Add Dolby Audio support - if debug_mode { + if debug_mode && !more_mode { fmt.Printf("Debug: Found Dolby Audio variant - %s (Bitrate: %d kbps)\n", variant.Audio, variant.Bandwidth/1000) } streamUrlTemp, err := masterUrl.Parse(variant.URI) if err != nil { - return "", err + return "", "", err } streamUrl = streamUrlTemp + split := strings.Split(variant.Audio, "-") + Quality = fmt.Sprintf("%s kbps", split[len(split)-1]) break } } else if dl_aac { if variant.Codecs == "mp4a.40.2" { - if debug_mode { + if debug_mode && !more_mode { fmt.Printf("Debug: Found AAC variant - %s (Bitrate: %d)\n", variant.Audio, variant.Bandwidth) } aacregex := regexp.MustCompile(`audio-stereo-\d+`) replaced := aacregex.ReplaceAllString(variant.Audio, "aac") if replaced == Config.AacType { - if !debug_mode { + if !debug_mode && !more_mode { fmt.Printf("%s\n", variant.Audio) } streamUrlTemp, err := masterUrl.Parse(variant.URI) @@ -1968,6 +1814,8 @@ func extractMedia(b string) (string, error) { panic(err) } streamUrl = streamUrlTemp + split := strings.Split(variant.Audio, "-") + Quality = fmt.Sprintf("%s kbps", split[2]) break } } @@ -1977,10 +1825,10 @@ func extractMedia(b string) (string, error) { length := len(split) length_int, err := strconv.Atoi(split[length-2]) if err != nil { - return "", err + return "", "", err } if length_int <= Config.AlacMax { - if !debug_mode { + if !debug_mode && !more_mode { fmt.Printf("%s-bit / %s Hz\n", split[length-1], split[length-2]) } streamUrlTemp, err := masterUrl.Parse(variant.URI) @@ -1988,15 +1836,17 @@ func extractMedia(b string) (string, error) { panic(err) } streamUrl = streamUrlTemp + KHZ := float64(length_int) / 1000.0 + Quality = fmt.Sprintf("%sB-%.1fkHz", split[length-1], KHZ) break } } } } if streamUrl == nil { - return "", errors.New("no codec found") + return "", "", errors.New("no codec found") } - return streamUrl.String(), nil + return streamUrl.String(), Quality, nil } func extractVideo(c string) (string, error) { MediaUrl, err := url.Parse(c)