') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); Author search by xartle · Pull Request #231 · markbeep/AudioBookRequest · GitHub
Skip to content

Author search - #231

Open
xartle wants to merge 2 commits into
markbeep:mainfrom
xartle:author-search
Open

Author search#231
xartle wants to merge 2 commits into
markbeep:mainfrom
xartle:author-search

Conversation

@xartle

Copy link
Copy Markdown

I added a really simple filter so search only shows results that match in the title or in the author. It didn't seem that was available from the API, so this does the same search, but limits the the books array to items that match. It seems to work well so far.

@xartle

Copy link
Copy Markdown
Author

I did run all the checks btw.

uv run basedpyright # strict static typing
uv run ruff format --check app # formatting
uv run alembic check

@markbeep

Copy link
Copy Markdown
Owner

Hey, thanks for the changes.

I'm not fully sold this is something that is required though. I've felt the audible search (which ABR uses under the hood) is fairly good at showing the correct results for title-only, author-only, or just a mix of search terms in the general direction of a book.

Do you have an example for some books that you tried searching for, but were pushed to later pages because all results were combined?

Additionally, this filtering is is very strict and quick to discard results that would be valid. If, for example, you typoed "The Silmarilion" (1 'L') the standard audible result would still know what you searched for, while your title matching discards the original "The Silmarillion".

Similarly for the authors, the ordering of the author's name matters. Entering "Brown Pierce" (or even "Brown, Pierce") works with the audible search, but your author filtering would reject the results.

@xartle

xartle commented Aug 28, 2026

Copy link
Copy Markdown
Author

Sure, "Virtual Light" was my test case book. There was only one title match, and the right one was on top, but there was a full page of search results that I found myself looking through. Most titles looked totally unconnected like "Bible Stories for Adults Modern Meets Mesopotamia: A Walk Through Life with Daniel Frank Milligan" and "The Ethics of Aristotle The Great Courses, Father Joseph Koterski S.J." Maybe connected on "Virtue"? I did think it was pretty funny really, but what got me thinking it could be a simple fix.

It's a good point on the restrictiveness. I was shooting for simple to implement but maybe I should have titled it "Exact search." :) Given that it's the same source search though, I could add some extra code to allow for misspellings. I'd been more thinking along the lines of, when I click on the authors name, if that used this mechanism I'd only see the author that I'd clicked on.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@xartle@markbeep