您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Facilitates uploading requests by adding an upload link to request page sidebar and populating the upload form with info available on the page.
当前为
// ==UserScript== // @name [bib] RequestUpload // @namespace varb // @version 0.6 // @description Facilitates uploading requests by adding an upload link to request page sidebar and populating the upload form with info available on the page. // @include /^https?://bibliotik.org/(request|torrent)s/\d+/ // @include /^https?://bibliotik.org/upload/\w+/ // @require https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @grant GM_addStyle // @grant GM_xmlhttpRequest // @license WTFPL Version 2; http://www.wtfpl.net/txt/copying/ // ==/UserScript== var FORMATS = {'MP3':1,'PDF':2,'CBR':3,'DJVU':4,'CBZ':5,'CHM':6,'FLAC':10,'SPX':13,'TXT':14,'EPUB':15,'MOBI':16,'M4A':17,'M4B':18,'AZW3':21}; var LANGS = {'English':1,'German':2,'French':3,'Spanish':4,'Italian':5,'Latin':6,'Japanese':7,'Swedish':8,'Norwegian':9,'Dutch':12,'Russian':13,'Portuguese':14,'Danish':15,'Korean':16,'Chinese':17,'Polish':18,'Arabic':19,'Irish':20,'Greek':21,'Turkish':22,'Hungarian':23,'Thai':24,'Indonesian':25,'Bulgarian':26}; $(document).ready(function () { console.log('requp: initialized'); var page = location.pathname.split('/')[1]; switch (page) { case 'requests': onReqPage(); break; case 'upload': onUploadPage(); break; case 'torrents': onTorrentPage(); break; default: console.log('requp: unhandled page ' + page); } }); function onReqPage() { var category, lang, overdrive = false, req = {}; var reqid = location.pathname.split('/').slice(-1); var reqdetails = $('#requestDetails').text(); if ($('#filled').length) return; var _match = reqdetails.match(/(?:(\w+),\s)?(\w+)\scategory/); if (!_match) { console.log('requp: cannot establish category'); return; } lang = _match[1]; category = _match[2]; // add upload link to req page sidebar var uppageurl = location.origin + '/upload/' + category.toLowerCase() + '?reqid=' + reqid; $('<a/>', {id: 'upreq', href: uppageurl, text: 'Upload Request'}) .appendTo('#sidebar ul:first').wrap('<li></li>').after('<span></span>') .on('click', function (e) { var $self = $(this); if (!overdrive) { GM_setValue(reqid, JSON.stringify(req)); return; } e.preventDefault(); $self.next().addClass('loading'); fetchODListing() .then(parseODListing) .then(updateISBN) .done(function (oddata) { GM_setValue(reqid, JSON.stringify($.extend(req, oddata))); location.href = $self.attr('href'); }) .fail(function (reason) { console.error('requp: ' + reason); if (confirm('Failed to acquire OverDrive listing.\nProceed to upload page?')) location.href = $self.attr('href'); $self.next().removeClass('loading'); }); }); // save fill request form req.fillform = $('#TorrentIdField').parents('form').find(':hidden').serializeArray(); // collect reusable info var $description = $('#description'); req.title = $('h1').text().match(/\/\s*(.+?)(?:\s\[Retail\]$|$)/)[1]; req.publishers = $('#published a').map(function () { return this.text }).toArray(); req.tags = $('#details_tags a').map(function () { if (this.text.indexOf('overdrive') === -1) return this.text; overdrive = true; }).toArray(); req.overdrive = overdrive; req.retail = reqdetails.indexOf('Retail') !== -1; req.lang = lang; req.format = $('#requestDetails strong').text().split('/'); var $authors = $('#creatorlist'); req.authors = $authors.find('a').map(function () { return this.text }).toArray(); if (category === 'Comics') { req.artists = $authors.next('#creatorlist').find('a') .map(function () { return this.text }).toArray(); } var _isbnmatch = $description.text().match(/Overdrive Listing \((\d+)\)/); req.isbn = _isbnmatch ? _isbnmatch[1] : ''; function fetchODListing() { var dfr = new $.Deferred(); var odurl = $('a:contains(Overdrive Listing)').attr('href'); if (!odurl) return dfr.reject('could not find od listing url').promise(); GM_xmlhttpRequest({ method: 'GET', url: odurl, onload: function (res) { dfr.resolve(res.responseText) }, onerror: function (res) { dfr.reject('failed fetching od listing: ' + res.status + ' ' + res.statusText); } }); return dfr.promise(); } function parseODListing(page) { var dfr = new $.Deferred(), $page = $(page), oddata = {}, ed; try { var subt = $('h2.subtitle', $page).contents()[0]; subt = subt && subt.textContent.trim() ? ': ' + subt.textContent.trim() : ''; oddata.title = $('h1.title', $page).text().trim() + subt; // because innerHTML doesn't behave identically across implementations oddata._odid = page.match(/meta.+?od:id.+?content=\"([-\w]+)\"/)[1]; oddata._formats = $('.meta-accordion span:contains(Format)', $page).next().text(); oddata.pubdate = $('dt:contains(Publication Date)', $page).next().text(); oddata.desc = $('.description', $page).contents().slice(0, -4).toArray() .reduce(function (p, c) { return p + stripNode(c) }, '').trim(); var $auth = $('.meta-accordion span:contains(Creators)', $page).next().find('dd'); if ($auth.length) oddata.authors = $auth.map(function () { return this.textContent }).toArray(); if (ed = $('dt:contains(Edition)', $page).next().text()) { var suffix = [null, 'st', 'nd', 'rd']; oddata.title += ' (' + ed + (ed.match(/([^1]|\b)[123]$/) ? suffix[ed.substr(-1)] : 'th') + ' Edition)'; } } catch (e) { return dfr.reject('failed parsing od listing: ' + e.message).promise(); } return dfr.resolve(oddata).promise(); function stripNode(n) { if (n.nodeType === 3) return n.textContent; if (n.nodeType === 1) switch (n.nodeName) { case 'P': if (n.textContent.trim()) return '\n' + stripChildNodes(n) + '\n'; break; case 'BR': return '\n'; case 'I': case 'EM': return '[i]' + stripChildNodes(n) + '[/i]'; case 'B': case 'STRONG': return '[b]' + stripChildNodes(n) + '[/b]'; case 'UL': return '\n[ul]' + stripChildNodes(n) + '[/ul]'; case 'OL': return '\n[ol]' + stripChildNodes(n) + '[/ol]'; case 'LI': return '[*]' + stripChildNodes(n) + '\n'; } return ''; function stripChildNodes(n) { return $(n.childNodes).toArray() .reduce(function (p, c) { return p + stripNode(c) }, ''); } } } function updateISBN(oddata) { var dfr = new $.Deferred(), requests = []; // look up isbn when one isn't available or when multiple formats are requested if (req.isbn && req.format.length === 1) return dfr.resolve(oddata).promise(); var formatcodes = {WMA: 25, MP3: 425, EPUB: 410, PDF: 50, Kindle: 420}; for (var format in formatcodes) { // skip if not available or requested var requested = format === 'Kindle' ? $(req.format).is(['MOBI','AZW3']) : $(req.format).is([format]); if (oddata._formats.indexOf(format) === -1 || !requested) continue; requests.push(isbnRequest(oddata._odid, format, formatcodes[format])); } $.when.apply($, requests).done(function (/* {format:'', isbn:''}|null, ... */) { oddata.formatisbn = {}; for (var i = 0; i < arguments.length; i++) { if (!arguments[i]) continue; oddata.formatisbn[arguments[i].format] = arguments[i].isbn; } // for other formats oddata.isbn = req.isbn || oddata.formatisbn.EPUB || oddata.formatisbn.PDF; dfr.resolve(oddata); }).fail(function (reason) { // no need to propagate the error console.error('requp: ' + reason); dfr.resolve(oddata); }); return dfr.promise(); function isbnRequest(odid, format, code) { var dfr = new $.Deferred(); GM_xmlhttpRequest({ method: 'GET', url: 'http://www.contentreserve.com/TitleInfo.asp?ID={'+odid+'}&Format='+code, onload: function (res) { var $page = $(res.responseText, null); var isbn = $('td:contains(ISBN):last', $page).next().text().trim(); dfr.resolve(isbn ? {format: format, isbn: isbn} : null); }, onerror: function (res) { dfr.reject('failed looking up isbn: ' + res.status + ' ' + res.statusText); } }); return dfr.promise(); } } } function onUploadPage() { var reqid, _match; if (_match = location.search.match(/reqid=(\w+)/)) reqid = _match[1]; else return; var _req = GM_getValue(reqid); if (!_req) { console.log('requp: no data available'); return; } var req = JSON.parse(_req); GM_deleteValue(reqid); // populate upload form fields $('#TitleField').val(req.title); $('#TagsField').val(req.tags.join(', ')); $('#AuthorsField').val(req.authors.join(', ')); if (req.artists && req.artists.length) $('#ArtistsField').val(req.artists.join(', ')); if (req.format.length === 1) $('#FormatField').val(FORMATS[req.format[0]]); $('#PublishersField').val(req.publishers.join(', ')); $('#LanguageField').val(LANGS[req.lang]); $('#RetailField').prop('checked', req.retail); $('#IsbnField').val(req.isbn); $('#DescriptionField').val(req.desc); $('#YearField').val(req.pubdate); if (req.formatisbn) { $('#FormatField').on('change', function () { var format = $(this).find(':selected').text(); $('#IsbnField').val(req.formatisbn[format] || req.isbn); }); } // save fill request form again for the torrent page $('#TitleField').parents('form').on('submit', function (e) { if (! (this.IsbnField.value && this.FormatField.value !== 'noformat')) { alert('Either ISBN or Format is empty.'); e.preventDefault(); } var key = this.IsbnField.value + $(this.FormatField).find(':selected').text(); GM_setValue(key, JSON.stringify({ id: reqid, fillform: req.fillform })); }); } function onTorrentPage() { var _match, _req; if (_match = $('#details_content_info').text().match(/^\s*(\w+).+?\((\d+)\)/)) { var key = _match[2] + _match[1]; // isbn + format if (_req = GM_getValue(key)) { GM_deleteValue(key); } else { console.log('requp: no linked request'); return; } } else { console.log('requp: failed to parse isbn/format'); return; } var req = JSON.parse(_req); var torrid = location.pathname.split('/').slice(-1); req.fillform.push({name: 'TorrentIdField', value: torrid}); // add fill request link $('<a/>', {href: '/requests/' + req.id, text: 'Fill Request', id: 'fillreq'}) .on('click', function (e) { var $self = $(this); e.preventDefault(); $self.next().addClass('loading').text(''); $.post(this.href, $.param(req.fillform)).done(function () { location.href = this.url; }).fail(function (xhr) { $self.next().removeClass('loading').text(xhr.status + ' ' + xhr.statusText); console.error('requp: failed to fill request'); }); }).appendTo('#sidebar ul').wrap('<li></li>').after('<span></span>'); } GM_addStyle('#fillreq+span,#upreq+span{margin-left:10px;font-size:0.8em;}.loading{height:12px;width:12px;background-repeat:no-repeat;display:inline-block;background-image:url(data:image/gif;base64,R0lGODlhDAAMAIQAAAQCBIyKjERCRCQiJNze3BQSFPTy9DQ2NHx+fBwaHPz6/AwKDJyanExOTOzq7Dw+PAQGBCwqLOTi5BQWFPT29Dw6PBweHPz+/JyenFRWVP///wAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQJCAAaACwAAAAADAAMAAAFQ6CmKcxTTA+jiJpzAHB8OBr1xnhFMXgPYA9fg0DICAo+msZgQeIgFNElEuwhVoGUD2Cx/BQ3X2Xl8s1YpMdkIsCsNCEAIfkECQgAEwAsAAAAAAwADACEBAIElJKUPDo85OLkHB4cZGZkFBIUREJE/Pr8nJqc7OrsHBocPD48NDY0bGpsFBYUREZEnJ6c7O7s////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABUDgNCEJYzxMgoiT0gBw3CjjG98CEtw8UPY3EzD2MPAgA0kBgOINRIjHIcGjiQgRhA1WWAVyrS3gJLCOSg9pVhQCACH5BAkIABIALAAAAAAMAAwAhAQCBJSWlDQ2NOzq7ERGRAwODMzKzDw+PJyenPz6/BQWFAQGBJyanDw6PExOTBQSFERCRPz+/P///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVCoCQlwfEoB5OI0iAAcCwM4xvfDXnvAHPwNxMwpngMYajYwWD4wSCMGK21gCESr8JqVADkWq+AKACYsRIMiMMBuYpCACH5BAkIABUALAAAAAAMAAwAhAQCBIyOjDw6POTi5BweHGRmZPz6/BQSFExKTOzq7JyanERCRBwaHDw+POTm5DQ2NGxqbPz+/BQWFOzu7JyenP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVCYFUZSnNIjWKIVfIAcPwk4xvfAnnvQMnfpl9McgAUJoPFDiVZVRy7hYIREU12FMMjEDEUcCsXoyijiQyUhUSyyIpCACH5BAkIABQALAAAAAAMAAwAhAQCBJSSlDQ2NMzKzBQSFERCROzq7JyanAwODDw+PBwaHPz6/AQGBJSWlDw6PMzOzBQWFExKTJyenPz+/P///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVAIEUtTUJAybGIlCEAcCwY4xvfDgkgzB0ficbCkPABEhHWwAhJipY+VGBY9BUOOyNMsrAZcy2vjCZaACGQAlcUAgAh+QQJCAAUACwAAAAADAAMAIQEAgSUkpQ8Ojzk4uQcHhxkZmT08vQUEhREQkScmpzs6uwcGhw8Pjzk5uQ0NjRsamz8+vwUFhRERkScnpz///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFQiBFQQlzREwCiZTiEBEgA44yOsFYzIAABQSWgQcoRVaUAdEEKCgGEmLkQJQyqjxEAjubQBxcH6UBJtZYJEYkgvCKQgAh+QQJCAAYACwAAAAADAAMAIQEAgSEgoQ0NjTc3twUEhT08vRMTkwMCgycmpw8Pjzs6uwcGhz8+vwEBgSEhoQ8Ojzk4uQUFhRUUlQMDgycnpxEQkTs7uz8/vz///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAFRSCGMUgiCAnCiJgiAAHDOICgjO+xjhPwkABAwyKyHIKlIMAwgBiUCYJyqoxIqdNIAjutILhKCuPF/bXIUxuLlIhEKmJRCAAh+QQJCAASACwAAAAADAAMAIQEAgSUlpQ8Ojzk4uQkIiRsamwUEhREQkT8+vycnpzs6uwcGhycmpw8Pjw0NjQUFhRERkTs7uz///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFQqAkIUFjPA2DiJLiADDwEI4yvnGxBgIZwzbRItH4AQYixONgMB4GioKsafyhqr8DAxtLIHDVXgscq7EQjMZj6RWFAAA7);}');