Extract images for tieba.baidu.com

Adds a button that get all attached images as original size to every post.

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name        Extract images for tieba.baidu.com
// @name:zh     贴吧壁纸收割机
// @namespace   https://github.com/cmheia/extract-images-for-tieba
// @description Adds a button that get all attached images as original size to every post.
// @include     http://tieba.baidu.com/p/*
// @include     https://tieba.baidu.com/p/*
// @author      cmheia
// @version     0.3.3
// @icon        http://tb1.bdstatic.com/tb/favicon.ico
// @grant       GM_setClipboard
// @grant       GM_xmlhttpRequest
// @license     MPL
// ==/UserScript==
(function () {
	'use strict';

	var $id = function (o) {
		return document.getElementById(o);
	};

	// 去重
	var doUnique = function (arr) {
		var result = [],
		hash = {};
		for (let i = 0, elem; (elem = arr[i]) !== undefined; i++) {
			if (!hash[elem]) {
				result.push(elem);
				hash[elem] = true;
			}
		}
		return result;
	};

	// 插入样式表
	var apendStyle = function (cssText) {
		var head = document.head || document.querySelectorAll('head')[0];
		var style = document.createElement('style');
		style.type = 'text/css';
		var textNode = document.createTextNode(cssText);
		style.appendChild(textNode);
		head.appendChild(style);
	};

	// 创建样式表
	var addStyle = function () {
		apendStyle(".margin8 {margin:8px;} .preview_item {padding:3px;display:inline-block;vertical-align:top;position:relative;} .preview_box {max-width:300px;max-height:300px;vertical-align:bottom;} .preview_container {z-index:11000;} .preview_selector {position:absolute;left:0;} .preview_excluded {background:#FFCCFF;}");
	};

	// 页面显示信息
	var msg = function (msg) {
		$id("extracted").innerHTML = msg;
	};

	// 取得分页数量
	var getPages = function () {
		var l_posts_num = document.querySelectorAll('.l_posts_num > li > span.red');
		return parseInt(l_posts_num[l_posts_num.length - 1].innerText);
	};

	// 取得IMG标签中的SRC
	var getImgTags = function (content) {
		// console.group("取得IMG标签中的SRC");
		let images1 = content.match(/<img[^<>]*class=\"BDE_Image\"[^<>]*src\=\"(?:(?:(?:http|https)\:\/\/)?[^<>]*(?:jpg|jpeg|gif|png|webp))[^\"]*\"[^<>]*>/g);
		let images2 = content.match(/<img[^<>]*src\=\"(?:(?:(?:http|https)\:\/\/)?[^<>]*(?:jpg|jpeg|gif|png|webp))[^\"]*\"[^<>]*class=\"BDE_Image\"[^<>]*>/g);
		let images = null;

		if (null === images1 && null === images2) {
			return [];
		}
		if (null !== images1 && null === images2) {
			images = images1;
		}
		if (null === images1 && null !== images2) {
			images = images2;
		}
		if (null !== images1 && null !== images2) {
			images = images1.concat(images2);
			images1 = images2 = null;
		}
		// console.log("匹配结果", images);
		var imageSrc = [];
		if (null !== images) {
			imageSrc = images.map(function (val, i) {
				let src = val.match(/(?:(http[s]*):\/\/)([^\.]*)([^\/]*)(.*)(?:\.(?:jpg|jpeg|gif|png|webp))/);
				// console.log(i, val);
				// console.log(src);
				if (null !== src) {
					if ("adscdn" === src[2] || ".bdstatic.com" === src[3] ) {
						// 先去广告
						// console.log("去广告");
						return undefined;
					}
					if (".baidu.com" === src[3] || ".bdimg.com" === src[3]) {
						let m = src[0].match(/(?:[\w\d\.]+)(?:jpg|jpeg|gif|png|webp)/);
						// console.log("return", m);
						return `${src[1]}://imgsrc.baidu.com/forum/pic/item/${m[0]}`;
					}
					if (".sinaimg.cn" === src[3]) {
						let m = src[0].match(/((?:http|https)\:\/\/[\w\d\.]+)sinaimg\.cn\/([\w\d]+)\/([\w\d\.\?]+)/);
						// console.log("return", m);
						return `${m[1]}sinaimg.cn/large/${m[3]}`;
					}
					// console.log("原始链接", src[0]);
					return src[0];
				} else {
					return undefined;
				}
			});
		}
		for (let i = imageSrc.length - 1; i >= 0; i--) {
			if (undefined === imageSrc[i]) {
				imageSrc.splice(i, 1);
			}
		}
		// console.log(imageSrc);
		// console.groupEnd();
		return imageSrc;
	};

	// 取得单个分页原图链接
	var extractSinglePage = function (content) {
		var images = getImgTags(content);

		var result = doUnique(images);
		// console.warn(result);
		if (null === result || 0 === result.length) {
			return null;
		}
		return result;
	};

	// 取得所有分页原图链接
	var extractAllPages = function (pages, auto) {
		var imageSrc = {};
		var parsedPages = 0;
		var failedPages = 0;

		var collectImages = function () {
			if (pages === parsedPages) {
				// console.log("提取失败", failedPages, "页");
				// console.log(imageSrc);
				var imageSrcArray = [];
				for (let i in imageSrc) {
					// console.debug("第", i, "页", imageSrc[i].length, "图");
					for (let j of imageSrc[i]) {
						imageSrcArray.push(j);
					}
				}

				var result = doUnique(imageSrcArray);
				if (null === result || 0 === result.length) {
					msg("然而并没有图片 (╯#-_-)╯~~~~~~~~~~~~~~~~~╧═╧");
				} else {
					exportAlbum(result, auto);
				}
			}
		};
		var parseRespond = function (xhr) {
			var currentPage = xhr.finalUrl.replace(/http[s]*\:\/\/tieba.baidu.com\/p\/(\d+)\?pn=(\d+)$/, "$2") - 0;

			msg(`到手第${currentPage}页(共${parsedPages}页),就剩${(pages - parsedPages)}页啦 (ฅ´ω\`ฅ)`);
			console.log('到手第', currentPage, '页(共', parsedPages, '页),剩', pages - parsedPages, '页');

			currentPage--;
			imageSrc[currentPage] = getImgTags(xhr.response);
			parsedPages++;
			collectImages();
		};
		var xhrErrorHandler = function (xhr) {
			var currentPage = xhr.finalUrl.replace(/http[s]*\:\/\/tieba.baidu.com\/p\/(\d+)\?pn=(\d+)$/, "$2") - 0;

			msg(`第${currentPage}页提取失败 (ಥ_ಥ)`);
			console.log('第', currentPage, '页提取失败');
			parsedPages++;
			failedPages++;

			collectImages();
		};

		for (let i = 1; i <= pages; i++) {
			let url = window.location.origin + window.location.pathname + "?pn=" + i;
			GM_xmlhttpRequest({
				method : 'GET',
				url : url,
				onload : parseRespond,
				onerror : xhrErrorHandler
			});
		}
	};

	// 仅收割当前分页
	var extracterImage = function (auto) {
		var result = extractSinglePage(document.querySelector('#j_p_postlist').innerHTML);
		if (null !== result && 0 < result.length) {
			exportAlbum(result, auto);
		} else {
			msg("然而并不能收割 (╯#-_-)╯~~~~~~~~~~~~~~~~~╧═╧");
		}
	};

	// 收割全部分页
	var extracterImages = function (auto) {
		// console.clear();
		var pages = getPages();
		if (0 === pages) {
			msg("度娘又改版了 (╯#-_-)╯~~~~~~~~~~~~~~~~~╧═╧");
		} else if (1 === pages) {
			let result = extractSinglePage(document.querySelector('#j_p_postlist').innerHTML);
			if (null !== result && 0 < result.length) {
				exportAlbum(result, auto);
			} else {
				msg("然而并不能收割 (╯#-_-)╯~~~~~~~~~~~~~~~~~╧═╧");
			}
		} else {
			extractAllPages(pages, auto);
			msg("正在搞这 " + pages + " 页图,不要急嘛 (๑•̀_•́๑)");
		}
	};

	// 新建图集
	var exportAlbum = function (images, auto) {
		if (null === images || 0 === images.length) {
			return;
		}
		var imageCount = images.length;

		if (auto) {
			GM_setClipboard(images.join("\r\n"));
			msg(`搞到这${imageCount}张图啦 (⺻▽⺻ )`);
			return;
		}

		// 删除图集
		var removeAlbum = function () {
			if (null !== $id("preview_window")) {
				document.body.removeChild($id("preview_window"));
			}
			msg("");
		};

		// 恢复被隐藏的元素
		var showOtherElements = function () {
			for (let i = 0; i < document.body.children.length; i++) {
				if ("DIV" === document.body.children[i].tagName && "y" === document.body.children[i].getAttribute("data-hide")) {
					document.body.children[i].style.display = "";
				}
			}
			document.querySelector(".tbui_aside_float_bar").style.display = "";
		};

		// 隐藏无关元素
		var hideOtherElements = function () {
			for (let i = 0; i < document.body.children.length; i++) {
				if ("DIV" === document.body.children[i].tagName && "com_userbar_message" !== document.body.children[i].id) {
					document.body.children[i].style.display = "none";
					document.body.children[i].setAttribute("data-hide", "y");
				}
			}
			document.querySelector(".tbui_aside_float_bar").style.display = "none";
		};

		// 导出选中图片链接
		var exportSelected = function () {
			var items = $id("preview_list");
			var counts = items.children.length;
			var result = [];
			for (let i = 0; i < counts; i++) {
				if (items.children[i].children[1].checked) {
					result.push(items.children[i].children[0].src);
				}
			}
			if (0 < result.length) {
				GM_setClipboard(result.join("\r\n"));
				$id("export_msg").innerHTML = "搞到这" + result.length + "张图啦 (⺻▽⺻ )";
			} else {
				$id("export_msg").innerHTML = "至少选择一张图吧 ◔ ‸◔?";
			}
		};

		// 关闭图片墙
		var closePreviewWindow = function () {
			showOtherElements();
			$id("preview_window").style.display = "none";
		};

		// 选中全部图片
		var previewSelectAll = function () {
			var items = $id("preview_list");
			var counts = items.children.length;
			for (let i = 0; i < counts; i++) {
				items.children[i].children[1].checked = true;
				items.children[i].className = "preview_item";
			}
			$id("previewer_selected").innerHTML = counts;
		};

		// 反选
		var previewSelectInvert = function () {
			var items = $id("preview_list");
			var counts = items.children.length;
			for (let i = 0; i < counts; i++) {
				items.children[i].children[1].checked = !items.children[i].children[1].checked;
				items.children[i].className = (items.children[i].children[1].checked) ? "preview_item" : "preview_item preview_excluded";
			}
			$id("previewer_selected").innerHTML = counts - parseInt($id("previewer_selected").innerHTML);
		};

		// 点击图片切换选中状态
		var previewSelector = function (o) {
			o.nextSibling.checked = !o.nextSibling.checked;
			var selected = $id("previewer_selected");
			selected.innerHTML = parseInt(selected.innerHTML) + ((o.nextSibling.checked === true) ? 1 : -1);
			if (o.nextSibling.checked) {
				o.parentNode.className = "preview_item";
			} else {
				o.parentNode.className = "preview_item preview_excluded";
			}
		};

		// 创建控制栏
		var controller = document.createElement('div');
		controller.id = "previewer_ctrl";
		controller.style = "position:relative";

		var info = document.createElement('span');
		info.id = "previewer_info";
		info.className = "margin8";
		info.innerHTML = "共<span class='red'>" + imageCount + "</span>图,已选<span id='previewer_selected' class='red'>" + imageCount + "</span>图";
		controller.appendChild(info);

		var buttonClose = document.createElement('a');
		buttonClose.href = "javascript:;";
		buttonClose.className = "margin8";
		buttonClose.innerHTML = "关闭图片墙";
		buttonClose.addEventListener("click", closePreviewWindow);
		controller.appendChild(buttonClose);

		var buttonSelectAll = document.createElement('a');
		buttonSelectAll.href = "javascript:;";
		buttonSelectAll.className = "margin8";
		buttonSelectAll.innerHTML = "全选";
		buttonSelectAll.addEventListener("click", previewSelectAll);
		controller.appendChild(buttonSelectAll);

		var buttonInvert = document.createElement('a');
		buttonInvert.href = "javascript:;";
		buttonInvert.className = "margin8";
		buttonInvert.innerHTML = "反选";
		buttonInvert.addEventListener("click", previewSelectInvert);
		controller.appendChild(buttonInvert);

		var buttonExportSelected = document.createElement('a');
		buttonExportSelected.href = "javascript:;";
		buttonExportSelected.className = "margin8";
		buttonExportSelected.innerHTML = "导出选定图片";
		buttonExportSelected.addEventListener("click", exportSelected);
		controller.appendChild(buttonExportSelected);

		var message = document.createElement('span');
		message.id = "export_msg";
		message.className = "margin8";
		controller.appendChild(message);

		// 创建图片列表
		var itemList = document.createElement('ul');
		itemList.id = "preview_list";

		var previewer = document.createElement('div');
		previewer.id = "preview_matrix";
		previewer.appendChild(itemList);

		for (let i = 0; i < imageCount; i++) {
			let item = document.createElement('li');
			item.innerHTML = "<img id='" + "preview_img_" + i + "' class='preview_box' src='" + images[i] + "'><input type='checkbox' id='" + "preview_cb_" + i + "' class='preview_selector' checked='checked'>";
			item.className = "preview_item";
			item.children[0].addEventListener("click", function () {
				previewSelector(this);
			});
			itemList.appendChild(item);
		}

		// 删除旧的图片墙
		removeAlbum();
		var container = document.createElement('div');
		container.id = "preview_window";
		container.className = "preview_container";
		container.appendChild(controller);
		container.appendChild(previewer);
		hideOtherElements();
		document.body.appendChild(container);

		var button = document.createElement('a');
		button.href = "javascript:;";
		button.innerHTML = "打开图片墙";
		button.addEventListener("click", function () {
			hideOtherElements();
			$id("preview_window").style.display = "";
		});
		$id("extracted").appendChild(button);
	};

	// 添加按钮
	var addButton = function () {
		var button = document.createElement('li');
		button.innerHTML = "<a href='javascript:;' class='margin8'>收割</a><span id='extracted'></span>";
		button.class = "l_reply_num";
		button.children[0].addEventListener("click", function () {
			extracterImages(false);
		});
		document.addEventListener("keyup", function (event) {
			// F9 = 120
			// F10 = 121
			if (120 === event.keyCode) {
				extracterImage(true);
			} else if (121 === event.keyCode) {
				extracterImages(true);
			}
		}, true);
		document.querySelector('.l_posts_num').appendChild(button);
	};

	// 运行
	(function () {
		var DOMObserverTimer = false;
		var DOMObserverConfig = {
			attributes : true,
			childList  : true,
		};
		var DOMObserver = new MutationObserver(function () {
				if (DOMObserverTimer !== 'false') {
					clearTimeout(DOMObserverTimer);
				}
				DOMObserverTimer = setTimeout(function () {
					DOMObserver.disconnect();
					if (!$id("extracted")) {
						// console.log("重新添加按钮");
						addButton();
					}
					DOMObserver.observe(document.querySelector('#j_p_postlist'), DOMObserverConfig);
				}, 100);
			});
		DOMObserver.observe(document.querySelector('#j_p_postlist'), DOMObserverConfig);
	})();

	addStyle();
	addButton();
})();