MyDownloader

包含多种下载方法的下载库

Ce script ne doit pas être installé directement. C'est une librairie destinée à être incluse dans d'autres scripts avec la méta-directive // @require https://update.greasyfork.org/scripts/515674/1477309/MyDownloader.js

// ==UserScript==
// @name         MyDownloader
// @version      2024.11.4
// @description  包含多种下载方法的下载库
// @author       You
// @grant        none
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_deleteValue
// @grant        GM_download
// @require https://update.greasyfork.org/scripts/480132/1476440/Get_all_img_Library.js
// ==/UserScript==

/**
 * 包含多种下载方法的下载类
 * @example
 const downloader = new Downloader();
 downloader.Download_img(imgs);
*/
function Downloader(){
	
	let downloading = 0;
	let downloaded = [];
	let downloadError = [];
	let imgs = null;
	let maxDownloadingCounts = 10;
	let timeout = null;
	let AllComplete = null;
	let OneSuccess = null;
	let OneError = null;
	let downloadType = ""
	
	
	/**
	 * @example Download_img(imgs)
	 */
	this.Download_img = async (imgs)=>{
		if(downloadType==""){
			await Test_downloadType(imgs.eq(0));
		}
		
		Set_download(imgs)
		console.log(downloadType);
		if(downloadType=="GM_download"){
			Donwload_img_by_GM();
		}else if(downloadType=="atag"){
			Donwload_img_by_atag();
		}else if(downloadType=="blob"){
			Donwload_img_by_blob();
		}else{
			alert("no this donwload type");
		}
	}
	/**
	 * @example downloadType.value = "GM_download" || "atag" || "blob"
	 */
	this.downloadType = {get value(){return downloadType;},set value(v){downloadType = v;}};
	/**
	 * @example maxDownloadingCounts.value = 100
	 */
	this.maxDownloadingCounts = {get value(){return maxDownloadingCounts;},set value(v){maxDownloadingCounts = v}};
	/**
	 * @example timeout.value = 1000
	 */
	this.timeout = {get value(){return timeout;},set value(v){timeout = v}};
	/**
	 * @example OneSuccess((success_img)=>{...})
	 */
	this.OneSuccess = foo=>OneSuccess = foo;
	/**
	 * @example OneError((error_img)=>{...})
	 */
	this.OneError = foo=>OneError = foo;
	/**
	 * @example AllComplete(()=>{...})
	 */
	this.AllComplete = foo=>AllComplete = foo;
	
	
	async function Test_downloadType(img){
		return new Promise((resolve)=>{
			let timeout = 3000;
			let isOk = false;
			GM_download({
				url:img[0].src,
				name:"test.png",
				onload:()=>{isOk = true;downloadType = "GM_download";resolve()},
			})
			setTimeout(()=>{
				if(!isOk){downloadType = "atag";}
				resolve()
			},timeout)
		})
	}
	function Set_download(iimgs){
		downloaded = [];
		downloading = 0;
		downloadError = [];
		imgs = iimgs;
	}
	
	function Donwload_img_by_GM(){
		if(!imgs || imgs.length == 0){alert("imgs is empty");return;}
		async function Download_one(i){
			if(i>=imgs.length){if(AllComplete){AllComplete()};return;}
			if(downloading>=maxDownloadingCounts){setTimeout(()=>{Download_one(i)},1000);return;}
			
			let name = '';
			let src = '';
			downloading++;
			try{
				await Check_and_get_nameAndsrc({img:imgs.eq(i),checkSrc:true})
						.then((m)=>{
							name = m.name;
							src = m.src;
						});
			}catch(error){
				console.log(error)
				console.log('imgsrc is error:');
				downloadError.push(i);
				downloading--;
				Download_one(i+1);
				return;
			}
			console.log(name)
			console.log(document.title)
			let timeout = false;
			setTimeout(() => {timeout = true;},10000);
			const donwimg = imgs.eq(i);
			GM_download({
				url:src,
				name:name,
				onload:function(){
					downloaded.push(downimg);
					downloading--;
					if(window.GAIL.showmass){
						window.GAIL.showmass((downloaded.length+downloadError.length)+"/"+imgs.length);
					}
					imgs.eq(i).remove();
					
					if(OneSuccess){OneSuccess(downimg);}
				},
				onerror:function(){
					downloading--;
					downloadError.push(downimg);
					if(OnError){OnError(downimg);}
				},
				onprogress:function(){
					if(timeout){return false;}
				}
			});
			setTimeout(function() {Download_one(i+1);}, 10);
		}
		Download_one(0);
	}
	
	async function Donwload_img_by_atag(nowIsImgPage) {
	    if (!nowIsImgPage) {
			if(!imgs || imgs.length==0){alert("imgs is empty");return;}
	        GM_setValue("downloadName", document.title);
	        for (let i = 0; i < imgs.length; i++) {
	            GM_setValue("downloadType", "start");
				let name;
				let src;
				await Check_and_get_nameAndsrc({img:imgs.eq(i),checkSrc:false})
						.then((m)=>{
							name = m.name;
							src = m.src;
						});
				GM_setValue("downloadSrc", src);
	            const myWindow = window.open(src, '_blank');
	            
	            await new Promise(resolve => {
	                const checkDownload = setInterval(() => {
	                    if (GM_getValue("downloadType") === "end") {
	                        resolve();
							clearInterval(checkDownload);
	                    }
	                }, 100);
	            });
				
				OneSuccess?.(imgs.eq(i));
				downloaded.push(imgs.eq(i));
				window?.GAIL?.showmass?.((downloaded.length+downloadError.length)+"/"+imgs.length);
				$(".mass_top").css({'font-size':'6vmin'});
	        }
			AllComplete?.();
	    } else {
			await Check_and_get_nameAndsrc({img:$("img"),checkSrc:true})
					.then((m)=>{
						let name = m.name;
						let src = m.src;
						$('<a></a>').attr({
							'href': src,
							'download': name,
						})[0].click();
					});
			await new Promise(resolve => setTimeout(resolve, 1000));
			GM_setValue("downloadType", "end");
			window.close();
	    }
	}
	this.Listening_Download_by_atag = ()=>{
		const locationHref = window.location.href;
		const GM_downloadSrc = GM_getValue("downloadSrc");
		if(!GM_downloadSrc){return;}
		if(GM_downloadSrc == locationHref|| locationHref.indexOf(GM_downloadSrc)>=0 || GM_downloadSrc.indexOf(locationHref)>=0 ){
			Donwload_img_by_atag(true);
		}
	}
	
	function Donwload_img_by_blob(){
		if(!imgs || imgs.length==0){alert("imgs is empty");return;}

		const obo = (i)=>{
			if(i>=imgs.length){return;}
			if(maxDownloadingCounts>1 && downloading >= maxDownloadingCounts){
				setTimeout(function() {obo(i)}, 1000);
			}
			let src = imgs.eq(i).attr('big_src');
			if(!src){src = imgs.eq(i).attr('src');}
			const checkimg = imgs.eq(i);
			downloading++;
			UrlToBlob({url:src,timeout:timeout})
				.then(blob=>{
					Check_and_get_nameAndsrc({img:checkimg}).then(args=>{
						const name = args.name;
						let a = $('<a></a>').attr({
							download:name,
							href:blob
						})
						a[0].click();
						downloaded.push(checkimg);
						downloading--;
						checkimg.attr('src',blob);
						OneSuccess?.(checkimg);
					})
					
					if(maxDownloadingCounts==1){
						obo(++i);
					}else{
						window.GAIL.showmass((downloaded.length+downloadError.length)+"/"+imgs.length);
					}
				})
				.catch(er=>{
					console.log(er);
					downloadError.push(imgs.eq(i));
					downloading--;
					if(maxDownloadingCounts==1){
						obo(++i);
					}else{
						window.GAIL.showmass((downloaded.length+downloadError.length)+"/"+imgs.length);
					}
				});
			if(maxDownloadingCounts>1){
				setTimeout(function() {obo(++i);}, 10);
			}
		}
		obo(0);
	}
	/**
	 * @example Urls({url:url,timeout : 100})
    */
	async function UrlToBlob(args) {
		return new Promise((resolve,reject)=>{
			if(!args.url){reject("no url");}
			if(args.timeout){
				const timeout = setTimeout(function() {reject("fetch timeout")}, args.timeout);
			}
			fetch(args.url)
					.then(response => {
						const contentLength = response.headers.get('Content-Length');
						const total = parseInt(contentLength, 10);
						let loaded = 0;
			
						// 克隆响应以便分别读取流和获得 Blob
						const clonedResponse = response.clone();
						const reader = clonedResponse.body.getReader();
			
						// 更新进度的函数
						function updateProgress({ done, value }) {
							if (done) {
								return; // 如果读取完毕,直接返回
							}
							loaded += value.byteLength; // 累加已加载字节
							const progress = (loaded / total) * 100; // 计算进度百分比
							console.log(`Loading: ${progress.toFixed(2)}%`);
							FetchShowProgress?.(progress);
							// 继续读取下一块数据
							return reader.read().then(updateProgress);
						}
			
						// 开始读取流以更新进度
						return reader.read().then(updateProgress).then(() => {
							// 完成后返回原始响应的 Blob
							return response.blob();
						});
					})
					.then(blob => {
						const blobUrl = URL.createObjectURL(blob);
						resolve(blobUrl);
					})
					.catch(error => {
						console.error('Error caching video:', error);
						reject(error);
					});
		});
	}
	function FetchShowProgress(pro){
		if(this.maxDownloadingCounts==1 && this.imgs.length==1){
			window?.GAIL?.showmass(pro);
			$(".mass_top").css('font-size',"10vmin");
		}
	}
	
	function Check_and_get_nameAndsrc(args){
		return new Promise(async (resolve,reject)=>{
			if(!args || !args.img){return reject();}
			let src = args.img.attr('big_src');
			if(!src){src = args.img.attr('src');}
			if(!src){src = args.img.attr('small_src');}
			if(!src){reject();}
			//console.log("check:"+src)
			if(args.checkSrc){
				try{
					await check_src_is_right(src);
				}catch(error){
					reject();
				}
			}
			let ext = src.match(/\.jpg|\.png|\.webp|\.gif|\.bmp/g);
			if(!ext){ext = '.png';}else{ext = ext[0];}
			let name = args.img.attr('name');
			if(!name){name = document.title + new Date().getTime() + ext;}
			resolve({name:name,src:src});
		});
	}
	function check_src_is_right(src){
		return new Promise((resolve,reject)=>{
			let iimg = new Image();
			iimg.onload = function(){
				if(this.width*this.height*this.naturalWidth*this.naturalHeight==0){reject();}else{resolve();}
			}
			iimg.onerror = function(){reject();}
			iimg.src = src;
			setTimeout(function() {iimg.abort();reject();}, 2000);
		})
	}
}
$(function(){
	let dd = new Downloader()
	dd.Listening_Download_by_atag()
})

window.Downloader = Downloader;