From 2a4e557acbc4f53fa4b70afc1aa967f219de0c81 Mon Sep 17 00:00:00 2001 From: zhaarey <157944548+zhaarey@users.noreply.github.com> Date: Wed, 29 May 2024 20:50:32 +0800 Subject: [PATCH] ad get-m3u8-from-device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit thanks BƎΛTS and NPGamma --- README.md | 1 + agent.js | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++ config.yaml | 1 + main.go | 47 +++++++++++++++++++++++++++++ 4 files changed, 135 insertions(+) diff --git a/README.md b/README.md index 0b85252..ca813ea 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ 6. main_select 支持手动填写m3u8,输入#号,比如#1 #2,支持txt读取m3u8,输入txt文件名 7. main 支持使用 go run main.go "txt文件地址" txt文件名需要指定格式 例如 cn_1707581102_THE BOOK 3.txt 建议使用这个[Reqable 脚本代码](https://telegra.ph/Reqable-For-Apple-Music-05-01) 自动生成 8. main 支持check 可以填入文本地址 或API数据库. +9. 新增get-m3u8-from-device 改为true 且设置端口`adb forward tcp:20020 tcp:20020`即从模拟器获取m3u8 本项目仅支持ALAC和Atmos - `alac (audio-alac-stereo)` diff --git a/agent.js b/agent.js index 8153da6..7c361f1 100644 --- a/agent.js +++ b/agent.js @@ -100,6 +100,92 @@ setTimeout(() => { } await s.close(); } + global.getM3U8 = function(adamID) { + var C3282k = Java.use("c.a.a.e.o.k"); + var m7125s = C3282k.a().s(); + var PurchaseRequest$PurchaseRequestPtr = Java.use("com.apple.android.storeservices.javanative.account.PurchaseRequest$PurchaseRequestPtr"); + + var c3249t = Java.cast(m7125s, Java.use("c.a.a.e.k.t")); + var create = PurchaseRequest$PurchaseRequestPtr.create(c3249t.n.value); + create.get().setProcessDialogActions(true); + create.get().setURLBagKey("subDownload"); + create.get().setBuyParameters(`salableAdamId=${adamID}&price=0&pricingParameters=SUBS&productType=S`); + create.get().run(); + var response = create.get().getResponse(); + if (response.get().getError().get() == null) { + var item = response.get().getItems().get(0); + var assets = item.get().getAssets(); + var size = assets.size(); + return assets.get(size - 1).get().getURL(); + } else { + return response.get().getError().get().errorCode(); + } + }; + + function performJavaOperations(adamID) { + return new Promise((resolve, reject) => { + Java.performNow(function () { + const url = getM3U8(adamID); + resolve(url); + }); + }); + } + + async function handleM3U8Connection(s) { + console.log("New M3U8 connection!"); + try { + const byteArrayToString = byteArray => { + let result = ''; + for (let i = 0; i < byteArray.length; ++i) { + result += String.fromCharCode(byteArray[i]); + } + return result; + }; + + const adamSize = (await s.input.readAll(1)).unwrap().readU8(); + if (adamSize !== 0) { + const adam = await s.input.readAll(adamSize); + const byteArray = new Uint8Array(adam); + let adamID = ""; + for (let i = 0; i < byteArray.length; i++) { + adamID += String.fromCharCode(byteArray[i]); + } + console.log("adamID:", adamID); + let m3u8Url; + performJavaOperations(adamID) + .then(async (url) => { + m3u8Url = url; + console.log("M3U8 URL: ", m3u8Url); + const m3u8Array = stringToByteArray(m3u8Url + "\n"); + // console.log("M3U8 ARRAY:", m3u8Array); + await s.output.writeAll(m3u8Array); + }) + .catch((error) => { + console.error("Error performing Java operations:", error); + }); + } + } catch (err) { + console.error("Error handling M3U8 connection:", err); + } + await s.close(); + } + + const stringToByteArray = str => { + const byteArray = []; + for (let i = 0; i < str.length; ++i) { + byteArray.push(str.charCodeAt(i)); + } + return byteArray; + }; + + Socket.listen({ + family: "ipv4", + port: 20020, + }).then(async function (listener) { + while (true) { + handleM3U8Connection(await listener.accept()); + } + }).catch(console.log); Socket.listen({ family: "ipv4", diff --git a/config.yaml b/config.yaml index 53ae90c..62ba637 100644 --- a/config.yaml +++ b/config.yaml @@ -8,3 +8,4 @@ alac-save-folder: AM-DL downloads atmos-save-folder: AM-DL-Atmos downloads check: "" force-api: false +get-m3u8-from-device: false diff --git a/main.go b/main.go index 106518a..9a12145 100644 --- a/main.go +++ b/main.go @@ -47,6 +47,7 @@ type Config struct { AtmosSaveFolder string `yaml:"atmos-save-folder"` ForceApi bool `yaml:"force-api"` Check string `yaml:"check"` + GetM3u8FromDevice bool `yaml:"get-m3u8-from-device"` } var config Config @@ -1259,6 +1260,52 @@ func rip(albumId string, token string, storefront string, userToken string) erro } } } + if config.GetM3u8FromDevice{ + adamID := track.ID + conn, err := net.Dial("tcp", "127.0.0.1:20020") + if err != nil { + fmt.Println("Error connecting to device:", err) + continue + } + defer conn.Close() + + fmt.Println("Connected to device") + + // Send the length of adamID and the adamID itself + adamIDBuffer := []byte(adamID) + lengthBuffer := []byte{byte(len(adamIDBuffer))} + + // Write length and adamID to the connection + _, err = conn.Write(lengthBuffer) + if err != nil { + fmt.Println("Error writing length to device:", err) + continue + } + + _, err = conn.Write(adamIDBuffer) + if err != nil { + fmt.Println("Error writing adamID to device:", err) + continue + } + + // Read the response (URL) from the device + response, err := bufio.NewReader(conn).ReadBytes('\n') + if err != nil { + fmt.Println("Error reading response from device:", err) + continue + } + + // Trim any newline characters from the response + + response = bytes.TrimSpace(response) + if len(response) > 0 { + fmt.Println("Received URL:", string(response)) + manifest.Attributes.ExtendedAssetUrls.EnhancedHls = string(response) + } else { + fmt.Println("Received an empty response") + continue + } + } if txtpath != "" { file, err := os.Open(txtpath) if err != nil {