// ==UserScript==
// @name [bib] RequestUpload
// @namespace varb
// @version 0.6.2
// @description Facilitates uploading and filling requests.
// @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');
$self.next().removeClass('loading').text('redirecting...');
})
.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
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 > p', $page).contents().slice(0, -5).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]\n';
case 'OL':
return '\n[ol]' + stripChildNodes(n) + '[/ol]\n';
case 'LI':
return '[*]' + stripChildNodes(n) + '\n';
case 'A':
return '[url=' + n.href + ']' + stripChildNodes(n) + '[/url]';
}
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;
$self.next().removeClass('loading').text('redirecting...');
}).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);}');