A sidekick for the AudioBookShelf web interface
// ==UserScript==
// ----------------------------------- Metadata --------------------------------------
// @name ABSidekick
// @author WirlyWirly
// @version 0.86
// @homepage https://github.com/WirlyWirly/ABSidekick
// @description A sidekick for the AudioBookShelf web interface
// Written on 🐺 LibreWolf via 🐵 Violentmonkey
// ----------------------------------- Matches --------------------------------------
// If ABSidekick does not run automatically, edit this '@match' line so that it has the same Audiobookshelf IP:PORT as used in the browser
// @match http://localhost:13378/audiobookshelf/*
// @include /https?://.+/audiobookshelf/.*/
// ----------------------------------- Permissions --------------------------------------
// @grant GM_addStyle
// @grant GM_getValue
// @grant GM_info
// @grant GM_listValues
// @grant GM_registerMenuCommand
// @grant GM_setValue
// ----------------------------------- Other --------------------------------------
// @namespace WirlyScripts
// @run-at document-end
// @supportURL https://github.com/WirlyWirly/ABSidekick/discussions
// ----------------------------------- Dependencies --------------------------------------
// @require https://cdn.jsdelivr.net/gh/sizzlemctwizzle/GM_config@43fd0fe4de1166f343883511e53546e87840aeaf/gm_config.js
// ----------------------------------- Script Links --------------------------------------
// @icon https://raw.githubusercontent.com/WirlyWirly/ABSidekick/main/.github/assets/icon.webp?raw=true
// ==/UserScript==
// =================================== CODE ======================================
// The current domain, which will be used when generating Audiobookshelf urls
let absURL = document.URL.match(/^(.+?\/audiobookshelf)\//)[1]
// Initialize the GM_config settings panel and populate the global SETTINGS object
let SETTINGS = gmcSettingsPanel()
// Create the GM_config settings panel button in the #appbar
waitForElement('#appbar a[href="/audiobookshelf/config"]', document.body).then(function(absConfigButton) {
// The ABS config button is now available in the #appbar
// Generate the 🛠️ button
let settingsShortcut = document.createElement('div')
settingsShortcut.id = 'gmConfigAppBar'
settingsShortcut.innerText = '🛠️'
settingsShortcut.title = 'Open the ABSidekick settings panel'
settingsShortcut.addEventListener('click', function() {
GM_config.open()
})
// Insert it after the ABS config button
absConfigButton.insertAdjacentElement('afterend', settingsShortcut)
})
// The <div> element that will be used to display hover covers
let hoverCoverElement = document.createElement('div')
document.body.appendChild(hoverCoverElement)
hoverCoverElement.outerHTML = `<div id="hoverCoverContainer"><img src="" style="border-radius: 10px; max-height: 100%; max-width: 100%"></div>`
// The functions that will monitor main elements and then initiate injections when the desired target is ready
appContentMain() // ItemPage
editPanelMain() // MatchTab
// =================================== MAINS ======================================
async function appContentMain() {
// Setup a MutationObserver to monitor the #app-content element for changes, which indicates a new page-type has been loaded and needs to be handled
let appContent = await waitForElement('#app-content', document.body)
// Check if the initial page load was already an itemPage
appContent.querySelector('#item-page-wrapper') ? itemPageInjector(appContent.querySelector('#item-page-wrapper')) : null
// Check if the initial page load was already a library\Series page
appContent.querySelector(':scope > div > #bookshelf:not([data-absidekick-injected])') ? librarySeriesInjector(appContent.querySelector('#bookshelf')) : null
let appContentObserver = new MutationObserver(async function(mutations) {
// The actions to perform when new mutations are detected to the '#app-content' element (switching tabs)
let itemPage = appContent.querySelector('div > #item-page-wrapper:not([data-absidekick-injected])')
let bookshelfPage = appContent.querySelector('div > #bookshelf:not([data-absidekick-injected])')
if ( itemPage ) {
// This is a itemPage (book page)
itemPageInjector(itemPage)
} else if ( bookshelfPage ) {
// This is a bookshelfPage (Home\Library\Series\Collections\Authors\Narrators\Stats)
let bookshelfObserver = new MutationObserver(async function(mutations) {
if ( bookshelfPage.querySelector(':scope > div[id="shelf-0"]') ) {
// The 'Home' or 'Library' or 'Series' tab was loaded
bookshelfObserver.disconnect()
//librarySeriesInjector(bookshelfPage)
} else if ( bookshelfPage.querySelector(':scope > div:last-child:nth-child(2)') ) {
// The 'Home' tab was loaded
bookshelfObserver.disconnect()
}
})
bookshelfObserver.observe(bookshelfPage, { childList: true })
}
})
let target = appContent
let config = { childList: true }
appContentObserver.observe(target, config)
}
async function editPanelMain() {
// Wait for the 'Edit Panel' to be loaded and then proceed with adding functionality
// Observer the <body> child elements until the <div> of the edit panel [data-v-779b4e02] is loaded
let modalOverlay = await waitForElement('body > div.modal[data-v-779b4e02]', document.body, false)
// Verify that this is the correct modalOverlay by querying for the 6 <button> elements that are used as the tabs in the Edit Panel
let editPanel = await waitForElement('div.relative:has(div[role="tablist"] > button:last-child:nth-child(6))', modalOverlay)
// Set identifiers for the edit panel and elements of interest
modalOverlay.id = 'modalOverlay'
modalOverlay.querySelector('div > h1').id = 'bookTitle'
editPanel.id = 'editPanel'
editPanel.querySelector('div[role="tablist"]').id = 'editPanelTabs'
editPanel.querySelector('div.absolute[role="tablist"] button:nth-child(1)').id = 'detailsTab'
editPanel.querySelector('div.absolute[role="tablist"] button:nth-child(2)').id = 'coverTab'
editPanel.querySelector('div.absolute[role="tablist"] button:nth-child(3)').id = 'chaptersTab'
editPanel.querySelector('div.absolute[role="tablist"] button:nth-child(4)').id = 'filesTab'
editPanel.querySelector('div.absolute[role="tablist"] button:nth-child(5)').id = 'matchTab'
editPanel.querySelector('div.absolute[role="tablist"] button:nth-child(6)').id = 'toolsTab'
editPanel.querySelector('button[aria-label="Previous"]').id = 'navigateRight'
editPanel.querySelector('button[aria-label="Next"]').id = 'navigateLeft'
// -- MatchTab ---
let matchTabButton = editPanel.querySelector('#matchTab')
matchTabButton.innerText = '🌱 Match'
matchTabButton.addEventListener('click', async function(event) {
// The match tab of the edit panel was clicked
await waitForElement('#match-wrapper', editPanel)
// Check that this match tab does not have a 'Title' button, which indicates that it needs injections
!editPanel.querySelector('#buttonTitle') ? matchTabInjector() : null
})
}
// =================================== INJECTORS ======================================
async function itemPageInjector(itemPage) {
// Inject the current itemPage with custom elements and setup a MutationObserver to monitor for any new match results
itemPage.setAttribute('data-absidekick-injected', '')
// Add identifiers to the various elements of interest
setId(itemPage, '#item-page-wrapper > div > :nth-child(1)', 'coverSection')
setId(itemPage, '#item-page-wrapper > div > :nth-child(2)', 'metadataSection')
setId(itemPage, '#coverSection img[src*="/api/items/"]', 'itemCover')
// - MetaData rows -
setId(itemPage, '#metadataSection > div.flex > div.mb-4', 'metaTop')
setId(itemPage, '#metaTop h1', 'itemTitle')
setId(itemPage, '#metaTop > :nth-child(2)', 'itemSubtitle')
setId(itemPage, '#metaTop > a[href*="/series/"]', 'itemSeries')
setId(itemPage, '#metaTop a[href*="/audiobookshelf/author/"]', 'itemAuthor')
setId(itemPage, '#metaTop > :last-child[data-v-338ea578', 'itemMetaRows')
// - Buttons above synopsis -
setId(itemPage, '#metadataSection > div:has(button.abs-btn)', 'buttonsRow')
setId(itemPage, '#buttonsRow > button.abs-btn', 'itemPlay')
// - Library files dropdown
setId(itemPage, '#metadataSection > :last-child', 'libraryFiles')
// Cover Container
itemPage.querySelector('#itemCover').parentElement.parentElement.classList.add('itemCover')
document.querySelector('#metadataSection').style.position = 'relative'
// Background blur
if ( SETTINGS.itemBackgroundBlur ) {
let coverURL = itemPage.querySelector('#itemCover').src
itemPage.parentElement.style.background = `url('${coverURL}') no-repeat center center fixed`
itemPage.parentElement.style.backgroundColor = '#0000'
itemPage.parentElement.style.backgroundSize = 'cover'
itemPage.classList.add('blurEffect')
}
// Meta Glass
if ( SETTINGS.itemMetaGlass ) {
// Metadata rows
itemPage.querySelector('#itemMetaRows').classList.add('itemMetaRows')
}
// Progress Glass
if ( SETTINGS.itemProgressGlass ) {
if ( itemPage.querySelector('#metadataSection > :nth-child(2)').innerText.match(/Started \d+\/\d+\/\d+/) ) {
itemPage.querySelector('#metadataSection > :nth-child(2)').classList.add('itemProgress')
}
}
// Buttons Glass
if ( SETTINGS.itemButtonGlass ) {
for ( let button of itemPage.querySelectorAll('#buttonsRow button.bg-primary') ) {
button.classList.add('itemButtonGlass')
}
}
// Summary Glass
if ( SETTINGS.itemSummaryGlass ) {
// Description
itemPage.querySelector('#item-description').parentElement.classList.add('descriptionContainer')
}
// Dropdown Glass
if ( SETTINGS.itemDropdownGlass ) {
for ( dropdownBar of itemPage.querySelectorAll('#metadataSection div.w-full.bg-primary:has(p)') ) {
dropdownBar.classList.add('itemDropdown')
}
}
// Hover Cover
let hoverElement = document.createElement('div')
hoverElement.innerText = '👀'
hoverElement.classList.add('hoverCoverToggle')
hoverElement.addEventListener('mouseenter', function(event) {
// Display the enlarged item cover
let { clientX, clientY } = event
viewHoverCover(`${absURL}/api/items/${itemId}/cover?raw=1`, clientX, clientY)
})
hoverElement.addEventListener('mouseout', function(event) {
// Stop displaying the enlarged result cover
let hoverCoverElement = document.getElementById('hoverCoverContainer')
hoverCoverElement.classList.remove('active')
hoverCoverElement.querySelector('img').src = ''
})
itemPage.querySelector('#itemCover').insertAdjacentElement('afterend', hoverElement)
// Fetch item metadata
let itemId = document.getElementById('itemCover').src.match(/\/items\/(.+?)\//)[1]
let metadata = await fetchItemMetadata(itemId)
// The object containing the data used to fill in templates
let templateVariables = {
title: metadata.media.metadata.title,
author: metadata.media.metadata.authors[0].name,
year: metadata.media.metadata.publishedYear,
asin: metadata.media.metadata.asin,
}
console.log(metadata)
console.log(templateVariables)
// Goodreads Button
let goodreadsButton = document.createElement('button')
goodreadsButton.innerText = 'Goodreads'
goodreadsButton.setAttribute('class', 'bg-primary border-gray-600 border rounded-md mx-0.5')
goodreadsButton.classList.add('itemButton')
SETTINGS.itemButtonGlass ? goodreadsButton.classList.add('itemButtonGlass') : null
goodreadsButton.title = 'Search Goodreads for this title'
goodreadsButton.addEventListener('click', function(event) {
// Open the Audible page using the available ASIN
let goodreadsURL = SETTINGS.goodreadsTemplate.replace(/%title%/, encodeURI(templateVariables.title))
window.open(goodreadsURL, '_blank')
})
itemPage.querySelector('#buttonsRow > div:last-child').insertAdjacentElement('beforebegin', goodreadsButton)
// Audible Button (if this item has a ASIN)
if ( templateVariables.asin ) {
let audibleButton = document.createElement('button')
audibleButton.innerText = 'Audible'
audibleButton.setAttribute('class', 'bg-primary border-gray-600 border rounded-md mx-0.5')
audibleButton.classList.add('itemButton')
SETTINGS.itemButtonGlass ? audibleButton.classList.add('itemButtonGlass') : null
audibleButton.title = 'Open the Audible page of this item'
audibleButton.addEventListener('click', function(event) {
// Open the Audible page using the available ASIN
let asinURL = SETTINGS.audibleTemplate.replace(/%asin%/, metadata.media.metadata.asin)
window.open(asinURL, '_blank')
})
itemPage.querySelector('#buttonsRow > div:last-child').insertAdjacentElement('beforebegin', audibleButton)
}
// Custom Buttons
for (let i = 1; i <= SETTINGS.customButtonCount; i++) {
// This custom button has a label, so generate and insert the button element
if ( SETTINGS[`custom_button_label_${i}`] != '' ) {
let customButton = document.createElement('button')
customButton.innerText = SETTINGS[`custom_button_label_${i}`]
customButton.setAttribute('class', 'bg-primary border-gray-600 border rounded-md mx-0.5')
customButton.classList.add('itemButton')
SETTINGS.itemButtonGlass ? customButton.classList.add('itemButtonGlass') : null
customButton.title = `🌐 ${SETTINGS[`custom_button_label_${i}`]}\n\n🔗 ${SETTINGS[`custom_button_template_${i}`]}`
customButton.addEventListener('click', function(event) {
let customURL = SETTINGS[`custom_button_template_${i}`].replace(/%title%/, templateVariables.title)
templateVariables.author ? customURL = customURL.replace(/%author%/, templateVariables.author) : customURL = customURL.replace(/%author%/, '')
templateVariables.year ? customURL = customURL.replace(/%year%/, templateVariables.year) : customURL = customURL.replace(/%year%/, '')
templateVariables.asin ? customURL = customURL.replace(/%asin%/, templateVariables.asin) : customURL = customURL.replace(/%asin%/, '')
window.open(customURL, '_blank')
})
itemPage.querySelector('#buttonsRow > div:last-child').insertAdjacentElement('beforebegin', customButton)
}
}
}
async function librarySeriesInjector(bookshelfPage) {
bookshelfPage.setAttribute('data-absidekick-injected', '')
// Wait until a cover image is available, indicating that the items have finished loading
await waitForElement('img[src*="/api/items/', bookshelfPage)
}
async function matchTabInjector() {
// Inject the MatchTab with custom elements and setup a MutationObserver to monitor for any new match results
let editPanel = document.querySelector('#editPanel')
let matchTab = editPanel.querySelector('#match-wrapper')
// Add identifiers to the various elements of interest
setId(matchTab, ':nth-child(1)', 'searchForm')
setId(matchTab, '#searchForm input[placeholder="Search.."]', 'inputTitle')
matchTab.querySelector('#searchForm input[placeholder="Search.."]').parentElement.previousElementSibling.id = 'labelInputTitle'
setId(matchTab, '#searchForm input[placeholder="Author"]', 'inputAuthor')
setId(matchTab, '#searchForm button[type="Submit"]', 'buttonSearch')
setId(matchTab, 'div.matchListWrapper', 'resultsList')
//matchTab.querySelector('#searchForm button[aria-label^="Provider"]').id = 'buttonProvider'
// Create the GM_config settings shortcut
let settingsShortcut = document.createElement('div')
matchTab.appendChild(settingsShortcut)
settingsShortcut.id = 'gmconfigShortcut'
settingsShortcut.innerText = '🛠️'
settingsShortcut.title = 'Open the ABSidekick settings panel'
settingsShortcut.addEventListener('click', function() {
GM_config.open()
})
// The `🏷️ Not Found` button
let notFoundButton = document.createElement('button')
matchTab.querySelector('#buttonSearch').insertAdjacentElement('afterend', notFoundButton)
notFoundButton.id = `buttonNotFound`
notFoundButton.innerText = `🏷️ Not Found`
notFoundButton.title = `Tag this item as not found, then continue to the next book in the list\n\n🏷️ Tags: ${SETTINGS.notFoundTagsList}`
notFoundButton.setAttribute('class', 'buttonNotFound abs-btn rounded-md shadow-md relative border border-gray-600 mt-5 ml-1 text-white bg-primary px-8 py-2')
notFoundButton.addEventListener('click', async function(event) {
if ( SETTINGS.apiKey == '' ) {
window.alert('❌ ABSidekick ❌\n\nThis button requires a valid ApiKey\n\nProvide an ApiKey in the ABSidekick settings panel')
return
} else {
if ( matchTab.querySelector('img.currentCover') ) {
// There is a current cover, so parse from it the absId then set the notFound tag
let absId = matchTab.querySelector('img.currentCover').src.match('\/api\/items\/(.+)\/cover')[1]
// GET the current saved tags
let metadata = await fetchItemMetadata(absId)
let currentTags = metadata.media.tags
// PATCH the current + custom tags
fetch(`${absURL}/api/items/${absId}/media`, {
method: 'PATCH',
body: JSON.stringify({ tags: currentTags.concat(SETTINGS.notFoundTagsList) }),
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${SETTINGS.apiKey}`
},
}).then(function(response) {
// The PATCH was successful, so cycle to the next item
cleanMatchTab()
SETTINGS.navigationDirection == 'Right' ? editPanel.querySelector('#navigateRight').click() : null
SETTINGS.navigationDirection == 'Left' ? editPanel.querySelector('#navigateLeft').click() : null
})
} else {
// There is not a current cover, so get it from the cover tab
// observer.disconnect()
// let absId = this.dataset.absId
// let metadata = await fetchItemMetadata(absId)
// let currentTags = metadata.media.tags
// let additionalTags = SETTINGS.notFoundTagsList
}
}
})
// Create the 'Title' search button
let titleSearchButton = document.createElement('button')
titleSearchButton.id = 'buttonTitle'
titleSearchButton.innerText = 'Title'
titleSearchButton.setAttribute('class', 'abs-btn rounded-md shadow-md relative border border-gray-600 mt-5 ml-1 text-white bg-primary px-8 py-2')
titleSearchButton.title = 'Fill the search field with the current title'
matchTab.querySelector('form > div').appendChild(titleSearchButton)
titleSearchButton.addEventListener('click', function(event) {
// The actions to take when the 'Title' button is clicked
titleSearch()
})
// Create the 'AutoMatch' button
let autoMatchButton = document.createElement('button')
matchTab.querySelector('form > div').appendChild(autoMatchButton)
autoMatchButton.id = 'buttonAutoMatch'
autoMatchButton.setAttribute('class', 'abs-btn rounded-md shadow-md relative border border-gray-600 mt-5 ml-1 text-white bg-primary px-8 py-2')
if ( SETTINGS.autoMatchEnabled == false ) {
autoMatchButton.innerText = `AutoMatch`
autoMatchButton.title = `Enable AutoMatch\n\nℹ️ Confidence >=${SETTINGS.autoMatchConfidence}%`
autoMatchButton.style.animation = ''
} else {
autoMatchButton.innerText = `🤖 AutoMatch`
autoMatchButton.title = `AutoMatch is enabled\n\nClick or press <TAB> to cancel\n\nℹ️ Confidence: >= ${SETTINGS.autoMatchConfidence}`
autoMatchButton.style.animation = 'pop .50s linear infinite alternate'
}
autoMatchButton.addEventListener('click', function(event) {
// Toggle AutoMatch
SETTINGS.autoMatchEnabled == false ? autoMatchStart(matchTab.querySelectorAll('#resultsList div.resultProcessed')) : autoMatchStop()
})
// When the edit panel is manually cycled, clean the match tab of old info
editPanel.querySelector('#navigateRight').addEventListener('mouseup', () => { cleanMatchTab() } )
editPanel.querySelector('#navigateLeft').addEventListener('mouseup', () => { cleanMatchTab() } )
let observer = new MutationObserver(async function(mutations) {
// Actions to take when there are changes in the match tab
// Query for only the newly added match results
let newMatchResults = matchTab.querySelectorAll('#resultsList > div > div.cursor-pointer:not(.resultProcessed)')
if ( newMatchResults.length > 0 ) {
// There are new match results, so process each item
for ( let result of newMatchResults ) {
// For each match, generate the new elements (buttons|coverDimensions)
// Add identifiers to the various elements of intereset in a match result
result.classList.add('resultProcessed')
result.parentElement.classList.add('resultContainer')
result.querySelector(':nth-child(1)').classList.add('resultCover')
result.querySelector('.resultCover img').classList.add('resultCoverImg')
result.querySelector(':nth-child(2)').classList.add('resultMeta')
result.querySelector('.resultMeta > :nth-child(1)').classList.add('resultTitle')
result.querySelector('.resultMeta > :nth-child(2)').classList.add('resultDetails')
try{ result.querySelector('.resultDetails div.rounded-full').classList.add('resultConfidence') } catch(error) {}
try{ result.querySelector('.resultMeta > div:has( > div.rounded-full > p)').classList.add('resultSeries') } catch(error) {}
result.querySelector('.resultMeta > div.overflow-hidden:has(> p)').classList.add('resultSynopsis')
// After the result images have a chance to load, create the Dimensions label
setTimeout(() => {
let matchCover = result.querySelector('.resultCoverImg')
let dimensionsElement = document.createElement('div')
dimensionsElement.classList.add('resultCoverDimensions')
dimensionsElement.innerText = `${matchCover.naturalWidth} x ${matchCover.naturalHeight}`
matchCover.parentElement.insertAdjacentElement('afterend', dimensionsElement)
dimensionsElement.addEventListener('mouseenter', function(event) {
// Display the enlarged result cover
let { clientX, clientY } = event
viewHoverCover(this.parentElement.querySelector('.resultCoverImg').src, clientX, clientY)
})
dimensionsElement.addEventListener('mouseout', function(event) {
// Stop displaying the enlarged result cover
let hoverCoverElement = document.getElementById('hoverCoverContainer')
hoverCoverElement.classList.remove('active')
})
}, 500)
// Save Button
let saveResultButton = document.createElement('button')
saveResultButton.innerText = 'Save Match'
saveResultButton.title = "Save this match result then continue to the next book\n\nℹ️ This is the same as clicking the 'Submit' button of the match result"
saveResultButton.classList.add('saveResult', 'resultButton')
saveResultButton.addEventListener('click', function(event) {
event.button == 0 ? saveResult(this) : null
observer.disconnect()
})
// Save + Tag Button
let saveTagButton = document.createElement('button')
saveTagButton.innerText = `Save + 🏷️`
saveTagButton.title = `Save this match result, add the custom tags, then continue to the next book\n\n🏷️ Tags: ${SETTINGS.saveTagsList}`
saveTagButton.classList.add('saveResultTags', 'resultButton')
saveTagButton.addEventListener('click', function(event) {
if ( SETTINGS.apiKey == '' ) {
window.alert('❌ ABSidekick ❌\n\nThis button requires a valid ApiKey\n\nProvide an ApiKey in the ABSidekick settings panel')
return
} else {
observer.disconnect()
saveResult(this, SETTINGS.saveTagsList)
}
})
// Audible Button
let asinButton = document.createElement('button')
asinButton.innerText = 'Audible'
asinButton.title = 'Open the Audible page of this match result\n\nℹ️ Only works if the match result has an ASIN'
asinButton.classList.add('asinSearch', 'resultButton')
asinButton.addEventListener('mouseup', function(event) {
event.button == 0 ? audibleLookup(this) : null
})
// Create and populate the element that will contain the ABSidekick buttons
let buttonHolder = document.createElement('div')
buttonHolder.classList.add('scriptButtons')
buttonHolder.appendChild(saveResultButton)
buttonHolder.appendChild(saveTagButton)
buttonHolder.appendChild(asinButton)
result.parentElement.appendChild(buttonHolder)
}
// --- Use the first match result to get the currentId ---
newMatchResults[0].click()
// Wait until the submit button is available, indicating the form details are ready
let submitButton = await waitForElement('button.bg-success[type="submit"]', matchTab)
let coverURL = matchTab.querySelector('form img[src*="/api/items/"]').src
SETTINGS.currentId = coverURL.match(/\/items\/(.+?)\//)[1]
// Click the back arrow to return to match results
let backArrowElement = matchTab.querySelector('div.cursor-pointer:has(> span.material-symbols')
backArrowElement.click()
// There is not a current cover image, so create one
if ( !matchTab.querySelector('img.currentCover') ) {
addBookData(SETTINGS.currentId)
}
// Start AutoMatch if enabled and this is not the same item as was previously saved
if ( SETTINGS.autoMatchEnabled == true && SETTINGS.previousId != SETTINGS.currentId ) {
autoMatchStart(newMatchResults, observer)
}
} else if ( matchTab.querySelector('#match-wrapper > :nth-child(3)').style.display != 'none' ) {
// The 'No Results' notice is visible, which indicates that the search returned no match results
// Update the 'No results' message
matchTab.querySelector('#match-wrapper > :nth-Child(3) > p').outerHTML = `<p class="noResults">Oh 💩, no results! 😭</p>`
let resultsList = document.getElementById('resultsList')
let searchQuery = matchTab.querySelector('#inputTitle').value
let bookTitle = document.getElementById('bookTitle').innerText
// Make sure a title search has not already been readied, that there are 0 match results, and that the search term is not already the title
if ( resultsList && !resultsList.classList.contains('titleSearchReady') && resultsList.childElementCount == 0 && searchQuery != bookTitle ) {
// The title was not used to perform the search, so prepare a title search
titleSearch()
// There is a currentId available and there is not already a current cover
if ( SETTINGS.currentId && !matchTab.querySelector('img.currentCover') ) {
addBookData(SETTINGS.currentId)
}
}
}
})
// Monitor the 'style=' property of #resultsList (.matchListWrapper), which changes between searches\navigations
let target = matchTab.querySelector('#resultsList')
let config = { childList: true , attributeFilter: ['style'] }
observer.observe(target, config)
}
// =================================== HELPERS ======================================
function waitForElement(cssTarget, observeTarget = document.body, observeSubTree = true) {
// Wait until the cssTarget exists within the observeTarget and then resolve the promise
// Source: https://stackoverflow.com/a/61511955
return new Promise( function(resolve) {
if ( observeTarget.querySelector(cssTarget) ) {
// The cssTarget already exists within the observeTarget, so immediately resolve the promise
return resolve(observeTarget.querySelector(cssTarget))
}
const observer = new MutationObserver( mutations => {
// The actions to take when there are new mutations to the observeTarget
if ( observeTarget.querySelector(cssTarget) ) {
// The cssTarget has been found within the observeTarget
observer.disconnect()
resolve(observeTarget.querySelector(cssTarget))
}
})
// If you get "parameter 1 is not of type 'Node'" error, see https://stackoverflow.com/a/77855838/492336
try {
observer.observe(observeTarget, { childList: true, subtree: observeSubTree })
} catch (error) {
// console.log(error)
}
})
}
function setId(baseElement, targetSelector, idValue) {
// Check the baseElement for the targetSelector and if it exists set the idValue
baseElement.querySelector(targetSelector) ? baseElement.querySelector(targetSelector).id = idValue : null
}
async function fetchItemMetadata(itemId) {
// Use the provided itemId to GET the metadata from the api
let response = await fetch(`${absURL}/api/items/${itemId}`, {
headers: { 'Authorization': `Bearer ${SETTINGS.apiKey}` }
})
let metadata = await response.json()
return metadata
}
function addBookData(itemId) {
// Add metadata of the provided book to the Match tab
// The cover image
let currentCoverElement = document.createElement('div')
document.querySelector('#searchForm > div').firstChild.insertAdjacentElement('beforebegin', currentCoverElement)
currentCoverElement.title = 'The current cover for this book'
currentCoverElement.classList.add('currentCover')
currentCoverElement.innerHTML = `<img class="currentCover" src="${absURL}/api/items/${itemId}/cover?&raw=1" data-abs-id="${itemId}">`
currentCoverElement.addEventListener('mouseenter', function(event) {
let { clientX, clientY } = event
viewHoverCover(this.parentElement.querySelector('img').src, clientX, clientY)
})
currentCoverElement.addEventListener('mouseout', function(event) {
let hoverCoverElement = document.getElementById('hoverCoverContainer')
hoverCoverElement.classList.remove('active')
})
}
function cleanMatchTab() {
// Clean the match tab of book-specific changes
let matchTab = document.getElementById('match-wrapper')
SETTINGS.previousId = SETTINGS.currentId
SETTINGS.currentId = ''
if ( matchTab ) {
// Remove a previous cover image
matchTab.querySelectorAll('div.currentCover').forEach((element) => { element.remove() })
// Remove 'Title' search styling and indicators
let labelTitle = matchTab.querySelector('#labelInputTitle')
if ( labelTitle.classList.contains('titleSearchReady') ) {
labelTitle.innerText = 'Search Title or ASIN'
labelTitle.classList.remove('titleSearchReady')
matchTab.querySelector('#resultsList').classList.remove('titleSearchReady')
}
}
}
async function titleSearch() {
// The 'Title' search button of the match tab was clicked or the initial search returned no results
// The <h1> element at the top of the screen, which displays the title of the current book
let bookTitle = document.getElementById('bookTitle').innerText
// Update the search field of the Match tab and simulate an 'input' event
let matchTab = document.getElementById('match-wrapper')
let titleField = matchTab.querySelector('#inputTitle')
titleField.value = bookTitle
titleField.dispatchEvent(new KeyboardEvent('input'))
// Update the Search field label to indicate this was a title search
matchTab.querySelector('#labelInputTitle').innerText = '📖 Title Search!'
matchTab.querySelector('#labelInputTitle').classList.add('titleSearchReady')
// Add a class to indicate that a title search has already been readied
matchTab.querySelector('#resultsList').classList.add('titleSearchReady')
// Click the search button
matchTab.querySelector('#buttonSearch').click()
}
function autoMatchStart(matchResults, mutationObserver = false) {
// Enable and then AutoMatch for a match entry among the provided array
// AutoMatch has not yet been enabled, so make the changes necessary to cancel it
if ( SETTINGS.autoMatchEnabled == false ) {
SETTINGS.autoMatchEnabled = true
// MatchTab button
let autoMatchButton = document.getElementById('buttonAutoMatch')
autoMatchButton.innerText = `🤖 AutoMatch`
autoMatchButton.title = `AutoMatch is enabled\n\nClick or press <Tab> to cancel\n\nℹ️ Confidence: >= ${SETTINGS.autoMatchConfidence}`
autoMatchButton.style.animation = 'pop .50s linear infinite alternate'
// Floating button
let autoMatchCancel = document.createElement('button')
document.body.appendChild(autoMatchCancel)
autoMatchCancel.outerHTML = `<button id="autoMatchCancel" title="AutoMatch is enabled\n\nClick or press <Tab> to cancel\n\nℹ️ Confidence: >= ${SETTINGS.autoMatchConfidence}" class="autoMatchCancel bg-primary rounded-md text-white">🤖 AutoMatch</button>`
autoMatchCancel = document.getElementById('autoMatchCancel')
autoMatchCancel.addEventListener('click', function(event) { autoMatchStop() })
// Global "TAB" Automatch cancel
window.addEventListener('keydown', autoMatchKeyStop)
}
// Check for a AutoMatch among the provided matchResults
for ( let result of matchResults ) {
// The element containing the confidence percentile
let confidenceElement = result.querySelector('div.resultConfidence')
// The confidence element was found
if ( confidenceElement ) {
let confidenceScore = confidenceElement.innerText.match(/(\d+)%/)[1]
// Check if the match result has a duration of 'exact match'
let durationText = result.querySelector('div.resultDetails').innerText.match(/Duration: .+?\((.+)\)/i)[1]
if ( confidenceScore >= SETTINGS.autoMatchConfidence || durationText == 'exact match' ) {
// This match result has met or exceeded the confidence threshold, so prepare to save it
result.parentElement.classList.add('autoMatchSelection')
// Wait the specified number of milliseconds before proceeding
setTimeout(() => {
if ( SETTINGS.autoMatchEnabled == true ) {
// AutoMatch was not disabled\canceled, so continue with the save
try{
// Try\Catch, just in case the user left the MatchTab
mutationObserver ? mutationObserver.disconnect() : null
let targetButton = SETTINGS.autoMatchTarget == 'Save Match' ? 'saveResult' : 'saveResultTags'
result.parentElement.querySelector(`button.${targetButton}`).click()
} catch(error) {}
}
}, SETTINGS.autoMatchDelay)
break
}
}
}
}
function autoMatchKeyStop(event) {
// The window event to cancel AutoMatch when <TAB> is pressed, specified globally so that the event can be added\removed within other functions
event.key == 'Tab' ? autoMatchStop() : null
}
function autoMatchStop() {
// Stop AutoMatch and revert elements to their default status
SETTINGS.autoMatchEnabled = false
try {
// Try\catch, just in case the user left the MatchTab
// Floating button
document.getElementById('autoMatchCancel') ? document.getElementById('autoMatchCancel').remove() : null
// Window "TAB" eventListener
window.removeEventListener('keydown', autoMatchKeyStop)
// MatchTab button
let autoMatchButton = document.querySelector('#buttonAutoMatch')
autoMatchButton.innerText = `AutoMatch`
autoMatchButton.title = `Toggle AutoMatch\n\nℹ️ Confidence: >=${SETTINGS.autoMatchConfidence}%`
autoMatchButton.style.animation = ''
} catch(error) {
console.log(error)
}
}
function viewHoverCover(imgURL, clientX, clientY) {
// Using the provided arguments, display the hover cover relative to the cursor location
let hoverCoverElement = document.getElementById('hoverCoverContainer')
hoverCoverElement.querySelector('img').src = imgURL
hoverCoverElement.classList.add('active')
let positionY =
clientY + hoverCoverElement.scrollHeight >= window.innerHeight
? window.innerHeight - hoverCoverElement.scrollHeight - 20
: clientY + 20;
let positionX =
clientX + hoverCoverElement.scrollWidth >= window.innerWidth
? window.innerWidth - hoverCoverElement.scrollWidth - 20
: clientX + 20;
hoverCoverElement.style.top = `${positionY}px`
hoverCoverElement.style.left = `${positionX}px`
}
// @saveResult
async function saveResult(matchButton, additionalTags = false) {
// A 'Save' button in the MatchTab was clicked
// The floating Edit panel
let editPanel = document.getElementById('editPanel')
// Click the book item, which will load the new data
matchButton.closest('div.resultContainer').querySelector('div.resultProcessed').click()
// If the editPanel or matchButton was not found (because the user navigated away during the AutoMatch delay), then cancel the save
if ( !editPanel || !matchButton) { return }
// Wait until the submit button is available, then click it
let submitButton = await waitForElement('button.bg-success[type="submit"]', editPanel.querySelector('#match-wrapper'))
// From the current cover, get the id of the book that is about to be saved
let coverURL = editPanel.querySelector('img[src*="/api/items/"]').src
SETTINGS.previousId = coverURL.match(/\/items\/(.+?)\//)[1]
// Click the green 'Submit' button to update this book with the metadata of the selected match result
submitButton.click()
// Wait until the details tab is ready, indicating the match has been saved
await waitForElement('#formWrapper', editPanel)
// API: Additional tag(s)
if ( additionalTags ) {
// GET the newly saved tags
let metadata = await fetchItemMetadata(SETTINGS.previousId)
let currentTags = metadata.media.tags
// PATCH the new + custom tags
fetch(`${absURL}/api/items/${SETTINGS.previousId}/media`, {
method: 'PATCH',
body: JSON.stringify({ tags: currentTags.concat(additionalTags) }),
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${SETTINGS.apiKey}`
},
})
}
if ( SETTINGS.navigationDirection != 'None' ) {
// If enabled, cycle the match tab to the previous\next book
let titleHeader = document.querySelector('#bookTitle').childNodes[0]
let titleObserver = new MutationObserver(async function(mutations) {
// The <h1> title has changed, indicating that the next book has been cycled
if ( mutations[0].oldValue != mutations[0].target.nodeValue ) {
titleObserver.disconnect()
// Get the id of the newly loaded book
editPanel.querySelector('#coverTab').click()
let coverURL = await waitForElement('img[src*="/api/items/"]', editPanel)
SETTINGS.currentId = coverURL.src.match(/\/items\/(.+?)\//)[1]
// Click on the Match tab, which has a 'click' event listener that will begin observation
editPanel.querySelector('#matchTab').click()
}
})
titleObserver.observe(titleHeader, { characterData: true, characterDataOldValue: true })
SETTINGS.navigationDirection == 'Right' ? editPanel.querySelector('#navigateRight').click() : null
SETTINGS.navigationDirection == 'Left' ? editPanel.querySelector('#navigateLeft').click() : null
}
}
async function audibleLookup(asinButton) {
// The 'Audible' lookup button of the MatchTab was clicked
// The floating Edit panel
let editPanel = document.getElementById('editPanel')
// Click the book item, which will load the form data
asinButton.closest('div.resultContainer').querySelector('div.resultProcessed').click()
// Wait until the submit button is available, indicating the form is available, then get the ASIN
let submitButton = await waitForElement('button.bg-success[type="submit"]', editPanel.querySelector('#match-wrapper'))
let asinURL
try {
let asinValue = editPanel.querySelector('#match-wrapper input[placeholder="ASIN"]').value
asinURL = SETTINGS.audibleTemplate.replace(/%asin%/, asinValue)
} catch(error) {}
// Click the back arrow to return to match results
let backArrowElement = editPanel.querySelector('div.cursor-pointer:has(> span.material-symbols')
backArrowElement.click()
asinURL ? window.open(asinURL, '_blank') : asinButton.innerText = 'No ASIN'
}
// =================================== GM_CONFIG ======================================
function gmcSettingsPanel() {
// Generate and initialize the GM_config settings panel. It has been done in this function for code cleanliness.
// Determine the saved number of custom search buttons that should be generated in the settings panel
let buttonCount
if ( GM_getValue('abSidekick') !== undefined ) {
// Parse the existing GM_config() settings object
let gmcSettingsObject = JSON.parse(GM_getValue('abSidekick'))
// Get the previously specified buttonCount to determine how many custom button rows should be generated
buttonCount = gmcSettingsObject['customButtonCount']
}
// New installs will not have a customButtonCount, so default to 1
buttonCount == undefined ? buttonCount = 1 : null
// Generate the appropriate number of custom button fields
let gmcButtonFields = {}
for (let i = 1; i <= buttonCount; i++) {
// --- GM_config() Fields ---
let currentLoopFields = {
[`custom_button_label_${i}`]: {
'type': 'text'
},
[`custom_button_template_${i}`]: {
'type': 'text'
}
}
gmcButtonFields = {...gmcButtonFields, ...currentLoopFields}
}
// The element that will containt the GM_config panel, so that it is not a floating iFrame and can be inspected
let configFrame = document.createElement('div')
document.body.appendChild(configFrame)
let reloadWindow
// @GM_config
GM_config.init({
'id': 'abSidekick',
'frame': configFrame,
'title': `
<a id="absidekickHeader" href="${GM_info.script.homepage}" target="_blank">🌱 ABSidekick</a><br>
<div>★ Hover over emojis for details ★</div>
`,
'fields': {...{
'autoMatchConfidence': {
'label': '🤖 AutoMatch Confidence',
'type': 'int',
'default': '100',
'title': "When AutoMatch is enabled, the first match result with AT-LEAST this confidence score will be the one that is selected and then saved\n\nℹ️ Click the '🤖 AutoMatch' button or press <TAB> to stop AutoMatch"
},
'autoMatchDelay': {
'label': '🕓 AutoMatch Save Delay',
'type': 'int',
'default': '2500',
'title': 'The delay in milliseconds between when AutoMatch selects a match result and when the save button is clicked\n\nℹ️ A longer delay will give you more time to verify the match and intervene if desired'
},
'autoMatchTarget': {
'label': '🎯 AutoMatch Target',
'type': 'select',
'options': ['Save Match', 'Save + 🏷️'],
'default': 'Save Match',
'title': 'When AutoMatch has selected a match result to save, this is the button that will be clicked to perform the save'
},
'matchTabColumns': {
'label': '🧇 Grid Columns',
'type': 'int',
'default': '2',
'title': 'The number of grid columns that will be used to display the match results'
},
'currentCoverHeight': {
'label': '🖼️ Current Cover Height',
'type': 'text',
'default': '75px',
'title': 'The maximum height of the current cover that is displayed above the match results'
},
'matchCoverHeight': {
'label': '🖼️ Match Cover Height',
'type': 'text',
'default': '192px',
'title': 'The maximum height of the cover displayed by each match result'
},
'matchTabWidth': {
'label': '↔️ Panel Width',
'type': 'text',
'default': '1200px',
'title': 'The width of the Edit Panel when the Match tab is active'
},
'matchTabHeight': {
'label': '↕️ Panel Height',
'type': 'text',
'default': '80%',
'title': 'The height of the Edit Panel when the Match tab is active'
},
'navigationDirection': {
'label': '🧭 Navigation Direction',
'type': 'select',
'options': ['Right', 'Left', 'None'],
'default': 'Right',
'title': 'The directional button (arrow) that will be clicked after a match result is saved'
},
'saveTagsList': {
'label': "Tags: Save + 🏷️",
'type': 'text',
'default': '',
'title': "A comma seperated list of tags that will be applied to a book when clicking the 'Save + 🏷️' button\n\nℹ️ Setting a unique tag is a simple way to distinguish books that have already been matched, either for simple record keeping or for future scripting"
},
'notFoundTagsList': {
'label': "Tags: 🏷️ Not Found",
'type': 'text',
'default': '',
'title': "A comma seperated list of tags that will be applied to a book when clicking the '🏷️ Not Found' button\n\nℹ️ Setting a unique tag is a simple way to distinguish books that have not had a proper match, either for simple record keeping or for future attempts\\scripting"
},
'itemBackgroundBlur': {
'label': '👓 Background Blur',
'type': 'checkbox',
'default': true,
'title': 'Use the cover image to provide a blurred background affect'
},
'itemMetaGlass': {
'label': '🪟 Meta Glass',
'type': 'checkbox',
'default': false,
'title': 'Apply a black glass effect to the metadata rows'
},
'itemProgressGlass': {
'label': '🪟 Progress Glass',
'type': 'checkbox',
'default': true,
'title': 'Apply a black glass effect to the progress indicator'
},
'itemButtonGlass': {
'label': '🪟 Button Glass',
'type': 'checkbox',
'default': true,
'title': 'Apply a black glass effect to the buttons'
},
'itemSummaryGlass': {
'label': '🪟 Summary Glass',
'type': 'checkbox',
'default': true,
'title': 'Apply a black glass effect to the summary text'
},
'itemDropdownGlass': {
'label': '🪟 Dropdown Glass',
'type': 'checkbox',
'default': true,
'title': 'Apply a black glass effect to the dropdown tables'
},
'customButtonCount': {
'label': '🌐 Custom Buttons',
'type': 'int',
'default': 1,
'title': `The number of custom button rows that will be generated\n\nThe 'Button Name' is what will be displayed in Audiobookshelf, while the 'Search Template' is the URL that will be opened in a new tab\n\nℹ️ Search Template Variables...\n\n%title% %author% %year% %asin%`
},
'customFontToggle': {
'label': '✏️ Roboto Condensed',
'type': 'select',
'options': ['Everywhere', 'Edit Panel', 'Off'],
'default': 'Everywhere',
'title': 'Set Roboto Condensed as the default font'
},
'globalBlackGlass': {
'label': 'Black Glass',
'type': 'checkbox',
'default': true,
'title': 'Apply a black glass look across Audiobookshelf'
},
'blackGlassRainbow': {
'label': 'Rainbow Shift',
'type': 'checkbox',
'default': false,
'title': 'When Black Glass is enabled, cycle the background as a dark-rainbow'
},
'blackGlassColor': {
'label': 'Solid Color',
'type': 'text',
'default': '#0b142a',
'title': 'When Black Glass is enabled, specify the primary background color'
},
'hoverCoverHeight': {
'label': '🖼️ Hover Cover Height',
'type': 'text',
'default': '500px',
'title': 'The maximum height of a cover image when it is hovered over and enlarged'
},
'apiKey': {
'label': '🔑 ApiKey',
'type': 'text',
'default': '',
'title': 'A valid AudioBookShelf ApiKey'
},
'audibleTemplate': {
'label': '🔎 Audible Template',
'type': 'text',
'default': 'https://www.audible.com/pd/%asin%',
'title': "The search template URL that will be used when clicking a 'Audible' button\n\nℹ️ The %asin% placeholder will be replaced with the actual ASIN of the relevent item"
},
'goodreadsTemplate': {
'label': '🔎 Goodreads Template',
'type': 'text',
'default': 'https://www.goodreads.com/search?q=%title%',
'title': "The search template URL that will be used when clicking a 'Goodreads' button\n\nℹ️ The %title% placeholder will be replaced with the actual title of the relevant item"
},
}, ...gmcButtonFields },
'events': {
'open': function() {
let gmcPanel = document.querySelector('#abSidekick')
reloadWindow = false
// Create Section Headers
function settingsHeader(text, beforeElement, titleText = '') {
let element = document.createElement('div')
element.innerText = text
element.classList.add('settingsHeaderRow')
element.title = titleText
beforeElement.insertAdjacentElement('beforebegin', element)
}
settingsHeader('Match Tab', document.querySelector('#abSidekick_autoMatchConfidence_var'), 'These settings apply to features of the Match Tab')
settingsHeader('Item Pages', document.querySelector('#abSidekick_itemBackgroundBlur_var'), 'These settings apply to item (book) pages')
settingsHeader('Globals', document.querySelector('#abSidekick_customFontToggle_var'), 'These settings apply to all ABSidekick features and possibly throughout the Audiobookshelf interface')
// Obfuscate apiKey input
let apiKeyElement = document.getElementById('abSidekick_field_apiKey')
apiKeyElement.placeholder = 'abc123'
apiKeyElement.type = 'password'
apiKeyElement.addEventListener('focus', function() { this.type = 'text' })
apiKeyElement.addEventListener('blur', function() { this.type = 'password' })
// Save Tags placeholder
document.getElementById('abSidekick_field_saveTagsList').placeholder = 'ABSidekick, Matched'
document.getElementById('abSidekick_field_notFoundTagsList').placeholder = 'Not Found, Unmatched'
// Make sure the Custom Button fields are in the same row
let insertBeforeElement = gmcPanel.querySelector('#abSidekick_customButtonCount_var').nextElementSibling
for (let i = 1; i <= buttonCount; i++) {
let buttonLabel = gmcPanel.querySelector(`#abSidekick_field_custom_button_label_${i}`)
buttonLabel.classList.add('customButtonLabel')
let buttonTemplate = gmcPanel.querySelector(`#abSidekick_field_custom_button_template_${i}`)
buttonTemplate.classList.add('customButtonTemplate')
let labelParent = buttonLabel.parentElement
let templateParent = buttonTemplate.parentElement
let rowDiv = document.createElement('div')
rowDiv.classList.add('customButtonContainer')
insertBeforeElement.insertAdjacentElement('beforebegin', rowDiv)
rowDiv.appendChild(buttonLabel)
rowDiv.appendChild(buttonTemplate)
labelParent.remove()
templateParent.remove()
buttonLabel.placeholder = 'Button Name'
buttonTemplate.placeholder = 'Search Template'
}
},
'save': function () {
// Actions to take when the 'Save' button is clicked
document.getElementById('abSidekick_saveBtn').innerText = '👍 Saved!'
reloadWindow = true
// Clear cached data when settings are saved
GM_listValues().forEach(key => {
if (key !== 'abSidekick') {
GM_setValue(key, null)
}
})
},
'close': function () {
// Actions to take when the 'Close' button is clicked
if (reloadWindow) {
if (this.frame) {
window.location.reload()
} else {
setTimeout(() => {
window.location.reload()
}, 250)
}
}
},
'reset': function () {
// Actions to take when the 'Reset' button is clicked
if (typeof resetToDefaults === 'function') {
resetToDefaults()
}
}
}
})
// Register the settings panel to be opened from the UserScript manager dialouge
GM_registerMenuCommand('🛠️ Settings', () => {
GM_config.open()
})
// @SETTINGS
// Get the saved GM_config settings
let SETTINGS = {
currentId: '',
previousId: '',
autoMatchEnabled: false,
metadata: {
asin: '',
author: '',
isbn: '',
narrator: '',
publisher: '',
subtitle: '',
title: '',
year: '',
},
// Match Tab
autoMatchConfidence: GM_config.get('autoMatchConfidence'),
autoMatchDelay: GM_config.get('autoMatchDelay'),
autoMatchTarget: GM_config.get('autoMatchTarget'),
currentCoverHeight: GM_config.get('currentCoverHeight'),
matchCoverHeight: GM_config.get('matchCoverHeight'),
hoverCoverHeight: GM_config.get('hoverCoverHeight'),
matchTabColumns: GM_config.get('matchTabColumns'),
matchTabHeight: GM_config.get('matchTabHeight'),
matchTabWidth: GM_config.get('matchTabWidth'),
navigationDirection: GM_config.get('navigationDirection'),
saveTagsList: GM_config.get('saveTagsList').split(','),
notFoundTagsList: GM_config.get('notFoundTagsList').split(','),
// Item Pages
itemBackgroundBlur: GM_config.get('itemBackgroundBlur'),
itemMetaGlass: GM_config.get('itemMetaGlass'),
itemProgressGlass: GM_config.get('itemProgressGlass'),
itemButtonGlass: GM_config.get('itemButtonGlass'),
itemSummaryGlass: GM_config.get('itemSummaryGlass'),
itemDropdownGlass: GM_config.get('itemDropdownGlass'),
customButtonCount: GM_config.get('customButtonCount'),
// Globals
customFontToggle: GM_config.get('customFontToggle'),
globalBlackGlass: GM_config.get('globalBlackGlass'),
blackGlassRainbow: GM_config.get('blackGlassRainbow'),
blackGlassColor: GM_config.get('blackGlassColor'),
audibleTemplate: GM_config.get('audibleTemplate'),
goodreadsTemplate: GM_config.get('goodreadsTemplate'),
apiKey: GM_config.get('apiKey'),
}
// Custom Buttons
for (let i = 1; i <= buttonCount; i++) {
SETTINGS[`custom_button_label_${i}`] = GM_config.get(`custom_button_label_${i}`)
SETTINGS[`custom_button_template_${i}`] = GM_config.get(`custom_button_template_${i}`)
}
return SETTINGS
}
// Settings panel styling
GM_addStyle(`
/* latin-ext */
@font-face {
font-family: 'Lilita One';
font-style: normal;
font-weight: 400;
font-display: swap;
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
src: url(data:application/octet-stream;base64,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) format('woff2');
}
/* latin-ext */
@font-face {
font-family: 'Roboto Condensed';
font-style: normal;
font-weight: 500;
font-display: swap;
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
src: url(data:application/octet-stream;base64,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) format('woff2');
}
/* latin */
@font-face {
font-family: 'Roboto Condensed';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(data:application/octet-stream;base64,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) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
#abSidekick * {
font-family: var(--fonts-roboto) !important;
}
#abSidekick {
backdrop-filter: blur(9px) !important;
background: #191d2aa3 !important;
border-radius: 10px !important;
border: 2px solid #2C3E50 !important;
box-shadow: 0px 0px 15px #2C3E50 !important;
color: #ffffff !important;
height: auto !important;
inset: .5vw .5vw auto auto !important;
line-height: 22px !important;
margin: 0 !important;
overflow: auto scroll !important;
padding: 0px 0px !important;
position: fixed !important;
width: 375px !important;
scrollbar-width: none;
}
div#abSidekick_header {
margin: 20px 0px 10px 0px !important;
}
#abSidekick_header > a {
text-decoration: none;
user-select: none;
text-shadow: 0 0 20px #2078b9;
font-size: 2rem;
font-family: var(--fonts-lilita) !important;
animation: textglow .5s linear infinite alternate;
}
#abSidekick_header > div {
color: #95a5a6;
display: block;
font-size: .9rem;
margin: 10px 0px 0px 0px;
}
#abSidekick div.settingsHeaderRow {
border-bottom: 2px solid #2C3E50;
border-top: 2px solid #2C3E50;
cursor: default;
display: flex;
font-family: var(--fonts-lilita) !important;
font-size: 1.2rem;
justify-content: center;
margin: 12px auto 8px auto;
text-shadow: 0px 0px 10px #2078b9;
padding: 5px;
}
#abSidekick div.config_var {
margin: 0px 0px 10px 20px;
}
#abSidekick label.field_label {
color: rgba(255, 255, 255, 0.9);
font-size: 1rem;
font-weight: 500;
margin: unset;
}
#abSidekick input[type="text"], #abSidekick input[type="password"], #abSidekick input[type="checkbox"], #abSidekick select {
/* Text Fields */
background: rgba(255, 255, 255, 0.9);
border-radius: 3px;
border: 1px solid #ddd;
color: #191d2a;
font-size: .9rem;
font-weight: 500;
margin: unset;
position: fixed;
right: 20px;
text-align: center;
transition: all 0.3s ease;
width: 100px;
}
#abSidekick #abSidekick_field_saveTagsList,
#abSidekick #abSidekick_field_notFoundTagsList,
#abSidekick #abSidekick_field_apiKey,
#abSidekick #abSidekick_field_audibleTemplate,
#abSidekick #abSidekick_field_goodreadsTemplate {
/* Long Text Fields */
width: 155px;
}
#abSidekick input[type="checkbox"] {
height: 1rem;
}
div.customButtonContainer {
display: grid;
grid-template-columns: auto auto;
gap: 20px;
margin: 0px 0px 10px 0px;
padding: 0px 20px 0px 20px;
}
#abSidekick div.customButtonContainer input[type="text"] {
position: unset;
margin: unset;
}
#abSidekick input.customButtonLabel {
width: 100px;
}
#abSidekick input.customButtonTemplate {
width: 215px;
}
#abSidekick select {
padding: 4px;
}
#abSidekick_buttons_holder {
margin: 12px auto auto auto;
border-top: 2px solid #2C3E50;
display: grid;
padding: 10px 0px 0px 0px;
}
button.saveclose_buttons {
background-color: #2C3E50;
border-radius: 5px;
border: none;
box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.2);
color: #FFFFFF;
cursor: pointer;
font-size: 1rem;
font-weight: 500;
margin: 15px auto 0px auto !important;
padding: 2px 12px !important;
width: 50%;
}
button.saveclose_buttons:hover {
background-color: #3a4a5b;
animation: pop .50s linear infinite alternate;
}
#abSidekick div.reset_holder {
margin: 10px 20px 20px 0px;
}
#abSidekick a.reset {
color: #95a5a6;
}
#abSidekick a.reset:hover {
animation: blinker 1s linear infinite;
}
`)
// =================================== CSS Styling ======================================
// Global styling
GM_addStyle(`
:root {
--fonts-lilita: 'Lilita One', 'Roboto Condensed', 'Source Sans Pro';
--fonts-roboto: 'Roboto Condensed', 'Source Sans Pro';
}
/* ---------- AppBar ---------- */
#gmConfigAppBar {
cursor: pointer;
font-size: 1.2rem;
margin: .25rem;
}
/* ---------- Animations ---------- */
@keyframes blinker {
50% {
opacity: .2;
}
}
@keyframes textglow {
0% {
text-shadow: 0px 0px 5px #2078b9;
}
100% {
text-shadow: 0px 0px 20px #2078b9;
}
}
@keyframes pop {
0% {
transform: scale(1.1);
-webkit-transform: scale(1.1);
}
100% {
transform: scale(0.90);
-webkit-transform: scale(0.90);
}
}
@keyframes rainbow-bg{
100%,0%{
background-color: rgb(30, 0, 0);
}
8%{
background-color: rgb(31, 15, 0);
}
16%{
background-color: rgb(30, 30, 0);
}
25%{
background-color: rgb(15, 30, 0);
}
33%{
background-color: rgb(0, 30, 0);
}
41%{
background-color: rgb(0, 31, 15);
}
50%{
background-color: rgb(0, 31, 31);
}
58%{
background-color: rgb(0, 15, 30);
}
66%{
background-color: rgb(0, 0, 30);
}
75%{
background-color: rgb(15, 0, 30);
}
83%{
background-color: rgb(30, 0, 30);
}
91%{
background-color: rgb(32, 0, 16);
}
}
/* ---------- Headers ---------- */
#itemTitle > div,
#appbar h1,
#bookTitle {
color: #efefef;
font-family: var(--fonts-lilita);
font-size: 2rem;
text-shadow: 0px 0px 15px #000000;
}
${SETTINGS.customFontToggle == 'Everywhere' ? `
*:not(.material-symbols) {
font-family: var(--fonts-roboto);
}` : '' }
`)
// Library\Series page styling
GM_addStyle(`
p[cy-id="title"] {
font-family: var(--fonts-lilita);
}
`)
// Black Glass
if ( SETTINGS.globalBlackGlass ) {
GM_addStyle(`
:root {
--color-bg: #000000ec;
--color-primary: #00000080;
--color-gray-400: rgb(173, 180, 192);
}
#appbar {
box-shadow: unset;
border-bottom: 1px solid black;
}
#toolbar,
div[aria-label="Library Sidebar"] {
background-color: var(--color-primary);
box-shadow: unset !important;
}
#bookshelf,
#page-wrapper,
div[aria-label="Library Sidebar"] div,
div[aria-label="Library Sidebar"] a {
background-color: #0000;
background-image: unset;
}
#__layout > div:has(#app-content) {
${ SETTINGS.blackGlassColor ? `background-color: ${SETTINGS.blackGlassColor};`: 'background-color: #0b142a;' }
${ SETTINGS.blackGlassRainbow ? 'animation: rainbow-bg 20s linear infinite;': '' }
}
.tracksTable tr {
background-color: #00000082;
}
.tracksTable tr:nth-child(2n) {
background-color: #12121263;
}
.tracksTable tr:hover:not(:has(th)) {
background-color: #242424;
}
#editPanelTabs > button:not(.tab-selected) {
background-color: #000000b2;
}
`)
}
// ItemPage styling
GM_addStyle(`
.itemBackground {
}
.blurEffect {
backdrop-filter: blur(75px);
}
.itemMetaRows,
.descriptionContainer {
background: #00000059;
border-radius: 5px;
padding: 10px;
}
.itemMetaRows {
padding: 1px 20px 15px 20px;
}
.itemProgress {
background: #00000059;
}
.itemButton {
padding: 5px 8px 5px 8px;
}
.itemButton:hover {
background: #393939;
}
.itemButtonGlass {
background: #00000059;
border: none;
}
.itemButtonGlass:hover {
background: #00000082;
border: none;
}
.itemDropdown {
background: #00000059;
}
div:has(.itemDropdown) table {
border: none;
}
div:has(.itemDropdown) table tr {
background: #00000082;
}
div:has(.itemDropdown) table tr:nth-child(2n) {
background: #00000059;
}
`)
// MatchTab styling
GM_addStyle(`
/* ---------- Edit Panel ---------- */
#editPanel:has(#match-wrapper) {
/* edit panel size */
height: ${SETTINGS.matchTabHeight} !important;
width: ${SETTINGS.matchTabWidth} !important;
min-width: unset !important;
}
#editPanel #matchTab {
font-family: var(--fonts-lilita);
font-weight: 400,
}
${SETTINGS.customFontToggle == 'Edit Panel' ? `
#editPanel *:not(.material-symbols) {
font-family: var(--fonts-roboto);
}` : '' }
/* ---------- Search Bar Row ---------- */
#gmconfigShortcut {
cursor: pointer;
font-size: 1rem;
position: absolute;
right: 5px;
top: 5px;
}
div.currentCover {
/* current cover size */
max-height: ${SETTINGS.currentCoverHeight};
box-shadow: 0px 0px 10px #000000;
margin-right: 4px;
margin-top: -12px;
border-radius: 3px;
}
img.currentCover {
/* current cover size */
max-height: inherit;
max-width: inherit;
border-radius: 3px;
border: 1px solid #000000;
}
div:has(> div.currentCover) {
/* search bar vertical item alignment */
align-items: end;
}
#labelInputTitle.titleSearchReady {
text-shadow: 0px 0px 8px rgb(22, 84, 0);
animation: blinker 1s linear infinite;
}
#match-wrapper form div:has( > div > div > input[placeholder="Author"]) {
/* search author size */
width: 110px;
}
/* ---------- Match Results Grid ---------- */
div.matchListWrapper {
/* MatchTab grid view */
display: grid;
grid-template-columns: repeat(${SETTINGS.matchTabColumns}, auto);
height: unset;
max-height: calc(100% - 80px);
scrollbar-color: #7a7a7a #0000;
scrollbar-width: thin;
padding: 0px 5px 0px 0px;
gap: 5px;
}
.noResults {
font-size: 1.5rem;
animation: pop .50s linear infinite alternate;
}
.resultContainer {
border: none;
padding: 2px 0px 0.5rem 2px;
}
.itemCover,
.resultCover {
/* match cover size */
height: unset;
max-height: ${SETTINGS.matchCoverHeight};
max-width: ${SETTINGS.matchCoverHeight};
border-radius: 5px;
min-width: unset;
position: relative;
width: unset;
box-shadow: 0px 0px 10px #000000;
margin: 0px 0px 0px 5px;
}
.itemCover {
/* item cover size */
max-height: unset;
margin: inherit;
}
.resultCoverImg {
border-radius: 5px;
border: 1px solid #000000;
}
.hoverCoverToggle,
.resultCoverDimensions {
/* cover dimensions */
background: #0000006e;
border-radius: 25px;
font-size: x-small;
left: 3px;
padding: 1px 7px;
position: absolute;
top: 5px;
width: fit-content;
z-index: 0;
}
.hoverCoverToggle {
font-size: 1rem;
z-index: 10;
}
.resultMeta {
/* match metadata size */
width: fit-content;
max-height: ${SETTINGS.matchCoverHeight};
overflow: auto;
scrollbar-width: thin;
scrollbar-color: #555 #0000;
}
.resultTitle h1 {
/* match titles */
font-weight: 600;
font-size: 1.1rem;
text-shadow: 0px 0px 10px #000000;
}
.resultSeries > div.rounded-full {
/* match series */
background-color: #00000080;
}
.resultSeries p {
/* match series */
color: white;
font-size: .8rem;
padding: 3px 5px 3px 5px;
}
.resultSynopsis {
/* match synopsis size */
max-height: 100%;
overflow: initial;
}
div.bg-success\\\/80 {
box-shadow: 0px 0px 15px #48974b;
border: 1px solid #FFFFFF;
animation: blinker .75s linear infinite;
}
/* ---------- Match Result Buttons ---------- */
div.scriptButtons {
display: grid;
grid-template-columns: repeat(3, auto);
margin: 5px 15px 5px 3px;
gap: 15px;
}
button.resultButton {
/* ABSidekick Button sizes */
border-radius: var(--radius-md);
border: none;
cursor: pointer;
font-size: medium;
padding: 3px;
width: unset;
}
button.saveResult {
background-color: #113400;
border: #A0DA83 solid 1px;
color: #A0DA83;
}
button.saveResult:hover {
background-color: #1d5900;
}
button.saveResultTags {
background-color: #153245;
border: #B6D3E7 solid 1px;
color: #B6D3E7;
}
button.saveResultTags:hover {
background-color: #224f6d;
}
button.asinSearch {
background-color: #431C00;
color: #F09D63;
border: #F09D63 solid 1px;
}
button.asinSearch:hover {
background-color: #7C3400;
}
/* ---------- Enlarged Cover Hover ---------- */
#hoverCoverContainer {
background: #00000050;
display: none;
}
#hoverCoverContainer.active {
/* enlarged img panel */
border-radius: 10px;
box-shadow: 0px 0px 20px #000000;
border: 1px solid black;
display: unset;
max-height: ${SETTINGS.hoverCoverHeight};
max-width: ${SETTINGS.hoverCoverHeight};
position: fixed;
z-index: 999;
}
/* ---------- AutoMatch ---------- */
.autoMatchSelection {
border-radius: 6px;
background-color: rgba(93, 175, 76, 0.46);
box-shadow: 0px 0px 5px rgba(93, 175, 76, 0.46);
}
.autoMatchSelection .resultDetails p,
.autoMatchSelection .resultSynopsis p {
color: var(--color-gray-300);
}
button.autoMatchCancel {
border: 1px solid #4A5565;
bottom: 5%;
box-shadow: 0px 0px 10px #4A5565;
cursor: pointer;
left: 5%;
padding: 15px 30px 15px 30px;
position: fixed;
z-index: 9999;
animation: pop .50s linear infinite alternate;
}
button.autoMatchCancel:hover {
background-color: #393939;
}
`)