Greasy Fork is available in English.
一键打包下载贴吧中一贴的原图
// ==UserScript== // @name 批量下载贴吧原图 // @name:zh 批量下载贴吧原图 // @name:en Batch srcImage downloader for tieba // @namespace https://github.com/Jeffrey-deng/userscript // @version 3.4.2 // @description 一键打包下载贴吧中一贴的原图 // @description:zh 一键打包下载贴吧中一贴的原图 // @description:en Batch Download Src Image From Baidu Tieba // @author Jeffrey.Deng // @supportURL https://imcoder.site/a/detail/HuXBzyC // @homepageURL https://imcoder.site // @weibo http://weibo.com/3983281402 // @match http://tieba.baidu.com/* // @match https://tieba.baidu.com/* // @match http://imgsrc.baidu.com/* // @match https://imgsrc.baidu.com/* // @match http://tiebapic.baidu.com/* // @match https://tiebapic.baidu.com/* // @connect baidu.com // @connect bdimg.com // @require https://cdn.bootcss.com/jquery/1.11.1/jquery.min.js // @require https://cdn.bootcss.com/toastr.js/2.1.3/toastr.min.js // @require https://cdn.bootcss.com/jszip/3.1.5/jszip.min.js // @resource toastr_css https://cdn.bootcss.com/toastr.js/2.1.3/toastr.min.css // @grant GM.xmlHttpRequest // @grant GM_xmlHttpRequest // @grant GM_notification // @grant GM_addStyle // @grant GM_getResourceText // @grant GM_registerMenuCommand // ==/UserScript== // @更新日志 // v.3.4.1 2020.7.11 1.修复jQuery下载失败问题 // v.3.3 2020.6.3 1.修复图片被删除但页面仍能看到却下载不到的问题 // v.3.1 2020.5.26 1.支持只下载楼主 // 2.图片后缀名根据图片实际类型命名 // v.3.0 2020.5.21 1.支持下载多页 // 2.支持下载被吞掉的图 // v.2.6.1 2019.12.16 1.修改压缩包名称为帖子的标题 // 如果还是要以前的id作为压缩包名称,那么修改449行:"packNameBy": "title", // 将 packNameBy 的值 title 修改为 id, 再按 ctrl + s 保存。 // v.2.6 2.19.12.16 1.修改图片域名为tiebapic.baidu.com时下载图片显示“你查看的图片不存在的”的问题 // v 2.5.1 2019.12.11 1.修复格式化数字排序未生效的问题 // V 2.5 2019.12.2 1.修改为toastr提示方式 // 2.采用队列下载 // V 2.4 2019.3.17 1.调整图片排序的命名,格式化数字(1显示为01),便于查看时顺序一样 // 2.edge会闪退,原因不知,未修复 // V 2.3 2018.5.31 1.兼容edge // V 2.2 2018.4.7 1.调整匹配图片策略 // V 2.1 2018.4.2 1.调用Tampermonkey API 实现跨域下载,无需修改启动参数 // V 2.0 2018.4.1 1.压缩包内增加贴子地址txt // 2.修复https不能下载 // V 1.9 2018.4.1 1.新增打包下载,图片重命名(需开启浏览器跨域) // V 1.8 2018.3.31 1.修复BUG // 2.可自定义输入文件名后缀 // V 1.7 2017.6.9 1.修复魅族等贴吧下载图标不显示的问题 // V 1.6 2017.6.5 1.提高下载的图片正确率 // V 1.5 2017.6.4 1.增加右键新标签打开图片直接打开原图 // V 1.4 2017.6.3 1.更新对 https 的支持 // 2.提高图片匹配成功率 (function (factory) { factory(document, jQuery); // console.time('ready_init_use_time'); // $().ready(function(){ // console.timeEnd('ready_init_use_time'); // factory(document, jQuery); // }); })(function (document, $) { var common_utils = (function (document, $) { function parseURL(url) { var a = document.createElement('a'); a.href = url; return { source: url, protocol: a.protocol.replace(':', ''), host: a.hostname, port: a.port, query: a.search, params: (function () { var ret = {}, seg = a.search.replace(/^\?/, '').split('&'), len = seg.length, i = 0, s; for (; i < len; i++) { if (!seg[i]) { continue; } s = seg[i].split('='); ret[s[0]] = s[1]; } return ret; })(), file: (a.pathname.match(/\/([^\/?#]+)$/i) || [, ''])[1], hash: a.hash.replace('#', ''), path: a.pathname.replace(/^([^\/])/, '/$1'), relative: (a.href.match(/tps?:\/\/[^\/]+(.+)/) || [, ''])[1], segments: a.pathname.replace(/^\//, '').split('/') }; } function ajaxDownload(url, callback, args, tryTimes) { tryTimes = tryTimes || 0; var GM_download = GM.xmlHttpRequest || GM_xmlHttpRequest, clearUrl = url.replace(/[&\?]?download_timestamp=\d+/, ''), retryUrl = clearUrl + (clearUrl.indexOf('?') === -1 ? '?' : '&') + 'download_timestamp=' + new Date().getTime(); GM_download({ method: 'GET', responseType: 'blob', url: url, onreadystatechange: function (responseDetails) { if (responseDetails.readyState === 4) { if (responseDetails.status === 200 || responseDetails.status === 304 || responseDetails.status === 0) { var blob = responseDetails.response, size = blob && blob.size; if (size && (size / 1024 >= 5)) { callback(blob, args); } else if (tryTimes++ == 3) { callback(blob, args); } else { ajaxDownload(retryUrl, callback, args, tryTimes); } } else { if (tryTimes++ == 3) { callback(null, args); } else { ajaxDownload(retryUrl, callback, args, tryTimes); } } } }, onerror: function (responseDetails) { if (tryTimes++ == 3) { callback(null, args); } else { ajaxDownload(retryUrl, callback, args, tryTimes); } console.log(responseDetails.status); } }); /*try { var xhr = new XMLHttpRequest(); xhr.open('GET', url, true); xhr.responseType = "blob"; xhr.onreadystatechange = function(evt) { if (xhr.readyState === 4) { if (xhr.status === 200 || xhr.status === 0) { callback(xhr.response, args); } else { callback(null, args); } } }; xhr.send(); } catch (e) { callback(null, args); }*/ } function fileNameFromHeader(disposition, url) { var result = null; if (disposition && /filename=.*/ig.test(disposition)) { result = disposition.match(/filename=.*/ig); return decodeURI(result[0].split("=")[1]); } return url.substring(url.lastIndexOf('/') + 1); } function downloadBlobFile(content, fileName) { if ('msSaveOrOpenBlob' in navigator) { navigator.msSaveOrOpenBlob(content, fileName); } else { var aLink = document.createElement('a'); aLink.download = fileName; aLink.style = "display:none;"; var blob = new Blob([content]); aLink.href = window.URL.createObjectURL(blob); document.body.appendChild(aLink); if (document.all) { aLink.click(); //IE } else { var evt = document.createEvent("MouseEvents"); evt.initEvent("click", true, true); aLink.dispatchEvent(evt); // 其它浏览器 } window.URL.revokeObjectURL(aLink.href); document.body.removeChild(aLink); } } function downloadUrlFile(url, fileName) { var aLink = document.createElement('a'); if (fileName) { aLink.download = fileName; } else { aLink.download = url.substring(url.lastIndexOf('/') + 1); } aLink.target = "_blank"; aLink.style = "display:none;"; aLink.href = url; document.body.appendChild(aLink); if (document.all) { aLink.click(); //IE } else { var evt = document.createEvent("MouseEvents"); evt.initEvent("click", true, true); aLink.dispatchEvent(evt); // 其它浏览器 } document.body.removeChild(aLink); } function paddingZero(num, length) { return (Array(length).join("0") + num).substr(-length); } /* Class: TaskQueue * Constructor: handler * takes a function which will be the task handler to be called, * handler should return Deferred object(not Promise), if not it will run immediately; * methods: append * appends a task to the Queue. Queue will only call a task when the previous task has finished */ var TaskQueue = function (handler) { var tasks = []; // empty resolved deferred object var deferred = $.when(); // handle the next object function handleNextTask() { // if the current deferred task has resolved and there are more tasks if (deferred.state() == "resolved" && tasks.length > 0) { // grab a task var task = tasks.shift(); // set the deferred to be deferred returned from the handler deferred = handler(task); // if its not a deferred object then set it to be an empty deferred object if (!(deferred && deferred.promise)) { deferred = $.when(); } // if we have tasks left then handle the next one when the current one // is done. if (tasks.length >= 0) { deferred.fail(function () { tasks = []; }); deferred.done(handleNextTask); } } } // appends a task. this.append = function (task) { // add to the array tasks.push(task); // handle the next task handleNextTask(); }; }; var context = { "ajaxDownload": ajaxDownload, "fileNameFromHeader": fileNameFromHeader, "downloadBlobFile": downloadBlobFile, "downloadUrlFile": downloadUrlFile, "parseURL": parseURL, "paddingZero": paddingZero, "TaskQueue": TaskQueue }; return context; })(document, jQuery); var options = { "type": 2, "isNeedConfirmDownload": true, "useQueueDownloadThreshold": 0, "suffix": null, "callback": { "parseLocationInfo_callback": function (location_info, options) { return common_utils.parseURL(document.location.href); }, "parseFiles_callback": function (location_info, options) { // file.url file.folder_sort_index // not folder_sort_index -> use fileName var files = []; return files; }, "makeNames_callback": function (arr, location_info, options) { var names = {}; var time = new Date().getTime(); names.zipName = "pack_" + time; names.folderName = names.zipName; names.infoName = null; names.infoValue = null; names.prefix = time; names.suffix = options.suffix; return names; }, "beforeFilesDownload_callback": function (files, names, location_info, options, zip, main_folder) { }, "beforeFileDownload_callback": function (file, location_info, options, zipFileLength, zip, main_folder, folder) { }, "eachFileOnload_callback": function (blob, file, location_info, options, zipFileLength, zip, main_folder, folder) { }, "allFilesOnload_callback": function (files, names, location_info, options, zip, main_folder) { }, "beforeZipFileDownload_callback": function (zip_blob, files, names, location_info, options, zip, main_folder) { common_utils.downloadBlobFile(zip_blob, names.zipName + ".zip"); } } }; var ajaxDownloadAndZipFiles = function (files, names, location_info, options) { // GM_notification("开始下载~", names.zipName); var notify_start = toastr.success("正在打包~", names.zipName, { "progressBar": false, "hideDuration": 0, "showDuration": 0, "timeOut": 0, "closeButton": false }); if (files && files.length > 0) { var zip = new JSZip(); var main_folder = zip.folder(names.folderName); var zipFileLength = 0; var maxIndex = files.length; var paddingZeroLength = (files.length + "").length; if (names.infoName) { main_folder.file(names.infoName, names.infoValue); } options.callback.beforeFilesDownload_callback(files, names, location_info, options, zip, main_folder); var downloadFile = function (file, resolveCallback) { return $.Deferred(function (dfd) { var folder = file.location ? main_folder.folder(file.location) : main_folder; var isSave = options.callback.beforeFileDownload_callback(file, location_info, options, zipFileLength, zip, main_folder, folder); if (isSave !== false) { common_utils.ajaxDownload(file.url, function (blob, file) { var isSave = options.callback.eachFileOnload_callback(blob, file, location_info, options, zipFileLength, zip, main_folder, folder); if (isSave !== false) { if (file.fileName) { folder.file(file.fileName, blob); } else { var suffix = names.suffix || file.url.substring(file.url.lastIndexOf('.') + 1); file.fileName = names.prefix + "_" + common_utils.paddingZero(file.folder_sort_index, paddingZeroLength) + "." + suffix; folder.file(file.fileName, blob); } } dfd.resolveWith(file, [blob, folder, isSave]); }, file); } else { dfd.resolveWith(file, [null, folder, false]); } }).done(function (blob, folder, isSave) { zipFileLength++; notify_start.find(".toast-message").text("正在打包~ 第 " + zipFileLength + " 张" + (isSave ? "" : "跳过")); resolveCallback && resolveCallback(); // resolve延迟对象 if (zipFileLength >= maxIndex) { var isDownloadZip = options.callback.allFilesOnload_callback(files, names, location_info, options, zip, main_folder); if (isDownloadZip !== false) { zip.generateAsync({type: "blob"}).then(function (content) { options.callback.beforeZipFileDownload_callback(content, files, names, location_info, options, zip, main_folder); }); // GM_notification({text: "打包下载完成!", title: names.zipName, highlight : true}); toastr.success("下载完成!", names.zipName, {"progressBar": false, timeOut: 0}); } notify_start.css("display", "none").remove(); } }); }; if (maxIndex < options.useQueueDownloadThreshold) { // 并发数在useQueueDownloadThreshold内,直接下载 for (var i = 0; i < maxIndex; i++) { downloadFile(files[i]); } } else { // 并发数在useQueueDownloadThreshold之上,采用队列下载 var queue = new common_utils.TaskQueue(function (file) { if (file) { var dfd = $.Deferred(); downloadFile(file, function () { dfd.resolve(); }); return dfd; } }); for (var j = 0; j < maxIndex; j++) { queue.append(files[j]); } } } else { notify_start.css("display", "none").remove(); toastr.error("未解析到图片!", "错误", {"progressBar": false}); } }; /** 批量下载 **/ function batchDownload(config) { try { options = $.extend(true, {}, options, config); var location_info = options.callback.parseLocationInfo_callback(options); var files = options.callback.parseFiles_callback(location_info, options); if (!(files && files.promise)) { files = $.when(files); } files.done(function (files) { if (files && files.length > 0) { if (!options.isNeedConfirmDownload || confirm("是否下载 " + files.length + " 张图片")) { var names = options.callback.makeNames_callback(files, location_info, options); options.location_info = location_info; options.files = files; options.names = names; if (options.type == 1) { urlDownload(files, names, location_info, options); } else { ajaxDownloadAndZipFiles(files, names, location_info, options); } } } else { toastr.error("未找到图片~", ""); } }).fail(function (message) { toastr.error(message, "错误"); }); } catch (e) { // GM_notification("批量下载照片 出现错误!", ""); console.warn("批量下载照片 出现错误!, exception: ", e); toastr.error("批量下载照片 出现错误!", ""); } } /** 下载 **/ function urlDownload(photos, names, location_info, options) { GM_notification("开始下载~", names.zipName); var index = 0; var interval = setInterval(function () { if (index < photos.length) { var url = photos[index].url; var fileName = null; if (!names.suffix) { fileName = names.prefix + "_" + (index + 1) + url.substring(url.lastIndexOf('.')); } else { fileName = names.prefix + "_" + (index + 1) + "." + names.suffix; } common_utils.downloadUrlFile(url, fileName); } else { clearInterval(interval); return; } index++; }, 100); } //右键新标签打开图片直接打开原图 function initRightClickOpenSource() { var url = document.location.toString(); var m = null; if (!(m = url.match(/^https?:\/\/(imgsrc|tiebapic)\.baidu\.com\/forum\/pic\/item\/.+/i))) { if ((m = url.match(/^(https?):\/\/(imgsrc|imgsa|tiebapic|\w+\.hiphotos)\.(?:bdimg|baidu)\.com\/(?:forum|album)\/.+\/(\w+\.(?:jpg|jpeg|gif|png|bmp|webp))(?:\?.+)?$/i))) { document.location = m[1] + "://" + (m[2] == "tiebapic" ? "tiebapic" : "imgsrc") + ".baidu.com/forum/pic/item/" + m[3]; } } } /*** start main ***/ //右键新标签打开图片直接打开原图 initRightClickOpenSource(); var getReplyAuthorUid = function ($reply) { // 获取回复UID let userInfoStr = $reply.find('.d_name').attr('data-field'); return (userInfoStr && userInfoStr.match(/"user_id":(\d+)/) && RegExp.$1) || 0; } var getPostAuthorUid = function () { // 获取楼主UID return getReplyAuthorUid($('#j_p_postlist').children('.j_l_post').first()); } // css GM_addStyle(GM_getResourceText('toastr_css')); // 添加按钮 var $lis_nav = $('#tb_nav').find('ul').eq(0).find('li'), li_count = $lis_nav.length, $li_right = $lis_nav.eq(li_count - 1), html = ''; if ($li_right.hasClass('none_right_border')) { var isStarTie = $li_right.hasClass('star_nav_tab'); if (isStarTie) { html = '<li class="none_right_border star_nav_tab" style="cursor: pointer"><div class="star_nav_tab_inner"><div class="space">' + '<a title="点击下载本页图片" class="star_nav_ico star_nav_ico_photo" id="batchDownloadBtn"><i class="icon"></i>下载</a></div></div></div>'; } else { html = '<li class="none_right_border j_tbnav_tab" style="cursor: pointer"><div class="tbnav_tab_inner"><p class="space">' + '<a title="点击下载本页图片" class="nav_icon icon_jingpin j_tbnav_tab_a" id="batchDownloadBtn" location="tabplay" >下载</a></p></div></div>'; } $li_right.removeClass('none_right_border').after(html); } else { html = '<li class="j_tbnav_tab" style="cursor: pointer"><a class="j_tbnav_tab_a" id="batchDownloadBtn">下载</a> </li>'; $li_right.after(html); } // 仪表盘控制栏添加按钮 GM_registerMenuCommand('下载图片', function() { tiebaImagesDownload(); }); GM_registerMenuCommand('只下楼主', function() { tiebaImagesDownload({"onlyLz": true}); }); $('#batchDownloadBtn').click(function () { tiebaImagesDownload({"onlyLz": (document.location.href.indexOf('see_lz=1') !== -1 && confirm("是否只下载楼主的图片"))}); }); var tiebaImagesDownload = unsafeWindow.tiebaImagesDownload = function (options) { var config = { "type": 2, "minWidth": 100, "suffix": null, "packNameBy": "title", // "id" or "title" "baiduLoadPhotosApi": "https://tieba.baidu.com/photo/bw/picture/guide", "findPhotoByApi": true, "onlyLz": false, "callback": { "parseFiles_callback": function (location_info, options) { let pn = location_info.params.pn || 1, authorUid = getPostAuthorUid(), findPhotosByPage = function () { let photo_arr = [], $part_nodes_one = $('.d_post_content,.d_post_content_main').find("img"); //var part_nodes_two = $('.d_post_content_main,.post_bubble_middle,.d_post_content').find("img"); $.each($part_nodes_one, function (i, img) { let $img = $(img); if (options.onlyLz) { // 只下楼主 let replyUid = getReplyAuthorUid($img.closest('.j_l_post')); if (replyUid != authorUid) { return; } } // 如果是广告图片则跳过 if (img.parentNode.tagName == "A" && img.parentNode.className.indexOf("j_click_stats") != -1) { return true; } if (img.clientWidth >= options.minWidth) { if ($img.hasClass("BDE_Image") || $img.hasClass("d_content_img")) { var photo = {}; photo.location = ""; var thumb_url = img.src; photo.folder_sort_index = photo_arr.length + 1; // 如果是用户上传的图片 if ($img.attr("pic_type") == "0") { var urlMatcher = thumb_url.match(/^(https?):\/\/([a-zA-Z]+)\..*\/([^/]+)$/); photo.url = urlMatcher[1] + "://" + (urlMatcher[2] == "tiebapic" ? "tiebapic" : "imgsrc") + ".baidu.com/forum/pic/item/" + urlMatcher[3]; photo.id = urlMatcher[3].match(/^[^.]+/)[0]; } // 如果是用户引用的图片 else { var m = thumb_url.match(/^(https?):\/\/(imgsrc|imgsa|tiebapic|\w+\.hiphotos)\.(?:bdimg|baidu)\.com\/(?:forum|album)\/.+\/((\w+)\.(?:jpg|jpeg|gif|png|bmp|webp))(?:\?.+)?$/i); // 如果引用的是贴吧图片 if (m !== null) { photo.url = m[1] + "://" + (m[2] == "tiebapic" ? "tiebapic" : "imgsrc") + ".baidu.com/forum/pic/item/" + m[3]; photo.id = m[4]; } else { photo.url = thumb_url; } } photo.size = $img.attr("size") || 0; photo.location = "photos"; photo_arr.push(photo); } } }); return photo_arr; }; let notify_photo_data_loading = toastr.success("正在请求图片数据~", "", { "progressBar": false, "hideDuration": 0, "showDuration": 0, "timeOut": 0, "closeButton": false }); return $.Deferred(function (finalDfd) { if (options.findPhotoByApi) { let photo_arr = [], curr_load_count = 0, loadQueue = new common_utils.TaskQueue(function (startPicId) { return $.Deferred(function (dfd) { $.get(options.baiduLoadPhotosApi, { 'tid': location_info.file, 'see_lz': options.onlyLz ? 1 : 0, // 只下楼主 'from_page': 0, // 'alt': 'jview', 'next': 50, 'prev': 0, 'pic_id': startPicId, '_': new Date().getTime(), }, function (resp) { let data = resp.data; if (data && data.pic_list) { let pic_amount = data.pic_amount, pic_list = data.pic_list, lastPicId, startPushPic = false; $.each(pic_list, function (key, pic) { let original = pic.img.original, photo; switch (true) { case original.id == startPicId: startPushPic = true; break; case !startPicId: startPushPic = true; case startPushPic: photo = {}; photo.location = "photos"; photo.folder_sort_index = photo_arr.length + 1; photo.id = original.id; photo.url = (original.waterurl && original.waterurl.replace(/^http:\/\//, 'https://')) || (`https://imgsrc.baidu.com/forum/pic/item/${original.id}.jpg`); photo.size = original.size; photo_arr.push(photo); curr_load_count++; lastPicId = original.id; } }); if (lastPicId && curr_load_count < pic_amount) { loadQueue.append(lastPicId); } else { // 队列下载结束 // 对比页面数据和api返回数据,两者合并结果,并尝试按页面显示顺序排序 let combine_photo_arr = [], page_photo_arr = findPhotosByPage().filter(function(photo) { return photo.size != 0; // 有些页面图片没写size,所以这里过滤了没写size的,暂时先这样处理 }).map(function(photo) { let has_delete = true; if (photo.id) { for (let p of photo_arr) { // 由于同样一张图片,id有两个,这里采用对比文件大小的方式来确定是否同一张图片 if (p.id == photo.id || (p.size != 0 && p.size == photo.size)) { has_delete = false; break; } } } photo.has_delete = has_delete; return photo; }), pageLength = page_photo_arr.length, serverLength = photo_arr.length, hasDeleteLength = page_photo_arr.filter(function(photo) { return photo.has_delete; }).length; if (hasDeleteLength > 0) { let start_left_index = 0, start_right_index = 0, unshift_length = 0, i, j, photo_url_arr = photo_arr.map(function (photo) { return photo.url; }); pn > 1 && $.each(page_photo_arr, function(i, photo) { let index = photo_url_arr.indexOf(photo.url); if (index != -1) { start_right_index = index; start_left_index = i; return false; } else { unshift_length++; } }); if (start_right_index > 0) { combine_photo_arr.push.apply(combine_photo_arr, photo_arr.slice(0, start_right_index)); } if (start_left_index > 0) { combine_photo_arr.push.apply(combine_photo_arr, page_photo_arr.slice(start_left_index - unshift_length, start_left_index)); } for (i = start_left_index, j = start_right_index; i < pageLength && j < serverLength;) { let photo, left = page_photo_arr[i], right = photo_arr[j]; if (left.id === right.id || (left.size != 0 && left.size == right.size)) { photo = right; i++; j++; } else { if (left.has_delete) { photo = left; i++; } else { photo = right; j++; } } combine_photo_arr.push(photo); } if (i <= pageLength - 1) { combine_photo_arr.push.apply(combine_photo_arr, page_photo_arr.slice(i, pageLength)); } if (j <= serverLength - 1) { combine_photo_arr.push.apply(combine_photo_arr, photo_arr.slice(j, serverLength)); } $.each(combine_photo_arr, function(i, photo) { photo.folder_sort_index = i + 1; }); } else { combine_photo_arr = photo_arr; } finalDfd.resolve(combine_photo_arr); } dfd.resolve(); } else { dfd.reject('api返回错误'); } }, 'json').fail(function () { dfd.reject('api返回错误'); }); }).fail(function (msg) { console.warn(msg); options.findPhotoByApi = false; finalDfd.resolve(findPhotosByPage()); }); }); loadQueue.append(null); } else { finalDfd.resolve(findPhotosByPage()); } }).always(function () { notify_photo_data_loading.css("display", "none").remove(); }); }, "makeNames_callback": function (photos, location_info, options) { var names = {}, tie_id = location_info.file, pn = location_info.params.pn || 1, title = $(".core_title_txt").attr("title"), forum = ($('#container').find('.card_title a.card_title_fname').text() || '贴').replace(/^\s*|\s*$/g, ''); names.infoName = "tie_info.txt"; names.infoValue = "id:" + tie_id + "\r\n" + "title:" + title + "\r\n" + "url:" + location_info.source + "\r\n" + "page:" + pn + "\r\n" + "image_amount:" + photos.length + "\r\n"; names.zipName = (options.packNameBy == "id" ? ("tie_" + tie_id) : (forum + '_' + tie_id + '_' + title)) + ((options.findPhotoByApi || pn == 1) ? "" : ("_" + pn)); names.folderName = names.zipName; names.prefix = tie_id + (options.findPhotoByApi ? "" : ("_" + common_utils.paddingZero(pn, 3))); names.suffix = options.suffix; names.tie = { 'id': tie_id, 'title': title, 'pn': pn, 'forum': forum }; return names; }, "beforeFilesDownload_callback": function (photos, names, location_info, options, zip, main_folder) { const paddingZeroLength = (photos.length + "").length; $.each(photos, function (i, photo) { photo.fileName = names.prefix + "_" + common_utils.paddingZero(photo.folder_sort_index, paddingZeroLength) + "." + (names.suffix || photo.url.substring(photo.url.lastIndexOf('.') + 1)); }); options.failFiles = undefined; }, "eachFileOnload_callback": function (blob, photo, location_info, options, zipFileLength, zip, main_folder, folder) { if (blob == null) { if (!options.failFiles) { options.failFiles = []; } options.failFiles.push(photo); } else if (!options.names.suffix && photo.location == 'photos' && blob.type && blob.type.indexOf('image/') === 0) { // 如果没有指定后缀名,那么后缀根据content-type来判断 let suffixRegex = /\.[^.]+$/, suffix = ('.' + blob.type.replace('image/', '').replace('jpeg', 'jpg')); photo.fileName = photo.fileName.replace(suffixRegex, suffix); photo.url = photo.url.replace(suffixRegex, suffix); } return true; }, "allFilesOnload_callback": function (photos, names, location_info, options, zip, main_folder) { let photo_urls_str = "", failPhotoListStr = ""; // 链接列表文件 $.each(photos, function (i, photo) { photo_urls_str += ((photo.location ? (photo.location + "/") : "" ) + photo.fileName) + "\t" + photo.url + "\r\n"; }); main_folder.file("photo_url_list.txt", photo_urls_str); // 帮助文件 main_folder.file("帮助.txt", "有些图片可能下载下来是裂掉的缩略图,可以从photo_url_list.text中按文件名手动找到链接下载。"); // 失败链接列表 if (options.failFiles && options.failFiles.length > 0) { toastr.error("共 " + options.failFiles.length + " 张下载失败,已记录在photos_fail_list.txt!", "", {"progressBar": false, timeOut: 0}); failPhotoListStr = ""; for (var i in options.failFiles) { var failFile = options.failFiles[i]; failPhotoListStr += (failFile.location + "/" + failFile.fileName + "\t" + failFile.url + "\r\n"); } main_folder.file("photos_fail_list.txt", failPhotoListStr); } } } }; if (options) { $.extend(true, config, options); } batchDownload(config); }; });