// ==UserScript== // @name Direct Image Uploader // @namespace https://greasyfork.org/users/321857-anakunda // @version 1.04 // @description Directly upload local / rehost remote image(s) to whatever dedicated image host by dropping/pasting them to target field // @author Anakunda // @match https://passthepopcorn.me/* // @match https://broadcasthe.net/* // @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/upload2.php // @match http*://tracker.czech-server.com/edit.php* // @connect * // @grant GM_xmlhttpRequest // @grant GM_getValue // @grant GM_setValue // @grant GM_deleteValue // @require https://greasyfork.org/scripts/401725-xhrlib/code/xhrLib.js // @require https://greasyfork.org/scripts/394414-ua-resource/code/UA-resource.js // @require https://greasyfork.org/scripts/401726-imagehostuploader/code/imageHostUploader.js // @downloadURL none // ==/UserScript== 'use strict'; ['input[id*="image"]', 'input[name*="image"]'] .forEach(selector => { document.querySelectorAll(selector).forEach(setInputHandlers) }); if (document.domain == 'tracker.czech-server.com') document.querySelectorAll('input[name="urlobr"]').forEach(setInputHandlers); Array.from(document.getElementsByTagName('textarea')).forEach(setTextAreahandlers); 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); }); } if (document.URL.includes('/reportsv2.php')) { setReportHandlers(); var reportTypeSelect = document.querySelector('select#type'); if (reportTypeSelect != null) reportTypeSelect.addEventListener('change', setReportHandlers); } function setReportHandlers(evt) { setTimeout(function() { document.querySelectorAll('input[id*="image"]').forEach(setInputHandlers); document.querySelectorAll('textarea').forEach(setTextAreahandlers); }, 2000); } function coverPreview(input, imgUrl, size) { var div, child = document.getElementById('cover-preview'); if (child == null) { return; // if (!(input instanceof HTMLElement) || input.parentNode.previousElementSibling == null) return; // var elem = document.createElement('div'); // elem.style = 'padding-top: 10px; float: right; width: 90%;'; // child = document.createElement('img'); // child.id = 'cover-preview'; // elem.append(child); // var div = document.createElement('div'); // div.id = 'cover-size'; // elem.append(div); // input.parentNode.previousElementSibling.append(document.createElement('br')); // input.parentNode.previousElementSibling.append(elem); } if ((div = div || document.getElementById('cover-size')) == null) return; if (urlParser.test(imgUrl)) { child.onload = function(evt) { this.onload = null; if (!this.naturalWidth || !this.naturalHeight) return; // invalid image (size > 0 ? Promise.resolve(size) : getRemoteFileSize(imgUrl)).then(function(size) { div.textContent = this.naturalWidth + '×' + this.naturalHeight + ' (' + formattedSize(size) + ')'; }.bind(this)).catch(reason => { div.textContent = this.naturalWidth + '×' + this.naturalHeight }); }; child.onerror = function(evt) { this.onerror = null; div.textContent = this.src = ''; console.warn('Image source cannot be updated:', evt, imgUrl); }; child.src = imgUrl; } else div.textContent = child.src = ''; } function imageDataHandler(evt, data) { if (!data) return true; if (data.files.length > 0) { if (!data.files[0].type.toLowerCase().startsWith('image/')) return true; evt.target.disabled = true; if (evt.target.hTimer) { clearTimeout(evt.target.hTimer); delete evt.target.hTimer; } evt.target.style.backgroundColor = '#800000'; let elem = evt.target, file = data.files[0], size = data.files[0].size; uploadImages([file], evt.target).then(function(urls) { elem.value = urls[0]; elem.style.backgroundColor = '#008000'; elem.style.color = 'white'; elem.hTimer = setTimeout(function() { elem.style.backgroundColor = null; elem.style.color = null; delete elem.hTimer; }, 10000); coverPreview(elem, urls[0], size); }).catch(function(error) { elem.style.backgroundColor = null; imageClear(evt); alert(error); }).then(function() { elem.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) verifyImageUrl(links[0], true).then(function(imageUrl) { evt.target.value = imageUrl; coverPreview(evt.target, imageUrl); if (imageUrl.toLowerCase().startsWith(ptpimgOrigin)) return; evt.target.disabled = true; rehostImages([imageUrl]) .then(urls => { if (urls.length > 0 && urls[0] != evt.target.value) evt.target.value = urls[0] }) .catch(e => { alert(e + ' (not rehosted)') }) .then(function() { evt.target.disabled = false }); }).catch(function(e) { console.error(e); alert(e); }); return false; } return true; } function descDropHandler(evt) { if (evt.dataTransfer == null || evt.shiftKey) return true; if (evt.dataTransfer.files.length > 0) { let images = []; Array.from(evt.dataTransfer.files).forEach(function(file) { switch (file.type) { case 'image/png': case 'image/jpeg': case 'image/gif': case 'image/bmp': //case 'image/webp': //case 'image/svg+xml': images.push(file); break; } }); if (images.length > 0) { evt.target.disabled = true; evt.target.style.background = '#FF000040 no-repeat center center url(' + ulImgData +')'; //evt.target.style.background = '#FF000040 no-repeat center center url(https://svgshare.com/i/H16.svg)'; let elem = evt.target; uploadImages(images).then(urlHandler).catch(error => { alert(error) }).then(function() { elem.style.background = null; elem.disabled = false; }); } 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) verifyImageUrls(content, true).then(function(imageUrls) { evt.target.disabled = true; rehostImages(imageUrls) .then(imgUrls => urlHandler(imgUrls, content.length == 1 && [ 'caps-a-holic.com', 'screenshotcomparison.com', ].some(patternt => content[0].includes(patternt)))) .catch(e => { alert(e + ' (not rehosted)') }) .then(function() { evt.target.disabled = false }); }); return false; } return true; function urlHandler(urls, coupled = false) { urls.forEach(function(url, ndx) { if (url.length <= 0 || !urlParser.test(url)) return; var phpBB = '[img]'.concat(url, '[/img]'); if (evt.target.value.trimRight().length <= 0) evt.target.value = phpBB; else if (evt.ctrlKey) { evt.target.value = evt.target.value.slice(0, evt.rangeOffset) + phpBB + evt.target.value.slice(evt.rangeOffset); } else if (/\[img\]\[\/img\]/i.test(evt.target.value)) { evt.target.value = RegExp.leftContext + phpBB + RegExp.rightContext; } else { evt.target.value = evt.target.value.trimRight().concat(ndx <= 0 || coupled && ndx % 2 == 0 ? '\n\n' : '\n', phpBB); } }); } } function imageUrlResolver(url) { return urlResolver(url).then(url => verifyImageUrl(url).catch(function(reason) { try { url = new URL(url) } catch(e) { return Promise.reject(e) } const notFound = Promise.reject('No title image for this URL'); switch (url.hostname) { // general image hostings case 'imgur.com': if (url.pathname.startsWith('/a/')) return globalFetch(url, { responseType: 'text' }).then(function(response) { if (!/^\s*image\s*:\s*(\{.+\}),\s*$/m.test(response.responseText)) return Promise.reject('Invalid page structure'); return JSON.parse(RegExp.$1).album_images.images.map(image => 'https://i.imgur.com/'.concat(image.hash, image.ext)); }); return globalFetch(url).then(response => verifyImageUrl(response.document.querySelector('link[rel="image_src"]').href)); case 'pixhost.to': if (url.pathname.startsWith('/gallery/')) return globalFetch(url).then(response => Promise.all(Array.from(response.document.querySelectorAll('div.images > a')) .map(a => imageUrlResolver(a.href)))); if (url.pathname.startsWith('/show/')) return globalFetch(url) .then(response => verifyImageUrl(response.document.querySelector('img#image').src)); break; case 'malzo.com': if (url.pathname.startsWith('/al/')) return imageHostGalleryResolver('malzo.com', url); break; case 'imgbb.com': case 'ibb.co': if (url.pathname.startsWith('/album/')) return imageHostGalleryResolver('imgbb.com', url); break; case 'jerking.empornium.ph': if (url.pathname.startsWith('/album/')) return imageHostGalleryResolver('jerking.empornium.ph', url); break; case 'imgbox.com': if (url.pathname.startsWith('/g/')) return globalFetch(url).then(response => Promise.all(Array.from(response.document.querySelectorAll('div#gallery-view-content > a')) .map(a => imageUrlResolver('https://imgbox.com'.concat(a.pathname))))); break; case 'postimg.cc': case 'postimage.org': if (url.pathname.startsWith('/gallery/')) return globalFetch(url, { responseType: 'text' }).then(function(response) { if (!/\bvar\s+embed_value=(\{[\S\s]+?\});/.test(response.responseText)) return Promise.reject('Invalid page structure'); var embed_value = JSON.parse(RegExp.$1); return Object.keys(embed_value).map(key => 'https://i.postimg.cc/' .concat(embed_value[key][2], '/', embed_value[key][0], '.', embed_value[key][1])); }); break; case 'www.imagevenue.com': return globalFetch(url, { headers: { Referer: 'http://www.imagevenue.com/' } }).then(function(response) { var images = Array.from(response.document.querySelectorAll('div.card img')).map(function(img) { return img.src.includes('://cdn-images') ? verifyImageUrl(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 globalFetch(url).then(response => verifyImageUrl(response.document.querySelector('a#share-dl').href)); case 'redacted.ch': if (url.pathname != '/image.php') break; return globalFetch(url, { method: 'HEAD' }).then(response => verifyImageUrl(response.finalUrl)); case 'demo.cloudimg.io': if (!/\b(https?:\/\/\S+)$/.test(url.pathname.concat(url.search, url.hash))) break; var resolved = RegExp.$1; if (/\b(?:https?):\/\/(?:\w+\.)*discogs\.com\//i.test(resolved)) break; return imageResolver(resolved); case 'fastpic.ru': if (url.pathname.startsWith('/view/')) return globalFetch(url).then(response => imageUrlResolver(response.document.querySelector('a.img-a').href)); if (url.pathname.startsWith('/fullview/')) return globalFetch(url).then(response => verifyImageUrl(response.document.getElementById('image').src)); break; case 'radikal.ru': case 'a.radikal.ru': return globalFetch(url).then(response => verifyImageUrl(response.document.querySelector('div.mainBlock img').src)); case 'imageban.ru': case 'ibn.im': return globalFetch(url) .then(response => verifyImageUrl(response.document.getElementById('img_main').src /* dataset.original */)); // music-related case 'www.musicbrainz.org': case 'musicbrainz.org': if (!['release', 'release-group'].some(branch => url.pathname.includes('/'.concat(branch, '/')))) break; return globalFetch(url).then(function(response) { var node = response.document.querySelector('a.artwork-image'); if (node != null) return verifyImageUrl(node.href); return (node = response.document.querySelector('div.cover-art > img')) != null ? verifyImageUrl(node.src) : Promise.reject('Cover missing'); }); case 'music.apple.com': if (!/^https?:\/\/(?:\w+\.)*apple\.com\/.*\/(\d+)(?=$|\?)/i.test(url)) break; return globalFetch(url).then(function(response) { var meta = response.document.querySelector('meta[property="og:image"][content]'); if (meta == null || !meta.content) return notFound; return verifyImageUrl(meta.content.replace(/\/\d+x\d+\w*(?=\.\w+$)/, '/100000x100000-999')) .catch(reason => verifyImageUrl(meta.content)); }); case 'www.deezer.com': case 'deezer.com': return globalFetch(url).then(function(response) { var meta = response.document.querySelector('meta[property="og:image"][content]'); if (meta == null || !meta.content) return notFound; return verifyImageUrl(meta.content.replace(/\/\d+x\d+\w*(?=\.\w+$)/, '/1400x1400-000000-100-0-0')) .catch(reason => verifyImageUrl(meta.content)); }); case 'www.qobuz.com': case 'qobuz.com': if (!url.pathname.includes('/album/')) break; return globalFetch(url).then(function(response) { var img = response.document.querySelector('div.album-cover > img'); if (img == null) return notFound; return verifyImageUrl(img.src.replace(/_\d{3}(?=\.\w+$)/, '_max')).catch(reason => verifyImageUrl(img.src)); }); case 'www.prestomusic.com': case 'prestomusic.com': if (!url.pathname.includes('/products/')) break; return globalFetch(url) .then(response => verifyImageUrl(response.document.querySelector('div.c-product-block__aside > a').href.replace(/\?\d+$/))); case 'www.bontonland.cz':case 'bontonland.cz': return globalFetch(url) .then(response => verifyImageUrl(response.document.querySelector('a.detailzoom').href)); case 'www.nativedsd.com':case 'nativedsd.com': if (!url.pathname.includes('/albums/')) break; return globalFetch(url) .then(response => verifyImageUrl(response.document.querySelector('a#album-cover').href)); case 'www.prostudiomasters.com': case 'prostudiomasters.com': if (!url.pathname.includes('/album/')) break; return globalFetch(url).then(function(response) { var a = response.document.querySelector('img.album-art'); return verifyImageUrl(a.currentSrc || a.src); }); case 'www.e-onkyo.com': case 'e-onkyo.com': if (!url.pathname.includes('/album/')) break; return globalFetch(url).then(function(response) { var a = response.document.querySelector('figure > a.colorbox'); return verifyImageUrl(new URL(response.finalUrl).origin.concat(a.pathname)); }) case 'store.acousticsounds.com': return globalFetch(url).then(function(response) { var link = response.document.querySelector('div#detail > link[rel="image_src"]'); return verifyImageUrl(link.href.replace(/\/medium\//i, '/large/')) .catch(reason => verifyImageUrl(link.href)); }); case 'www.indies.eu': case 'indies.eu': if (!url.pathname.includes('/alba/')) break; return globalFetch(url) .then(response => verifyImageUrl(response.document.querySelector('div.obrazekDetail > img').src)); case 'www.beatport.com': case 'beatport.com': if (!url.pathname.includes('/release/')) break; return globalFetch(url) .then(response => verifyImageUrl(response.document.querySelector('div > img.interior-release-chart-artwork').src)); case 'www.supraphonline.cz': case 'supraphonline.cz': if (!url.pathname.includes('/album/')) break; return globalFetch(url) .then(response => verifyImageUrl(response.document.querySelector('meta[itemprop="image"]').content.replace(/\?.*$/, ''))); case 'vgmdb.net': if (!url.pathname.includes('/album/')) break; return globalFetch(url).then(function(response) { var div = response.document.querySelector('div#coverart'); return /\b(?:url)\s*\(\"(.*)"\)/i.test(div.style['background-image']) ? verifyImageUrl(RegExp.$1) : notFound; }); case 'www.ototoy.jp': case 'ototoy.jp': return globalFetch(url).then(function(response) { var img = response.document.querySelector('div#tralbumArt > a.popupImage'); return verifyImageUrl(img.dataset.src || img.src); }); case 'music.yandex.ru': if (!url.pathname.includes('/album/')) break; return globalFetch(url).then(function(response) { var script = response.document.querySelector('script.light-data'); return script != null ? verifyImageUrl(JSON.parse(script.text).image) : notFound; }); // movie-related case 'www.imdb.com': case 'imdb.com': if (!['title/tt', 'name/nm'].some(cat => url.pathname.startsWith('/' + cat))) break; return globalFetch(url).then(function(response) { var script = response.document.head.querySelector(':scope > script[type="application/ld+json"]'); var image = JSON.parse(script.text).image; return verifyImageUrl(image); }).catch(reason => notFound); case 'www.themoviedb.org': case 'themoviedb.org': if (!['movie', 'person'].some(cat => url.pathname.startsWith('/' + cat + '/'))) break; return globalFetch(url).then(function(response) { var 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 || node.src).replace(/\/p\/\w+\//i, '/p/original/')); }).catch(reason => notFound); }); case 'www.omdb.org': case 'omdb.org': if (!['movie', 'person'].some(cat => url.pathname.startsWith('/' + cat + '/'))) break; return globalFetch(url).then(function(response) { var node = response.document.querySelector('meta[property="og:image"][content]'); return verifyImageUrl(node.content).catch(reason => notFound); }); case 'www.thetvdb.com': case 'thetvdb.com': if (!['movies', 'series', 'people'].some(cat => url.pathname.startsWith('/' + cat + '/'))) break; return globalFetch(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 globalFetch(url).then(response => 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 globalFetch(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 globalFetch(url).then(response => verifyImageUrl(response.document.querySelector('div.mojo-primary-image img').src)); case 'www.metacritic.com': case 'metacritic.com': return globalFetch(url).then(response => verifyImageUrl(response.document.querySelector('meta[property="og:image"]').content)); case 'www.csfd.cz': case 'csfd.cz': if (!['film', 'tvurce'].some(cat => url.pathname.startsWith('/' + cat + '/'))) break; return globalFetch(url).then(function(response) { var 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 globalFetch(url).then(function(response) { var a = response.document.querySelector('a.boxPlakaty'); if (a == null) return Promise.reject('Invalid page structure'); a.hostname = 'www.fdb.cz'; return globalFetch(a.href).then(function(response) { var imgs = response.document.querySelectorAll('span#popup_plakaty > img'); return imgs.length > 0 ? imgs[0].src : notFound; }); }); case 'www.caps-a-holic.com': case 'caps-a-holic.com': if (url.pathname == '/c.php') return globalFetch(url).then(function(response) { const resXtractor = /\b(\d{3,})\s?[x×]\s?(\d{3,})\b/; const baseUrl = 'https://caps-a-holic.com/c_image.php?a=0&x=0&y=0&l=1'; var max_height = [ response.document.querySelector('div.c_table > div:nth-of-type(2)'), response.document.querySelector('div.c_table > div:nth-of-type(3)'), ].map(node => node != null && resXtractor.test(node.textContent) ? parseInt(RegExp.$2) : NaN); return Promise.all(Array.from(response.document.querySelectorAll('div > a > img.thumb')).map(function(img) { var query = new URLSearchParams(new URL(img.parentNode.href).search); return [ baseUrl.concat('&s=', parseInt(query.get('s1')), '&max_height=', max_height[0]), baseUrl.concat('&s=', parseInt(query.get('s2')), '&max_height=', max_height[1]), ]; })); }); break; case 'www.screenshotcomparison.com': case 'screenshotcomparison.com': if (url.pathname.startsWith('/comparison/')) return globalFetch(url).then(function(response) { const origin = 'https://screenshotcomparison.com'; return Promise.all(Array.from(response.document.querySelectorAll('div#img_nav li > a')).map(function(a) { return globalFetch(origin.concat(a.pathname), { responseType: 'text' }).then(response => [ /\bimages\[1\]='(\S+?)'/.test(response.responseText) && RegExp.$1, /\bimages\[0\]='(\S+?)'/.test(response.responseText) && RegExp.$1, ].map(src => origin.concat(src))); })); }); break; } return globalFetch(url, { headers: { 'Referer': url.origin } }).then(function(response) { var meta = [ 'head > meta[property="og:image"][content]', 'head > meta[itemprop="image"][content]', 'head > meta[name="og:image"][content]', ].reduce((acc, selector) => acc || response.document.querySelector(selector), null); return meta != null && meta.content ? verifyImageUrl(meta.content) : notFound; }); })); }