mirror of
https://github.com/truewhile/MeBox.git
synced 2026-10-01 12:06:38 +08:00
fix(115): resolve direct link via app/chrome/downurl + bind CDN link to client UA
115 deprecated the plain web /files/download endpoint (ordinary cookies no longer get file_url), breaking 302 playback with 'no file_url'. Switch Resolve() to the current proapi.115.com/app/chrome/downurl endpoint, which uses 115's m115 (RSA+XOR) request/response encryption (vendored from the MIT-licensed SheltonZhu/115driver). 115 CDN links are bound to the User-Agent used to request them, so resolve with the playback client's own UA (plumbed from the play handler) — the host can then issue a pure 302 the client fetches directly, preserving true offload. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
@@ -2,8 +2,10 @@ package cloud
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
@@ -77,11 +79,6 @@ func Test115ListAndResolve(t *testing.T) {
|
||||
w.Write([]byte(`{"state":true,"data":[
|
||||
{"cid":"100","n":"Movies","s":0},
|
||||
{"fid":"200","n":"Inception.mkv","s":456,"pc":"pick200"}]}`))
|
||||
case "/files/download":
|
||||
if r.URL.Query().Get("pickcode") != "pick200" {
|
||||
t.Errorf("bad pickcode %q", r.URL.Query().Get("pickcode"))
|
||||
}
|
||||
w.Write([]byte(`{"state":true,"file_url":"https://cdn.115/x.mkv?t=1"}`))
|
||||
default:
|
||||
t.Errorf("unexpected path %s", r.URL.Path)
|
||||
}
|
||||
@@ -92,6 +89,20 @@ func Test115ListAndResolve(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// The downurl endpoint is m115-encrypted end-to-end (the server side
|
||||
// requires 115's private key), so stub the decrypted payload via the seam
|
||||
// and assert the pickcode→URL extraction. The live crypto/transport path is
|
||||
// exercised by integration testing against the real 115 API.
|
||||
p115, ok := p.(*pan115Provider)
|
||||
if !ok {
|
||||
t.Fatalf("expected *pan115Provider, got %T", p)
|
||||
}
|
||||
p115.downURLPayload = func(ctx context.Context, pickcode string) ([]byte, error) {
|
||||
if pickcode != "pick200" {
|
||||
t.Errorf("bad pickcode %q", pickcode)
|
||||
}
|
||||
return []byte(`{"200":{"file_name":"Inception.mkv","file_size":"456","url":{"url":"https://cdn.115/x.mkv?t=1"}}}`), nil
|
||||
}
|
||||
entries, err := p.List(context.Background(), "")
|
||||
if err != nil {
|
||||
t.Fatalf("list: %v", err)
|
||||
@@ -117,6 +128,41 @@ func Test115ListAndResolve(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Test115DownURLEndpointAndError exercises the live fetchDownURLPayload path:
|
||||
// it must POST an m115-encrypted `data` body to /app/chrome/downurl?t=... and
|
||||
// surface 115's error when state=false (no decryption needed for that branch).
|
||||
func Test115DownURLEndpointAndError(t *testing.T) {
|
||||
var gotData, gotT string
|
||||
pro := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path != "/app/chrome/downurl" {
|
||||
t.Errorf("unexpected path %s", r.URL.Path)
|
||||
}
|
||||
gotT = r.URL.Query().Get("t")
|
||||
_ = r.ParseForm()
|
||||
gotData = r.PostFormValue("data")
|
||||
w.Write([]byte(`{"state":false,"error":"not exist"}`))
|
||||
}))
|
||||
defer pro.Close()
|
||||
|
||||
p, err := New(Type115, map[string]any{"cookie": "UID=1", "pro_base": pro.URL}, pro.Client())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = p.Resolve(context.Background(), "pickX")
|
||||
if err == nil || !strings.Contains(err.Error(), "not exist") {
|
||||
t.Fatalf("want upstream error surfaced, got %v", err)
|
||||
}
|
||||
if gotT == "" {
|
||||
t.Errorf("missing t query param")
|
||||
}
|
||||
if gotData == "" {
|
||||
t.Errorf("missing encrypted data body")
|
||||
}
|
||||
if _, derr := base64.StdEncoding.DecodeString(gotData); derr != nil {
|
||||
t.Errorf("data body is not base64: %v", derr)
|
||||
}
|
||||
}
|
||||
|
||||
func Test115QRFlow(t *testing.T) {
|
||||
// status sequence: waiting → scanned → confirmed
|
||||
calls := 0
|
||||
|
||||
Reference in New Issue
Block a user