diff --git a/internal/search/audiobookbay.go b/internal/search/audiobookbay.go
index 410c2e6..69b65ec 100644
--- a/internal/search/audiobookbay.go
+++ b/internal/search/audiobookbay.go
@@ -14,6 +14,8 @@ import (
"github.com/PuerkitoBio/goquery"
)
+const abbBrowserUserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36"
+
// AudioBookBay searches an AudioBookBay-style scrape source for audiobook torrents.
// Mirrors and trackers are loaded from the runtime sources registry.
type AudioBookBay struct {
@@ -59,7 +61,7 @@ func (a *AudioBookBay) searchDomain(ctx context.Context, domain, query string) (
q.Set("s", query)
q.Set("tt", "1")
req.URL.RawQuery = q.Encode()
- req.Header.Set("User-Agent", a.cfg.UserAgent)
+ setABBRequestHeaders(req)
resp, err := a.client.Do(req)
if err != nil {
@@ -137,6 +139,7 @@ func ResolveABBMagnet(ctx context.Context, client *http.Client, userAgent, abbPa
if len(fallbackTrackers) == 0 {
return "", fmt.Errorf("no AudioBookBay fallback trackers configured (registry not loaded?)")
}
+ _ = userAgent // retained for API compatibility; ABB now uses a browser-like header set.
infoHashRe := regexp.MustCompile(`(?i)Info\s*Hash:.*?
]*>\s*([0-9a-fA-F]{40})`)
trackerRe := regexp.MustCompile(` | ((?:udp|http)://[^<]+) | `)
@@ -149,7 +152,7 @@ func ResolveABBMagnet(ctx context.Context, client *http.Client, userAgent, abbPa
if err != nil {
continue
}
- req.Header.Set("User-Agent", userAgent)
+ setABBRequestHeaders(req)
resp, err := client.Do(req)
if err != nil {
@@ -206,3 +209,11 @@ func ResolveABBMagnet(ctx context.Context, client *http.Client, userAgent, abbPa
}
return "", fmt.Errorf("failed to resolve ABB magnet from all domains")
}
+
+func setABBRequestHeaders(req *http.Request) {
+ req.Header.Set("User-Agent", abbBrowserUserAgent)
+ req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8")
+ req.Header.Set("Accept-Language", "en-US,en;q=0.9")
+ req.Header.Set("Upgrade-Insecure-Requests", "1")
+ req.Header.Set("Accept-Encoding", "identity")
+}
diff --git a/internal/search/audiobookbay_test.go b/internal/search/audiobookbay_test.go
new file mode 100644
index 0000000..b5b421e
--- /dev/null
+++ b/internal/search/audiobookbay_test.go
@@ -0,0 +1,92 @@
+package search
+
+import (
+ "context"
+ "io"
+ "net/http"
+ "strings"
+ "testing"
+
+ "github.com/JeremiahM37/librarr/internal/config"
+ "github.com/JeremiahM37/librarr/internal/sources/sourcestest"
+)
+
+type abbRoundTripper struct {
+ body string
+ req *http.Request
+}
+
+func (r *abbRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
+ r.req = req.Clone(req.Context())
+ return &http.Response{
+ StatusCode: http.StatusOK,
+ Header: make(http.Header),
+ Body: io.NopCloser(strings.NewReader(r.body)),
+ Request: req,
+ }, nil
+}
+
+func TestAudioBookBaySearchSetsBrowserHeaders(t *testing.T) {
+ rt := &abbRoundTripper{
+ body: `
+
+
+
Language: English Keywords:
+
+ `,
+ }
+ reg, err := sourcestest.Registry()
+ if err != nil {
+ t.Fatalf("load registry: %v", err)
+ }
+ cfg := &config.Config{
+ UserAgent: "test",
+ Sources: reg,
+ }
+ cfg.Sources.AudioBookBay.Mirrors = []string{"audiobookbay.lu"}
+ a := &AudioBookBay{cfg: cfg, client: &http.Client{Transport: rt}}
+
+ results, err := a.searchDomain(context.Background(), "audiobookbay.lu", "The Martian")
+ if err != nil {
+ t.Fatalf("searchDomain returned error: %v", err)
+ }
+ if len(results) != 1 {
+ t.Fatalf("expected 1 result, got %d", len(results))
+ }
+ if got := rt.req.Header.Get("User-Agent"); got != abbBrowserUserAgent {
+ t.Fatalf("User-Agent = %q, want %q", got, abbBrowserUserAgent)
+ }
+ if got := rt.req.Header.Get("Accept-Language"); got != "en-US,en;q=0.9" {
+ t.Fatalf("Accept-Language = %q, want en-US,en;q=0.9", got)
+ }
+ if got := rt.req.Header.Get("Upgrade-Insecure-Requests"); got != "1" {
+ t.Fatalf("Upgrade-Insecure-Requests = %q, want 1", got)
+ }
+}
+
+func TestResolveABBMagnetUsesBrowserHeaders(t *testing.T) {
+ rt := &abbRoundTripper{
+ body: `
+ The Martian
+
+ | Info Hash: | 0123456789ABCDEF0123456789ABCDEF01234567 |
+ | udp://tracker.example:1337/announce |
+
+ `,
+ }
+ client := &http.Client{Transport: rt}
+
+ magnet, err := ResolveABBMagnet(context.Background(), client, "test", "/abss/the-martian-andy-weir/", []string{"audiobookbay.lu"}, []string{"udp://fallback.example:1337/announce"})
+ if err != nil {
+ t.Fatalf("ResolveABBMagnet returned error: %v", err)
+ }
+ if !strings.HasPrefix(magnet, "magnet:?xt=urn:btih:0123456789ABCDEF0123456789ABCDEF01234567") {
+ t.Fatalf("unexpected magnet: %s", magnet)
+ }
+ if got := rt.req.Header.Get("User-Agent"); got != abbBrowserUserAgent {
+ t.Fatalf("User-Agent = %q, want %q", got, abbBrowserUserAgent)
+ }
+ if got := rt.req.Header.Get("Accept-Encoding"); got != "identity" {
+ t.Fatalf("Accept-Encoding = %q, want identity", got)
+ }
+}