// ==UserScript==
// @name [RED] Cover Inspector
// @namespace https://greasyfork.org/users/321857-anakunda
// @version 1.12.0
// @run-at document-end
// @description Adds cover sticker if needs updating for unsupported host / big size / small resolution
// @author Anakunda
// @copyright 2020, Anakunda (https://greasyfork.org/users/321857-anakunda)
// @license GPL-3.0-or-later
// @iconURL https://i.ibb.co/mh2prQR/clouseau.png
// @match https://redacted.ch/torrents.php?id=*
// @match https://redacted.ch/torrents.php
// @match https://redacted.ch/torrents.php?action=advanced
// @match https://redacted.ch/torrents.php?action=advanced&*
// @match https://redacted.ch/torrents.php?*&action=advanced
// @match https://redacted.ch/torrents.php?*&action=advanced&*
// @match https://redacted.ch/torrents.php?action=basic
// @match https://redacted.ch/torrents.php?action=basic&*
// @match https://redacted.ch/torrents.php?*&action=basic
// @match https://redacted.ch/torrents.php?*&action=basic&*
// @match https://redacted.ch/torrents.php?page=*
// @match https://redacted.ch/torrents.php?action=notify
// @match https://redacted.ch/torrents.php?action=notify&*
// @match https://redacted.ch/torrents.php?type=*
// @match https://redacted.ch/artist.php?id=*
// @connect *
// @grant GM_xmlhttpRequest
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_openInTab
// @require https://openuserjs.org/src/libs/Anakunda/libLocks.min.js
// @require https://openuserjs.org/src/libs/Anakunda/gazelleApiLib.min.js
// ==/UserScript==
const isFirefox = /\b(?:Firefox)\b/.test(navigator.userAgent) || Boolean(window.InstallTrigger);
const httpParser = /^(https?:\/\/.+)*$/i;
const preferredHosts = ['https://ptpimg.me/'];
const preferredTypes = ['jpeg', 'webp', 'gif'];
try { var fileSizeCache = new Map(JSON.parse(sessionStorage.fileSizeCache)) } catch(e) { fileSizeCache = new Map }
try { var fileTypeCache = new Map(JSON.parse(sessionStorage.fileTypeCache)) } catch(e) { fileTypeCache = new Map }
function defaultErrorHandler(response) {
console.error('HTTP error:', response);
let e = 'HTTP error ' + response.status;
if (response.statusText) e += ' (' + response.statusText + ')';
if (response.error) e += ' (' + response.error + ')';
return e;
}
function defaultTimeoutHandler(response) {
console.error('HTTP timeout:', response);
const e = 'HTTP timeout';
return e;
}
function getRemoteFileSize(url) {
if (!httpParser.test(url)) return Promise.reject('getRemoteFileSize(...): parameter not valid URL');
if (fileSizeCache.has(url)) return Promise.resolve(fileSizeCache.get(url));
const getByXHR = (method = 'GET') => new Promise(function(resolve, reject) {
function success() {
fileSizeCache.set(url, size);
sessionStorage.fileSizeCache = JSON.stringify(Array.from(fileSizeCache));
resolve(size);
}
let size, hXHR = GM_xmlhttpRequest({ method: method, url: url, binary: true, responseType: 'blob',
onreadystatechange: function(response) {
if (typeof size == 'number' && size >= 0 || response.readyState < XMLHttpRequest.HEADERS_RECEIVED) return;
if ((size = /^(?:Content-Length)\s*:\s*(\d+)\b/im.exec(response.responseHeaders)) != null
&& (size = parseInt(size[1])) >= 0) success();
else if (method == 'HEAD') reject('Content size missing in header'); else return;
if (method != 'HEAD') hXHR.abort();
},
onload: function(response) { // fail-safe
if (typeof size == 'number' && size >= 0) return;
if (response.status >= 200 && response.status < 400) {
/*if (response.response) {
size = response.response.size;
success();
} else */if (response.responseText) {
size = response.responseText.length;
success();
} else reject('Body missing');
} else reject(defaultErrorHandler(response));
},
onerror: response => { reject(defaultErrorHandler(response)) },
ontimeout: response => { reject(defaultTimeoutHandler(response)) },
});
});
return getByXHR('GET')/*.catch(reason => getByXHR('GET'))*/;
}
function getRemoteFileType(url) {
if (!httpParser.test(url)) return Promise.reject('getRemoteFileType: parameter not valid URL');
if (fileTypeCache.has(url)) return Promise.resolve(fileTypeCache.get(url));
const getByXHR = (method = 'GET') => new Promise(function(resolve, reject) {
let contentType, hXHR = GM_xmlhttpRequest({ method: method, url: url,
onreadystatechange: function(response) {
if (contentType !== undefined || response.readyState < XMLHttpRequest.HEADERS_RECEIVED) return;
if ((contentType = /^(?:Content-Type)\s*:\s*(.+?)(?:\s*;(.+?))?\s*$/im.exec(response.responseHeaders)) != null) {
fileTypeCache.set(url, contentType = contentType[1].toLowerCase());
sessionStorage.fileTypeCache = JSON.stringify(Array.from(fileTypeCache));
resolve(contentType);
} else reject('MIME type missing in header');
if (method != 'HEAD') hXHR.abort();
},
onerror: response => { reject(defaultErrorHandler(response)) },
ontimeout: response => { reject(defaultTimeoutHandler(response)) },
});
});
return getByXHR('HEAD')
.catch(reason => /^HTTP error (403|416)\b/.test(reason) ? getByXHR('GET') : Promise.reject(reason));
}
function formattedSize(size) {
return size < 1024**1 ? Math.round(size) + ' B'
: size < 1024**2 ? (Math.round(size * 10 / 2**10) / 10) + ' KiB'
: size < 1024**3 ? (Math.round(size * 100 / 2**20) / 100) + ' MiB'
: size < 1024**4 ? (Math.round(size * 100 / 2**30) / 100) + ' GiB'
: size < 1024**5 ? (Math.round(size * 100 / 2**40) / 100) + ' TiB'
: (Math.round(size * 100 / 2**50) / 100) + ' PiB';
}
const groupId = document.location.pathname == '/torrents.php'
&& parseInt(new URLSearchParams(document.location.search).get('id')) || undefined;
const imageHostHelper = ajaxApiKey ? (function() {
const input = document.head.querySelector('meta[name="ImageHostHelper"]');
return (input != null ? Promise.resolve(input) : new Promise(function(resolve, reject) {
const mo = new MutationObserver(function(mutationsList, mo) {
for (let mutation of mutationsList) for (let node of mutation.addedNodes) {
if (node.nodeName != 'META' || node.name != 'ImageHostHelper') continue;
clearTimeout(timer); mo.disconnect();
return resolve(node);
}
}), timer = setTimeout(function(mo) {
mo.disconnect();
reject('Timeout reached');
}, 10000, mo);
mo.observe(document.head, { childList: true });
})).then(function(node) {
console.assert(node instanceof HTMLElement);
const propName = node.getAttribute('propertyname');
console.assert(propName);
return unsafeWindow[propName] || Promise.reject('Assertion failed: \'' + propName + '\' not in unsafeWindow');
});
})() : Promise.reject('AJAX API key not configured or unsupported page');
let acceptableCoverSize = GM_getValue('acceptable_cover_size');
if (!(acceptableCoverSize >= 0)) GM_setValue('acceptable_cover_size', acceptableCoverSize = 2048);
let acceptableCoverResolution = GM_getValue('acceptable_cover_resolution');
if (!(acceptableCoverResolution >= 0)) GM_setValue('acceptable_cover_resolution', acceptableCoverResolution = 300);
function getHostFriendlyName(imageUrl) {
if (httpParser.test(imageUrl)) try { imageUrl = new URL(imageUrl).hostname.toLowerCase() } catch(e) {
console.error(e);
return;
} else return;
const knownHosts = {
'2i': ['2i.cz'],
'Abload': ['abload.de'],
'AllMusic': ['rovicorp.com'],
'AllThePics': ['allthepics.net'],
'Amazon': ['media-amazon.com', 'ssl-images-amazon.com', 'amazonaws.com'],
'Apple': ['mzstatic.com'],
'Bandcamp': ['bcbits.com'],
'Beatport': ['beatport.com'],
'BilderUpload': ['bilder-upload.eu'],
'Boomkat': ['boomkat.com'],
'CasImages': ['casimages.com'],
'Catbox': ['catbox.moe'],
'CloudFront': ['cloudfront.net'],
'CubeUpload': ['cubeupload.com'],
'Deezer': ['dzcdn.net'],
'Discogs': ['discogs.com'],
'Extraimage': ['extraimage.org'],
'FastPic': ['fastpic.ru'],
'Forumbilder': ['forumbilder.com'],
'FreeImageHost': ['freeimage.host'],
'FunkyImg': ['funkyimg.com'],
'GeTt': ['ge.tt'],
'GeekPic': ['geekpic.net'],
'GetaPic': ['getapic.me'],
'Gifyu': ['gifyu.com'],
'GooPics': ['goopics.net'],
'HRA': ['highresaudio.com'],
'imageCx': ['image.cx'],
'ImageBan': ['imageban.ru'],
'ImagensBrasil': ['imagensbrasil.org'],
'ImageRide': ['imageride.com'],
'ImageToT': ['imagetot.com'],
'ImageVenue': ['imagevenue.com'],
'ImgBank': ['imgbank.cz'],
'ImgBB': ['ibb.co'],
'ImgBox': ['imgbox.com'],
'ImgCDN': ['imgcdn.dev'],
'Imgoo': ['imgoo.com'],
'ImgPile': ['imgpile.com'],
'imgsha': ['imgsha.com'],
'Imgur': ['imgur.com'],
'ImgURL': ['png8.com'],
'IpevRu': ['ipev.ru'],
'Jerking': ['jerking.empornium.ph'],
'JPopsuki': ['jpopsuki.eu'],
'Juno': ['junodownload.com'],
'Lensdump': ['lensdump.com'],
'LightShot': ['prntscr.com'],
'LostPic': ['lostpic.net'],
'MetalArchives': ['metal-archives.com'],
'Mobilism': ['mobilism.org'],
'Mora': ['https://mora.jp'],
'MusicBrainz': ['coverartarchive.org'],
'NoelShack': ['noelshack.com'],
'PicaBox': ['picabox.ru'],
'PicLoad': ['free-picload.com'],
'PimpAndHost': ['pimpandhost.com'],
'PixHost': ['pixhost.to'],
'PomfCat': ['pomf.cat'],
'PostImg': ['postimg.cc'],
'Qobuz': ['qobuz.com'],
'Ra': ['thesungod.xyz'],
'Radikal': ['radikal.ru'],
'SavePhoto': ['savephoto.ru'],
'Shopify': ['shopify.com'],
'Slowpoke': ['slow.pics'],
'SM.MS': ['sm.ms'],
'SVGshare': ['svgshare.com'],
'Tidal': ['tidal.com'],
'Twitter': ['twimg.com'],
'Upimager': ['upimager.com'],
'Uupload.ir': ['uupload.ir'],
'VgyMe': ['vgy.me'],
'Wiki': ['wikimedia.org'],
'Z4A': ['z4a.net'],
'路过图床': ['imgchr.com'],
};
for (let name in knownHosts) if (knownHosts[name].some(function(domain) {
domain = domain.toLowerCase();
return imageUrl.endsWith('.' + domain) || imageUrl == domain;
})) return name;
return undefined;
}
function getImageMax(imageUrl) {
const friendlyName = getHostFriendlyName(imageUrl);
return imageHostHelper.then(ihh => (function() {
const func = friendlyName && {
'Deezer': 'getDeezerImageMax',
'Discogs': 'getDiscogsImageMax',
}[friendlyName];
return func && func in ihh ? ihh[func](imageUrl) : Promise.reject('No imagemax function');
})().catch(function(reason) {
let sub = friendlyName && {
'Bandcamp': [/_\d+(?=\.(\w+)$)/, '_10'],
'Deezer': ihh.dzrImageMax,
'Apple': ihh.itunesImageMax,
'Qobuz': [/_\d{3}(?=\.(\w+)$)/, '_org'],
'Boomkat': [/\/(?:large|medium|small)\//i, '/original/'],
'Beatport': [/\/image_size\/\d+x\d+\//i, '/image/'],
'Tidal': [/\/(\d+x\d+)(?=\.(\w+)$)/, '/1280x1280'],
'Amazon': [/\._\S+?_(?=\.)/, ''],
'HRA': [/_(\d+x\d+)(?=\.(\w+)$)/, ''],
}[friendlyName];
if (sub) sub = String(imageUrl).replace(...sub); else return Promise.reject('No imagemax substitution');
return 'verifyImageUrl' in ihh ? ihh.verifyImageUrl(sub) : sub;
}).catch(reason => 'verifyImageUrl' in ihh ? ihh.verifyImageUrl(imageUrl) : imageUrl));
}
function inspectImage(img, id) {
console.assert(img instanceof HTMLImageElement, 'img instanceof HTMLImageElement');
if (!(img instanceof HTMLImageElement)) return;
img.onload = evt => { evt.currentTarget.hidden = false };
img.parentNode.style.position = 'relative';
const inListing = img.clientWidth > 0 && img.clientWidth < 100, image = new Image;
if (id && /^(?:cover_(\d+))$/.test(img.id) & parseInt(RegExp.$1) > 0) id = undefined;
function imgsrcHandler(imageUrl) {
function editOnClick(elem) {
function editOnClick(evt) {
evt.stopPropagation();
evt.preventDefault();
//document.getSelection().removeAllRanges();
const url = '/torrents.php?' + new URLSearchParams({
action: 'editgroup',
groupid: id,
}).toString();
if ((evt.shiftKey || evt.ctrlKey) && typeof GM_openInTab == 'function')
GM_openInTab(document.location.origin + url, evt.shiftKey); else document.location.assign(url);
return false;
}
console.assert(elem instanceof HTMLElement, 'elem instanceof HTMLElement');
if (!(elem instanceof HTMLElement)) return;
elem.classList.add('edit');
elem.style.cursor = 'pointer';
elem.style.userSelect = 'none';
elem.style['-webkit-user-select'] = 'none';
elem.style['-moz-user-select'] = 'none';
elem.style['-ms-user-select'] = 'none';
elem.onclick = editOnClick;
}
if (imgSrc.includes('static/common/noartwork/')) {
if (id) editOnClick(img);
return;
}
const sticker = document.createElement('div');
sticker.className = 'cover-inspector';
sticker.style = `
position: absolute; color: white; border: thin solid lightgray;
font-family: "Segoe UI", sans-serif; font-weight: 700;
cursor: default; transition-duration: 0.25s; z-index: 1;
` + (inListing ? 'right: 1px; bottom: 1px; padding: 0; font-size: 6pt; text-align: right;'
: ' right: -3pt; bottom: -7pt; padding: 1px; font-size: 8.5pt;');
const clickHandler = evt => { lightbox.init(evt.currentTarget.src, 220) };
function span(content, className, isOK = false, tooltip) {
const span = document.createElement('SPAN');
if (className) span.className = className;
if (tooltip) span.title = tooltip;
span.style.padding = inListing ? '0 2px' : '0 4px';
if (!isOK) span.style.color = 'yellow';
span.textContent = content;
return span;
}
Promise.all([
new Promise(function(resolve, reject) {
image.onload = evt => { resolve(evt.currentTarget) };
image.onerror = evt => { reject(evt.message || 'Image load error') };
image.src = imageUrl;
}),
getRemoteFileSize(imageUrl).catch(function(reason) {
console.warn('Failed to get remote image size (' + imageUrl + '):', reason);
return undefined;
}),
getRemoteFileType(imageUrl).catch(function(reason) {
console.warn('Failed to get remote image type (' + imageUrl + '):', reason);
return undefined;
}),
]).then(function(results) {
if (results[0].naturalWidth <= 0 || results[0].naturalHeight <= 0
|| results[1] < 2 * 2**10 && results[0].naturalWidth == 400 && results[0].naturalHeight == 100
|| results[1] == 503) return Promise.reject('Image is invalid');
const isProxied = imageUrl.startsWith(document.location.origin + '/image.php?'),
isPreferredHost = preferredHosts.some(preferredHost => imageUrl.startsWith(preferredHost)),
isSizeOK = acceptableCoverSize == 0 || results[1] <= acceptableCoverSize * 2**10,
isResolutionOK = acceptableCoverResolution == 0
|| (results[0].naturalWidth >= acceptableCoverResolution
&& results[0].naturalHeight >= acceptableCoverResolution),
isTypeOK = !results[2] || preferredTypes.some(format => results[2] == 'image/' + format);
const divisor = () => inListing ? document.createElement('BR') : '/';
function isOutside(target, related) {
if (target instanceof HTMLElement) {
target = target.parentNode;
while (related instanceof HTMLElement) if ((related = related.parentNode) == target) return false;
}
return true;
}
sticker.onmouseenter = img.onmouseenter = evt => { sticker.style.opacity = 1 };
let rehost, convert;
if (isPreferredHost && isSizeOK && isResolutionOK && isTypeOK) {
sticker.style.backgroundColor = 'teal';
sticker.style.opacity = 0;
sticker.onmouseleave = img.onmouseleave =
evt => { if (isOutside(evt.currentTarget, evt.relatedTarget)) sticker.style.opacity = 0 };
if (results[2]) sticker.append(divisor(), span(results[2], 'mime-type', true));
} else {
sticker.style.backgroundColor = '#ae2300';
sticker.style.opacity = 0.75;
sticker.onmouseleave = img.onmouseleave =
evt => { if (isOutside(evt.currentTarget, evt.relatedTarget)) sticker.style.opacity = 0.75 };
if (inListing && id) editOnClick(sticker);
if (!isTypeOK) {
sticker.append(divisor(), convert = span(results[2], 'mime-type', false));
if (id && httpParser.test(imageUrl)) imageHostHelper.then(function(ihh) {
convert.style.cursor = 'pointer';
convert.onclick = function(evt) {
evt.stopPropagation();
if (evt.currentTarget.hasAttribute('disabled')) return false;
convert.setAttribute('disabled', 1);
img.style.opacity = 0.3;
ihh.reduceImageSize(imageUrl, 2160, 90)
.then(output => ihh.rehostImages([output.uri]).then(ihh.singleImageGetter)
.then(rehostedImgUrl => queryAjaxAPI('groupedit', { id: id }, {
image: rehostedImgUrl,
summary: 'Cover downsize', //+ ' (' + formattedSize(results[1]) + ' => ' + formattedSize(output.size) + ')',
}).then(function(response) {
console.log(response);
sticker.remove();
Promise.resolve(img.src = rehostedImgUrl).then(imgsrcHandler);
img.onclick = clickHandler;
}))).catch(function(reason) {
if ('logFail' in ihh) ihh.logFail(reason);
img.style.opacity = 1;
convert.removeAttribute('disabled');
});
};
convert.style.transitionDuration = '0.25s';
convert.onmouseenter = evt => { evt.currentTarget.style.textShadow = '0 0 5px lime' };
convert.onmouseleave = evt => { evt.currentTarget.style.textShadow = null };
convert.title = 'Click to convert it to JPG';
});
}
}
sticker.prepend(span(results[0].naturalWidth + '×' + results[0].naturalHeight, 'resolution', isResolutionOK),
divisor(), rehost = span(formattedSize(results[1]), 'size', isSizeOK));
if (isProxied) {
sticker.prepend(rehost = span('PROXY', 'proxy'), divisor());
imageUrl = new URL(imageUrl);
imageUrl = imageUrl.searchParams.get('i') || imageUrl.searchParams.values().next().value
|| decodeURIComponent(imageUrl.search.slice(1));
} else if (!isPreferredHost)
sticker.prepend(rehost = span(getHostFriendlyName(imageUrl) || 'XTRN',
'external-host', false, 'Image at unpreferred image host'), divisor());
else if (isSizeOK) {
rehost = null;
if (convert instanceof HTMLElement) convert.classList.add('click2go');
}
if (rehost instanceof HTMLElement && id && httpParser.test(imageUrl)) imageHostHelper.then(function(ihh) {
rehost.classList.add('click2go');
rehost.style.cursor = 'pointer';
rehost.onclick = function(evt) {
evt.stopPropagation();
if (evt.currentTarget.hasAttribute('disabled')) return false;
rehost.setAttribute('disabled', 1);
img.style.opacity = 0.3;
getImageMax(imageUrl).then(maxImgUrl => ihh.rehostImageLinks([maxImgUrl], true).then(ihh.singleImageGetter))
.then(rehostedImgUrl => queryAjaxAPI('groupedit', { id: id }, {
image: rehostedImgUrl,
summary: 'Cover rehost',
}).then(function(response) {
console.log(response);
sticker.remove();
Promise.resolve(img.src = rehostedImgUrl).then(imgsrcHandler);
img.onclick = clickHandler;
})).catch(function(reason) {
if ('logFail' in ihh) ihh.logFail(reason);
img.style.opacity = 1;
rehost.removeAttribute('disabled');
});
};
rehost.style.transitionDuration = '0.25s';
rehost.onmouseenter = evt => { evt.currentTarget.style.textShadow = '0 0 5px lime' };
rehost.onmouseleave = evt => { evt.currentTarget.style.textShadow = null };
rehost.title = 'Hosted at: ' + new URL(imageUrl).hostname + ' (click to rehost to preferred image host)';
});
sticker.title = imageUrl;
img.insertAdjacentElement('afterend', sticker);
return false;
}).catch(function(reason) {
sticker.append(span('INVALID'));
sticker.style.left = 0;
sticker.style.bottom = '-11pt';
sticker.style.width = '100%';
sticker.style.backgroundColor = 'red';
if (id) editOnClick(sticker);
sticker.title = reason;
img.insertAdjacentElement('afterend', sticker);
img.hidden = true; //img.remove();
});
}
img.onload = evt => { if (evt.currentTarget.style.opacity < 1) evt.currentTarget.style.opacity = 1 };
if (id) imageHostHelper.then(function(ihh) {
img.classList.add('drop');
img.ondragover = evt => false;
if (img.clientWidth > 100) {
img.ondragenter = evt => { evt.currentTarget.parentNode.parentNode.style.backgroundColor = 'lawngreen' };
img[isFirefox ? 'ondragexit' : 'ondragleave'] =
evt => { evt.currentTarget.parentNode.parentNode.style.backgroundColor = null };
}
img.ondrop = function(evt) {
function dataSendHandler(endPoint) {
const sticker = evt.currentTarget.parentNode.querySelector('div.cover-inspector');
img.style.opacity = 0.3;
if (sticker != null) sticker.disabled = true;
endPoint([items[0]], true, false, true, {
ctrlKey: evt.ctrlKey,
shiftKey: evt.shiftKey,
altKey: evt.altKey,
}).then(results => queryAjaxAPI('groupedit', { id: id }, {
image: ihh.singleImageGetter(results),
summary: 'Cover update',
}).then(function(response) {
console.log(response);
if (sticker != null) sticker.remove();
Promise.resolve(img.src = ihh.singleImageGetter(results)).then(imgsrcHandler);
if (typeof lightbox == 'object') img.onclick = evt => { lightbox.init(evt.currentTarget.src, 220) };
else document.location.reload();
})).catch(function(reason) {
if ('logFail' in ihh) ihh.logFail(reason);
if (sticker != null) sticker.disabled = false;
img.style.opacity = 1;
});
}
evt.stopPropagation();
console.debug(evt.dataTransfer);
var items = evt.dataTransfer.getData('text/uri-list');
if (items) items = items.split(/\r?\n/); else {
items = evt.dataTransfer.getData('text/x-moz-url');
if (items) items = items.split(/\r?\n/).filter((item, ndx) => ndx % 2 == 0);
else if (items = evt.dataTransfer.getData('text/plain'))
items = items.split(/\r?\n/).filter(RegExp.prototype.test.bind(httpParser));
}
if (Array.isArray(items) && items.length > 0 && 'rehostImageLinks' in ihh) {
if (confirm('Update torrent cover from the dropped URL?\n\n' + items[0]))
dataSendHandler(ihh.rehostImageLinks);
} else if (evt.dataTransfer.files.length > 0 && 'uploadImages' in ihh) {
items = Array.from(evt.dataTransfer.files)
.filter(file => file instanceof File && file.type.startsWith('image/'));
if (items.length > 0 && confirm('Update torrent cover from the dropped file?'))
dataSendHandler(ihh.uploadImages);
}
if (img.clientWidth > 100) evt.currentTarget.parentNode.parentNode.style.backgroundColor = null;
return false;
};
});
let imgSrc = img.dataset.gazelleTempSrc || img.src;
if (typeof img.onclick == 'function' && /\b(?:lightbox\.init)\('(.+?)'/.test(img.onclick.toString())) imgSrc = RegExp.$1
else if (imgSrc.startsWith('https://i.imgur.com/')) imgSrc = imgSrc.replace(/\/(\w{7,})m\.(\w+)$/, '/$1.$2');
console.debug('imgSrc:', imgSrc);
imgsrcHandler(imgSrc);
}
for (let img of document.body.querySelectorAll([
'div#covers p > img',
'div.box_image > div > img',
].join(', '))) inspectImage(img, groupId);
function setTableHandlers(table, hdr, marginLeft) {
if (!(table instanceof HTMLElement) || !(hdr instanceof HTMLElement)) return;
function getGroupId(root) {
if (root instanceof HTMLElement) for (let a of root.getElementsByTagName('A'))
if (a.origin == document.location.origin && a.pathname == '/torrents.php') {
const urlParams = new URLSearchParams(a.search);
if (urlParams.has('action')) continue;
const id = parseInt(urlParams.get('id'));
if (id >= 0) return id;
}
return null;
}
const a = document.createElement('A');
a.className = 'brackets';
a.style.marginLeft = marginLeft;
a.textContent = 'Inspect all covers';
a.href = '#';
a.onclick = function(evt) {
evt.currentTarget.style.visibility = 'collapse';
for (let tr of table.querySelectorAll('tbody > tr.group, tbody > tr.torrent')) {
const img = tr.querySelector('div.group_image > img');
if (img != null) inspectImage(img, getGroupId(tr.querySelector('div.group_info')));
}
const currentTarget = evt.currentTarget;
imageHostHelper.then(function(ihh) {
currentTarget.textContent = 'Rehost all';
currentTarget.onclick = function(evt) {
evt.currentTarget.remove();
for (let div of table.querySelectorAll('div.cover-inspector > span.click2go:not([disabled])')) div.click();
return false;
};
setTimeout(a => { a.style.visibility = 'visible' }, 1000, currentTarget);
}, reason => { currentTarget.remove() });
return false;
};
hdr.append(a);
}
switch (document.location.pathname) {
case '/artist.php': {
const table = document.getElementById('discog_table');
if (table != null) setTableHandlers(table, table.querySelector(':scope > div.box'), '2em');
break;
}
case '/torrents.php': {
const table = document.body.querySelector('table.torrent_table');
if (table == null) break;
const a = table.querySelector(':scope > tbody > tr.colhead > td > a');
if (a != null) setTableHandlers(table, a.parentNode, '5em');
break;
}
}
if (groupId) imageHostHelper.then(function(ihh) {
function setCoverFromLink(a) {
console.assert(a instanceof HTMLAnchorElement, 'a instanceof HTMLAnchorElement');
if (!(a instanceof HTMLAnchorElement)) throw 'Invalid invoker';
ihh.rehostImageLinks([a.href], true).then(rehostedImages => queryAjaxAPI('groupedit', { id: groupId }, {
image: ihh.singleImageGetter(rehostedImages),
summary: 'Cover update',
}).then(function(response) {
console.log(response);
document.location.reload();
})).catch(ihh.logFail);
}
const contextId = '522a6889-27d6-4ea6-a878-20dec4362fbd', menu = document.createElement('menu');
menu.type = 'context';
menu.id = contextId;
menu.className = 'cover-inspector';
let menuInvoker;
const setMenuInvoker = evt => { menuInvoker = evt.currentTarget };
function addMenuItem(label, callback) {
if (label) {
const menuItem = document.createElement('MENUITEM');
menuItem.label = label;
if (typeof callback == 'function') menuItem.onclick = callback;
menu.append(menuItem);
}
return menu.children.length;
}
addMenuItem('Set cover image from this link', evt => { setCoverFromLink(menuInvoker) });
document.body.append(menu);
function clickHandler(evt) {
if (!evt.altKey) return true;
evt.preventDefault();
if (confirm('Set torrent group cover from this link?')) setCoverFromLink(evt.currentTarget);
return false;
}
function setAnchorHandlers(a) {
console.assert(a instanceof HTMLAnchorElement, 'a instanceof HTMLAnchorElement');
if (!(a instanceof HTMLAnchorElement)) return false;
a.setAttribute('contextmenu', contextId);
a.oncontextmenu = setMenuInvoker;
if (a.protocol.startsWith('http') && !a.onclick) {
a.onclick = clickHandler;
a.title = 'Alt + click to set torrent image from this URL (or use context menu command)';
}
return true;
}
document.body.querySelectorAll([
'div.torrent_description > div.body a',
'table#torrent_details > tbody > tr.torrentdetails > td > blockquote a',
].join(', ')).forEach(setAnchorHandlers);
});