diff --git a/main.go b/main.go index 9a75e78..9d46b51 100644 --- a/main.go +++ b/main.go @@ -400,7 +400,7 @@ func writeCover(sanAlbumFolder, name string, url string) error { return err } if exists { - return nil + _ = os.Remove(covPath) } if Config.CoverFormat == "png" { re := regexp.MustCompile(`\{w\}x\{h\}`) @@ -970,7 +970,7 @@ func rip(albumId string, token string, storefront string, mediaUserToken string, continue } if isInArray(selected, trackNum) { - counter.Total++ + //counter.Total++ downloadTrack(trackNum, trackTotal, meta, track, albumId, token, storefront, mediaUserToken, sanAlbumFolder, Codec, &counter) } } @@ -1167,10 +1167,9 @@ func main() { counter = structs.Counter{} } } - func mvDownloader(adamID string, saveDir string, token string, mediaUserToken string)(error){ - vidPath := filepath.Join(saveDir, fmt.Sprintf("%s.mp4", adamID)) - audPath := filepath.Join(saveDir, fmt.Sprintf("%s.m4a", adamID)) + vidPath := filepath.Join(saveDir, fmt.Sprintf("%s_vid.mp4", adamID)) + audPath := filepath.Join(saveDir, fmt.Sprintf("%s_aud.mp4", adamID)) mvOutPath := filepath.Join(saveDir, fmt.Sprintf("%s.mkv", adamID)) exists, _ := fileExists(mvOutPath) if exists { @@ -1183,33 +1182,28 @@ func mvDownloader(adamID string, saveDir string, token string, mediaUserToken st audiom3u8url, _ := extractMvAudio(mvm3u8url) //fmt.Println(videom3u8url) //fmt.Println(audiom3u8url) - videokey, _ := runv3.Run(adamID, videom3u8url, token, mediaUserToken, true) - audiokey, _ := runv3.Run(adamID, audiom3u8url, token, mediaUserToken, true) - //fmt.Println(videokey) - //fmt.Println(audiokey) - cmd1 := exec.Command("n-m3u8dl-re", videom3u8url, "--key", videokey, "--decryption-engine", "MP4DECRYPT", "--save-dir", saveDir, "--save-name", adamID) - cmd2 := exec.Command("n-m3u8dl-re", audiom3u8url, "--key", audiokey, "--decryption-engine", "MP4DECRYPT", "--save-dir", saveDir, "--save-name", adamID) - cmd3 := exec.Command("MP4Box", "-quiet", "-add", vidPath, "-add", audPath, "-keep-utc", "-new", mvOutPath) - fmt.Printf("MVvid Downloading...") - if err := cmd1.Run(); err != nil { - fmt.Printf("MVvid Download failed: %v\n", err) - return err - } - fmt.Printf("\rMVvid Downloaded. \n") - fmt.Printf("MVaud Downloading...") - if err := cmd2.Run(); err != nil { - fmt.Printf("MVaud Download failed: %v\n", err) - return err - } - fmt.Printf("\rMVaud Downloaded. \n") + + videokeyAndUrls, _ := runv3.Run(adamID, videom3u8url, token, mediaUserToken, true) + audiokeyAndUrls, _ := runv3.Run(adamID, audiom3u8url, token, mediaUserToken, true) + //fmt.Println(videokeyAndUrls) + //fmt.Println(audiokeyAndUrls) + + fmt.Println("MV-VIDEO") + _ = runv3.ExtMvData(videokeyAndUrls, vidPath) + + fmt.Println("MV-AUDIO") + _ = runv3.ExtMvData(audiokeyAndUrls, audPath) + + muxCmd := exec.Command("MP4Box", "-quiet", "-add", vidPath, "-add", audPath, "-keep-utc", "-new", mvOutPath) fmt.Printf("MV Remuxing...") - if err := cmd3.Run(); err != nil { + if err := muxCmd.Run(); err != nil { fmt.Printf("MV mux failed: %v\n", err) return err } fmt.Printf("\rMV Remuxed. \n") _ = os.Remove(vidPath) _ = os.Remove(audPath) + return nil } diff --git a/utils/runv3/key/key.go b/utils/runv3/key/key.go index 166fa70..449cd1f 100644 --- a/utils/runv3/key/key.go +++ b/utils/runv3/key/key.go @@ -64,8 +64,8 @@ func (w *Key) GetKey(ctx context.Context, licenseServerURL string, PSSH string, for _, key := range keys { if key.Type == wv.License_KeyContainer_CONTENT { - command += hex.EncodeToString(key.ID) + ":" + hex.EncodeToString(key.Value) - //command += hex.EncodeToString(key.Value) + //command += hex.EncodeToString(key.ID) + ":" + hex.EncodeToString(key.Value) + command += hex.EncodeToString(key.Value) keybt = key.Value } } diff --git a/utils/runv3/runv3.go b/utils/runv3/runv3.go index 0f3cba9..310939e 100644 --- a/utils/runv3/runv3.go +++ b/utils/runv3/runv3.go @@ -22,6 +22,7 @@ import ( "github.com/grafov/m3u8" "strings" "github.com/schollz/progressbar/v3" + "os/exec" ) type PlaybackLicense struct { @@ -151,7 +152,7 @@ func GetWebplayback(adamId string, authtoken string, mutoken string, mvmode bool // 遍历 Assets for i, _ := range obj.List[0].Assets { if obj.List[0].Assets[i].Flavor == "28:ctrp256" { - kidBase64, fileurl, err := extractKidBase64(obj.List[0].Assets[i].URL) + kidBase64, fileurl, err := extractKidBase64(obj.List[0].Assets[i].URL, false) if err != nil { return "", "", err } @@ -174,7 +175,7 @@ type Songlist struct { Status int `json:"status"` } -func extractKidBase64(b string) (string, string, error) { +func extractKidBase64(b string, mvmode bool) (string, string, error) { resp, err := http.Get(b) if err != nil { return "", "", err @@ -193,7 +194,7 @@ func extractKidBase64(b string) (string, string, error) { return "", "", err } var kidbase64 string - var fileurl string + var urlBuilder strings.Builder if listType == m3u8.MEDIA { mediaPlaylist := from.(*m3u8.MediaPlaylist) if mediaPlaylist.Key != nil { @@ -201,15 +202,30 @@ func extractKidBase64(b string) (string, string, error) { kidbase64 = split[1] lastSlashIndex := strings.LastIndex(b, "/") // 截取最后一个斜杠之前的部分 - fileurl = b[:lastSlashIndex] + "/" + mediaPlaylist.Map.URI + urlBuilder.WriteString(b[:lastSlashIndex]) + urlBuilder.WriteString("/") + urlBuilder.WriteString(mediaPlaylist.Map.URI) + //fileurl = b[:lastSlashIndex] + "/" + mediaPlaylist.Map.URI //fmt.Println("Extracted URI:", mediaPlaylist.Map.URI) + if mvmode { + for _, segment := range mediaPlaylist.Segments { + if segment != nil { + fmt.Println("Extracted URI:", segment.URI) + urlBuilder.WriteString(";") + urlBuilder.WriteString(b[:lastSlashIndex]) + urlBuilder.WriteString("/") + urlBuilder.WriteString(segment.URI) + //fileurl = fileurl + ";" + b[:lastSlashIndex] + "/" + segment.URI + } + } + } } else { fmt.Println("No key information found") } } else { fmt.Println("Not a media playlist") } - return kidbase64, fileurl, nil + return kidbase64, urlBuilder.String(), nil } func extsong(b string)(bytes.Buffer){ resp, err := http.Get(b) @@ -245,7 +261,7 @@ func Run(adamId string, trackpath string, authtoken string, mutoken string, mvmo var kidBase64 string var err error if mvmode { - kidBase64, _, err = extractKidBase64(trackpath) + kidBase64, fileurl, err = extractKidBase64(trackpath, true) if err != nil { return "", err } @@ -283,7 +299,8 @@ func Run(adamId string, trackpath string, authtoken string, mutoken string, mvmo return "", err } if mvmode { - return keystr, nil + keyAndUrls := "1:" + keystr + ";" + fileurl + return keyAndUrls, nil } body := extsong(fileurl) fmt.Print("Downloaded\n") @@ -312,23 +329,58 @@ func Run(adamId string, trackpath string, authtoken string, mutoken string, mvmo } return "", nil } -// DecryptMP4Auto decrypts a fragmented MP4 file with the set of keys retreived from the widevice license -// by automatically selecting the appropriate key. Supports CENC and CBCS schemes. -// func DecryptMP4Auto(r io.Reader, keys []*Key, w io.Writer) error { -// // Extract content key -// var key []byte -// for _, k := range keys { -// if k.Type == wvpb.License_KeyContainer_CONTENT { -// key = k.Key -// break -// } -// } -// if key == nil { -// return fmt.Errorf("no %s key type found in the provided key set", wvpb.License_KeyContainer_CONTENT) -// } -// // Execute decryption -// return DecryptMP4(r, key, w) -// } + +func ExtMvData (keyAndUrls string, savePath string)(error) { + segments := strings.Split(keyAndUrls, ";") + key := segments[0] + //fmt.Println(key) + urls := segments[1:] + tempFile, err := os.CreateTemp("", "enc_mv_data-*.mp4") + if err != nil { + fmt.Printf("创建文件失败:%v\n", err) + return err + } + defer tempFile.Close() + defer os.Remove(tempFile.Name()) + + // 依次下载每个链接并写入文件 + bar := progressbar.DefaultBytes( + -1, + "Downloading...", + ) + barWriter := io.MultiWriter(tempFile, bar) + for _, url := range urls { + resp, err := http.Get(url) + if err != nil { + fmt.Printf("下载链接 %s 失败:%v\n", url, err) + return err + } + + // 将响应体写入输出文件 + _, err = io.Copy(barWriter, resp.Body) + defer resp.Body.Close() // 注意及时关闭响应体,避免资源泄露 + if err != nil { + fmt.Printf("写入文件失败:%v\n", err) + return err + } + + //fmt.Printf("第 %d 个链接 %s 下载并写入完成\n", idx+1, url) + } + tempFile.Close() + fmt.Println("\nDownloaded.") + + cmd1 := exec.Command("mp4decrypt", "--key", key, tempFile.Name(), savePath) + //outlog, err := cmd1.CombinedOutput() + if err := cmd1.Run(); err != nil { + fmt.Printf("Decrypt failed: %v\n", err) + //fmt.Printf("Output:\n%s\n", outlog) + return err + } else { + fmt.Println("Decrypted.") + } + return nil +} + // 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 {