Greasy Fork

Greasy Fork is available in English.

What.CD to bB Crossposter

Fills the bB upload form with data from WCD

当前为 2015-10-09 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==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(/&amp;/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(/&amp;/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);
		},
	};

}