您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Add various search links to Audible (MyAnonaMouse, AudioBookBay, Mobilism, Goodreads, Anna's Archive, Z-Library)
当前为
// ==UserScript== // @name Audible Search Alternatives // @namespace https://greasyfork.org/en/users/1370284 // @version 0.0.1 // @license MIT // @description Add various search links to Audible (MyAnonaMouse, AudioBookBay, Mobilism, Goodreads, Anna's Archive, Z-Library) // @match https://*.audible.*/pd/* // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @require https://openuserjs.org/src/libs/sizzle/GM_config.js // ==/UserScript== GM_config.init({ id: 'audible-search-options', title: 'Search options', fields: { enableMAM: { label: 'Enable MAM', type: 'checkbox', default: true }, urlMAM: { label: 'MAM Base URL', type: 'text', default: 'https://www.myanonamouse.net' }, enableABB: { label: 'Enable ABB', type: 'checkbox', default: true }, urlABB: { label: 'ABB Base URL', type: 'text', default: 'https://audiobookbay.lu' }, enableMobilism: { label: 'Enable Mobilism', type: 'checkbox', default: true }, urlMobilism: { label: 'Mobilism Base URL', type: 'text', default: 'https://forum.mobilism.org' }, enableGoodreads: { label: 'Enable Goodreads', type: 'checkbox', default: true }, urlGoodreads: { label: 'Goodreads Base URL', type: 'text', default: 'https://www.goodreads.com' }, enableAnna: { label: "Enable Anna's Archive", type: 'checkbox', default: true }, urlAnna: { label: "Anna's Base URL", type: 'text', default: 'https://annas-archive.org' }, enableZLib: { label: 'Enable Z-Library', type: 'checkbox', default: true }, urlZLib: { label: 'Z-Library Base URL', type: 'text', default: 'https://z-lib.gs' }, } }); GM_registerMenuCommand('Open Settings', () => { GM_config.open(); }); const getSearchLinkMAM = (search) => { const baseUrl = GM_config.get('urlMAM'); const url = new URL(`${baseUrl}/tor/browse.php`) url.searchParams.set('tor[text]', search) return url.href } const getSearchLinkABB = (search) => { const baseUrl = GM_config.get('urlABB'); const url = new URL(baseUrl) url.searchParams.set('s', search) return url.href } const getSearchLinkMobilism = (search) => { const baseUrl = GM_config.get('urlMobilism'); const url = new URL(`${baseUrl}/search.php`) url.searchParams.set('keywords', search) url.searchParams.set('sr', 'topics') url.searchParams.set('sf', 'titleonly') return url.href } const getSearchLinkGoodreads = (search) => { const baseUrl = GM_config.get('urlGoodreads'); const url = new URL(`${baseUrl}/search`) url.searchParams.set('q', search) return url.href } const getSearchLinkAnna = (search) => { const baseUrl = GM_config.get('urlAnna'); const url = new URL(`${baseUrl}/search`) url.searchParams.set('q', search) url.searchParams.set('lang', 'en') return url.href } const getSearchLinkZLib = (search) => { const baseUrl = GM_config.get('urlZLib'); const url = new URL(`${baseUrl}/s/${search}`) return url.href } async function fetchJsonDL(document2) { try { const acceptedTypes = ['Audiobook', 'Product', 'BreadcrumbList'] const result = {} const ldJsonScripts = document2.querySelectorAll( 'script[type="application/ld+json"]' ) ldJsonScripts.forEach((script) => { try { const jsonLdData = JSON.parse(script.textContent?.trim() || '') const items = Array.isArray(jsonLdData) ? jsonLdData : [jsonLdData] items.forEach((item) => { if (acceptedTypes.includes(item['@type'])) { result[item['@type']] = { ...result[item['@type']], ...item } } }) } catch (error) { console.error('Error parsing JSON-LD:', error) } }) return result } catch (error) { console.error(`Error parsing data: `, error) return {} } } const waitForLdJsonScripts = () => { return new Promise((resolve, reject) => { const checkLdJson = async () => { const data = await fetchJsonDL(document) if (!!data?.Audiobook) { resolve(data) } } // Use MutationObserver to monitor DOM changes for new <script> elements const observer = new MutationObserver(async (mutationsList, observer) => { for (const mutation of mutationsList) { if (mutation.addedNodes) { for (const node of mutation.addedNodes) { if ( node.nodeType === 1 && // Only process element nodes node.tagName === 'SCRIPT' && node.type === 'application/ld+json' ) { await checkLdJson() // Process the new script tag } } } } }) // Start observing the document for added script tags observer.observe(document, { childList: true, subtree: true, }) // Also check initially in case the scripts are already present checkLdJson().then((data) => { if (Object.keys(data).length > 0) { observer.disconnect() // Stop observing when data is found resolve(data) } }) // Set a timeout in case the ld+json script never appears setTimeout(() => { observer.disconnect() reject(new Error('Timeout: ld+json script not found')) }, 2000) // Timeout after 2 seconds if the ld+json script doesn't appear }) } const style = document.createElement('style'); style.textContent = ` .custom-bc-tag { text-decoration: none; transition: background-color 0.2s ease; } .custom-bc-tag:hover { background-color: #f0f0f0; text-decoration: none; } `; document.head.appendChild(style); // Helper function to create link elements const createLink = (text, href, title) => { const link = document.createElement('a'); link.href = href; link.textContent = text; link.target = '_blank'; link.classList.add('bc-tag', 'bc-size-footnote', 'bc-tag-outline', 'bc-badge-tag', 'bc-badge', 'custom-bc-tag'); link.style.whiteSpace = 'nowrap'; link.title = title || text; return link; }; const createLinksContainer = () => { const container = document.createElement('div'); container.style.marginTop = '8px' container.style.display = 'flex' container.style.alignItems = 'center' container.style.flexWrap = 'wrap' container.style.gap = '4px' container.style.maxWidth = '300px' return container; } waitForLdJsonScripts() .then((data) => { injectSearchLinks(data) }) .catch((error) => { console.error('Error:', error.message) }) const injectSearchLinks = async (data) => { // data const title = data.Audiobook?.name const author = data.Audiobook?.author?.[0]?.name const titleAuthor = `${title} ${author} ` // elements const authorLabelEl = document.querySelector('.authorLabel') const infoParentEl = authorLabelEl?.parentElement if (!infoParentEl) { console.warn("Can't find the parent element to inject links.") return } const linksContainer = createLinksContainer() if (GM_config.get('enableMAM')) { const linkTitle = createLink('🐭 title', getSearchLinkMAM(title), 'Search MyAnonaMouse by title') const linkTitleAuthor = createLink('🐭 title + author', getSearchLinkMAM(titleAuthor), 'Search MyAnonaMouse by title & author') linksContainer.append(linkTitle, linkTitleAuthor) } if (GM_config.get('enableABB')) { const linkTitleAuthor = createLink('🎧 ABB', getSearchLinkABB(titleAuthor.toLowerCase()), 'Search AudioBookBay') linksContainer.append(linkTitleAuthor) } if (GM_config.get('enableMobilism')) { const linkTitleAuthor = createLink('📱 Mobilism', getSearchLinkMobilism(titleAuthor), 'Search Mobilism') linksContainer.append(linkTitleAuthor) } if (GM_config.get('enableGoodreads')) { const linkTitleAuthor = createLink('🔖 Goodreads', getSearchLinkGoodreads(titleAuthor), 'Search Goodreads') linksContainer.append(linkTitleAuthor) } if (GM_config.get('enableAnna')) { const linkTitleAuthor = createLink('📚 Anna', getSearchLinkAnna(titleAuthor), "Search Annas Archive") linksContainer.append(linkTitleAuthor) } if (GM_config.get('enableZLib')) { const linkTitleAuthor = createLink('📕 Z-Library', getSearchLinkZLib(titleAuthor), 'Search Z-Library') linksContainer.append(linkTitleAuthor) } infoParentEl.parentElement.appendChild(linksContainer) }