qui - quiCKIE

A quiCKIE way to send torrents from various trackers to qui!

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==

// ----------------------------------- MetaData --------------------------------------

// @name        qui - quiCKIE
// @author      WirlyWirly + contributors 🫶
// @version     0.3
// @description A quiCKIE way to send torrents from various trackers to qui!
//              To be used with a running instance of qui: https://getqui.com/
//              Written on LibreWolf via Violentmonkey

// @icon        https://gist.github.com/user-attachments/assets/b3d2b863-6aaf-48ab-a4d5-28d0c5df3bae
// @namespace   https://github.com/WirlyWirly
// @run-at      document-end

// @resource    configMenuCSS https://gist.github.com/WirlyWirly/1ffd87e5a3d3f7ce206860d8c100df88/raw/quiCKIEConfigMenu.css
// @require     https://cdn.jsdelivr.net/gh/sizzlemctwizzle/GM_config@43fd0fe4de1166f343883511e53546e87840aeaf/gm_config.js

// ----------------------------------- Permissions --------------------------------------

// @grant   GM_getResourceText
// @grant   GM_getValue
// @grant   GM_listValues
// @grant   GM_registerMenuCommand
// @grant   GM_setValue
// @grant   GM_xmlhttpRequest

// ----------------------------------- Matches --------------------------------------

// How to add new trackers: https://gist.github.com/WirlyWirly/1ffd87e5a3d3f7ce206860d8c100df88?permalink_comment_id=5974494#gistcomment-5974494

// @match   https://animebytes.tv/artist.php?id=*
// @match   https://animebytes.tv/collage.php?id=*
// @match   https://animebytes.tv/company.php?id=*
// @match   https://animebytes.tv/series.php?id=*
// @match   https://animebytes.tv/torrents*

// @match   https://bibliotik.me/collections/*
// @match   https://bibliotik.me/torrents/*

// @match   https://broadcasthe.net/collages.php?id=*
// @match   https://broadcasthe.net/series.php?id=*
// @match   https://broadcasthe.net/torrents.php*

// @match   https://www.empornium.sx/collage/*
// @match   https://www.empornium.sx/top10.php*
// @match   https://www.empornium.sx/torrents.php*
// @match   https://www.empornium.sx/user.php?id=*
//
// @match   https://gazellegames.net/collections.php?id=*
// @match   https://gazellegames.net/torrents.php*

// @match   https://www.happyfappy.org/collage/*
// @match   https://www.happyfappy.org/top10.php*
// @match   https://www.happyfappy.org/torrents.php*
// @match   https://www.happyfappy.org/user.php?id=*

// @match   https://hdbits.org/browse.php*
// @match   https://hdbits.org/details.php?id=*
// @match   https://hdbits.org/film/info?id=*

// @match   https://www.myanonamouse.net/
// @match   https://www.myanonamouse.net/t/*
// @match   https://www.myanonamouse.net/tor/browse.php*

// @match   https://orpheus.network/artist.php?id=*
// @match   https://orpheus.network/collages.php?id=*
// @match   https://orpheus.network/top10.php*
// @match   https://orpheus.network/torrents.php*

// @match   https://passthepopcorn.me/torrents.php?id=*

// @match   https://redacted.sh/artist.php?id=*
// @match   https://redacted.sh/collages.php?id=*
// @match   https://redacted.sh/torrents.php*

// ----------------------------------- Script Links --------------------------------------
//
// @homepage    https://gist.github.com/WirlyWirly/1ffd87e5a3d3f7ce206860d8c100df88
// ==/UserScript==


// =================================== TRACKER FIELDS ======================================

