您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
Upload Taobao and Yupoo QCs from BaseTao to Imgur
当前为
// ==UserScript== // @name FR:ES - BaseTao // @namespace https://www.reddit.com/user/RobotOilInc // @author RobotOilInc // @version 1.0.0 // @description Upload Taobao and Yupoo QCs from BaseTao to Imgur // @match https://www.basetao.net/index/myhome/myorder/* // @match https://basetao.net/index/myhome/myorder/* // @match https://www.basetao.com/index/myhome/myorder/* // @match https://basetao.com/index/myhome/myorder/* // @include https://www.basetao.net/index/orderphoto/itemimg/* // @include https://basetao.net/index/orderphoto/itemimg/* // @include https://www.basetao.com/index/orderphoto/itemimg/* // @include https://basetao.com/index/orderphoto/itemimg/* // @connect self // @connect imgur.com // @connect fashionreps.tools // @connect 127.0.0.1 // @connect localhost // @require https://code.jquery.com/jquery-3.6.0.min.js // @require http://greasyfork.icu/scripts/426288-webptojpg/code/WebpToJpg.js // @require https://cdnjs.cloudflare.com/ajax/libs/spark-md5/3.0.0/spark-md5.min.js // @require https://unpkg.com/[email protected]/dist/swagger-client.browser.js // @grant GM_openInTab // @grant GM_notification // @grant GM_xmlhttpRequest // @run-at document-end // @icon https://i.imgur.com/1aQAxbC.png // ==/UserScript== /* jshint esversion: 8 */ /* globals $:false, SparkMD5:false, WebpToJpg: false, SwaggerClient: false, GM_openInTab: false, GM_notification: false */ const _version = '1.0.0'; const _url = 'https://localhost:8000/'; const _swagger_doc_url = `${_url}api/doc.json`; const toDataURL = (url) => fetch(url) .then((response) => response.blob()) .then((blob) => new Promise((resolve, reject) => { const reader = new FileReader(); reader.onloadend = () => resolve(reader.result); reader.onerror = reject; reader.readAsDataURL(blob); })); class Element { constructor($element) { const $baseElement = $element.parents('tr').find('td[colspan=\'2\']').first(); this.object = $element; this.url = $baseElement.find('.goodsname_color').first().attr('href').replace('http://', 'https://'); this.title = $baseElement.find('.goodsname_color').first().text(); this.size = $baseElement.find('.size_color_color:nth-child(2) > u').text(); this.orderId = $element.parents('tr').prev().find('td.tdpd > span:nth-child(2)').text(); const color = $baseElement.find('.size_color_color:nth-child(1) > u').text(); if (color !== '-' && color !== 'NO') { this.color = color; } } /** * @returns {{color, size}} */ get sizingInfo() { return { color: this.color, size: this.size }; } /** * @param imageUrls {string[]} */ set images(imageUrls) { this.imageUrls = imageUrls; } /** * @returns {boolean} */ get isSupported() { return this.url.indexOf('item.taobao.com') !== -1 || this.url.indexOf('yupoo.com') !== -1; } /** * @returns {string} */ get website() { if (this.url.indexOf('item.taobao.com') !== -1) { return 'taobao'; } if (this.url.indexOf('yupoo.com') !== -1) { return 'yupoo'; } return 'unknown'; } } class Client { /** * @param client {SwaggerClient} * @param userHash {string} */ constructor(client, userHash) { this.client = client; this.userHash = userHash; } /** * @param element * @returns {Promise<null|string>} */ existingAlbumByOrderId(element) { return this.client.apis.QualityControl.hasUploadedByOrderId({ usernameHash: this.userHash, orderId: element.orderId, }).then((response) => { if (typeof response.body === 'undefined') { return null; } if (!response.body.success) { return null; } return response.body.albumId; }).catch((err) => { console.error('Could not check if the album exists an album', err); return '-1'; }); } /** * @param url {string} * @returns {Promise<boolean>} */ exists(url) { return this.client.apis.QualityControl.exists({ url }).then((response) => { if (typeof response.body === 'undefined') { return null; } if (!response.body.success) { return null; } return response.body.exists; }).catch((err) => { console.error('Could not check if the album exists an album', err); return false; }); } /** * @param element {Element} * @param album {string} */ uploadQc(element, album) { const postQualityControlCollection = this.client.apis.QualityControl.postQualityControlCollection({}, { method: 'post', requestContentType: 'application/json', requestBody: { usernameHash: this.userHash, albumId: album, color: element.color, orderId: element.orderId, purchaseUrl: element.url, sizing: element.size, source: `BaseTao to Imgur ${_version}`, website: element.website, }, }); console.log(postQualityControlCollection); return postQualityControlCollection; } } class Imgur { constructor(clientId) { this.clientId = clientId; } async CreateAlbum(options) { let result; try { result = await $.ajax({ url: 'https://api.imgur.com/3/album', headers: { Authorization: `Client-ID ${this.clientId}` }, type: 'POST', data: { title: options.title, privacy: 'hidden', description: `Auto uploaded using BaseTao to Imgur ${_version}: http://greasyfork.icu/scripts/387421-fr-es-basetao-extension`, }, }); } catch (error) { // Log the error somewhere console.error(`Could not make an album: ${error.statusText}`, error); // If we uploaded too fast, tell the user if (error.responseJSON.data.error.code === 429) { GM_notification(`Imgur is telling us to slow down: ${error.responseJSON.data.error.message}`, 'FR:ES - BaseTao'); return result; } // Tell the user that "something" is wrong GM_notification('Could not make an album, please try again later...', 'FR:ES - BaseTao'); } return result; } async AddImageToAlbum(base64Image, deleteHash, purchaseUrl) { try { await $.ajax({ url: 'https://api.imgur.com/3/image', headers: { Authorization: `Client-ID ${this.clientId}` }, type: 'POST', data: { album: deleteHash, type: 'base64', image: base64Image, description: `W2C: ${purchaseUrl}`, }, }); return true; } catch (error) { // If we uploaded too many files, tell the user if (error.responseJSON.data.error.code === 429) { GM_notification(`Imgur is telling us to slow down: ${error.responseJSON.data.error.message}`, 'FR:ES - BaseTao'); } // Log errors somewhere console.error(`An error happened when uploading the image: ${error.responseJSON.data.error.message}`, error); return false; } } RemoveAlbum(deleteHash) { $.ajax({ url: `https://api.imgur.com/3/album/${deleteHash}`, headers: { Authorization: `Client-ID ${this.clientId}` }, type: 'DELETE', }); } } // eslint-disable-next-line func-names (async function () { /** @type {SwaggerClient} */ let client; // Try to create Swagger client from our own documentation try { client = await new SwaggerClient({ url: _swagger_doc_url }); } catch (error) { GM_notification('We are unable to connect to FR:ES. Please try again later.', 'FR:ES - BaseTao'); console.error(`An error happened: ${error.statusText}`, error); return; } const username = $('#dropdownMenu1').text(); if (typeof username === 'undefined' || username == null || username === '') { GM_notification('You need to be logged in to use this extension.', 'FR:ES - BaseTao'); return; } const ClientIds = ['97a5f748b20b0ad', '4d01890bba4949c', 'fe9f4d770f42e53', '0999af252aaaba1', '6870f32f716ff3b', 'bf02cd90c8a4f1a', '5ed540f56122e1c']; const baseTao = new Client(client, SparkMD5.hash(username)); const imgur = new Imgur(ClientIds[Math.floor(Math.random() * ClientIds.length)]); /** * @param client {Client} * @param element {Element} * @returns {Promise<void>} * @constructor */ // eslint-disable-next-line no-shadow const UploadToImgur = async (client, element) => { const $processing = $('<ul><li><span style="cursor: progress;margin-left:-29px;"><img src="https://i.imgur.com/lnFQTQz.gif" alt="Processing..."></span></li></ul>'); const $base = element.object; $base.after($processing).hide(); GM_notification('Pictures are being uploaded....', 'FR:ES - BaseTao'); // Create the album const response = await imgur.CreateAlbum(element); const deleteHash = response.data.deletehash; const albumId = response.data.id; const AlbumLink = `https://imgur.com/a/${albumId}`; // Upload all QC images let uploadedImages = 0; const promises = []; $.each(element.imageUrls, (key, imageUrl) => { // Convert to base64, since Imgur cannot access our images promises.push(toDataURL(imageUrl).then(async (data) => { // Store our base64 and if the file is WEBP, convert it to JPG let base64Image = data; if (base64Image.indexOf('image/webp') !== -1) { base64Image = await WebpToJpg(base64Image); } // Remove the unnecessary `data:` part const cleanedData = base64Image.replace(/(data:image\/.*;base64,)/, ''); // Upload the image to the album return imgur.AddImageToAlbum(cleanedData, deleteHash, element.url); }).then((uploaded) => { if (uploaded === false) { return; } uploadedImages++; })); }); // Wait until everything has been tried to be uploaded await Promise.all(promises); // If not all images have been uploaded, abort everything if (uploadedImages !== element.imageUrls.length) { GM_notification('Error: Image upload failed. Try again later.', 'FR:ES - BaseTao'); imgur.RemoveAlbum(deleteHash); $processing.remove(); $base.show(); return; } // Tell the user it was uploaded and open the album in the background GM_notification('Pictures have been uploaded!', 'FR:ES - BaseTao'); GM_openInTab(AlbumLink, true); // Tell QC Suite about our uploaded QC's (if it's from TaoBao) if (element.isSupported) { client.uploadQc(element, albumId); } // Wrap the logo in a href to the new album const $image = $base.find('img'); $image.wrap(`<a href='${AlbumLink}' target='_blank' title='Go to album'></a>`); $image.removeAttr('title'); // Remove processing $processing.remove(); // Update the marker const $qcMarker = $base.find('.qc-marker').first(); $qcMarker.attr('title', 'You have uploaded your QC') .css('cursor', 'help') .css('color', 'green') .text('✓'); // Remove all other QC markers $base.find('.qc-marker:not(:first-child)').remove(); // Remove the click handler $base.off(); // Show it again $base.show(); }; const uploadHandler = async function () { const $this = $(this); const itemUrl = $this.data('item-url'); const element = new Element($this); // Go to the QC pictures URL and grab all image src's $.get(itemUrl, (data) => { const imageUrls = []; $('<div/>').html(data).find('div.container.container-top60 > img').each(function () { imageUrls.push($(this).attr('src')); }); element.images = imageUrls; UploadToImgur(baseTao, element); }); }; $('.myparcels-ul').find('span.glyphicon.glyphicon-picture').each(async function () { const $this = $(this); const element = new Element($this); // This plugin only works for TaoBao, anything else will be ignored, so only allow a basic upload if (!element.isSupported) { const $upload = $('<ul><li><span style="cursor: pointer;margin-left:-29px;"><img src="https://s.imgur.com/images/favicon-16x16.png" alt="Create a basic album"></span></li></ul>'); $upload.find('span').first().after($('<span class="qc-marker" style="cursor:help;margin-left:5px;color:red;font-weight: bold;" title="Not a supported URL, but you can still create an album. The QC\'s are not stored and you\'ll have to create a new album if you lose the link.">✖</span>')); $upload.data('item-url', $(this).parent().attr('href')); $upload.on('click', uploadHandler); $this.parents('td').first().append($upload); return; } const $loading = $('<ul><li><span style="cursor: pointer;margin-left:-29px;"><img src="https://i.imgur.com/lnFQTQz.gif" alt="Loading..."></span></li></ul>'); $this.parents('td').first().append($loading); // Define upload object const $upload = $('<ul><li><span class="qc-marker" style="cursor: pointer;margin-left:-29px;"><img src="https://s.imgur.com/images/favicon-16x16.png" alt="Upload your QC"></span></li></ul>'); $upload.data('item-url', $(this).parent().attr('href')); // If we couldn't talk to FR:ES, assume everything is dead and use the basic uploader. const albumId = await baseTao.existingAlbumByOrderId(element); if (albumId === '-1') { $upload.find('span').first().after($('<span class="qc-marker" style="cursor:help;margin-left:5px;color:red;font-weight: bold;" title="FR:ES returned an error, but you can still create an album. The QC\'s are not stored and you\'ll have to create a new album if you lose the link.">⚠️</span>')); $upload.data('item-url', $(this).parent().attr('href')); $upload.on('click', uploadHandler); $this.parents('td').first().append($upload); $loading.remove(); return; } // Has anyone ever uploaded a QC, if not, show a red marker const exists = await baseTao.exists(element.url); if (!exists) { $upload.find('span').first().after($('<span class="qc-marker" style="cursor:help;margin-left:5px;color:red;font-weight: bold;" title="No QC in database, please upload.">(!)</span>')); $upload.on('click', uploadHandler); $this.parents('td').first().append($upload); $loading.remove(); return; } // Have you ever uploaded a QC? If so, link to that album const $image = $upload.find('img'); if (albumId !== null && albumId !== '-1') { $upload.find('span').first().after($('<span class="qc-marker" style="cursor:help;margin-left:5px;color:green;font-weight: bold;" title="You have uploaded a QC">✓</span>')); $image.wrap(`<a href='https://imgur.com/a/${albumId}' target='_blank' title='Go to album'></a>`); $image.removeAttr('title'); $this.parents('td').first().append($upload); $loading.remove(); return; } // A previous QC exists, but you haven't uploaded yours yet, show orange marker $upload.find('span').first().after($('<span class="qc-marker" style="cursor:help;margin-left:5px;color:orange;font-weight: bold;" title="Your QC is not yet in the database, please upload.">(!)</span>')); $upload.on('click', uploadHandler); $this.parents('td').first().append($upload); $loading.remove(); }); }());