diff --git a/package.json b/package.json index f6779d3..189590d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nzbunity", - "version": "1.16.0", + "version": "1.16.1", "license": "MPL-2.0", "description": "Send and control NZB files directly with SABnzbd or NZBGet download clients.", "author": "clutterskull@gmail.com", diff --git a/src/background/util.ts b/src/background/util.ts index af33e38..c6c3584 100644 --- a/src/background/util.ts +++ b/src/background/util.ts @@ -437,6 +437,14 @@ class PageUtil { static readonly backgroundNormal:string = 'rgb(23, 162, 184)'; static readonly backgroundPending:string = 'rgb(156, 166, 168)'; + static ready(callback: () => void): void { + if (document.readyState != 'loading'){ + callback(); + } else { + document.addEventListener('DOMContentLoaded', callback); + } + } + static request(options:RequestOptions):Promise { options.url = `${window.location.origin}${options.url || ''}`; return Util.request(options); diff --git a/src/content/sites/nzbfinder.ts b/src/content/sites/nzbfinder.ts index 0cc7720..5f2a09b 100644 --- a/src/content/sites/nzbfinder.ts +++ b/src/content/sites/nzbfinder.ts @@ -3,9 +3,6 @@ class NZBUnityNzbfinder { public apikey: string; public replace: boolean = false; - // TODO: Remove after not likely to revert to v4 - private v4: boolean = false; - constructor() { Util.storage.get(['Providers', 'ReplaceLinks']) .then((opts) => { @@ -16,19 +13,8 @@ class NZBUnityNzbfinder { if (enabled) { console.info(`[NZB Unity] Initializing 1-click functionality...`); - // TODO: Remove after not likely to revert to v4 - this.v4 = /v4/i.test(this.getMeta('version')); - - if (this.v4) { - let uidMatch = $('script:not([src])').text().match(/uid\s*=\s*["']([0-9]+)["']/i); - let apikeyMatch = $('script:not([src])').text().match(/rsstoken\s*=\s*["']([a-z0-9]+)["']/i); - - this.uid = uidMatch ? uidMatch[1] : null; - this.apikey = apikeyMatch ? apikeyMatch[1] : null; - } else { - this.uid = this.getMeta('user_id'); - this.apikey = this.getMeta('api_token'); - } + this.uid = this.getMeta('user_id'); + this.apikey = this.getMeta('api_token'); if (this.uid && this.apikey) { console.info(`Got uid and api key: ${this.uid}, ${this.apikey}`); @@ -48,304 +34,158 @@ class NZBUnityNzbfinder { getNzbUrl(id: string): string { return `${this.getMeta('root_url') ?? window.location.origin}/getnzb?id=${id}&r=${this.apikey}`; + } + isList(link: HTMLElement): boolean { + return link.parentElement.tagName === 'TD'; } - initializeLinks() { - // TODO: Remove after not likely to revert to v4 - if (this.v4) { - // V4 - // Create direct download links on individual items - $('a[href*="/getnzb?id="]').each((i, el) => { - let a:JQuery = $(el); - let guidMatch:string[] = a.attr('href').match(/\/getnzb\?id=([\w-]+)/i); - let id:string = guidMatch && guidMatch[1]; - let url:string = this.getNzbUrl(id); + getLinkContainer(link: HTMLElement): HTMLElement { + return link.closest(this.isList(link) ? 'tr' : 'article'); + } - // Get the category - let category:string = ''; - let catSrc:string = 'default'; + getFirstChild(container: HTMLElement): HTMLElement { + return container.querySelector(':scope > :first-child') as HTMLElement; + } - if (window.location.pathname.startsWith('/details')) { - let catLink = $('.page-content a[href*="/browse/"]').filter(':not([href*="/browse/group?"])'); - if (catLink.length) { - category = catLink.text(); - catSrc = 'href'; - } + getCategory(link: HTMLElement): string { + const cat = this.getLinkContainer(link).querySelector('#catname')?.textContent.trim(); - let split:string[] = category.split(/[^\w-]/); // Either "Movies: HD" or "Movies HD" - category = split.length ? split[0] : category; + if (/^\w+$/.test(cat)) { + // If the category is a single word, it's probably a sub-category + // try to get the main category from the url + const catMatch = new RegExp(`/(\\w+)/${cat}`).exec(window.location.pathname); + if (catMatch) { + return `${catMatch[1]} ${cat}`; + } + } - let opts:CreateAddLinkOptions = { url: url, category: category }; + return cat; + } - if (this.replace) { - PageUtil.bindAddUrl(opts, a, true); - } else { - let link:JQuery = PageUtil.createAddUrlLink(opts); - link - .html(`${link.html()} NZB Unity`) - .addClass('btn btn-default btn-transparent') - .css({ display: '', height: '', width: '' }) - .insertBefore(a); - } + initializeLinks() { + // V5 + // Create direct download links on individual items + document.querySelectorAll('a[href*="/getnzb?id="]').forEach(el => { + const a = el as HTMLElement; + const guidMatch = a.getAttribute('href').match(/\/getnzb\?id=([\w-]+)/i) as string[]; + const id = guidMatch && guidMatch[1]; + const url = this.getNzbUrl(id); + + // Get the category + let category = this.getCategory(a) ?? ''; + const opts:CreateAddLinkOptions = { url, category }; + + if (window.location.pathname.startsWith('/details')) { + // Item detail + const catLink = document.querySelectorAll('table a[href*="/browse/"]:not([href*="/browse/group?"])'); + if (catLink.length) { + opts.category = (catLink[0] as HTMLElement).innerText.trim(); + } + if (this.replace) { + PageUtil.bindAddUrl(opts, a, true); } else { - if (a.closest('tr').find('.label:first-child').length) { - // List - category = a.closest('tr').find('.label:first-child').text(); - catSrc = 'href'; - } + const link = PageUtil.createAddUrlLink(opts) + .addClass(this.getFirstChild(a).classList.value) + .insertBefore(a); - if (a.closest('.row').find('.label-primary').length) { - // Covers - category = a.closest('.row').find('.label-primary').text(); - catSrc = 'href-cover'; - } - - let split:string[] = category.split(/[^\w-]/); // Either "Movies: HD" or "Movies HD" - category = split.length ? split[0] : category; + if (a.parentElement.id === 'buttongroup') { + // Info tab, side + link + .html(`${link.html()} NZB Unity`) + .css({ display: '', height: '', width: '' }); - let opts:CreateAddLinkOptions = { url: url, category: category }; + link.find('> img') + .css({ margin: '0 5px 0 3px' }); - if (this.replace) { - PageUtil.bindAddUrl(opts, a, true); + this.getFirstChild(a).classList.remove('rounded-t-md'); } else { - let link:JQuery = PageUtil.createAddUrlLink(opts) - .css({ margin: '0 0.5em 0 0' }) - .insertBefore(a); - - a.parent().css({ 'white-space': 'nowrap' }); + // Similar tab, table. + link + .addClass('align-bottom') + .css({ margin: '0 0 2px 0' }); } } - }); - - // Create download all buttons - $('.nzb_multi_operations_download').each((i, el) => { - let getNzbUrl = (id:string) => { return this.getNzbUrl(id); }; - let button:JQuery = PageUtil.createButton() - .text('NZB Unity') - .addClass('btn') - .css({ - 'border-top-right-radius': '0', - 'border-bottom-right-radius': '0', - 'height': '25px', - 'line-height': '11px', - 'margin': '0', - }) - .on('click', (e) => { - e.preventDefault(); + } else { + if (this.replace) { + PageUtil.bindAddUrl(opts, a, true); + } else if (this.isList(a)) { + // List + const link = PageUtil.createAddUrlLink(opts) + .addClass(this.getFirstChild(a).classList.value) + .removeClass('h-5 w-5') + .css({ display: 'inline', height: '', width: '' }) + .insertBefore(a); - let checked:JQuery = $('#nzb_multi_operations_form .nzb_check:checked'); - if (checked.length) { - console.info(`[NZB Unity] Adding ${checked.length} NZB(s)`); - button.trigger('nzb.pending'); + link.find('> img') + .css({ display: 'inline' }); - Promise.all(checked.map((i, el) => { - let check = $(el); - let id = check.val(); + } else { + // Covers + PageUtil.createAddUrlLink(opts) + .addClass(this.getFirstChild(a).classList.value) + .addClass('align-bottom h-full') + .css({ display: '', height: '', width: '' }) + .insertBefore(a); + } + } + }); + + // Create download all buttons + document.querySelectorAll('#multidownload').forEach(el => { + const button = PageUtil.createButton() + .removeAttr('style') + .text('NZB Unity') + .addClass(el.classList.value) + .css({ + background: `transparent url(${PageUtil.iconGreen}) no-repeat scroll 10px center`, + 'background-color': '', + cursor: 'pointer', + 'padding-left': '30px', + 'white-space': 'nowrap', + }) + .on('click', (e) => { + e.preventDefault(); + + const checked = document.querySelectorAll('input[type="checkbox"][name="chk"]:checked'); + if (checked.length) { + console.info(`[NZB Unity] Adding ${checked.length} NZB(s)`); + button.trigger('nzb.pending'); + + Promise + .all(Array.from(checked).map(el => { + const check = el as HTMLInputElement; + const id = check.value; if (/[a-d0-9\-]+/.test(id)) { - // Get the category - let category:string = ''; - let catSrc:string = 'default'; - - if (check.closest('tr').find('.label:first-child').length) { - category = check.closest('tr').find('.label:first-child').text(); - catSrc = 'href'; - } - - let split:string[] = category.split(/[^\w-]/); // Either "Movies: HD" or "Movies HD" - category = split.length ? split[0] : category; - - let options = { - url: getNzbUrl(id), - category: category + const opts: CreateAddLinkOptions = { + url: this.getNzbUrl(id), + category: this.getCategory(check), }; - console.info(`[NZB Unity] Adding URL`, options); - return Util.sendMessage({ 'content.addUrl': options }); + console.info(`[NZB Unity] Adding URL`, opts); + return Util.sendMessage({ 'content.addUrl': opts }); } else { return Promise.resolve(); } })) - .then((results:any[]) => { - setTimeout(() => { - if (results.some((r) => { return r === false; })) { - button.trigger('nzb.failure'); - } else { - button.trigger('nzb.success'); - } - }, 1000); - }); - } - }) - .insertBefore(el); - }); - } else { - // V5 - // Create direct download links on individual items - $('a[href*="/getnzb?id="]').each((i, el) => { - const a = $(el); - const guidMatch: string[] = a.attr('href').match(/\/getnzb\?id=([\w-]+)/i); - const id: string = guidMatch && guidMatch[1]; - const url: string = this.getNzbUrl(id); - - // Get the category - let category:string = ''; - - if (window.location.pathname.startsWith('/details')) { - // Item detail - const catLink = $('table a[href*="/browse/"]').filter(':not([href*="/browse/group?"])'); - if (catLink.length) { - category = catLink.text(); - } - - const opts:CreateAddLinkOptions = { url, category }; - - if (this.replace) { - PageUtil.bindAddUrl(opts, a, true); - } else { - const link = PageUtil.createAddUrlLink(opts) - .addClass(a.find('> :first-child').attr('class')) - .insertBefore(a); - - if (/buttongroup/i.test(a.parent().prop('id'))) { - // Info tab, side - link - .html(`${link.html()} NZB Unity`) - .css({ display: '', height: '', width: '' }); - - link.find('> img') - .css({ margin: '0 5px 0 3px' }); - - a.find('> :first-child').removeClass('rounded-t-md'); - } else { - // Similar tab, table. - link - .addClass('align-bottom') - .css({ margin: '0 0 2px 0' }); - } - } - } else if (window.location.pathname.startsWith('/browse')) { - // List - const catMatch = /^\/browse\/(\w+)\/(\w+)\/?$/.exec(window.location.pathname); - if (catMatch) { - category = window.location.pathname.split('/').slice(2).join(' '); - } - - const opts:CreateAddLinkOptions = { url, category }; - - if (this.replace) { - PageUtil.bindAddUrl(opts, a, true); - } else { - PageUtil.createAddUrlLink(opts) - .addClass(a.find('> :first-child').attr('class')) - .removeClass('h-5 w-5') - .css({ display: '', height: '', width: '', margin: '0 2px 2px 0' }) - .insertBefore(a); - - a.parent().css({ - display: 'flex', - 'flex-wrap': 'nowrap', - 'align-items': 'flex-end', - 'justify-content': 'flex-end', - 'margin-top': '5px', - }); - } - } else { - // Covers - const catMatch = /^\/(\w+)\/(\w+)\/?$/.exec(window.location.pathname); - if (catMatch) { - // Probably in the path - category = catMatch.slice(1).join(' '); - } - - const opts:CreateAddLinkOptions = { url, category }; - - if (this.replace) { - PageUtil.bindAddUrl(opts, a, true); - } else { - PageUtil.createAddUrlLink(opts) - .addClass(a.find('> :first-child').attr('class')) - .addClass('align-bottom h-full') - .css({ display: '', height: '', width: '' }) - .insertBefore(a); - } - } - }); - - // Create download all buttons - Array.from(document.querySelectorAll('main button[type="button"]')) - .filter(el => el.innerHTML.trim() === 'Download') - .forEach(el => { - const button = PageUtil.createButton() - .text('NZB Unity') - .addClass(el.getAttribute('class')) - .css({ - 'background-color': '', - 'background-position': '7px center', - border: '', - 'border-radius': '', - color: '', - display: '', - 'font-size': '', - 'font-weight': '', - height: '', - margin: '', - 'padding-left': '28px', - 'text-shadow': '', - }) - .on('click', (e) => { - e.preventDefault(); - - const checked = $('input[type="checkbox"][name="chk"]:checked'); - if (checked.length) { - console.info(`[NZB Unity] Adding ${checked.length} NZB(s)`); - button.trigger('nzb.pending'); - - Promise.all(checked.map((i, el) => { - const check = $(el); - const id = check.val() as string; - - if (/[a-d0-9\-]+/.test(id)) { - const url = this.getNzbUrl(id); - - // Get the category - let category:string = ''; - - if (check.closest('tr').find('.rounded-md:first-child').length) { - category = check.closest('tr').find('.rounded-md:first-child').text().trim(); - } - - const opts: CreateAddLinkOptions = { url, category }; - - console.info(`[NZB Unity] Adding URL`, opts); - return Util.sendMessage({ 'content.addUrl': opts }); + .then(results => { + setTimeout(() => { + if (results.some(r => r === false)) { + button.trigger('nzb.failure'); } else { - return Promise.resolve(); + button.trigger('nzb.success'); } - })) - .then((results:any[]) => { - setTimeout(() => { - if (results.some((r) => { return r === false; })) { - button.trigger('nzb.failure'); - } else { - button.trigger('nzb.success'); - } - }, 1000); - }); - } - }) - .insertBefore(el); + }, 1000); + }); + } + }) + .insertBefore(el); - el.classList.remove('rounded-l-md'); - }); - } + el.classList.remove('rounded-l-md'); + }); } } -$(($) => { - let nzbIntegration = new NZBUnityNzbfinder(); -}); - -undefined; +PageUtil.ready(() => new NZBUnityNzbfinder()); diff --git a/src/manifest.json b/src/manifest.json index 97378fc..bf798bb 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "NZB Unity", - "version": "1.16.0", + "version": "1.16.1", "author": "clutterskull@gmail.com", "description": "Send and control NZB files directly with SABnzbd or NZBGet download clients.", "background": {