// @trackerFields
const settingsPanelFields = {
    // Each tracker MUST have the 4 fields displayed in the settings panel; Category (+ row label), savePath, tags, startPaused
    // Each field below uses the tracker's domain as the start of the field name. This allows for cross-site compatability while saving resources.
    //     ! The same domain is used by the 'if' blocks below to determine what site the script is running on. See @trackerIfBlocks 

    // Example: https://broadcasthe.net/ --> broadcasthe --> broadcasthe-category
    // Example: https://www.myanonamouse.net/ --> myanonamouse --> myanonamouse-savePath

    // --- AnimeBytes ---
    'animebytes-category': {
        'label': 'AnimeBytes',
        'type': 'text'
    },
    'animebytes-savePath': {
        'type': 'text'
    },
    'animebytes-tags': {
        'type': 'text'
    },
    'animebytes-startPaused': {
        'type': 'checkbox',
        'default': false
    },


    // --- Bibliotik ---
    'bibliotik-category': {
        'label': 'Bibliotik',
        'type': 'text'
    },
    'bibliotik-savePath': {
        'type': 'text'
    },
    'bibliotik-tags': {
        'type': 'text'
    },
    'bibliotik-startPaused': {
        'type': 'checkbox',
        'default': false
    },


    // --- BroadcasTheNet ---
    'broadcasthe-category': {
        'label': 'BroadcasTheNet',
        'type': 'text'
    },
    'broadcasthe-savePath': {
        'type': 'text'
    },
    'broadcasthe-tags': {
        'type': 'text'
    },
    'broadcasthe-startPaused': {
        'type': 'checkbox',
        'default': false
    },


    // --- Empornium ---
    'empornium-category': {
        'label': 'Empornium',
        'type': 'text'
    },
    'empornium-savePath': {
        'type': 'text'
    },
    'empornium-tags': {
        'type': 'text'
    },
    'empornium-startPaused': {
        'type': 'checkbox',
        'default': false
    },


    // --- GazelleGames ---
    'gazellegames-category': {
        'label': 'GazelleGames',
        'type': 'text'
    },
    'gazellegames-savePath': {
        'type': 'text'
    },
    'gazellegames-tags': {
        'type': 'text'
    },
    'gazellegames-startPaused': {
        'type': 'checkbox',
        'default': false
    },

    
    // --- HappyFappy ---
    'happyfappy-category': {
        'label': 'HappyFappy',
        'type': 'text'
    },
    'happyfappy-savePath': {
        'type': 'text'
    },
    'happyfappy-tags': {
        'type': 'text'
    },
    'happyfappy-startPaused': {
        'type': 'checkbox',
        'default': false
    },


    // --- HDBits ---
    'hdbits-category': {
        'label': 'HDBits',
        'type': 'text'
    },
    'hdbits-savePath': {
        'type': 'text'
    },
    'hdbits-tags': {
        'type': 'text'
    },
    'hdbits-startPaused': {
        'type': 'checkbox',
        'default': false
    },


    // --- MyAnonaMouse ---
    'myanonamouse-category': {
        'label': 'MyAnonaMouse',
        'type': 'text'
    },
    'myanonamouse-savePath': {
        'type': 'text'
    },
    'myanonamouse-tags': {
        'type': 'text'
    },
    'myanonamouse-startPaused': {
        'type': 'checkbox',
        'default': false
    },


    // --- Orpheus ---
    'orpheus-category': {
        'label': 'Orpheus',
        'type': 'text'
    },
    'orpheus-savePath': {
        'type': 'text'
    },
    'orpheus-tags': {
        'type': 'text'
    },
    'orpheus-startPaused': {
        'type': 'checkbox',
        'default': false
    },


    // --- PassThePopcorn ---
    'passthepopcorn-category': {
        'label': 'PassThePopcorn',
        'type': 'text'
    },
    'passthepopcorn-savePath': {
        'type': 'text'
    },
    'passthepopcorn-tags': {
        'type': 'text'
    },
    'passthepopcorn-startPaused': {
        'type': 'checkbox',
        'default': false
    },


    // --- Redacted ---
    'redacted-category': {
        'label': 'Redacted',
        'type': 'text'
    },
    'redacted-savePath': {
        'type': 'text'
    },
    'redacted-tags': {
        'type': 'text'
    },
    'redacted-startPaused': {
        'type': 'checkbox',
        'default': false
    },

}

// =================================== CONFIG MENU ======================================

// The element the settings menu will be appended to, so that it's not a floating iFrame and can be inspected.
let configFrame = document.createElement('div')
document.body.appendChild(configFrame)

