// ==UserScript== // @name Image Host Helper // @namespace https://greasyfork.org/users/321857-anakunda // @version 1.085 // @description Directly upload local / rehost remote images or galleries to whatever supported image host by dropping/pasting them to target field // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAAsSAAALEgHS3X78AAAHTklEQVR4nO1aCUwUVxj+38zuyuwuRGGByHIpqEXFo1hj1SZiW1MarVqPREgkattUadPUI4iNpGrVxph6VJR4Rm3UlhrsZaBnqtXEo1qqxqJFZGFBTmHZe2fm9c3CrhzLsbtDRtP9wsLO9b/vffPPfzxGhjGG/zNkUhOQGgEBpCYgNQICSE1AagQEkJqA1AgIIDUBqREQQGoCUiMggNQEpEZAADGNFetNk77Qc5mXHuPUR3aItfFISZpt2nWcAmBVNG4exkBpWjj1TbqWPp0cpqwSk4O3EEWAa3WmEbml/ObiRn4B5pEcKATQ9tMJPBnPwCFNSStoSlrwtLwKx9rl0a2f5yTKPosMZsxicPEWfguQ/69xQfZdbr/BgSKARoDo3s9Hrl/kvFYORewpx1t+bbSnHR6Hl0+OUJb6y8db+CXAJ3eM7+Te4/dhhOR9TdwTULsQt1rR1DnXHMVnnjfNSR2quuUPJ2/hswD5940Lcu9xeWTyMtTV170EIsGhzo7iMm6yhUVy88xxGqWu3miRsRgYOQKLRs2w/o3QM3wS4Hq9OTH7Hy5fjMm7IIhQY0MJ8/5kz2sUhoZHNhzmwChkEIUNEQpHw1g1XH9ZQ/+UqqEuRwUzRnFG9VGAjaXcJvLMa3xx+94giFBugTHlQjh0K4uggmxfewwzjlVxa2OCuNJlMez+VfGyw5Fq/wOn1wKQVJfyYwO/ENEi3foucM67q+kO25U2GLX5Hr+noMa+ZM8YPuvVKNUNf8bzWoCTem4pzyOF2He/v2gLnAjuGmHKvOvsL/vHGpdkDlcX+WrPKwHqjBblhSZIEyoaqSE8LmYeDX73FvdliMw4Z36s+oIvdrwSoMyE46ptEN/NRX2E638yvgZS4Torj0Ky7nBHk0PMUxMHK+u8teGVADorH83xWI4o/xXApCwcFYK4RjumG2x+iCBkDwtK2HafXXf0BVjn7fVeCdDKIgVgcSYfzSAoelFF/9XMwaKrZmCJXZ9TKolHBY/winVNlp1JoUytN5f2W4Amk4WpsfIaf91fcHs1IXwyhYF4JeX8bB49CDbctndom7yDQMnIoiF5Dx0rFtu5H9Q01RwdhB5FBDO2vq7tU4CzOuPM03o+/UoLfolUa1rkRwAUHnmKKLBnPAMzwp8MvX5kEIkvPH+k3EH5ml4pwiuvAm/N03FbFBRnilRA1ZTBjksZWvrE3BjVxZ6u61GAK6TDI03Ozt+b8BtOt++hw/MKHIbsUUGwPE7Rabdgc3cyQz0wYfitjuuzofIEQVwhNpG/lB1DcKUVkiqrIamghl32ykPD2U+TZBtSNMqyrtd5FODUQ+OsrFvcsWYWRQkTFyPoY1LYL46Vwxbi7p6gliHnY/HyH2YoNfLgq6ehjl9oQRhE/1wPi2e1OKYdTDalL4hTdUqX3QQ4pzNNX1HCnbFiNMQfd+8IzAFM1sjgAHH93jxcy1BwYhIDr182QaPD98zQEU4TRIgmFmkzS9gClcw0+zWt6prreCcBHraYQ9+/wx4huVW8yZOIr1W23d1QRd8zmjyEhvwJDGRctwBxZVFEECDYMXEoYtVt9vDFYMt0bQjTKuzvJMDeci6rygIjxSpzhYg/hPSzZ8hdHanuv6ILtXKotmFY87cVOHGoOOFstkxo3P4KdsXWZNgt7HMLUNJgjiqsxekgQpHjHpB8lHLAByocaG+53bm9bXQQJKg8i3Gu2gGnKh1OojwRT05uBMeLRqcNxPZXpGboJsAhHbtMb4PhYrmcE8SW3oLRqQqHe9fqBEwE8Hz6zRYOCnRCPUC1qUf5mXV64KSzQKJr0y1AYS2/iMWUwvNVfoznjsbtc+plRvL2Tm8gO01hCAcGdypyC1BtpcaLFfieAbhvg1sAFY11ZoxipeEjHdwCpIbB+fMN8Db2uSJ/dkAeQ/ciq1uAt2LoY5eb+flNLEQOzGLX0wEhNYcpoMa17RZgbqz66nS9oejbGsgcSB+Q9xJnBmiZsTNIWp0Zir53bXYqhD6Mp3cW1bKLSMZWDgQXweYdAw8mEoYxPHlFV/g2iPhljRUPQN57AuHuMzJs+CBetsu1r5MAM4aqbq9JMHy0/T7ehWlxmiAXBFvCRDNvWEhT2P395LYUiUCM1SZPcI5Iqqv1I6jsKZHK+6793ZqhbckhuxvshtBDFXgjpvxYpekBztLWg1Hcfmwgpu/Um7j+e8OoTbljgvM7HvPYDh9MCckdxrRWby/jP251oEhnRSZWUyKOmX7BNfHBClyzMZHKWf1c8PGu5/S4IJIzOjg/LcJ8Pr+CXVlcj+dW29Gw9grqWUgSmFSVVm0QfpAWjgpXxskPjg1TVno6sdclsQkapS5fAzk36037jlfxGd/Vsm9WWXACjyjNwPD2HxTmq+IZKJs9lC5cqpV9PV6j0vd2fr8WRSeGq/QTw2EHaZ92iEPz6UHgHSGpCUiNgABSE5AaAQGkJiA1AgJITUBqBASQmoDUCAggNQGpERBAagJS4z/F0X/2U+WfagAAAABJRU5ErkJggg== // @author Anakunda // @copyright 2020, Anakunda (https://greasyfork.org/cs/users/321857-anakunda) // @license GPL-3.0-or-later // @match https://passthepopcorn.me/* // @match https://redacted.ch/* // @match https://orpheus.network/* // @match https://broadcasthe.net/* // @match https://notwhat.cd/* // @match https://dicmusic.club/* // @match https://*/torrents.php?id=* // @match https://*/artist.php?id=* // @match https://*/artist.php?action=edit&artistid=* // @match https://*/reportsv2.php?action=report&id=* // @match https://*/forums.php?action=new* // @match https://*/forums.php?*action=viewthread* // @match https://*/requests.php?action=view* // @match https://*/collages.php?id=* // @match https://*/collages.php?action=edit&collageid=* // @match https://*/collages.php?action=comments&collageid=* // @match https://*/collages.php?action=new // @match http*://tracker.czech-server.com/* // @connect * // @grant GM_xmlhttpRequest // @grant GM_getValue // @grant GM_setValue // @grant GM_deleteValue // @require https://greasyfork.org/scripts/408084-xhrlib/code/xhrLib.js // @require https://greasyfork.org/scripts/404516-progressbars/code/progressBars.js // @require https://greasyfork.org/scripts/401726-imagehostuploader/code/imageHostUploader.js // @downloadURL none // ==/UserScript== 'use strict'; if (document.getElementById('upload-assistant') != null) return; // don't clash with Upload Assistant const itunesEntityParser = /^(?:https?):\/\/(?:[\w\-]+\.)*apple\.com\/(?:\S+\/)?(album|artist)\/(?:\S+\/)?(\d+)\b/i; const itunesImageMax = [/\/(\d+x\d+)\w*(?=\.\w+$)/, '/100000x100000-999']; const dzrEntityParser = /^(?:https?):\/\/(?:[\w\-]+\.)*deezer\.com\/(?:\S+\/)?(album|artist|track|comment|playlist|radio|user)\/(\d+)\b/i; const dzImageMax = GM_getValue('deezer_get_png_cover', false) ? [/\/(\d+x\d+)(?:\-\d+)*\.\w+$/, '/1400x1400.png'] : [/\/(\d+x\d+)(?:\-\d+)*(?=\.\w+$)/, '/1400x1400-000000-' + (parseInt(GM_getValue('deezer_jpeg_quality')) || 100) + '-0-0'] const discogsKey = 'LWiNvIWBobGMRhfSCAiC'; const discogsSecret = 'HAQUKFmebpCSLyRNwjmSgOMgbnxsVQcp'; const lfmApiKey = '920db0d2f86108f2fbe1917b53d63858'; Array.prototype.flatten = function() { return this.reduce(function(flat, toFlatten) { return flat.concat(Array.isArray(toFlatten) ? toFlatten.flatten() : toFlatten); }, []); }; PTPimg.prototype.setSession = function() { return this.apiKey ? Promise.resolve(this.apiKey) : globalXHR(this.origin).then(response => { var apiKey = response.document.getElementById('api_key'); if (apiKey == null) { let counter = GM_getValue('ptpimg_reminder_read', 0); if (counter < 3) { alert(` PTPimg API key could not be captured. Please login to ${this.origin}/ and redo the action. If you don\'t have PTPimg account or don\'t want to use it, consider to remove PTPimg from 'upload_hosts' and 'rehost_hosts' storage entries. `); GM_setValue('ptpimg_reminder_read', ++counter); } return Promise.reject('API key not configured'); } else if (!(this.apiKey = apiKey.value)) return Promise.reject('assertion failed: empty PTPimg API key'); GM_setValue('ptpimg_api_key', this.apiKey); Promise.resolve(this.apiKey) .then(apiKey => { alert(`Your PTPimg API key [${apiKey}] was successfully configured`) }); return this.apiKey; }); } var cheveretoCustomHosts = GM_getValue('chevereto_custom_hosts'); if (cheveretoCustomHosts !== undefined) try { JSON.parse(cheveretoCustomHosts).forEach(function(siteDef) { if (!siteDef.host_name || !siteDef.alias) { console.warn('Incomplete Chevereto custom site definition:', siteDef); return; } imageHostHandlers[siteDef.alias.replace(nonWordStripper, '').toLowerCase()] = new Chevereto( siteDef.host_name, siteDef.alias, siteDef.types, siteDef.size_limit, { sizeLimitAnonymous: siteDef.size_limit_anonymous, configPrefix: siteDef.config_prefix, apiEndpoint: siteDef.api_endpoint, apiFieldName: siteDef.api_field_name, apiResultKey: siteDef.api_result_key, jsonEndpoint: siteDef.json_endpoint, }); }); } catch (e) { console.warn(e) } else GM_setValue('chevereto_custom_hosts', '[]'); console.log('Image host handlers:', imageHostHandlers); ['upload_hosts', 'rehost_hosts'].forEach(propName => { if (!GM_getValue(propName)) GM_setValue(propName, [ 'PTPimg', 'ImgBB', 'PixHost', 'ImgBox', 'Slowpoke', 'FunkyIMG', 'PostImage', 'Abload', 'VgyMe', 'Jerking', 'Gifyu', 'GeekPic', 'LightShot', 'ImgURL', 'Radikal', 'Z4A', 'PicaBox', 'PimpAndHost', 'CasImages', 'ImageBan', 'UuploadIr', 'Imgur', 'Catbox', 'ImageVenue', 'GetaPic', 'FastPic', 'SVGshare', ].join(', ')) }); [ ['passthepopcorn.me', [ 'PTPimg', 'ImgBB', 'PixHost', 'Slowpoke', 'ImgBox', 'FunkyIMG', 'Jerking', 'Gifyu', 'Abload', 'VgyMe', 'GeekPic', 'LightShot', 'ImgURL', 'Radikal', 'Z4A', 'PicaBox', 'PimpAndHost', 'ImageBan', 'UuploadIr', 'Catbox', 'ImageVenue', 'GetaPic', ]], ['notwhat.cd', ['NWCD']], ].forEach(hostDefaults => { if (!GM_getValue(hostDefaults[0])) GM_setValue(hostDefaults[0], hostDefaults[1].join(', ')) }); var imageHosts = new ImageHostManager(logFail, GM_getValue(document.domain) || GM_getValue('upload_hosts'), GM_getValue(document.domain) || GM_getValue('rehost_hosts')); imageHostUploaderInit(inputDataHandler, textAreaDropHandler, textAreaPasteHandler, imageUrlResolver); // Set single input UI handlers document.querySelectorAll([ 'image', 'picture', 'cover', 'photo', 'avatar', 'poster', 'screen', ].map(pattern => ['id', 'name'].map(attr => 'input[type="text"][' + attr + '*="' + pattern + '"]')).join(',')) .forEach(setInputHandlers); if (document.URL.includes('/torrents.php?id=')) { let a = document.querySelector('span.additional_add_artists > a'); if (a != null) a.addEventListener('click', function() { document.querySelectorAll('input[name="image[]"]').forEach(setInputHandlers); }); } // Set multiple inputs UI handlers for (let textArea of document.getElementsByTagName('textarea')) { if (textArea.className != 'ua-input') setTextAreahandlers(textArea); } // site-specific extensions switch (document.domain) { case 'passthepopcorn.me': // Auto-fill missing/invalid images from IMDB if (/\/artist\.php\?action=edit&artistid=(\d+)\b/i.test(document.URL)) { let artistId = parseInt(RegExp.$1), input = document.querySelector('input[name="image"]'); if (input != null) verifyImageUrl(input.value).catch(function(reason) { if (input.value) input.value = ''; localXHR('/artist.php?id=' + artistId).then(function(dom) { let imdb = dom.querySelector('div#artistinfo > div.panel__body > ul.list > li > a'); if (imdb != null) imageUrlResolver(imdb.href) .then(setCover.bind(input), reason => { logFail('No IMDB photo of this artist') }); }); }); } else if (/\/torrents\.php??action=editgroup&groupid=(\d+)\b/i.test(document.URL)) { let groupId = parseInt(RegExp.$1), input = document.querySelector('input[name="image"]'); if (input != null) verifyImageUrl(input.value).catch(function(reason) { if (input.value) input.value = ''; localXHR('/torrents.php?id=' + groupId).then(function(dom) { let imdb = dom.querySelector('a#imdb-title-link'); if (imdb != null) imageUrlResolver(imdb.href) .then(setCover.bind(input), reason => { logFail('No IMDB poster for this movie') }); }); }); } // HJ Toolkit patch setTimeout(function() { if (document.querySelector('div.HJ-toolkit-badge') != null) { let hjtkTimer = setInterval(function() { document.querySelectorAll([ 'textarea[id^="HJMA"]', 'textarea.form-control[name="screen"]', 'textarea.form-control[name="comp"]', ].join(',')).forEach(setTextAreahandlers); }, 1000); } }, 1000); break; case 'redacted.ch': case 'orpheus.network': case 'notwhat.cd': case 'dicmusic.club': // Auto-fill missing/invalid artist images if (document.URL.includes('/artist.php?action=edit&')) { let input = document.querySelector('input[name="image"]'); if (input != null) verifyImageUrl(input.value).catch(function() { if (input.value) input.value = ''; let artist = document.querySelector('div.header > h2 > a'); if (artist != null) artist = artist.textContent.trim(); else throw 'Artist name not found'; function resultsFilter(results0, nameExtractor) { const tailingBracketStripper = [/\s*\([^\(\)]+\)\s*$/, '']; let results = results0, f; if (results.length > 1) { f = results0.filter(result => nameExtractor(result).replace(...tailingBracketStripper).toASCII().replace(/\s+|/g, '').toLowerCase() == artist.replace(...tailingBracketStripper).toASCII().replace(/\s+/g, '').toLowerCase()); if (f.length > 0) results = f; } if (results.length > 1) { f = results0.filter(result => nameExtractor(result).replace(...tailingBracketStripper).toASCII().toLowerCase() == artist.replace(...tailingBracketStripper).toASCII().toLowerCase()); if (f.length > 0) results = f; } if (results.length > 1) { f = results0.filter(result => nameExtractor(result).replace(...tailingBracketStripper).toLowerCase() == artist.replace(...tailingBracketStripper).toLowerCase()); if (f.length > 0) results = f; } return results; } let lookupWorkers = [ // Qobuz globalXHR('https://www.qobuz.com/shop', { responseType: 'text' }).then(function(response) { const rx = /^\s*(?:(?:window\.)?qobuz\.algolia(\d+))\s*=\s*(\{.*\});/gm; let result = [], m; while ((m = rx.exec(response.responseText)) != null) { let obj = JSON.parse(m[2]); if (obj.api_key && obj.application_id) result[parseInt(m[1]) - 1] = obj; } return result[0] && result[1] ? result : Promise.reject('unexpected page structure'); }).then(algolia => globalXHR('https://' + algolia[1].application_id.toLowerCase() + '-1.algolianet.com/1/indexes/' + algolia[1].index.main_artists + '/query?' + new URLSearchParams({ 'x-algolia-application-id': algolia[1].application_id, 'x-algolia-api-key': algolia[1].api_key, }).toString(), { responseType: 'json' }, { 'params': 'query=' + encodeURIComponent(artist) })).then(function(response) { if (response.response.nbHits <= 0) return Promise.reject('Qobuz: no matches'); let results = resultsFilter(response.response.hits, result => result.name); if (results.length > 1) return Promise.reject('Qobuz: ambiguity'); //console.info('Qobuz returns ambiguous results for "' + artist + '":', results); if (results.length <= 0) return Promise.reject('Qobuz: no matches'); //console.debug('Qobuz search results for "' + artist + '":', results); return urlParser.test(results[0].image) ? results[0].image.replace(/(\/artists\/covers)\/\w+\//i, '$1/large/') : Promise.reject('Qobuz: artist exists but no photo'); }), // Discogs globalXHR('https://api.discogs.com/database/search?' + new URLSearchParams({ query: artist, type: 'artist', sort: 'score,desc', strict: false, }).toString(), { responseType: 'json', headers: { 'Authorization': 'Discogs key="' + discogsKey + '", secret="' + discogsSecret + '"' }, }).then(response => { if (response.response.items <= 0) return Promise.reject('Discogs: no matches'); let results = resultsFilter(response.response.results.filter(result => result.type == 'artist'), result => result.title); if (results.length > 1) console.info('Discogs returns ambiguous results for "' + artist + '":', results); if (results.length <= 0) return Promise.reject('Discogs: no matches'); //console.debug('Discogs search results for "' + artist + '":', results); let artistCovers = results.map(result => { if (result.cover_image.includes('/spacer.gif')) return null; if (/^(?:https?):\/\/(?:img\.discogs\.com)\/.+\/(\S+?\.\w+)\b/i.test(result.cover_image)) return 'https://www.discogs.com/image/' + RegExp.$1; return result.cover_image; }); return urlParser.test(artistCovers[0]) ? artistCovers[0] : Promise.reject('Discogs: artist exists but no photo'); }), // iTunes globalXHR('https://itunes.apple.com/search?' + new URLSearchParams({ term: '"' + artist + '"', media: 'music', entity: 'musicArtist', attribute: 'artistTerm', //country: 'US', }).toString(), { responseType: 'json' }).then(function(response) { if (response.response.resultCount <= 0) return Promise.reject('Apple Music: no matches'); let results = resultsFilter(response.response.results.filter(result => result.wrapperType == 'artist' && result.artistType == 'Artist'), result => result.artistName); if (results.length > 1) console.info('Apple Music returns ambiguous results for "' + artist + '":', results); if (results.length <= 0) return Promise.reject('Apple Music: no matches'); //console.debug('Apple Music search results for "' + artist + '":', results); return imageUrlResolver(results[0].artistLinkUrl); }), // Deezer globalXHR('https://api.deezer.com/search/artist?' + new URLSearchParams({ q: artist, order: 'RANKING', //strict: 'on', }).toString(), { responseType: 'json' }).then(function(response) { if (response.response.total <= 0) return Promise.reject('Deezer: no matches'); let results = resultsFilter(response.response.data.filter(result => result.type == 'artist'), result => result.name); if (results.length > 1) console.info('Deezer returns ambiguous results for "' + artist + '":', results); if (results.length <= 0) return Promise.reject('Deezer: no matches'); //console.debug('Deezer search results for "' + artist + '":', results); return globalXHR(results[0].picture, { method: 'HEAD', }).then(response => verifyImageUrl(response.finalUrl)).catch(function(reason) { console.warn('Deezer API image retrieval failed:', reason); return response.response.data[0].picture_xl || response.response.data[0].picture_big || response.response.data[0].picture_medium || response.response.data[0].picture_small; }).then(function(imageUrl) { if (!urlParser.test(imageUrl) || imageUrl.includes('/images/artist//')) return Promise.reject('Deezer: artist exists but no photo'); return imageUrl.replace(...dzImageMax); }); }), // Last.fm globalXHR('http://ws.audioscrobbler.com/2.0/?' + new URLSearchParams({ method: 'artist.getinfo', artist: artist, format: 'json', api_key: lfmApiKey, }).toString(), { responseType: 'json' }).then(function(response) { if (response.response.error) return Promise.reject(response.response.message); //console.debug('Last.fm search result for "' + artist + '":', response.response.artist); const rx = /\/(\d+)x(\d+)\//; let biggest = response.response.artist.image.map(im => im['#text']).reduce(function(a, b) { let r = [a, b].map(RegExp.prototype.exec.bind(rx)) .map(r => r != null ? parseInt(r[1]) * parseInt(r[2]) : -Infinity); return r[1] > r[0] ? b : a; }); return rx.test(biggest) && !biggest.endsWith('/2a96cbd8b46e442fc41c2b86b821562f.png') ? biggest : Promise.reject('Last.fm: artist exists but no photo'); }), ]; const lookUp = (index = 0) => index < lookupWorkers.length ? lookupWorkers[index].then(setCover.bind(input)).catch(reason => lookUp(index + 1)) : Promise.reject('Image of this artist was not found'); lookUp().catch(logFail); }); } break; case 'tracker.czech-server.com': document.querySelectorAll('input[name="urlobr"]').forEach(setInputHandlers); break; } if (document.location.pathname.startsWith('/reportsv2.php')) { function setReportHandlers(evt) { setTimeout(function() { document.querySelectorAll('input[id*="image"]').forEach(setInputHandlers); document.querySelectorAll('textarea').forEach(setTextAreahandlers); }, 2000); } setReportHandlers(); let reportTypeSelect = document.querySelector('select#type'); if (reportTypeSelect != null) reportTypeSelect.addEventListener('change', setReportHandlers); } var opti_PNG = GM_getValue('optipng', false); function coverPreview(imgUrl, size) { let div = document.getElementById('image-preview'); if (div != null) document.body.removeChild(div); if (!urlParser.test(imgUrl)) return; div = document.createElement('div'); div.id = 'image-preview'; div.style = 'position: absolute; bottom: 20px; right: 20px; border: thin solid silver; ' + 'background-color: #8888; padding: 10px; opacity: 0; transition: opacity 1s ease-in-out;'; const cleanUp = () => { if (div.parentNode == null) return; div.style.opacity = 0; setTimeout(() => { document.body.removeChild(div) }, 1000); }; div.ondblclick = cleanUp; let img = document.createElement('img'); img.style = 'width: 225px;'; img.onload = function(evt) { document.body.append(div); setTimeout(() => { div.style.opacity = 1 }); setTimeout(cleanUp, 12000); if (!img.naturalWidth || !img.naturalHeight) return; // invalid image let info = document.createElement('div'); info.id = 'image-info'; info.style = 'text-align: center; background-color: #29434b; padding: 5px; color: white;' + 'font: 500 10pt "Segoe UI", Verdana, sans-serif;'; div.append(info); const resolution = img.naturalWidth + '×' + img.naturalHeight; (size > 0 ? Promise.resolve(size) : size instanceof Promise ? size : getRemoteFileSize(imgUrl)).then(function(size) { if (!(size >= 0)) throw 'invalid size'; let imageSizeLimit = GM_getValue('image_size_reduce_threshold'), html = resolution + ' ( 0 && size > imageSizeLimit * 2**10) html += ' style="color: red;"'; html += '>' + formattedSize(size) + ')'; info.innerHTML = html; }).catch(reason => { info.textContent = resolution }); }; img.onerror = function(evt) { console.warn('Image source couldnot be loaded:', evt, imgUrl) }; img.src = imgUrl; div.append(img); } function writeInfo() { let input = document.querySelector('input[name="summary"]'); if (input != null && !input.disabled && !input.value) input.value = 'Image update/rehost'; } function setCover(url) { return verifyImageUrl(url).then(imageUrl => { this.value = imageUrl; writeInfo(); let size = getRemoteFileSize(imageUrl); coverPreview(imageUrl, size); return checkImageSize(imageUrl, this, size).then(imageUrl => { this.disabled = true; return imageHosts.rehostImages([imageUrl]).then(singleImageGetter).then(imageUrl => { if (imageUrl == null) throw 'invalid image'; this.value = imageUrl; }); }).catch(reason => { this.value = imageUrl; logFail(reason + ' (not rehosted)'); }).then(() => { this.disabled = false; return imageUrl; }); }); } function inputDataHandler(evt, data) { const rehoster = imageUrl => imageHosts.rehostImages([imageUrl]).then(singleImageGetter).then(function(imageUrl) { if (!urlParser.test(imageUrl)) { console.warn('rehostImages returns invalid image URL:', imageUrl); throw 'invalid image URL'; } evt.target.value = imageUrl; writeInfo(); }); if (!data) return true; if (data.files.length > 0) { if (data.files[0].type && !data.files[0].type.startsWith('image/')) return true; evt.target.disabled = true; if (evt.target.hTimer) { clearTimeout(evt.target.hTimer); delete evt.target.hTimer; } evt.target.style.color = 'white'; evt.target.style.backgroundColor = 'darkred'; let progressBar = { }; function progressHandler(worker, param = null) { if (param && typeof param == 'object') { if (param.readyState > 1 || progressBar.current != undefined && worker !== progressBar.current || Date.now() < progressBar.lastUpdate + 100) return; let pct = Math.floor(Math.min(param.done * 100 / param.total, 100)); if (pct <= progressBar.lastPct) return; evt.target.value = 'Uploading... [' + (progressBar.lastPct = pct) + '%]'; progressBar.lastUpdate = Date.now(); } else if (param == null) { progressBar = { current: worker }; evt.target.value = 'Uploading...'; } } const file = data.files[0]; evt.target.disabled = true; checkImageSize(file, evt.target, progressHandler).catch(function(reason) { logFail('Downsizing of source image not possible (' + reason + '), uploading original size'); return file; }).then(function(result) { const uploader = file => imageHosts.uploadImages([file], progressHandler).then(singleImageGetter).then(function(imageUrl) { evt.target.value = imageUrl; coverPreview(imageUrl, file.size); writeInfo(); }); if (urlParser.test(result)) return rehoster(result).catch(function(reason) { logFail('Downsizing of source image failed (' + reason + '), uploading original size'); return uploader(file); }); if (result instanceof File) return uploader(result); console.warn('invalid checkImageSize(...) result:', result); return Promise.reject('invalid checkImageSize(...) result'); }).then(function() { evt.target.style.backgroundColor = '#008000'; evt.target.hTimer = setTimeout(function() { evt.target.style.backgroundColor = null; evt.target.style.color = null; delete evt.target.hTimer; }, 10000); }, function(reason) { imageClear(evt); evt.target.style.backgroundColor = null; evt.target.style.color = null; Promise.resolve(reason).then(msg => { alert(msg) }); }).then(() => { evt.target.disabled = false }); return false; } else if (data.items.length > 0) { let links = data.getData('text/uri-list'); if (links) links = links.split(/\r?\n/); else { links = data.getData('text/x-moz-url'); if (links) links = links.split(/\r?\n/).filter((item, ndx) => ndx % 2 == 0); else if (links = data.getData('text/plain')) links = links.split(/\r?\n/); } if (Array.isArray(links) && links.length > 0) imageUrlResolver(links[0]).then(verifyImageUrl).then(function(imageUrl) { evt.target.disabled = true; evt.target.value = imageUrl; let size = getRemoteFileSize(imageUrl); coverPreview(imageUrl, size); checkImageSize(imageUrl, evt.target, size).then(rehoster).catch(function(reason) { evt.target.value = imageUrl; Promise.resolve(reason).then(msg => { alert(msg + ' (not rehosted)') }); }).then(() => { evt.target.disabled = false }); }).catch(function(e) { console.error(e); alert(e); }); return false; } return true; } function rehoster(promises, resultsHandler, target = null) { if (!Array.isArray(promises)) throw 'invalid parameter'; return Promise.all(promises).then(function(resolved) { let resolvedUrls = resolved.flatten(); if (target instanceof HTMLElement) { target.disabled = true; if (resolvedUrls.length > 1 && !['notwhat.cd'].some(hostname => document.domain == hostname)) var progressBar = new RHProgressBar(target, resolvedUrls.length); } return (opti_PNG && target instanceof HTMLElement ? Promise.all(resolvedUrls.map(resolvedUrl => optiPNG(resolvedUrl).catch(reason => Promise.resolve(resolvedUrl)))) : Promise.resolve(resolvedUrls)).then(srcUrls => imageHosts.rehostImages(srcUrls, RHProgressBar.prototype.update.bind(progressBar)).catch(function(reason) { Promise.resolve(reason).then(msg => { alert(msg + ' (not rehosted)') }); RHProgressBar.prototype.update.call(progressBar, -1, false); return verifyImageUrls(srcUrls); }).then(function(results) { resultsHandler(results, arrayGrouping(resolved).flatten()); RHProgressBar.prototype.cleanUp.call(progressBar); if (target instanceof HTMLElement) target.disabled = false; })); }); } function textAreaDropHandler(evt) { if (!evt.dataTransfer || evt.shiftKey) return true; if (evt.dataTransfer.files.length > 0) { let images = Array.from(evt.dataTransfer.files).filter(file => !file.type || file.type.startsWith('image/')); if (images.length <= 0) return true; evt.target.disabled = true; if (!['notwhat.cd'].some(hostname => document.domain == hostname)) var progressBar = new ULProgressBar(evt.target, images.map(image => image.size)); (function() { if (!opti_PNG || !images.every(image => image.type == 'image/png')) return Promise.reject('!optiPNG'); ULProgressBar.prototype.update.call(progressBar, -1); return rehoster([Promise.all(images.map((image, index) => optiPNG(image, (param = null) => ULProgressBar.prototype.update.call(progressBar, -1, param, index))))], resultsHandler); })().catch(reason => imageHosts.uploadImages(images, ULProgressBar.prototype.update.bind(progressBar)) .then(resultsHandler, reason => { Promise.resolve(reason).then(msg => { alert(msg) }) })).then(function() { ULProgressBar.prototype.cleanUp.call(progressBar); evt.target.disabled = false; }); evt.stopPropagation(); return false; } else if (evt.dataTransfer.items.length > 0) { let content = evt.dataTransfer.getData('text/uri-list'); if (content) content = content.split(/(?:\r?\n)+/); else { content = evt.dataTransfer.getData('text/x-moz-url'); if (content) content = content.split(/(?:\r?\n)+/).filter((item, ndx) => ndx % 2 == 0); }; if (!Array.isArray(content) || content.length <= 0) return true; rehoster(content.map(imageUrlResolver), resultsHandler, evt.target); evt.stopPropagation(); return false; } return true; function resultsHandler(results, groups = undefined) { if (results.length <= 0) return; if (evt.altKey && !evt.target.noBBCode) { let modal = document.createElement('div'); modal.id = 'ihh-template-selector-background'; modal.style = 'position: fixed; left: 0; top: 0; width: 100%; height: 100%; background-color: #0008;' + 'opacity: 0; transition: opacity 0.15s linear;'; modal.innerHTML = `
Insert as:
`; document.body.append(modal); let form = document.getElementById('ihh-template-selector'), btnInsert = document.querySelector('div#ihh-template-selector input#btn-insert'), btnCancel = document.querySelector('div#ihh-template-selector input#btn-cancel'); if (form == null || btnInsert == null || btnCancel == null) { console.warn('Dialog creation error'); insertResults(); return; } [ ['BBcode: original size', 1], ['BBcode: thumbnails with link to original', 2], ['BBcode: thumbnails with link to share page', 3], ['BBcode: screenshot comparison (PTP)', 4], ['BBcode: screenshot comparison + encode images (PTP)', 5], ['Markdown: original size', 9], ['HTML: original size', 6], ['HTML: thumbnails with link to original', 7], ['HTML: thumbnails with link to share page', 8], ['Raw links', 0], ].forEach(function(item) { let radio = document.createElement('input'); radio.type = 'radio'; radio.name = 'template'; radio.value = item[1]; radio.style = 'margin: 5px 15px 5px 0px; cursor: pointer;'; let label = document.createElement('label'); label.style = 'color: white; cursor: pointer; -webkit-user-select: none; ' + '-moz-user-select: none; -ms-user-select: none; user-select: none;'; label.append(radio); label.append(item[0]); form.insertBefore(label, btnInsert); let br = document.createElement('br'); form.insertBefore(br, btnInsert); }); if (!results.some(result => typeof result == 'object' && urlParser.test(result.original) && urlParser.test(result.thumb))) disableItem(2, 7); if (!results.some(result => typeof result == 'object' && urlParser.test(result.original) && urlParser.test(result.share))) disableItem(3, 8); if (results.length % 2 != 0) disableItem(4, 5); form.onclick = evt => { evt.stopPropagation() }; btnInsert.onclick = function(evt) { let template = document.querySelector('div#ihh-template-selector input[name="template"]:checked'); if (template != null) template = parseInt(template.value); modal.remove(); insertResults(template); }; modal.onclick = btnCancel.onclick = evt => { modal.remove() }; window.setTimeout(() => { modal.style.opacity = 1 }); function disableItem(...n) { n.forEach(function(n) { let radio = document.querySelector('div#ihh-template-selector input[type="radio"][value="' + n + '"]'); if (radio == null) return; radio.parentNode.style.opacity = 0.5; radio.disabled = true; }); } } else insertResults(); function insertResults(template = 1) { if (evt.target.noBBCode) template = 0; if (typeof template != 'number' || isNaN(template)) return; let code = '', nl = [6, 7, 8].includes(template) ? '
\n' : '\n', _template; results.forEach(function(result, index) { if (_template == 1 && /\[img\]\[\/img\]/i.test(evt.target.value)) { evt.target.value = RegExp.leftContext + '[img]' + getImgUrl(result) + '[/img]' + RegExp.rightContext; return; } _template = template; if (template == 2 && (typeof result != 'object' || !urlParser.test(result.original) || !urlParser.test(result.thumb)) || template == 3 && (typeof result != 'object' || !urlParser.test(result.share) || !urlParser.test(result.thumb))) _template = 1; else if (template == 7 && (typeof result != 'object' || !urlParser.test(result.original) || !urlParser.test(result.thumb)) || template == 8 && (typeof result != 'object' || !urlParser.test(result.share) || !urlParser.test(result.thumb))) _template = 6; else _template = template; if (index > 0) { let thumb = [2, 3, 7, 8].includes(_template); code += isGroupBoundary(groups, index) ? thumb ? nl : nl + nl : thumb ? ' ' : nl; } switch (_template) { case 0: case 4: case 5: code += getImgUrl(result); break; case 1: code += '[img]' + getImgUrl(result) + '[/img]'; break; case 2: code += '[url=' + getImgUrl(result) + '][img]' + result.thumb + '[/img][/url]'; break; case 3: code += '[url=' + result.share + '][img]' + result.thumb + '[/img][/url]'; break; case 6: code += ''; break; case 7: code += ''; break; case 8: code += ''; break; case 9: code += '![](' + getImgUrl(result) + ')'; break; } }); if ([4, 5].includes(template)) { code = '[comparison=Source, Encode]' + code + '[/comparison]'; if (template == 5) { code += nl; results.forEach((result, index) => { if (index % 2 != 0) code += nl + '[img]' + getImgUrl(result) + '[/img]' }); } } if (evt.target.value.trimRight().length <= 0) evt.target.value = code; else if (evt.ctrlKey) { evt.target.value = evt.target.value.slice(0, evt.rangeOffset) + code + evt.target.value.slice(evt.rangeOffset); } else evt.target.value = evt.target.value.trimRight() + nl + nl + code; function getImgUrl(result) { if (typeof result == 'object' && urlParser.test(result.original)) return result.original; if (typeof result == 'string' && urlParser.test(result)) return result; throw 'Invalid result format'; } } } } function textAreaPasteHandler(evt) { if (!evt.clipboardData) return true; if (evt.clipboardData.files.length > 0) { let images = Array.from(evt.clipboardData.files).filter(file => !file.type || file.type.startsWith('image/')); if (images.length <= 0) return true; evt.target.disabled = true; if (!['notwhat.cd'].some(hostname => document.domain == hostname)) var progressBar = new ULProgressBar(evt.target, images.map(image => image.size)); (function() { if (!opti_PNG || !images.every(image => image.type == 'image/png')) return Promise.reject('!optiPNG'); ULProgressBar.prototype.update.call(progressBar, -1); return rehoster([Promise.all(images.map((image, index) => optiPNG(image, (param = null) => ULProgressBar.prototype.update.call(progressBar, -1, param, index))))], resultsHandler); })().catch(reason => imageHosts.uploadImages(images, ULProgressBar.prototype.update.bind(progressBar)) .then(resultsHandler, reason => { Promise.resolve(reason).then(msg => { alert(msg) }) })) .then(function() { // __finally ULProgressBar.prototype.cleanUp.call(progressBar); evt.target.disabled = false; }); evt.stopPropagation(); return false; } else if (evt.clipboardData.items.length > 0) { let urls = evt.clipboardData.getData('text/plain').split(/(?:\r?\n)+/); if (urls.length <= 0 || !urls.every(RegExp.prototype.test.bind(urlParser))) return true; // Promise.all(urls.map(imageUrlResolver)).then(function(resolved) { // evt.target.disabled = true; // var resolvedUrls = resolved.flatten(); // if (resolvedUrls.length > 1 && !['notwhat.cd'].some(hostname => document.domain == hostname)) // progressBar = new RHProgressBar(evt.target, resolvedUrls.length); // imageHosts.rehostImages(resolvedUrls, RHProgressBar.prototype.update.bind(progressBar)).catch(function(reason) { // Promise.resolve(reason).then(msg => { alert(msg + ' (not rehosted)') }); // RHProgressBar.prototype.update.call(progressBar, -1, false); // return verifyImageUrls(resolvedUrls); // }).then(function(results) { // resultsHandler(results, arrayGrouping(resolved).flatten()); // progressBar.cleanUp.call(progressBar); // evt.target.disabled = false; // }); // }); // evt.stopPropagation(); // return false; } return true; function resultsHandler(results, groups = undefined) { let selStart = evt.target.selectionStart, phpBB = ''; results.forEach(function(result, index) { let thumb = evt.altKey && !evt.target.noBBCode && typeof result == 'object' && urlParser.test(result.originasl) && urlParser.test(result.thumb); if (index > 0) phpBB += isGroupBoundary(groups, index) ? thumb ? '\n' : '\n\n' : thumb ? ' ' : '\n'; if (typeof result == 'object' && result.original) var imgUrl = result.original; else if (typeof result == 'string') imgUrl = result; else throw 'Invalid result format'; phpBB += evt.target.noBBCode ? phpBB += imgUrl : !thumb ? '[img]' + imgUrl + '[/img]' : '[url=' + imgUrl + '][img]' + result.thumb + '[/img][/url]'; }); if (phpBB.length <= 0) return; evt.target.value = evt.target.value.slice(0, selStart) + phpBB + evt.target.value.slice(evt.target.selectionEnd); evt.target.setSelectionRange(selStart + phpBB.length, selStart + phpBB.length); } } function arrayGrouping(arr) { return Array.isArray(arr) ? arr.map(function(elem) { if (!Array.isArray(elem)) return 1; return elem.every(elem => !Array.isArray(elem)) ? elem.length : arrayGrouping(elem); }) : null; } function isGroupBoundary(groups, index) { return index > 0 && Array.isArray(groups) && groups.some((len, ndx, arr) => index == arr.slice(0, ndx).reduce((acc, len) => acc + len, 0)); } function checkImageSize(image, elem = null, param) { let imageSizeLimit = GM_getValue('image_size_reduce_threshold'); if (!(imageSizeLimit > 0)) return Promise.resolve(image); if (!(elem instanceof HTMLElement)) elem = null; if (elem != null) elem.disabled = true; return (image instanceof File ? Promise.resolve(image.size) : param > 0 ? Promise.resolve(param) : param instanceof Promise ? param : getRemoteFileSize(image)).then(function(size) { if (size <= imageSizeLimit * 2**10) return image; const haveRhHosts = Array.isArray(imageHosts.rhHostChain) && imageHosts.rhHostChain.length > 0; if (!haveRhHosts && !GM_getValue('force_reduce', true)) return Promise.reject('no hosts to upload result'); return reduceImageSize(image, GM_getValue('image_reduce_maxheight', 2160), GM_getValue('image_reduce_jpegquality', 90), typeof param == 'function' ? param : null).then(function(output) { if (elem != null) { elem.value = output.uri; if (image instanceof File) coverPreview(output.uri, output.size); } Promise.resolve(output.size).then(reducedSize => { console.log('cover size reduced by ' + Math.round((size - reducedSize) * 100 / size) + '% (' + Math.ceil(size / 2**10) + ' → ' + Math.ceil(reducedSize / 2**10) + ' KiB)'); }); return haveRhHosts ? output.uri : (function() { let fallbackHost = new Chevereto('imgcdn.dev', 'ImgCDN', ['jpeg', 'png', 'gif', 'bmp', 'webp'], 30, { sizeLimitAnonymous: 20 }); if (!fallbackHost.apiKey) fallbackHost.apiKey = '5386e05a3562c7a8f984e73401540836'; return output.size > fallbackHost.sizeLimit * 2**20 ? Promise.reject('size limit exceeded') : fallbackHost.rehost([output.uri]).then(singleImageGetter); })().catch(function(reason) { console.warn('Upload to ImgCDN fail:', reason); return imageHostHandlers['pixhost'].rehost([output.uri]).then(singleImageGetter); }); }); }).catch(function(reason) { logFail('failed to get remote image size or optimize the image: ' + reason + ' (size reduction was not performed)'); return image; }).then(function(finalResult) { if (elem != null) { if (urlParser.test(finalResult)) { if (finalResult != elem.value) elem.value = finalResult; } else elem.value = ''; elem.disabled = false; } return finalResult; }); } function imageUrlResolver(url) { return urlResolver(url).then(url => verifyImageUrl(url).catch(function(reason) { const notFound = Promise.reject('No title image for this URL'); function getFromMeta(root) { let meta = root instanceof Document || root instanceof Element ? [ 'meta[property="og:image:secure_url"][content]', 'meta[property="og:image"][content]', 'meta[name="og:image"][content]', 'meta[itemprop="og:image"][content]', 'meta[itemprop="image"][content]', ].reduce((elem, selector) => elem || root.querySelector(selector), null) : null; return meta != null && urlParser.test(meta.content) ? meta.content : undefined; } try { url = new URL(url) } catch(e) { return Promise.reject(e) } if (url.hostname.endsWith('pinterest.com')) return pinterestResolver(url); else if (url.hostname.endsWith('free-picload.com')) { if (url.pathname.startsWith('/album/')) return imageHostHandlers.picload.galleryResolver(url); } else if (url.hostname.endsWith('bandcamp.com')) return globalXHR(url).then(function(response) { let ref = response.document.querySelector('div#tralbumArt > a.popupImage'); ref = ref != null ? ref.href : getFromMeta(response.document); return ref ? Promise.resolve(ref.replace(/_\d+(?=\.\w+$)/, '_0')) : notFound; }); else if (url.hostname.endsWith('7digital.com') && url.pathname.startsWith('/artist/')) return globalXHR(url).then(function(response) { let img = response.document.querySelector('img[itemprop="image"]'); return img != null ? img.src : notFound; }); else if (url.hostname.endsWith('geekpic.net')) return globalXHR(url).then(function(response) { let a = response.document.querySelector('div.img-upload > a.mb'); return a != null ? a.href : notFound; }); else if (url.hostname.endsWith('qq.com') && url.pathname.includes('/album/')) return globalXHR(url).then(function(response) { let img = response.document.querySelector('img#albumImg'); return img != null ? img.src.replace(/(?:_\d+)?(\.\w+)(?:\?.*)?$/, '$1').replace(/R\d+x\d+/, '') : notFound; }); else switch (url.hostname) { // general image hostings case 'www.imgur.com': case 'imgur.com': if (url.pathname.startsWith('/a/')) return globalXHR(url, { responseType: 'text' }).then(function(response) { if (/^\s*(?:image)\s*:\s*(\{.+\}),\s*$/m.test(response.responseText)) try { return JSON.parse(RegExp.$1).album_images.images.map(image => 'https://i.imgur.com/' + image.hash + image.ext); } catch(e) { debug.warn(e) } return notFound; }); return globalXHR(url).then(response => response.document.querySelector('link[rel="image_src"]').href); case 'pixhost.to': if (url.pathname.startsWith('/gallery/')) return globalXHR(url).then(response => Promise.all(Array.from(response.document.querySelectorAll('div.images > a')).map(a => imageUrlResolver(a.href)))); if (url.pathname.startsWith('/show/')) return globalXHR(url) .then(response => response.document.querySelector('img#image').src); break; case 'malzo.com': if (url.pathname.startsWith('/al/')) return imageHostHandlers.malzo.galleryResolver(url); break; case 'imgbb.com': case 'ibb.co': if (url.pathname.startsWith('/album/')) return imageHostHandlers.imgbb.galleryResolver(url); break; case 'jerking.empornium.ph': if (url.pathname.startsWith('/album/')) return imageHostHandlers.jerking.galleryResolver(url); break; case 'imgbox.com': if (url.pathname.startsWith('/g/')) return globalXHR(url).then(response => Promise.all(Array.from(response.document.querySelectorAll('div#gallery-view-content > a')) .map(a => imageUrlResolver('https://imgbox.com' + a.pathname)))); break; case 'postimage.org': case 'postimg.cc': if (!url.pathname.startsWith('/gallery/')) break; return PostImage.resultsHandler(url).then(results => results.map(result => result.original)); case 'www.imagevenue.com': case 'imagevenue.com': return globalXHR(url, { headers: { Referer: 'http://www.imagevenue.com/' } }).then(function(response) { let images = Array.from(response.document.querySelectorAll('div.card img')).map(function(img) { return img.src.includes('://cdn-images') ? Promise.resolve(img.src) : imageUrlResolver(img.parentNode.href); }); return images.length > 1 ? Promise.all(images) : images.length == 1 ? images[0] : notFound; }); case 'www.imageshack.us': case 'imageshack.us': return globalXHR(url).then(response => response.document.querySelector('a#share-dl').href); case 'www.flickr.com': case 'flickr.com': if (!url.pathname.startsWith('/photos/')) break; return globalXHR(url).then(function(response) { if (/\b(?:modelExport)\s*:\s*(\{.+\}),/.test(response.responseText)) try { let urls = JSON.parse(RegExp.$1).main['photo-models'].map(function(photoModel) { let sizes = Object.keys(photoModel.sizes).sort((a, b) => photoModel.sizes[b].width * photoModel.sizes[b].height - photoModel.sizes[a].width * photoModel.sizes[a].height); return sizes.length > 0 ? 'https:'.concat(photoModel.sizes[sizes[0]].url) : null; }); if (urls.length == 1) return urls[0]; else if (urls.length > 1) return urls; } catch(e) { console.warn(e) } return notFound; }); case 'photos.google.com': return googlePhotosResolver(url); case 'www.500px.com': case 'web.500px.com': case '500px.com': if (/^\/photo\/(\d+)\b/i.test(url.pathname)) return _500pxUrlHandler('photos?ids='.concat(RegExp.$1)); else if (/\/galleries\/([\w\-]+)/i.test(url.pathname)) { let galleryId = RegExp.$1; return globalXHR(url, { rsponseType: 'text' }).then(function(response) { if (!/\b(?:App\.CuratorId)\s*=\s*"(\d+)"/.test(response.responseText)) return Promise.reject('Unexpected page structure'); return _500pxUrlHandler('users/' + RegExp.$1 + '/galleries/' + galleryId + '/items?sort=position&sort_direction=asc&rpp=999'); }); } break; case 'www.pxhere.com': case 'pxhere.com': if (url.pathname.includes('/photo/')) return globalXHR(url).then(response => JSON.parse(response.document.querySelector('div.hub-media-content > script[type="application/ld+json"]').text).contentUrl); else if (url.pathname.includes('/collection/')) return pxhereCollectionResolver(url); break; case 'www.unsplash.com': case 'unsplash.com': if (url.pathname.startsWith('/photos/')) return globalXHR(url.origin + url.pathname + '/download', { method: 'HEAD' }) .then(response => response.finalUrl.replace(/\?.*$/, '')); else if (url.pathname.includes('/collections/')) return unsplashCollectionResolver(url); break; case 'www.pexels.com': case 'pexels.com': if (url.pathname.startsWith('/photo/')) return globalXHR(url) .then(response => response.document.querySelector('meta[property="og:image"][content]').content.replace(/\?.*$/, '')); else if (url.pathname.startsWith('/collections/')) return pexelsCollectionResolver(url); break; case 'www.piwigo.org': case 'piwigo.org': /*if (url.pathname.includes('/picture/')) */return globalXHR(url, { responseType: 'text' }).then(function(response) { if (/^(?:RVAS)\s*=\s*(\{[\S\s]+?\})$/m.test(response.responseText)) try { let derivatives = eval('(' + RegExp.$1 + ')').derivatives.sort((a, b) => b.w * b.h - a.w * a.h); return derivatives.length > 0 ? 'https://piwigo.org/demo/'.concat(derivatives[0].url) : notFound; } catch(e) { console.warn(e) } return Promise.reject('Unexpected page structure'); }); break; case 'www.freeimages.com': case 'freeimages.com': if (url.pathname.startsWith('/photo/')) return globalXHR(url).then(function(response) { let types = Array.from(response.document.querySelectorAll('ul.download-type > li > span.reso')) .sort((a, b) => eval(b.textContent.replace('x', '*')) - eval(a.textContent.replace('x', '*'))); return types.length > 0 ? url.origin.concat(types[0].parentNode.querySelector('a').pathname) : notFound; }); break; case 'redacted.ch': if (url.pathname != '/image.php') break; return globalXHR(url, { method: 'HEAD' }).then(response => response.finalUrl); case 'demo.cloudimg.io': { if (!/\b(https?:\/\/\S+)$/.test(url.pathname.concat(url.search, url.hash))) break; let resolved = RegExp.$1; if (/\b(?:https?):\/\/(?:\w+\.)*discogs\.com\//i.test(resolved)) break; return imageUrlResolver(resolved); } case 'www.pimpandhost.com': case 'pimpandhost.com': if (!url.pathname.startsWith('/image/')) break; return globalXHR(url).then(function(response) { let elem = resopnse.document.querySelector('div.main-image-wrapper'); if (elem != null && elem.dataset.src) return 'https:'.concat(elem.dataset.src); elem = resopnse.document.querySelector('div.img-wrapper > a > img'); return elem != null ? 'https:'.concat(elem.src) : notFound; }); case 'www.screencast.com': case 'screencast.com': return globalXHR(url).then(function(response) { let ref = response.document.querySelectorAll('ul#containerContent > li a.media-link'); if (ref.length <= 0) return getFromMeta(response.document) || notFound; return Promise.all(Array.from(ref).map(a => imageUrlResolver('https://www.screencast.com' + a.href))); }); case 'abload.de': if (!url.pathname.startsWith('/image.php')) break; return globalXHR(url).then(function(response) { let elem = response.document.querySelector('img#image'); if (elem == null) return notFound; let src = new URL(elem.src); return imageHostHandlers.abload.origin + src.pathname + src.search; }); case 'fastpic.ru': if (url.pathname.startsWith('/view/')) return globalXHR(url).then(response => imageUrlResolver(response.document.querySelector('a.img-a').href)); if (url.pathname.startsWith('/fullview/')) return globalXHR(url).then(function(response) { let node = response.document.getElementById('image'); if (node != null) return node.src; return /\bvar\s+loading_img\s*=\s*'(\S+?)';/.test(response.responseText) ? RegExp.$1 : notFound; }); break; case 'www.radikal.ru': case 'radikal.ru': case 'a.radikal.ru': return globalXHR(url).then(response => response.document.querySelector('div.mainBlock img').src); case 'imageban.ru': case 'ibn.im': return globalXHR(url).then(response => response.document.querySelector('a[download]').href); case 'svgshare.com': return globalXHR(url).then(function(response) { let link; response.document.querySelectorAll('ul#shares > li > input[type="text"]') .forEach(input => { if (!link && /^(?:https?:\/\/.+\.svg)$/.test(input.value)) link = input.value; }); return link || notFound; }); case 'slow.pics': if (!url.pathname.startsWith('/c/')) break; return globalXHR(url).then(function(response) { let nodes = response.document.querySelectorAll('img.card-img-top'); if (nodes.length > 1) return Array.from(nodes).map(img => img.src); else if (nodes.length > 0) return nodes[0].src; nodes = response.document.querySelectorAll('a#comparisons + div.dropdown-menu > a.dropdown-item'); if (nodes.length > 0) return Promise.all(Array.from(nodes).map(a => globalXHR(url.origin + a.pathname).then(response => Array.from(response.document.querySelectorAll('div#preload-images > img')).map(img => img.src)))); return notFound; }); case 'www.amazon.com': case 'amazon.com': case 'www.amazon.ae': case 'www.amazon.com.au': case 'www.amazon.com.br': case 'www.amazon.ca': case 'www.amazon.cn': case 'www.amazon.de': case 'www.amazon.es': case 'www.amazon.fr': case 'www.amazon.co.uk': case 'www.amazon.in': case 'www.amazon.it': case 'www.amazon.co.jp': case 'www.amazon.com.mx': case 'www.amazon.nl': case 'www.amazon.sa': case 'www.amazon.se': case 'www.amazon.sg': case 'www.amazon.com.tr': return globalXHR(url).then(function(response) { const rx = /\._\S+?_(?=\.)/, getImgOrigin = colorImage => (colorImage.hiRes || colorImage.large || colorImage.thumb).replace(rx, ''); let obj = /^\s*(?:var\s+obj\s*=\s*jQuery\.parseJSON)\('(\{.+\})'\);/m.exec(response.responseText); if (obj != null) { try { obj = JSON.parse(obj[1]) } catch(e) { try { obj = eval('(' + obj[1] + ')') } catch(e) { obj = { } } } let variants = Object.keys(obj.colorImages); if (variants.length > 0) return variants.map(key => obj.colorImages[key].map(getImgOrigin)); } let colorImages = /^\s*'colorImages':\s*(\{.+\}),?$/m.exec(response.responseText); if (colorImages != null) { try { colorImages = JSON.parse(colorImages[1].replace(/'/g, '"')) } catch(e) { try { colorImages = eval('(' + colorImages[1] + ')') } catch(e) { colorImages = { } } } if (Array.isArray(colorImages.initial) && colorImages.initial.length > 0) return colorImages.initial.map(getImgOrigin); } let img = ['div#ppd-left img', 'img#igImage', 'img#imgBlkFront'] .reduce((acc, sel) => acc || response.document.querySelector(sel), null); if (img == null) return notFound; if (img.dataset.aDynamicImage) try { let imgUrl = Object.keys(JSON.parse(img.dataset.aDynamicImage))[0]; if (urlParser.test(imgUrl)) return imgUrl.replace(rx, ''); } catch(e) { } return urlParser.test(img.src) ? img.src.replace(rx, '') : notFound; }); case 'www.casimages.com': case 'casimages.com': if (!url.pathname.startsWith('/i/')) break; return globalXHR(url).then(function(response) { let elem = response.document.querySelector('div.logo > a'); if (elem != null) return elem.href; elem = response.document.querySelector('div.logo img'); return elem != null ? elem.src : notFound; }); case 'www.getapic.me': case 'getapic.me': return globalXHR(url, { responseType: 'json' }).then(function(response) { if (!response.response.result.success) return Promise.reject(response.response.result.errors); if (Array.isArray(response.response.result.data.images)) return response.response.result.data.images.map(image => image.url); return response.response.result.data.image ? response.response.result.data.image.url : notFound; }); // music-related case 'www.musicbrainz.org': case 'musicbrainz.org': if (!['release', 'release-group'].some(branch => url.pathname.includes('/' + branch + '/'))) break; return globalXHR(url).then(function(response) { let node = response.document.querySelector('p.subheader > span.small > a'); return (node != null ? imageUrlResolver('https://musicbrainz.org' + node.pathname) : Promise.reject('no release group')).catch(function(reason) { return (node = response.document.querySelector('a.artwork-image')) != null ? node.href : (node = response.document.querySelector('div.cover-art > img')) != null ? node.src : notFound; }); }); case 'music.apple.com': if (!itunesEntityParser.test(url)) break; return globalXHR(url).then(function(response) { let meta = getFromMeta(response.document); return meta && !meta.includes('/assets/meta/') ? verifyImageUrl(meta.replace(...itunesImageMax)).catch(reason => meta) : notFound; }); case 'www.deezer.com': case 'deezer.com': if (!dzrEntityParser.test(url)) break; return globalXHR('https://api.deezer.com/' + RegExp.$1 + '/' + RegExp.$2 + '/image', { method: 'HEAD', }).then(response => verifyImageUrl(response.finalUrl.replace(...dzImageMax))).catch(function(reason) { console.warn('Deezer API image retrieval failed:', reason, url); return globalXHR(url).then(function(response) { let meta = getFromMeta(response.document); return meta ? verifyImageUrl(meta.replace(...dzImageMax)).catch(reason => meta) : notFound; }); }); case 'www.qobuz.com': case 'qobuz.com': if (!url.pathname.includes('/album/')) break; return globalXHR(url).then(function(response) { let img = response.document.querySelector('div.album-cover > img'); if (img == null) return notFound; return verifyImageUrl(img.src.replace(/_\d{3}(?=\.\w+$)/, '_org')) .catch(reason => verifyImageUrl(img.src.replace(/_\d{3}(?=\.\w+$)/, '_max'))) .catch(reason => img.src); }); case 'www.discogs.com': case 'discogs.com': return globalXHR(url).then(function(response) { const dcOrigin = 'https://www.discogs.com'; let master = response.document.getElementById('all-versions-link'); return (master == null ? Promise.reject('no master') : globalXHR(dcOrigin + master.pathname).then(response => getFromMeta(response.document) || notFound)) .catch(reason => getFromMeta(response.document) || notFound) .then(imgUrl => /^(?:https?):\/\/(?:img\.discogs\.com)\/.+\/(\S+?\.\w+)\b/i.test(imgUrl) ? dcOrigin + '/image/' + RegExp.$1 : imgUrl); }); case 'www.boomkat.com': case 'boomkat.com': if (!url.pathname.startsWith('/products/')) break; return globalXHR(url).then(function(response) { let img = response.document.querySelector('img[itemprop="image"]'); if (img == null) return notFound; return verifyImageUrl(img.src.replace(/\/large\//i, '/original/')).catch(reason => img.src); }); case 'www.bleep.com': case 'bleep.com': if (!url.pathname.startsWith('/release/')) break; return globalXHR(url).then(function(response) { let meta = getFromMeta(response.document); return meta ? verifyImageUrl(meta.replace(/\/r\/[a-z]\//i, '/r/')).catch(reason => meta) : notFound; }); case 'www.soundcloud.com': case 'soundcloud.com': return globalXHR(url).then(function(response) { let meta = getFromMeta(response.document); return meta ? verifyImageUrl(meta.replace(/\bt\d+x\d+(?=\.\w+$)/, 'original')).catch(reason => meta) : notFound; }); case 'www.prestomusic.com': case 'prestomusic.com': if (!url.pathname.includes('/products/')) break; return globalXHR(url) .then(response => verifyImageUrl(response.document.querySelector('div.c-product-block__aside > a').href.replace(/\?\d+$/))); case 'www.bontonland.cz':case 'bontonland.cz': return globalXHR(url).then(response => response.document.querySelector('a.detailzoom').href); case 'www.prostudiomasters.com': case 'prostudiomasters.com': if (!url.pathname.includes('/album/')) break; return globalXHR(url).then(function(response) { let a = response.document.querySelector('img.album-art'); return verifyImageUrl(a.currentSrc).catch(reason => a.src); }); case 'www.e-onkyo.com': case 'e-onkyo.com': if (!url.pathname.includes('/album/')) break; return globalXHR(url).then(function(response) { let meta = getFromMeta(response.document); return meta ? meta.replace(/\/s\d+\//, '/s0/') : notFound; }) case 'store.acousticsounds.com': return globalXHR(url).then(function(response) { let link = response.document.querySelector('div#detail > link[rel="image_src"]'); return verifyImageUrl(link.href.replace(/\/medium\//i, '/xlarge/')).catch(reason => link.href); }); case 'www.indies.eu': case 'indies.eu': if (!url.pathname.includes('/alba/')) break; return globalXHR(url).then(response => verifyImageUrl)(response.document.querySelector('div.obrazekDetail > img').src); case 'www.beatport.com': case 'classic.beatport.com': case 'pro.beatport.com': case 'beatport.com': if (!url.pathname.startsWith('/release/')) break; return globalXHR(url).then(function(response) { let elem = getFromMeta(response.document); return elem || ((elem = response.document.querySelector('div.artwork')) != null ? 'https:' + elem.dataset.modalArtwork : notFound); }).then(imgUrl => imgUrl.replace(/\/image_size\/\d+x\d+\//i, '/image/')); case 'www.beatsource.com': case 'beatsource.com': if (!url.pathname.startsWith('/release/')) break; return globalXHR(url).then(function(response) { let imgUrl = getFromMeta(response.document); return imgUrl ? imgUrl.replace(/\/image_size\/\d+x\d+\//i, '/') : notFound; }); case 'www.supraphonline.cz': case 'supraphonline.cz': if (!url.pathname.includes('/album/')) break; return globalXHR(url).then(response => verifyImageUrl(response.document.querySelector('meta[itemprop="image"]') .content.replace(/\?.*$/, '')).catch(reason => notFound)); case 'vgmdb.net': if (!url.pathname.includes('/album/')) break; return globalXHR(url).then(function(response) { let div = response.document.querySelector('div#coverart'); return verifyImageUrl(/\b(?:url)\s*\(\"(.*)"\)/i.test(div.style['background-image']) && RegExp.$1).catch(reason => notFound); }); case 'www.ototoy.jp': case 'ototoy.jp': return globalXHR(url).then(function(response) { let img = response.document.querySelector('div#tralbumArt > a.popupImage'); return verifyImageUrl(img.dataset.src).catch(reason => img.src); }); case 'music.yandex.ru': if (!url.pathname.includes('/album/')) break; return globalXHR(url).then(function(response) { let script = response.document.querySelector('script.light-data'); return verifyImageUrl(JSON.parse(script.text).image).catch(reason => notFound); }); case 'www.pias.com': case 'store.pias.com': case 'pias.com': return globalXHR(url).then(function(response) { let node = getFromMeta(response.document); if (node) return verifyImage(node.replace(/\/[sbl]\//i, '/')).catch(reason => node); node = response.document.querySelector('img[itemprop="image"]'); return node != null ? verifyImage(node.src.replace(/\/[sbl]\//i, '/')).catch(reason => node.src) : notFound; }); case 'www.eclassical.com': case 'eclassical.com': return globalXHR(url).then(function(response) { let a = response.document.querySelector('div#articleImage > a'); return a != null ? a.href : notFound; }); case 'www.hdtracks.com': case 'hdtracks.com': if (!/\/album\/(\w+)\b/.test(url)) break; return fetch('https://hdtracks.azurewebsites.net/api/v1/album/' + RegExp.$1).then(response => response.json()) .then(result => result.status.toLowerCase() == 'ok' ? result.cover : Promise.reject(result.status)); case 'www.muziekweb.nl': case 'muziekweb.nl': if (!/\/Link\/(\w+)\b/i.test(url)) break; return globalXHR(url).then(function(response) { let meta = getFromMeta(response.document) return meta ? meta.replace(/\/COVER\/\w+\b/i, '/COVER/SUPERLARGE') : notFound; }); case 'www.deejay.de': case 'deejay.de': return globalXHR(url).then(function(response) { let elem = response.document.querySelector('div#gallery > a') || response.document.querySelector('div.cover a'); if (elem != null) return 'https://www.deejay.de' + elem.pathname; return (elem = getFromMeta(response.document)) ? elem : notFound; }).then(imgUrl => verifyImageUrl(imgUrl.replace(/\/images\/\w+\//i, '/images/xxl/')).catch(() => imgUrl)); case 'music.163.com': if (!/\/album.*\b(?:id)=(\d+)\b/i.test(url.href)) break; return globalXHR('https://music.163.com/api/album/' + RegExp.$1, { responseType: 'json' }) .then(response => response.response.album.picUrl ? response.response.album.picUrl.replace(/\b(?:p[123])(?=\.music\.\d+\.net\b)/i, 'p4') : notFound); case 'www.tidal.com': case 'tidal.com': if (!(/\/album\/(\d+)(?:\/|$)/i.test(url.pathname) && !/\b(?:albumId)=(\d+)\b/i.test(url.search))) break; return globalXHR('https://api.tidal.com/v1/albums/' + RegExp.$1 + '?countrycode=US&token=_DSTon1kC8pABnTw', { responseType: 'json', }).then(response => response.response.cover ? 'https://resources.tidal.com/images/' + response.response.cover.replace(/-/g, '/') + '/1280x1280.jpg' : notFound); case 'www.extrememusic.com': case 'extrememusic.com': if (!url.pathname.startsWith('/albums/')) break; return globalXHR(url).then(function(response) { let meta = getFromMeta(response.document); return meta ? meta.replace(/\/album\/\w+\//i, '/album/600/') : notFound; }); // movie-related case 'www.imdb.com': case 'imdb.com': if (!['title/tt', 'name/nm'].some(cat => url.pathname.startsWith('/' + cat))) break; return globalXHR(url).then(function(response) { const galleryDetector = /\/mediaindex(?:[\/\?].*)?$/i, imgStripper = /\._V\d+_[\w\,]*(?=\.)/; if (!galleryDetector.test(response.finalUrl)) { let node = response.document.head.querySelector(':scope > script[type="application/ld+json"]'); if (node != null) try { let image = JSON.parse(node.text).image; if (typeof image == 'string') return verifyImageUrl(image.replace(imgStripper, '')).catch(reason => notFound); } catch(e) { console.warn(e) } node = response.document.querySelector('meta[property="og:image"][content]'); return node != null && !/\/imdb\w*_logo\./i.test(node.content) ? node.content.replace(imgStripper, '') : notFound; } var titleId = /\/title\/(tt\d+)\//i.test(response.finalUrl) && RegExp.$1; return titleId ? globalXHR(response.finalUrl.replace(galleryDetector, '/mediaviewer'), { responseType: 'text' }).then(function(response) { if (/\b(?:window\.IMDbMediaViewerInitialState)\s*=\s*(\{.*\});/.test(response.responseText)) try { let allImages = eval('(' + RegExp.$1 + ')').mediaviewer.galleries[titleId].allImages; if (allImages.length > 0) return allImages.map(image => image.src.replace(imgStripper, '')); } catch(e) { console.warn(e) } return notFound; }) : Promise.reject('title id not found'); }); case 'www.themoviedb.org': case 'themoviedb.org': if (!['movie', 'person'].some(cat => url.pathname.startsWith('/' + cat + '/'))) break; return globalXHR(url).then(function(response) { let node = response.document.querySelector('meta[property="og:image"][content]'); return verifyImageUrl(node.content.replace(/\/p\/\w+\//i, '/p/original/')).catch(function(reason) { node = response.document.querySelector('div.image_content > img'); return verifyImageUrl(node.dataset.src.replace(/\/p\/\w+\//i, '/p/original/')) .catch(reason => verifyImageUrl(node.src.replace(/\/p\/\w+\//i, '/p/original/'))) .catch(reason => verifyImageUrl(dataset.src)).catch(reason => node.src); }).catch(reason => notFound); }); case 'www.omdb.org': case 'omdb.org': if (!['movie', 'person'].some(cat => url.pathname.startsWith('/' + cat + '/'))) break; return globalXHR(url).then(function(response) { let node = response.document.querySelector('meta[property="og:image"][content]'); return node != null ? verifyImageUrl(node.content) : notFound; }); case 'www.thetvdb.com': case 'thetvdb.com': if (!['movies', 'series', 'people'].some(cat => url.pathname.startsWith('/' + cat + '/'))) break; return globalXHR(url).then(response => verifyImageUrl(response.document.querySelector('img.img-responsive').src)); case 'www.rottentomatoes.com': case 'rottentomatoes.com': if (!['m', 'celebrity', 'tv'].some(cat => url.pathname.startsWith('/' + cat + '/'))) break; return globalXHR(url).then(function(response) { //if (/\b(?:context\.shell)\s*=\s*(\{.+?});/.test(response.responseText)) try { // return JSON.parse(RegExp.$1).header.certifiedMedia.certifiedFreshMovieInTheater4.media.posterImg; //} catch(e) { console.warn(e) } return verifyImageUrl(response.document.querySelector('meta[property="og:image"]').content); }); case 'www.bcdb.com': case 'bcdb.com': if (!['cartoon'].some(cat => url.pathname.startsWith('/' + cat + '/'))) break; return globalXHR(url).then(response => verifyImageUrl(document.location.protocol.concat(response.document.querySelector('meta[property="og:image"]').content))); case 'www.boxofficemojo.com': case 'boxofficemojo.com': if (!['releasegroup'].some(cat => url.pathname.startsWith('/' + cat + '/'))) break; return globalXHR(url).then(response => verifyImageUrl(response.document.querySelector('div.mojo-primary-image img').src)); case 'www.metacritic.com': case 'metacritic.com': return globalXHR(url).then(function(response) { let image = response.document.querySelector('meta[property="og:image"]').content; return verifyImageUrl(image.replace(/-\d+h(?=(?:\.\w+)?$)/, '')).catch(reason => image); }); case 'www.csfd.cz': case 'csfd.cz': if (!['film', 'tvurce'].some(cat => url.pathname.startsWith('/' + cat + '/'))) break; return globalXHR(url).then(function(response) { const gallerySel = 'div.ct-general.photos > div.content > ul > li > div.photo'; if (response.document.querySelectorAll(gallerySel).length > 0) return new Promise(function(resolve, reject) { let urls = [], origin = new URL(response.finalUrl).origin; loadPage(response.finalUrl.replace(/\/strana-\d+(?=$|\/|\?)/, '')); function loadPage(url) { GM_xmlhttpRequest({ method: 'GET', url: url, onload: function(response) { if (response.status < 200 || response.status >= 400) return reject(defaultErrorHandler(response)); let dom = domParser.parseFromString(response.responseText, 'text/html'); Array.prototype.push.apply(urls, Array.from(dom.querySelectorAll(gallerySel)) .map(div => /^(?:url)\s*\("?(.+?)"?\)$/i.test(div.style.backgroundImage) ? 'https:'.concat(RegExp.$1).replace(/\?.*$/, '') : null)); let nextPage = dom.querySelector('div.paginator > a.next[href]'); if (nextPage != null) loadPage(origin.concat(nextPage.pathname, nextPage.search)); else resolve(urls); }, onerror: response => { reject(defaultErrorHandler(response)) }, ontimeout: response => { reject(defaultTimeoutHandler(response)) }, }); } }); let img = ['img.film-poster', 'img.creator-photo', 'div.image > img'] .reduce((acc, selector) => acc || response.document.querySelector(selector), null); return img != null ? verifyImageUrl(img.src.replace(/\?.*$/, '')) : notFound; }); case 'www.fdb.cz': case 'fdb.cz': //if (!url.pathname.startsWith('/film/')) break; return globalXHR(url).then(function(response) { let a = response.document.querySelector('a.boxPlakaty'); if (a == null) return Promise.reject('Invalid page structure'); a.hostname = 'www.fdb.cz'; return globalXHR(a.href).then(function(response) { let imgs = response.document.querySelectorAll('span#popup_plakaty > img'); return imgs.length > 0 ? verifyImageUrl(imgs[0].src) : notFound; }); }); case 'www.caps-a-holic.com': case 'caps-a-holic.com': if (url.pathname != '/c.php') break; return globalXHR(url).then(function(response) { function heightExtractor(n) { let node = response.document.querySelector('div.main > div.c_table > div[style]:nth-of-type(' + n + ')'); if (node != null && /\b(\d{3,})\s?[x×]\s?(\d{3,})\b/.test(node.textContent)) return parseInt(RegExp.$2); console.warn(response.finalUrl, 'failed to get resolution (' + n + ')', node); return null; } const baseUrl = 'https://caps-a-holic.com/c_image.php?a=0&x=0&y=0&l=1'; return Array.from(response.document.querySelectorAll('div.main > div[style] > a > img.thumb')).map(function(img) { let query = new URLSearchParams(new URL(img.parentNode.href).search); return [ `${baseUrl}&s=${parseInt(query.get('s1'))}&max_height=${heightExtractor(2)}`, `${baseUrl}&s=${parseInt(query.get('s2'))}&max_height=${heightExtractor(3)}`, ]; }); }); case 'www.screenshotcomparison.com': case 'screenshotcomparison.com': if (url.pathname.startsWith('/comparison/')) return globalXHR(url).then(function(response) { const origin = new URL(response.finalUrl).origin; return Array.from(response.document.querySelectorAll('div#img_nav li > a')).map(function(a) { return globalXHR(origin.concat(a.pathname), { responseType: 'text' }).then(response => [ /\b(?:images)\[1\]='(\S+?)'/.test(response.responseText) && RegExp.$1, /\b(?:images)\[0\]='(\S+?)'/.test(response.responseText) && RegExp.$1, ].map(src => origin.concat(src))); }); }); break; case 'www.dvdbeaver.com': case 'dvdbeaver.com': if (url.pathname.startsWith('/film')) return globalXHR(url).then(function(response) { const origin = new URL(response.finalUrl).origin; return Array.from(response.document.querySelectorAll('div[align="center"] > table > tbody > tr > td > a[target="_blank"] > img')) .map(img => origin.concat(img.parentNode.pathname)); }); break; } return globalXHR(url, { headers: { 'Referer': url.origin } }).then(function(response) { if (url.pathname.startsWith('/album/') && response.document.querySelector('div#tabbed-content-group > div.content-listing > div.pad-content-listing') != null) return new Chevereto(url.hostname).galleryResolver(url); return getFromMeta(response.document) || notFound; }); })); } function logFail(message) { let log = document.getElementById('ihh-console'); if (log == null) { log = document.createElement('div'); log.id = 'ihh-console'; log.style = 'position: fixed; bottom: 20px; right: 20px; width: 64em; border: solid lightsalmon 4px;' + ' background-color: antiquewhite; padding: 10px; opacity: 1;' + ' transition: opacity 1000ms linear; -webkit-transition: opacity 1000ms linear;'; document.body.append(log); } else if (log.hTimer) { clearTimeout(log.hTimer); log.style.opacity = 1; } let div = document.createElement('div'); div.style = 'font: 600 9pt Verdana, sans-serif; color: red;'; div.textContent = message; log.append(div); log.hTimer = setTimeout(function(node) { node.style.opacity = 0; node.hTimer = setTimeout(function(node) { node.remove() }, 1000, node); }, 30000, log); }