// ==UserScript==
// @name [bib] RequestUpload
// @namespace varb
// @version 0.6.4
// @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_listValues
// @grant GM_xmlhttpRequest
// @license WTFPL Version 2; http://www.wtfpl.net/txt/copying/
// ==/UserScript==
// helpers
(function () {
// retrieve an array of text contents from the set of matched elements
$.fn.list = function () {
return $.map(this, function (e) {
return e.textContent;
});
};
// flatten a set of matched elements wrapping content in bbcode entities
$.fn.toBBCode = function () {
var nodes = this.get(),
frag = document.createDocumentFragment();
for (var i = 0; i < nodes.length; i++) {
frag.appendChild(nodes[i].cloneNode(true));
}
return stripNode(frag);
};
var entityFor = {
P: function (s) { return '\n' + s + '\n'; },
BR: function () { return '\n'; },
I: function (s) { return '[i]' + s + '[/i]'; },
EM: function (s) { return this.I(s); },
B: function (s) { return '[b]' + s + '[/b]'; },
STRONG: function (s) { return this.B(s); },
UL: function (s) { return '\n[ul]' + s + '[/ul]\n'; },
OL: function (s) { return '\n[ol]' + s + '[/ol]\n'; },
LI: function (s) { return '[*]' + s + '\n'; },
A: function (s, n) { return '[url=' + n.href + ']' + s + '[/url]'; }
};
function stripNode(node) {
var result = '',
parent = node;
if (node.nodeType === 3) {
return node.textContent;
}
if (node.nodeType !== 1 && node.nodeType !== 11) {
return '';
}
node = node.firstChild;
while (node) {
result += stripNode(node);
node = node.nextSibling;
}
return entityFor[parent.nodeName] ? entityFor[parent.nodeName](result, parent) : result;
}
})();
$(function () {
var requestpage = {
category: null,
reqid: null,
odid: null,
odformats: null,
metadata: {},
scrapeRequestInfo: function () {
var $reqdetails = $('#requestDetails'),
$tags = $('#details_tags a'),
$authors = $('#creatorlist'),
reqdmatch = $reqdetails.text().match(/(?:(\w+),\s)?(\w+)\scategory/),
isbnmatch = $('#description').text().match(/Overdrive Listing \((\d+)\)/);
if (!reqdmatch) {
console.log('requp: cannot establish request category');
return;
}
this.reqid = location.pathname.split('/').slice(-1);
this.category = reqdmatch[2];
this.metadata.fillform = $('#TorrentIdField').parents('form').find(':hidden').serializeArray();
this.metadata.overdrive = !!$tags.filter(':contains(overdrive)').length;
this.metadata.lang = reqdmatch[1];
this.metadata.title = $('h1').text().match(/\/\s*(.+?)(?:\s\[Retail\]$|$)/)[1];
this.metadata.publishers = $('#published a').list();
this.metadata.tags = $tags.not(':contains(overdrive)').list();
this.metadata.format = $reqdetails.find('strong').text().split('/');
this.metadata.retail = $reqdetails.text().indexOf('Retail') !== -1;
this.metadata.authors = $authors.find('a').list();
this.metadata.isbn = isbnmatch ? isbnmatch[1] : null;
if (this.category === 'Comics') {
this.metadata.artists = $authors.next('#creatorlist').find('a').list();
}
return true;
},
fetchODListing: function () {
var dfr = new $.Deferred(),
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();
},
parseODListing: function (page) {
var dfr = new $.Deferred(),
suffix = [null, 'st', 'nd', 'rd'],
$page = $(page),
$meta = $('.meta-accordion', $page),
subtitle, authors, ed;
try {
// <head> may be stipped by some browsers
this.odid = page.match(/meta.+?od:id.+?content=\"([-\w]+)\"/)[1];
this.odformats = $meta.find('span:contains(Format)').next().text();
subtitle = $('h2.subtitle', $page).contents()[0];
subtitle = subtitle && subtitle.textContent.trim();
this.metadata.title = $('h1.title', $page).text().trim() + (subtitle ? ': ' + subtitle : '');
this.metadata.pubyear = $meta.find('dt:contains(Publication Date)').next().text();
// ignore extraneous elements in description
$('.description', $page).children().remove('.tags, .meta').andSelf()
.find('span:contains(»)').next().andSelf().remove();
this.metadata.desc = $('.description', $page).toBBCode().trim();
authors = $meta.find('span:contains(Creators)').next().find('dd').list();
if (authors.length) {
this.metadata.authors = authors;
}
ed = $meta.find('dt:contains(Edition)').next().text();
if (ed) {
ed += (ed.search(/([^1]|\b)[123]$/) !== -1) ? suffix[ed.substr(-1)] : 'th';
this.metadata.title += ' (' + ed + ' Edition)';
}
} catch (e) {
return dfr.reject('failed parsing od listing: ' + e.name + ', ' + e.message).promise();
}
return dfr.resolve().promise();
},
updateISBN: function () {
var dfr = new $.Deferred(),
that = this,
requests = [],
formatcodes = {WMA: 25, MP3: 425, EPUB: 410, PDF: 50, Kindle: 420},
format, requested;
// look up isbn when one isn't available or when multiple formats are requested
if (this.metadata.isbn && this.metadata.format.length === 1) {
return dfr.resolve().promise();
}
for (format in formatcodes) {
// skip if not requested or unavailable
requested = (format === 'Kindle')
? $(this.metadata.format).is(['MOBI', 'AZW3'])
: $(this.metadata.format).is([format]);
if (requested && this.odformats.indexOf(format) !== -1) {
requests.push(isbnRequest(this.odid, format, formatcodes[format]));
}
}
$.when.apply($, requests).done(function (/* {format:'', isbn:''}|null, ... */) {
that.metadata.formatisbn = {};
for (var i = 0; i < arguments.length; i++) {
if (arguments[i]) {
that.metadata.formatisbn[arguments[i].format] = arguments[i].isbn;
}
}
// for other formats
that.metadata.isbn = that.metadata.isbn || that.metadata.formatisbn.EPUB
|| that.metadata.formatisbn.PDF;
dfr.resolve();
}).fail(function (reason) {
// no need to propagate the error
console.error('requp: ' + reason);
dfr.resolve();
});
return dfr.promise();
function isbnRequest(odid, format, code) {
var dfr = new $.Deferred();
GM_xmlhttpRequest({
method: 'GET',
url: '//www.contentreserve.com/TitleInfo.asp?ID={'+odid+'}&Format='+code,
onload: function (res) {
var $page = $(res.responseText, null),
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();
}
},
run: function () {
var that = this;
if ($('#filled').length) {
console.log('requp: nothing to do here');
return;
}
if (!that.scrapeRequestInfo()) {
return;
}
// link upload page
var upurl = location.origin + '/upload/' + that.category.toLowerCase() + '?reqid=' + that.reqid;
$('<a/>', {id: 'upreq', href: upurl, text: 'Upload Request'})
.appendTo('#sidebar ul:first')
.wrap('<li></li>')
.after('<span></span>')
.on('click', function (e) {
var $that = $(this);
if (!that.metadata.overdrive) {
GM_setValue(that.reqid, JSON.stringify(that.metadata));
return;
}
e.preventDefault();
$that.next().addClass('loading');
that.fetchODListing()
.then(function (page) {
return that.parseODListing(page);
})
.then(function () {
return that.updateISBN();
})
.done(function () {
GM_setValue(that.reqid, JSON.stringify(that.metadata));
location.href = $that.attr('href');
$that.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 = $that.attr('href');
}
$that.next().removeClass('loading');
});
});
}
};
var handle = {
requests: function () {
requestpage.run();
},
upload: function () {
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},
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},
match = location.search.match(/reqid=(\w+)/),
reqid = match && match[1]
req = GM_listValues().indexOf(reqid) !== -1 && JSON.parse(GM_getValue(reqid));
if (!req) {
console.log('requp: no request data found');
return;
}
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.pubyear);
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.');
return;
}
var key = this.IsbnField.value + $(this.FormatField).find(':selected').text();
GM_setValue(key, JSON.stringify({ id: reqid, fillform: req.fillform }));
});
},
torrents: function () {
var match = $('#details_content_info').text().match(/^\s*(\w+).+?\((\d+)\)/),
key = match && match[2] + match[1], // isbn + format
req = GM_listValues().indexOf(key) !== -1 && JSON.parse(GM_getValue(key)),
torrid = location.pathname.split('/').slice(-1);
if (!req) {
console.log('requp: no fill request form');
return;
}
GM_deleteValue(key);
req.fillform.push({name: 'TorrentIdField', value: torrid});
// add fill request link
$('<a/>', {href: '/requests/' + req.id, text: 'Fill Request', id: 'fillreq'})
.appendTo('#sidebar ul')
.wrap('<li></li>')
.after('<span></span>')
.on('click', function (e) {
var $that = $(this);
e.preventDefault();
$that.next().addClass('loading').text('');
$.post(this.href, $.param(req.fillform)).done(function () {
location.href = this.url;
$that.next().removeClass('loading').text('redirecting...');
}).fail(function (xhr) {
$that.next().removeClass('loading').text(xhr.status + ' ' + xhr.statusText);
console.error('requp: failed to fill request');
});
});
}
};
return function () {
var page = location.pathname.split('/')[1];
console.log('requp: initialized');
if (handle[page]) {
handle[page]();
} else {
console.log('requp: unhandled page ' + page);
}
};
}());
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,R\
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\
7EQjMZj6RWFAAA7);}');