Greasy Fork is available in English.
Fills the bB upload form with data from WCD
当前为
// ==UserScript==
// @name What.CD to bB Crossposter
// @description Fills the bB upload form with data from WCD
// @namespace BlackNullerNS
// @include http*://what.cd/torrents.php*
// @include http*://what.cd/artist.php?id=*
// @version 1.3.2
// @grant GM_xmlhttpRequest
// ==/UserScript==
var span, pos, lnk, announceLink;
var bb = document.createElement('a');
bb.textContent = '+bB';
bb.setAttribute('href', '#');
bb.setAttribute('title', 'Crosspost to bB');
var userId = document.getElementById('nav_userinfo').firstElementChild.getAttribute('href').split('?id=')[1];
if (document.location.href.indexOf('type=seeding&userid=' + userId) > -1 || document.location.href.indexOf('type=uploaded&userid=' + userId) > -1 || document.location.href.indexOf('type=leeching&userid=' + userId) > -1 || document.location.href.indexOf('type=snatched&userid=' + userId) > -1) {
var t, torrents = document.getElementsByClassName('torrent_links_block');
for (var i = 0, l = torrents.length; i < l; i++) {
t = torrents.item(i);
pos = t.lastElementChild.nextSibling;
t.insertBefore(document.createTextNode(" | "), pos);
lnk = bb.cloneNode(true);
lnk.onclick = crossPostToBb;
t.insertBefore(lnk, pos);
}
return;
}
var item, rows = document.querySelectorAll(".tl_snatched, .wcds_seeding, .wcds_snatched, .wcds_uploaded, .wcds_leeching");
if (rows.length > 0) {
for (var i = 0, l = rows.length; i < l; i++) {
item = rows.item(i);
if (item.classList.contains("tl_snatched")) {
if (!item.parentNode.className) {
span = item.parentNode.parentNode.firstElementChild;
} else {
continue;
}
} else {
span = item.parentNode.firstElementChild;
}
pos = span.lastElementChild.nextSibling;
span.insertBefore(document.createTextNode(" | "), pos);
lnk = bb.cloneNode(true);
lnk.onclick = crossPostToBb;
span.insertBefore(lnk, pos);
}
return;
}
function loadTorrentInfo(a, callback)
{
var tr = a.closest('tr');
var id = ('id' in tr && tr.id)
? tr.id.replace('torrent', '')
: a.parentNode.firstElementChild.getAttribute('href').split('&id=')[1].split('&')[0];
GM_xmlhttpRequest({
url: 'https://what.cd/ajax.php?action=torrent&id=' + id,
method: 'GET',
onload: function(response){
try {
var data = JSON.parse(response.responseText);
if (data.status !== 'success') {
alert('Request failed!');
return;
}
} catch (e) {
alert('Bad response from What.CD API');
return;
}
if (!('torrent' in data.response)) {
alert('Unexpected response from What.CD API');
return;
}
callback(data.response);
},
onerror: function(response){
alert('API request error!');
},
timeout: 7000,
ontimeout: function(response){
alert('API request timed out!');
}
});
return false;
}
function crossPostToBb(data)
{
if (!('torrent' in data)) {
return loadTorrentInfo(this, crossPostToBb);
}
var form = document.createElement('form');
form.setAttribute('target', '_blank');
form.setAttribute('method', 'post');
form.setAttribute('action', 'https://baconbits.org/upload.php');
input(form, 'type', data.group.categoryName);
var artist = '';
var groupName = data.group.name.replace(/&/g, '&');
if (data.group.categoryName === 'Music') {
var media = data.torrent.media;
if (media === 'WEB') media = 'Web';
if (data.group.musicInfo.artists.length > 2) {
artist = 'Various Artists';
} else if (data.group.musicInfo.artists.length === 2) {
artist = data.group.musicInfo.artists[0].name + ' & ' + data.group.musicInfo.artists[1].name;
} else {
artist = data.group.musicInfo.artists[0].name;
}
artist = artist.replace(/&/g, '&');
input(form, 'artist', artist);
input(form, 'media', media);
input(form, 'format', data.torrent.format);
input(form, 'bitrate', data.torrent.encoding);
if (data.torrent.remasterTitle) {
input(form, 'remaster', 1);
input(form, 'remaster_year', data.torrent.remasterYear);
input(form, 'remaster_title', data.torrent.remasterTitle);
}
}
if (data.group.categoryName === 'Music' || data.group.categoryName === 'Audiobooks') {
input(form, 'album_desc', strip(data.group.wikiBody));
input(form, 'release_desc', strip(data.torrent.description));
} else {
input(form, 'desc', strip(data.group.wikiBody) + "\n" + strip(data.torrent.description));
}
input(form, 'submit', 'true');
input(form, 'image', data.group.wikiImage);
input(form, 'title', groupName);
input(form, 'year', data.group.year);
input(form, 'tags', data.group.tags.join(', '));
if (data.torrent.scene) {
input(form, 'scene', 1);
}
var div = document.createElement("div");
div.setAttribute("id","bbConfirm");
div.setAttribute("style", "position:fixed;width:260px;top:50%;left:50%;margin-top:-5%;margin-left:-130px;padding:30px;display:inline-block;border:1px solid #666;border-radius:6px;-moz-box-shadow: 0px 0px 7px #2e2e2e;-webkit-box-shadow: 0px 0px 7px #2e2e2e;box-shadow: 0px 0px 7px #2e2e2e;background:#fff;text-align:center;z-index:200;transition:all 0.5s;transition-delay:0s;");
var dl = document.createElement("a");
dl.style.cursor = "pointer";
dl.appendChild(document.createTextNode("Download modified torrent"));
dl.onclick = function(){
var xhr = new XMLHttpRequest();
xhr.mozBackgroundRequest = true;
xhr.timeout = 10000;
xhr.responseType = "arraybuffer";
xhr.ontimeout = xhr.onerror = function() {
alert('Unable to fetch the torrent!');
}
xhr.onload = function () {
var filename = (artist ? artist + " - " : "") + groupName + (data.group.year ? " (" + data.group.year + ")" : "") + (data.torrent.format ? " [" + data.torrent.format + "]" : "") + ".torrent";
if (announceLink) {
downloadTorrent(this.response, filename);
return;
}
var binary = this.response;
GM_xmlhttpRequest({
url: "https://baconbits.org/upload.php",
method: 'GET',
onload: function(response){
announceLink = response.responseText.split('http://tracker.baconbits.org:34000/')[1].split('/')[0];
announceLink = 'http://tracker.baconbits.org:34000/' + announceLink + '/announce';
downloadTorrent(binary, filename);
},
timeout: 10000,
ontimeout: function() {
alert('Unable to fetch announce link!');
},
onerror: function() {
alert('Unable to fetch announce link!');
}
});
};
xhr.open("GET", "https://what.cd/torrents.php?action=download&id=" + data.torrent.id, true);
xhr.send();
};
var dldiv = document.createElement("div");
dldiv.style.marginBottom = '18px';
dldiv.appendChild(dl);
var search = document.createElement("div");
search.style.marginBottom = '18px';
search.textContent = 'Searching bB...';
var btnText = "Repost to bB";
var btn = document.createElement("button");
btn.type = "submit";
btn.appendChild(document.createTextNode(btnText));
var a = document.createElement("a");
a.style.cursor = "pointer";
a.appendChild(document.createTextNode("Cancel"));
div.appendChild(search);
div.appendChild(dldiv);
div.appendChild(btn);
div.appendChild(document.createElement("br"));
div.appendChild(document.createElement("br"));
div.appendChild(a);
form.appendChild(div);
document.body.appendChild(form);
if (data.torrent.hasLog) {
btn.textContent = 'Loading LOG, please wait...';
btn.setAttribute('disabled', true);
var logUrl = 'https://what.cd/torrents.php?action=viewlog&torrentid='+ data.torrent.id +'&groupid=' + data.group.id;
GM_xmlhttpRequest({
url: logUrl,
method: 'GET',
onload: function(response){
if (response.responseText.indexOf('<pre class="log_file">') === -1) {
search.innerHTML = '<a href="'+ logUrl +'" target="_blank">No LOG found!</a>';
return;
}
var logContent = response.responseText.split('<pre class="log_file">')[1].split('</pre>')[0];
input(form, 'release_desc', (data.torrent.description ? strip(data.torrent.description) + "\n\n" : "") + "[spoiler=LOG][pre]" + logContent + '[/pre][/spoiler]');
btn.removeAttribute('disabled');
btn.textContent = btnText;
},
onerror: function(response){
btn.textContent = btnText;
btn.removeAttribute('disabled');
alert('LOG request error!');
},
timeout: 7000,
ontimeout: function(response){
btn.textContent = btnText;
btn.removeAttribute('disabled');
alert('LOG request timed out!');
}
});
}
form.onsubmit = function(){
setTimeout(function(){
form.parentNode.removeChild(form);
}, 1000);
};
a.onclick = function(){
form.parentNode.removeChild(form);
};
var url = data.group.categoryName === 'Music'
? 'https://baconbits.org/torrents.php?artistname='+ encodeURIComponent(artist) +'&action=advanced&torrentname='+ encodeURIComponent(groupName) +'&format='+ encodeURIComponent(data.torrent.format) +'&disablegrouping=1'
: 'https://baconbits.org/torrents.php?disablegrouping=1&searchstr=' + encodeURIComponent(groupName);
GM_xmlhttpRequest({
url: url,
method: 'GET',
onload: function(response){
if (response.responseText.indexOf('action=download') === -1) {
search.innerHTML = '<a href="'+ url +'" target="_blank">No duplicates found on bB, go ahead!</a>';
return;
}
div.style.width = '600px';
div.style.marginLeft = '-300px';
var searchLink = document.createElement('a');
searchLink.setAttribute('href', url);
searchLink.setAttribute('target', '_blank');
searchLink.style.fontWeight = 'bold';
searchLink.textContent = 'Found on bB:';
search.textContent = '';
search.appendChild(searchLink);
search.appendChild(document.createElement('br'));
var dom = document.createElement("div");
dom.insertAdjacentHTML("afterbegin", response.responseText.replace(/<img /g, '<meta '));
var td, a, row, results = dom.getElementsByClassName("torrent");
for (var i = 0, l = results.length; i < l; i++) {
td = results.item(i).firstElementChild.nextElementSibling;
remove(td.firstElementChild);
remove(td.lastElementChild);
while (td.lastElementChild.tagName === 'BR') {
remove(td.lastElementChild);
}
row = document.createElement('div');
row.innerHTML = td.innerHTML + ' [' + td.nextElementSibling.nextElementSibling.nextElementSibling.textContent + ']';
row.firstElementChild.setAttribute('href', 'https://baconbits.org/' + row.firstElementChild.getAttribute('href'));
if (row.firstElementChild.nextElementSibling) {
row.firstElementChild.nextElementSibling.setAttribute('href', 'https://baconbits.org/' + row.firstElementChild.nextElementSibling.getAttribute('href'));
}
search.appendChild(row);
}
},
timeout: 7000,
ontimeout: function(response){
search.textContent = 'bB request timed out!';
}
});
return false;
}
function downloadTorrent(binary, filename)
{
var str = '';
var data_str = new Uint8Array(binary);
for (i = 0; i < data_str.length; ++i) {
str += String.fromCharCode(data_str[i]);
}
var benc = bencode();
var bnc = benc.decode(str);
bnc.info.unique = random_string(30);
bnc.announce = announceLink;
var uri = "data:application/x-bittorrent;base64," + btoa(benc.encode(bnc));
var link = document.createElement("a");
link.href = uri;
link.style = "visibility:hidden";
link.download = decodeHTML(filename);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
function remove(node)
{
node.parentNode.removeChild(node);
}
function input(form, name, value)
{
var input = document.createElement('input');
input.setAttribute('type', 'hidden');
input.setAttribute('name', name);
input.setAttribute('value', value);
form.appendChild(input);
}
function strip(html)
{
html = html.replace(/<a href="javascript:void\(0\);" onclick="BBCode\.spoiler\(this\);">Show<\/a>/g, '');
html = html.replace(/<blockquote class="[^"]*?spoiler[^"]*?">([.\s\S]+?)<\/blockquote>/g, "[spoiler]$1[/spoiler]");
html = html.replace(/<a.*?href="[a-z]+.php\?[^"]+".*?>(.+?)<\/a>/g, "$1");
html = html.replace(/<a.*?href="([^"]+)".*?>(.+?)<\/a>/g, "[url=$1]$2[/url]");
html = html.replace(/<img.*?src="([^"]+)".*?>/g, "[img]$1[/img]");
html = html.replace(/<span class="size(\d+)">(.+?)<\/span>/g, "[size=$1]$2[/size]");
html = html.replace(/<span style="color: ([^"]+?);">(.+?)<\/span>/g, "[color=$1]$2[/color]");
html = html.replace(/<ol.*?>(.+?)<\/ol>/g, "[list=1]\n$1[/list]");
html = html.replace(/<ul.*?>(.+?)<\/ul>/g, "[list]\n$1[/list]");
html = html.replace(/<li.*?>(.+?)<\/li>/g, "[#]$1\n");
html = html.replace(/<b>(.+?)<\/b>/g, "[b]$1[/b]");
html = html.replace(/<strong>(.+?)<\/strong>/g, "[b]$1[/b]");
html = html.replace(/<i>(.+?)<\/i>/g, "[i]$1[/i]");
html = html.replace(/<em>(.+?)<\/em>/g, "[i]$1[/i]");
html = html.replace(/<s>(.+?)<\/s>/g, "[s]$1[/s]");
html = html.replace(/<u>(.+?)<\/u>/g, "[u]$1[/u]");
html = html.replace(/\[hide/g, "[spoiler");
html = html.replace(/\[\/hide\]/g, "[/spoiler]");
var tmp = document.createElement("DIV");
tmp.innerHTML = html;
return tmp.textContent || tmp.innerText || "";
}
function random_string(length)
{
var text = "";
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for( var i=0; i < length; i++ )
text += possible.charAt(Math.floor(Math.random() * possible.length));
return text;
}
function decodeHTML(str)
{
var d = document.createElement('div');
d.innerHTML = str;
return d.textContent;
}
// Module for encoding/decoding Bencoded data
function bencode() {
"use strict";
// Encoding functions
var encode = function (value) {
// Type
var t = typeof(value);
// Number
if (t == "number") return encode_int(Math.floor(value));
// String
if (t == "string") return encode_string(value);
// Array
if (Array.isArray(value)) return encode_list(value);
// Dict
return encode_dict(value);
};
var encode_int = function (value) {
return "i" + value + "e";
};
var encode_string = function (value) {
return "" + value.length + ":" + value;
};
var encode_list = function (value) {
var str = [ "l" ],
i;
// List values
for (i = 0; i < value.length; ++i) {
str.push(encode(value[i]));
}
// End
str.push("e");
return str.join("");
};
var encode_dict = function (value) {
var str = [ "d" ],
keys = [],
i;
// Get and sort keys
for (i in value) keys.push(i);
keys.sort();
// Push values
for (i = 0; i < keys.length; ++i) {
str.push(encode_string(keys[i]));
str.push(encode(value[keys[i]]));
}
// End
str.push("e");
return str.join("");
};
// Decoding class
var Decoder = function () {
this.pos = 0;
};
Decoder.prototype = {
constructor: Decoder,
decode: function (str) {
// Errors
var k = str[this.pos];
if (!(k in decode_generic)) throw "Invalid format";
// Call
return decode_generic[k].call(this, str);
},
decode_int: function (str) {
// Skip the "i" prefix
++this.pos;
var end = str.indexOf("e", this.pos),
value;
// No end
if (end < 0) throw "Invalid format";
// Assume proper number format
value = parseInt(str.substr(this.pos, end - this.pos), 10);
// Done
this.pos = end + 1;
return value;
},
decode_string: function (str) {
var delim = str.indexOf(":", this.pos),
length, value;
// No end
if (delim < 0) throw "Invalid format";
// Assume proper number format
length = parseInt(str.substr(this.pos, delim - this.pos), 10);
value = str.substr(delim + 1, length);
// Done
this.pos = delim + length + 1;
return value;
},
decode_list: function (str) {
// Skip the "l" prefix
++this.pos;
// Read list
var list = [],
value;
// Loop until end or exception
while (str[this.pos] != "e") {
value = this.decode(str); // this throws errors if str[this.pos] is out of bounds
list.push(value);
}
// Done; skip "e" suffix
++this.pos;
return list;
},
decode_dict: function (str) {
// Skip the "d" prefix
++this.pos;
// Read dict
var dict = {},
key, value;
// Loop until end or exception
while (str[this.pos] != "e") {
key = this.decode_string(str);
value = this.decode(str); // this throws errors if str[this.pos] is out of bounds
dict[key] = value;
}
// Done; skip "e" suffix
++this.pos;
return dict;
},
};
// Generic decode functions
var decode_generic = {
"l": Decoder.prototype.decode_list,
"d": Decoder.prototype.decode_dict,
"i": Decoder.prototype.decode_int,
},
i;
for (i = 0; i < 10; ++i) decode_generic[i.toString()] = Decoder.prototype.decode_string;
// Encode/decode functions
return {
/**
encode: function (obj)
Encodes an object into a Bencode'd string
@param obj
The object to encode
This should only be one of the following:
string
number (floats are floor'd to integers)
array (containing things only from this list)
object (containing things only from this list)
Strings should be encoded in some way such that each character is in the range [0,255]
@return
A string representing the object
*/
encode: encode,
/**
decode: function (str)
Decodes a Bencode'd string back into its original type
@param str
The string to decode
@return
The original object represented by str
@throws
Any one of the following self-explanatory strings
"Invalid format"
"Invalid string"
"Invalid int"
*/
decode: function (str) {
// Create a decoder and call
return (new Decoder()).decode(str);
},
};
}