let reloadWindow = false
GM_config.init({
    // The quiCKIE settings menu, which can then be displayed by calling 'GM_config.open()'
    'id': 'quiCKIE_config',
    'frame': configFrame,
    'title': `
        <div>
            <div style="padding: 35px 0 0 0"></div>
            🐰
            <span style="user-select: none; font-family: 'Bebas Neue', Helvetica, Tahoma, Geneva, sans-serif; background: none; background-color: #FFFFFF; -webkit-background-clip: text; -webkit-text-fill-color: transparent; -webkit-filter: brightness(110%); filter: brightness(110%); text-shadow: 0 0 20px rgba(0, 124, 255, 0.60); transition: all 0.3s; font-weight: bold;"><a href="${GM_info.script.homepage}" target="_blank" style="text-decoration: none; background: none;">quiCKIE</a></span>
            🐰
            <div style="margin-top: 15px"><span style="color: #95a5a6; display: block; font-size: 10pt">Settings marked with * are required</span></div>
        </div>
    `,

    'fields': {...settingsPanelFields, ...{
        // Merge these two field objects so that GM_config reads them properly

        'quiURL': {
            'label': 'quiURL*',
            'type': 'text',
            'default': '',
            'title': 'The full URL to the qui instance you want to send torrents to\nexample: https://qui.mydomain.com/instances/1'
        },
        'quiApiKey': {
            'label': 'ApiKey*',
            'type': 'text',
            'default': '',
            'title': 'The API Key to use for accessing qui'
        },
    }},
    'events': {
        'open': function (doc) {
            // Actions to take When GM_config.open() is called...

            let style = this.frame.style
            style.borderRadius = "5px"
            style.boxShadow = "0px 4px 12px rgba(0, 0, 0, 0.1)"
            style.height = "740px"
            style.inset = ""
            style.right = "8%"
            style.top = "2%"
            style.width = "620px"

            // Placeholders for the text-input fields
            document.getElementById('quiCKIE_config_field_quiURL').placeholder = 'http://localhost:7476/qui/instances/1'
            document.getElementById('quiCKIE_config_field_quiApiKey').placeholder = 'abc123'

            document.getElementById('quiCKIE_config_field_animebytes-category').placeholder = 'AnimeBytes'
            document.getElementById('quiCKIE_config_field_animebytes-savePath').placeholder = '/downloads/AnimeBytes/'
            document.getElementById('quiCKIE_config_field_animebytes-tags').placeholder = 'anime,private'

            reloadWindow = false

            // Instead of a bunch of stacked elements that GM_config generates for each field, we'll display them in a table with columns/rows 
            let table = document.createElement('table')
            let thead = document.createElement('thead')
            let tbody = document.createElement('tbody')

            table.appendChild(thead)
            table.appendChild(tbody)

            // Give the table an id for use in CSS
            table.id = 'quiCKIE_config_table'

            // Insert the table after the GM_config header
            document.getElementById('quiCKIE_config_header').insertAdjacentElement('afterend', table)

            // Generate table headers for each column
            let headersRow = document.createElement('tr')
            for (let columnHeader of ['Tracker', 'Category', 'SavePath', 'Tags', 'Paused']) {
                let headerElement = document.createElement('th')
                headerElement.innerHTML = columnHeader
                headerElement.id = `quiCKIE_config_table_header_${columnHeader.toLowerCase()}`
                headerElement.classList.add('quiCKIE_config_table_header_th')
                headersRow.appendChild(headerElement)
            }

            // Append the headers to the thead (tableHeader) element
            thead.appendChild(headersRow)

            // Add the mouse-over text for each column header
            document.getElementById('quiCKIE_config_table_header_tracker').setAttribute('title', 'The tracker for which the fiels to the right will be applied to')
            document.getElementById('quiCKIE_config_table_header_category').setAttribute('title', 'Specify the category to apply to these torrents')
            document.getElementById('quiCKIE_config_table_header_savepath').setAttribute('title', 'Specify the full-path for where to save these torrents')
            document.getElementById('quiCKIE_config_table_header_tags').setAttribute('title', 'A comma seperated list of tags to add to these torrents')
            document.getElementById('quiCKIE_config_table_header_paused').setAttribute('title', 'Pause these torrents when they are added')

            // For each @match url of this script, parse it to get the unique domain (the same ones used in the GM_config fields)
            let allMatchDomains = []
            for ( let url of GM_info.script.matches ) {
                let uniqueDomain = url.match(/^https?\:\/\/(\w+\.)?(.+?)(\.\w+\/)/)[2].toLowerCase()
                allMatchDomains.push(uniqueDomain)
            }

            // Create a new array with no duplicate domain entries
            let uniqueMatchDomains = [...new Set(allMatchDomains)]

            for (let uniqueDomainKey of uniqueMatchDomains) {
                // For each tracker, create 1 tr (tablerow). For each field of that tracker, create 1 td (tabledata). Populate each td with 1 field from that tracker.

                // 1 row for this tracker, appended to the tbody (tableBody)
                let trackerRow = document.createElement('tr')
                tbody.appendChild(trackerRow)

                // 1 td per-field of tracker settings
                let labelData = document.createElement('td')
                let categoryData = document.createElement('td')
                let savePathData = document.createElement('td')
                let tagsData = document.createElement('td')
                let startPausedData = document.createElement('td')

                // Move each td into the tr
                trackerRow.appendChild(labelData)
                trackerRow.appendChild(categoryData)
                trackerRow.appendChild(savePathData)
                trackerRow.appendChild(tagsData)
                trackerRow.appendChild(startPausedData)

                // Locate the fields generated by GM_config
                let label = document.getElementById(`quiCKIE_config_${uniqueDomainKey}-category_field_label`)
                let category = document.getElementById(`quiCKIE_config_field_${uniqueDomainKey}-category`)
                let savePath = document.getElementById(`quiCKIE_config_field_${uniqueDomainKey}-savePath`)
                let tags = document.getElementById(`quiCKIE_config_field_${uniqueDomainKey}-tags`)
                let startPaused = document.getElementById(`quiCKIE_config_field_${uniqueDomainKey}-startPaused`)

                // Move the GM_Config fields into their corresponding td element
                labelData.appendChild(label)
                categoryData.appendChild(category)
                savePathData.appendChild(savePath)
                tagsData.appendChild(tags)
                startPausedData.appendChild(startPaused)

                // Clean-up: Remove the now empty GM_config elements
                document.getElementById(`quiCKIE_config_${uniqueDomainKey}-category_var`).remove()
                document.getElementById(`quiCKIE_config_${uniqueDomainKey}-savePath_var`).remove()
                document.getElementById(`quiCKIE_config_${uniqueDomainKey}-tags_var`).remove()
                document.getElementById(`quiCKIE_config_${uniqueDomainKey}-startPaused_var`).remove()

            }

            // Create GitHub version element
            let githubSVG = '<svg width="16" height="16" viewBox="0 0 98 96" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#clip0_730_27136)"><path d="M41.4395 69.3848C28.8066 67.8535 19.9062 58.7617 19.9062 46.9902C19.9062 42.2051 21.6289 37.0371 24.5 33.5918C23.2559 30.4336 23.4473 23.7344 24.8828 20.959C28.7109 20.4805 33.8789 22.4902 36.9414 25.2656C40.5781 24.1172 44.4062 23.543 49.0957 23.543C53.7852 23.543 57.6133 24.1172 61.0586 25.1699C64.0254 22.4902 69.2891 20.4805 73.1172 20.959C74.457 23.543 74.6484 30.2422 73.4043 33.4961C76.4668 37.1328 78.0937 42.0137 78.0937 46.9902C78.0937 58.7617 69.1934 67.6621 56.3691 69.2891C59.623 71.3945 61.8242 75.9883 61.8242 81.252L61.8242 91.2051C61.8242 94.0762 64.2168 95.7031 67.0879 94.5547C84.4102 87.9512 98 70.6289 98 49.1914C98 22.1074 75.9883 6.69539e-07 48.9043 4.309e-07C21.8203 1.92261e-07 -1.9479e-07 22.1074 -4.3343e-07 49.1914C-6.20631e-07 70.4375 13.4941 88.0469 31.6777 94.6504C34.2617 95.6074 36.75 93.8848 36.75 91.3008L36.75 83.6445C35.4102 84.2188 33.6875 84.6016 32.1562 84.6016C25.8398 84.6016 22.1074 81.1563 19.4277 74.7441C18.375 72.1602 17.2266 70.6289 15.0254 70.3418C13.877 70.2461 13.4941 69.7676 13.4941 69.1934C13.4941 68.0449 15.4082 67.1836 17.3223 67.1836C20.0977 67.1836 22.4902 68.9063 24.9785 72.4473C26.8926 75.2227 28.9023 76.4668 31.2949 76.4668C33.6875 76.4668 35.2187 75.6055 37.4199 73.4043C39.0469 71.7773 40.291 70.3418 41.4395 69.3848Z" fill="white"/></g><defs><clipPath id="clip0_730_27136"><rect width="98" height="96" fill="white"/></clipPath></defs></svg>'

            let versionElement = document.createElement('a')
            versionElement.classList = 'version_label reset'
            versionElement.title = 'Go to homepage'
            versionElement.target = '_blank'
            versionElement.href = `${GM_info.script.homepage}`
            versionElement.innerHTML = `${githubSVG} Version ${GM_info.script.version}`

            doc.getElementById('quiCKIE_config_buttons_holder').appendChild(versionElement)

            // Add success animation to save button
            let saveButton = doc.getElementById('quiCKIE_config_saveBtn')
            saveButton.addEventListener('click', () => {
                // When the save button is clicked, temporarily assign a css class to produce the animation
                saveButton.classList.add('success')
                setTimeout(() => saveButton.classList.remove('success'), 500)
            })

        },
        'save': function () {
            // Actions to take when the 'Save' button is clicked
            reloadWindow = true
            // Clear cached data when settings are saved
            GM_listValues().forEach(key => {
                if (key !== 'quiCKIE_config') {
                    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()
            }
        }
    },
    // The CSS to use for the menu, loaded through the @resource line 
    'css': GM_getResourceText('configMenuCSS')
})

// Register the settings menu to be opened from the UserScript manager dialouge
GM_registerMenuCommand('Settings', () => {
    GM_config.open()
})


// =================================== FUNCTIONS ======================================

function quiAddTorrent(quiURL, quiApiKey, torrentURL, category = '', savePath = '', tags = '', startPaused = 'false') {
    // Add a torrent to qui using the provided arguments

    // Generate the API endpoint url to send the POST
    let quiHost = quiURL.match(/^(.*)\/(instances\/\d+)/)[1]
    let quiInstance = quiURL.match(/^(.*)\/(instances\/\d+)/)[2]
    let quiApiAddTorrentURL = `${quiHost}/api/${quiInstance}/torrents`

    // The form data that will be passed to qui
    let form = new FormData()
    form.append('urls', torrentURL)
    form.append('category', category)
    form.append('savePath', savePath)
    form.append('tags', tags)
    form.append('startPaused', startPaused)

    GM_xmlhttpRequest({
        // Use the internal GM function to prevent source-origin errors
        method: 'POST',
        url: quiApiAddTorrentURL,
        data: form,
        headers: {
            'X-API-Key': quiApiKey,
        },
        onload: function(response) {
            // Actions to take after the request has completed 
            
            if (response.status == 201) {
                // Success: The torrent has been added to qui

                document.getElementById('__CLICKED__').textContent = ' ✔️ '
                document.getElementById('__CLICKED__').removeAttribute('id')

            } else {
                // Failed: The torrent was NOT added to qui, log the response and display an alert...

                document.getElementById('__CLICKED__').textContent = ' ❌ '
                document.getElementById('__CLICKED__').removeAttribute('id')

                console.log(response)
                window.alert(`❌ quiCKIE Failed to Add the Torrent to qui ❌\n\nStatus Code: ${response.status}\n\n${response.responseText}`)

            }

        }
    })

}

function createBunnyButton(torrentURL, buttonText = ' 🐰 ', fontSize='inherit') {
    // Create the bunnyButton that will be displayed on the site

    let bunnyButton = document.createElement('a')
    bunnyButton.classList.add('quiCKIE_bunnyButton')
    bunnyButton.href = 'javascript:void(0)'
    bunnyButton.textContent = buttonText
    bunnyButton.title = `quiCKIE\n-----------------\nCategory: ${SETTINGS.category}\nSavePath: ${SETTINGS.savePath}\nTags: ${SETTINGS.tags}\nStartPaused: ${SETTINGS.startPaused}`

    bunnyButton.setAttribute('style', `font-size: ${fontSize}; text-align: center; text-decoration: none`)

    bunnyButton.addEventListener('mouseover', function(event) {
        // When this bunnyButton is hovered over...
        
        this.style.textShadow = '0 0 5px rgb(4, 184, 255)'
    })

    bunnyButton.addEventListener('mouseout', function(event) {
        // When this bunnyButton is hovered out...
        
        this.style.textShadow = ''
    })

    bunnyButton.addEventListener('mouseup', function(event) {
        // When this bunnyButton is clicked, determine what kind of click it was and respond accordingly...

        if ( event.ctrlKey ) {
            // Ctrl-Click: Open the quiURL in a new tab

            window.open(SETTINGS.quiURL).focus()

        } else if ( event.shiftKey ) {
            // Shift-Click: Open the quiURL in a new window

            window.open(SETTINGS.quiURL, '_blank').focus()
            
        } else if ( event.button == 0 ) {
            // Left-Click: Add a torrent to qui

            this.id = '__CLICKED__'
            this.textContent = ' 🕓 '
            quiAddTorrent(SETTINGS.quiURL, SETTINGS.quiApiKey, torrentURL, SETTINGS.category, SETTINGS.savePath, SETTINGS.tags, SETTINGS.startPaused)

        } else if ( event.button == 1 ) {
            // Middle-Click: Open the quiURL in a new tab
            
            window.open(SETTINGS.quiURL, '_blank').focus()
        }

    })

    return bunnyButton

}


// =================================== CODE ======================================

// To save resources while allowing cross-site compatibility, the domain of the site is used when saving settings and creating GM_config fields
// Example: https://broadcasthe.net/ --> broadcasthe
let trackerDomain = document.location.hostname.match(/^(www\.)?(.*?)(\.\w+)$/)[2].toLowerCase()

const SETTINGS = {
    // The saved settings (cache) that will be used, each 
    quiURL: GM_config.get('quiURL'),
    quiApiKey: GM_config.get('quiApiKey'),
    category: GM_config.get(`${trackerDomain}-category`),
    savePath: GM_config.get(`${trackerDomain}-savePath`),
    tags: GM_config.get(`${trackerDomain}-tags`),
    startPaused: GM_config.get(`${trackerDomain}-startPaused`) ? true : false,
}


// @trackerIfBlocks
// Because the site's domain is unique, we can use it to determine what tracker this is and how bunnyButtons should be generated
//     ! This is the same domain used when creating the tracker's settings fields below @trackerFields
if ( trackerDomain == 'animebytes' ) {
    // ----------------------------------- AnimeBytes -----------------------------------
    // Browse | Collages | Company | Series 

    // An array of all the torrent download elements on the page 
    let allDownloadElements = document.querySelectorAll('a[href^="/torrent/"][title="Download torrent"]')

    for (let downloadElement of allDownloadElements) {
        // For each download element, generate a bunnyButton and insert it after the download element

        let bunnyButton = createBunnyButton(downloadElement.href)

        // Insert the bunnyButton after the site's download element
        downloadElement.insertAdjacentElement('afterend', bunnyButton)
        
        // Insert a '|' between the site's download element and the new bunnyButton
        downloadElement.insertAdjacentText('afterend', '|')

    }

} else if ( trackerDomain == 'bibliotik' ) {
    // ----------------------------------- Bibliotik -----------------------------------
    // Browse | Details

    let allDownloadElements = document.querySelectorAll('a[href^="/torrents/"][title="Download"]')

    for (let downloadElement of allDownloadElements) {

        let bunnyButton = createBunnyButton(downloadElement.href)

        downloadElement.insertAdjacentElement('afterend', bunnyButton)
        downloadElement.insertAdjacentText('afterend', '  ')

    }

} else if ( trackerDomain == 'broadcasthe' ) {
    // ----------------------------------- BroadcasTheNet -----------------------------------
    // Browse | Series | Season\Episodes

    let allDownloadElements = document.querySelectorAll('a[href^="torrents.php?action=download&id="]')

    for (let downloadElement of allDownloadElements) {

        let bunnyButton = createBunnyButton(downloadElement.href)

        downloadElement.insertAdjacentElement('afterend', bunnyButton)
        downloadElement.insertAdjacentText('afterend', ' |')

    }

} else if ( trackerDomain == 'empornium' ) {
    // ----------------------------------- Empornium -----------------------------------
    // Browse | Collages | Details | Top10 
    
    let allDownloadElements = document.querySelectorAll('a[href^="/torrents.php?action=download&id="]')

    for (let downloadElement of allDownloadElements) {

        let bunnyButton = createBunnyButton(downloadElement.href, '🐰', '125%')

        downloadElement.insertAdjacentElement('afterend', bunnyButton)
        downloadElement.insertAdjacentText('afterend', '  ')

    }

} else if ( trackerDomain == 'gazellegames' ) {
    // ----------------------------------- GazelleGames -----------------------------------
    // Browse | Bundles | Game

    let allDownloadElements = document.querySelectorAll('a[href^="torrents.php?action=download&id="]')

    for (let downloadElement of allDownloadElements) {

        let bunnyButton = createBunnyButton(downloadElement.href)

        downloadElement.insertAdjacentElement('afterend', bunnyButton)
        downloadElement.insertAdjacentText('afterend', '|')

    }

} else if ( trackerDomain == 'happyfappy' ) {
    // ----------------------------------- HappyHappy -----------------------------------
    // Browse | Details | Top10 | Collages

    let allDownloadElements = document.querySelectorAll('a[href^="/torrents.php?action=download&id="]')

    for (let downloadElement of allDownloadElements) {

        let bunnyButton = createBunnyButton(downloadElement.href, '🐰', '125%')

        downloadElement.insertAdjacentElement('afterend', bunnyButton)
        downloadElement.insertAdjacentText('afterend', '  ')

    }

} else if ( trackerDomain == 'hdbits' ) {
    // ----------------------------------- HDBits -----------------------------------
    // Browse | Details | Film  

    let allDownloadElements = document.querySelectorAll('a.js-download[href^="/download.php/"]')

    for (let downloadElement of allDownloadElements) {

        let bunnyButton = createBunnyButton(downloadElement.href, '🐰', '150%')

        downloadElement.insertAdjacentElement('afterend', bunnyButton)
        downloadElement.insertAdjacentText('afterend', '  ')

    }

} else if ( trackerDomain == 'myanonamouse' ) {
    // ----------------------------------- MyAnonaMouse -----------------------------------
    // Browse | Details | Homepage

    if ( document.URL.match(/\/t\/\d+/) ) {
        // The book details page, which doesn't require a MutationObserver

        let downloadButton = document.querySelector('a[href^="/tor/download.php/"][title*="Download"]')

        let bunnyButton = createBunnyButton(downloadButton.href, '🐰', '150%')

        downloadButton.insertAdjacentElement('afterend', bunnyButton)


    } else {
        // The Browse or Homepage, both of which require a MutationObserver
       
        let observer = new MutationObserver(function(mutations) {
            // Functionality to run when changes are detected to the target element

            try {

                let allDownloadElements = document.querySelectorAll('a[href^="/tor/download.php/"][title*="Download"]')

                for (let downloadElement of allDownloadElements) {

                    let bunnyButton = createBunnyButton(downloadElement.href, '🐰', '150%')

                    downloadElement.insertAdjacentElement('afterend', bunnyButton)

                }

            } catch(error) {
                // console.log(error)
                return

            }
        })

        let target = document.getElementById('ssr')
        let config = { childList: true }

        observer.observe(target, config)
    }

} else if ( trackerDomain == 'orpheus' ) {
    // ----------------------------------- Orpheus -----------------------------------
    // Album | Artist | Browse | Collages

    let allDownloadElements = document.querySelectorAll('a[href^="torrents.php?action=download&id="]')

    for (let downloadElement of allDownloadElements) {

        let bunnyButton = createBunnyButton(downloadElement.href)

        downloadElement.insertAdjacentElement('afterend', bunnyButton)
        downloadElement.insertAdjacentText('afterend', '|')

    }

} else if ( trackerDomain == 'passthepopcorn' ) {
    // ----------------------------------- PassThepopcorn -----------------------------------
    // Film
    
    let allDownloadElements = document.querySelectorAll('a[href^="torrents.php?action=download&id="]')

    for (let downloadElement of allDownloadElements) {

        let bunnyButton = createBunnyButton(downloadElement.href)

        downloadElement.insertAdjacentElement('afterend', bunnyButton)
        downloadElement.insertAdjacentText('afterend', ' |')

    }

} else if ( trackerDomain == 'redacted' ) {
    // ----------------------------------- Redacted -----------------------------------
    // Album | Artist | Browse

    let allDownloadElements = document.querySelectorAll('a[href^="torrents.php?action=download&id="]')

    for (let downloadElement of allDownloadElements) {

        let bunnyButton = createBunnyButton(downloadElement.href)

        downloadElement.insertAdjacentElement('afterend', bunnyButton)
        downloadElement.insertAdjacentText('afterend', '|')

    }

}