更新adobe 优化408
This commit is contained in:
@@ -58,9 +58,8 @@ var profileURLs = []string{
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
apiKey string
|
apiKey string
|
||||||
proxy string
|
proxy string
|
||||||
arpSessionID string // cached per-client, reused across requests (matches adobe2api)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClient(apiKey, proxy string) *Client {
|
func NewClient(apiKey, proxy string) *Client {
|
||||||
@@ -70,17 +69,6 @@ func NewClient(apiKey, proxy string) *Client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// getARPSessionID returns a cached ARP session id matching adobe2api's format:
|
|
||||||
// base64({"sid":"<uuid>","ftr":"<hex16>_<ts_ms>_<pid>_dUAL43-mnts-ants-d4_31ck__tt"})
|
|
||||||
// Generated once per client and reused — adobe2api reuses the same session id per
|
|
||||||
// token/profile instead of rotating every request.
|
|
||||||
func (c *Client) getARPSessionID() string {
|
|
||||||
if c.arpSessionID != "" {
|
|
||||||
return c.arpSessionID
|
|
||||||
}
|
|
||||||
c.arpSessionID = buildARPSessionID()
|
|
||||||
return c.arpSessionID
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) SetProxy(proxy string) {
|
func (c *Client) SetProxy(proxy string) {
|
||||||
c.proxy = strings.TrimSpace(proxy)
|
c.proxy = strings.TrimSpace(proxy)
|
||||||
@@ -300,6 +288,7 @@ func (c *Client) FetchAccountProfile(ctx context.Context, token string) (map[str
|
|||||||
"user-agent",
|
"user-agent",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
defer ReleasePID(token)
|
||||||
|
|
||||||
resp, err := sess.client.Do(req)
|
resp, err := sess.client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -470,7 +459,7 @@ func (c *Client) submitImage(ctx context.Context, sess *tlsSession, token, promp
|
|||||||
"sec-fetch-mode": {"cors"},
|
"sec-fetch-mode": {"cors"},
|
||||||
"sec-fetch-dest": {"empty"},
|
"sec-fetch-dest": {"empty"},
|
||||||
"user-agent": {sess.fp.userAgent},
|
"user-agent": {sess.fp.userAgent},
|
||||||
"x-arp-session-id": {c.getARPSessionID()},
|
"x-arp-session-id": {buildARPSessionID(token)},
|
||||||
http.HeaderOrderKey: {
|
http.HeaderOrderKey: {
|
||||||
"authorization",
|
"authorization",
|
||||||
"x-api-key",
|
"x-api-key",
|
||||||
@@ -569,6 +558,7 @@ func (c *Client) pollImage(ctx context.Context, sess *tlsSession, token, pollURL
|
|||||||
"user-agent",
|
"user-agent",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
defer ReleasePID(token)
|
||||||
|
|
||||||
resp, err := sess.client.Do(req)
|
resp, err := sess.client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -648,7 +638,7 @@ func (c *Client) submitVideo(ctx context.Context, sess *tlsSession, token, endpo
|
|||||||
"sec-fetch-mode": {"cors"},
|
"sec-fetch-mode": {"cors"},
|
||||||
"sec-fetch-dest": {"empty"},
|
"sec-fetch-dest": {"empty"},
|
||||||
"user-agent": {sess.fp.userAgent},
|
"user-agent": {sess.fp.userAgent},
|
||||||
"x-arp-session-id": {c.getARPSessionID()},
|
"x-arp-session-id": {buildARPSessionID(token)},
|
||||||
http.HeaderOrderKey: {
|
http.HeaderOrderKey: {
|
||||||
"authorization",
|
"authorization",
|
||||||
"x-api-key",
|
"x-api-key",
|
||||||
@@ -753,6 +743,7 @@ func (c *Client) pollVideo(ctx context.Context, sess *tlsSession, token, pollURL
|
|||||||
"user-agent",
|
"user-agent",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
defer ReleasePID(token)
|
||||||
|
|
||||||
resp, err := sess.client.Do(req)
|
resp, err := sess.client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -7,15 +7,23 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"math/big"
|
"math/big"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// arpPIDPool maps access tokens to unique PIDs so the same account always
|
||||||
|
// reuses its PID and different accounts never collide. Guarded by arpPIDMu.
|
||||||
|
var (
|
||||||
|
arpPIDMu sync.Mutex
|
||||||
|
arpTokenPID = map[string]int{} // token → pid
|
||||||
|
arpPIDToken = map[int]string{} // pid → token
|
||||||
|
)
|
||||||
|
|
||||||
// adobeUserIDPat matches Adobe IMS user IDs embedded in cookies (e.g.
|
// adobeUserIDPat matches Adobe IMS user IDs embedded in cookies (e.g.
|
||||||
// "4BDA81F069FC6DA40A495FAB@AdobeID").
|
// "4BDA81F069FC6DA40A495FAB@AdobeID").
|
||||||
var adobeUserIDPat = regexp.MustCompile(`[A-Fa-f0-9]{20,}@AdobeID`)
|
var adobeUserIDPat = regexp.MustCompile(`[A-Fa-f0-9]{20,}@AdobeID`)
|
||||||
@@ -96,12 +104,11 @@ func decodeJWTPayload(token string) map[string]any {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildARPSessionID() string {
|
func buildARPSessionID(token string) string {
|
||||||
// Matches adobe2api's format exactly:
|
// Matches adobe2api's format exactly:
|
||||||
// base64({"sid":"<uuid>","ftr":"<hex16>_<ts_ms>_<pid>_dUAL43-mnts-ants-d4_31ck__tt"})
|
// base64({"sid":"<uuid>","ftr":"<hex16>_<ts_ms>_<pid>_dUAL43-mnts-ants-d4_31ck__tt"})
|
||||||
// Two fields only (no "ark") — mirrors what a real browser session sends.
|
// Two fields only (no "ark") — mirrors what a real browser session sends.
|
||||||
pid := os.Getpid()
|
ftr := randomHex(16) + "_" + strconv.FormatInt(time.Now().UnixMilli(), 10) + "_" + strconv.Itoa(allocPID(token)) + "_dUAL43-mnts-ants-d4_31ck__tt"
|
||||||
ftr := randomHex(16) + "_" + strconv.FormatInt(time.Now().UnixMilli(), 10) + "_" + strconv.Itoa(pid) + "_dUAL43-mnts-ants-d4_31ck__tt"
|
|
||||||
raw := map[string]any{
|
raw := map[string]any{
|
||||||
"sid": uuid.NewString(),
|
"sid": uuid.NewString(),
|
||||||
"ftr": ftr,
|
"ftr": ftr,
|
||||||
@@ -110,6 +117,39 @@ func buildARPSessionID() string {
|
|||||||
return base64.StdEncoding.EncodeToString(b)
|
return base64.StdEncoding.EncodeToString(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// allocPID returns a unique PID bound to token. Same token always gets the
|
||||||
|
// same PID; different tokens never share a PID. Picks randomly from
|
||||||
|
// [1000, 99999] and retries on collision.
|
||||||
|
func allocPID(token string) int {
|
||||||
|
arpPIDMu.Lock()
|
||||||
|
defer arpPIDMu.Unlock()
|
||||||
|
|
||||||
|
if pid, ok := arpTokenPID[token]; ok {
|
||||||
|
return pid
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
pid := randomInt(1000, 99999)
|
||||||
|
if _, used := arpPIDToken[pid]; !used {
|
||||||
|
arpPIDToken[pid] = token
|
||||||
|
arpTokenPID[token] = pid
|
||||||
|
return pid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReleasePID releases the PID bound to token so it can be reused by another
|
||||||
|
// account. Call this when a token/session is finished (e.g. after the Adobe
|
||||||
|
// API request completes or on token expiry).
|
||||||
|
func ReleasePID(token string) {
|
||||||
|
arpPIDMu.Lock()
|
||||||
|
defer arpPIDMu.Unlock()
|
||||||
|
|
||||||
|
if pid, ok := arpTokenPID[token]; ok {
|
||||||
|
delete(arpPIDToken, pid)
|
||||||
|
delete(arpTokenPID, token)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func randomHex(n int) string {
|
func randomHex(n int) string {
|
||||||
if n <= 0 {
|
if n <= 0 {
|
||||||
return ""
|
return ""
|
||||||
|
|||||||
Reference in New Issue
Block a user