Extract images for tieba.baidu.com

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

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 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();
})();