mirror of
https://github.com/the1812/Bilibili-Evolved.git
synced 2025-11-04 21:22:45 +08:00
Add types for batch download
This commit is contained in:
parent
a597f9e0f3
commit
7bfd55a793
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
min/batch-download.min.js
vendored
2
min/batch-download.min.js
vendored
@ -1 +1 @@
|
||||
(()=>{return(t,e)=>{const i=12;class r{constructor(){this.itemList=[];this.itemFilter=(()=>true)}async getItemList(){}async collectData(){}async collectAria2(r,s){const n=JSON.parse(await this.collectData(r));if(s){const r=t.aria2RpcOption;const{sendRpc:s}=await e.importAsync("aria2-rpc");for(const t of n){const e=t.fragments.map((e,s)=>{let n="";if(t.fragments.length>1){n=" - "+(s+1)}const a=[];if(r.secretKey!==""){a.push(`token:${r.secretKey}`)}a.push([e.url]);a.push({referer:document.URL.replace(window.location.search,""),"user-agent":UserAgent,out:`${t.title}${n}.flv`,split:i,dir:r.baseDir+r.dir||undefined,"max-download-limit":r.maxDownloadLimit||undefined});const o=encodeURIComponent(`${t.title}${n}`);return{params:a,id:o}});await s(e,true)}}else{return`\n# Generated by Bilibili Evolved Video Export\n# https://github.com/the1812/Bilibili-Evolved/\n${n.map(t=>{return t.fragments.map(e=>{return`\n${e.url}\n referer=${t.referer}\n user-agent=${UserAgent}\n out=${t.title}.flv\n split=${i}\n`.trim()})}).join("\n")}\n`.trim()}}}class s extends r{static async test(){if(!document.URL.includes("/www.bilibili.com/video/av")){return false}return await SpinQuery.select("#multi_page")!==null}async getItemList(){if(this.itemList.length>0){return this.itemList}const t=`https://api.bilibili.com/x/web-interface/view?aid=${unsafeWindow.aid}`;const e=await Ajax.getJson(t);if(e.code!==0){Toast.error(`获取视频选集列表失败, message=${e.message}`,"批量下载");return""}const i=e.data.pages;if(i===undefined){Toast.error(`获取视频选集列表失败, 没有找到选集信息.`,"批量下载");return""}this.itemList=i.map(t=>{return{title:`P${t.page} ${t.part}`,cid:t.cid,aid:unsafeWindow.aid}});return this.itemList}async collectData(t){const e=[];for(const i of(await this.getItemList()).filter(this.itemFilter)){const r=`https://api.bilibili.com/x/player/playurl?avid=${i.aid}&cid=${i.cid}&qn=${t}&otype=json`;const s=await Ajax.getJsonWithCredentials(r);const n=s.data||s.result||s;if(n.quality!==t){console.warn(`${i.title} 不支持所选画质, 已回退到较低画质. (quality=${n.quality})`)}const a=n.durl.map(t=>{return{length:t.length,size:t.size,url:t.url}});e.push({fragments:a,title:i.title.replace(/[\/\\:\*\?"<>\|]/g," "),totalSize:a.map(t=>t.size).reduce((t,e)=>t+e),cid:i.cid,referer:document.URL.replace(window.location.search,"")})}return JSON.stringify(e)}}class n extends r{static async test(){return document.URL.includes("/www.bilibili.com/bangumi")}async getItemList(){if(this.itemList.length>0){return this.itemList}const t=document.querySelector("meta[property='og:url']");if(t===null){Toast.error("获取番剧数据失败: 无法找到 Season ID","批量下载");return""}const e=t.getAttribute("content").match(/play\/ss(\d+)/)[1];if(e===undefined){Toast.error("获取番剧数据失败: 无法解析 Season ID","批量下载");return""}const i=await Ajax.getJson(`https://api.bilibili.com/pgc/web/season/section?season_id=${e}`);if(i.code!==0){Toast.error(`获取番剧数据失败: 无法获取番剧集数列表, message=${i.message}`,"批量下载");return""}this.itemList=i.result.main_section.episodes.map((t,e)=>{return{aid:t.aid,cid:t.cid,title:t.long_title?`${t.title} - ${t.long_title}`:`${e+1} - ${t.title}`}});return this.itemList}async collectData(t){const e=[];for(const i of(await this.getItemList()).filter(this.itemFilter)){const r=`https://api.bilibili.com/pgc/player/web/playurl?avid=${i.aid}&cid=${i.cid}&qn=${t}&otype=json`;const s=await Ajax.getJsonWithCredentials(r);const n=s.data||s.result||s;if(n.quality!==t){console.warn(`${i.title} 不支持所选画质, 已回退到较低画质. (quality=${n.quality})`)}const a=n.durl.map(t=>{return{length:t.length,size:t.size,url:t.url}});e.push({fragments:a,title:i.title.replace(/[\/\\:\*\?"<>\|]/g," "),totalSize:a.map(t=>t.size).reduce((t,e)=>t+e),cid:i.cid,referer:document.URL.replace(window.location.search,"")})}return JSON.stringify(e)}}const a=[n,s];let o=null;class c{constructor(){this.itemFilter=(()=>true)}static async test(){for(const t of a){if(await t.test()===true){o=t;return true}}o=null;return false}getExtractor(){if(o===null){logError("[批量下载] 未找到合适的解析模块.");throw new Error(`[Batch Download] module not found.`)}const t=new o;t.itemFilter=this.itemFilter;return t}async getItemList(){const t=this.getExtractor();return await t.getItemList()}async collectData(t,e){const i=this.getExtractor();const r=await i.collectData(t.quality);e.dismiss();return r}async collectAria2(t,e,i){const r=this.getExtractor();const s=await r.collectAria2(t.quality,i);e.dismiss();return s}}return{export:{BatchExtractor:c}}}})();
|
||||
(()=>{return(t,e)=>{const i=12;class r{constructor(){this.itemList=[];this.itemFilter=(()=>true)}async collectAria2(r,s){const n=JSON.parse(await this.collectData(r));if(s){const r=t.aria2RpcOption;const{sendRpc:s}=await e.importAsync("aria2-rpc");for(const t of n){const e=t.fragments.map((e,s)=>{let n="";if(t.fragments.length>1){n=" - "+(s+1)}const a=[];if(r.secretKey!==""){a.push(`token:${r.secretKey}`)}a.push([e.url]);a.push({referer:document.URL.replace(window.location.search,""),"user-agent":UserAgent,out:`${t.title}${n}.flv`,split:i,dir:r.baseDir+r.dir||undefined,"max-download-limit":r.maxDownloadLimit||undefined});const o=encodeURIComponent(`${t.title}${n}`);return{params:a,id:o}});await s(e,true)}}else{return`\n# Generated by Bilibili Evolved Video Export\n# https://github.com/the1812/Bilibili-Evolved/\n${n.map(t=>{return t.fragments.map(e=>{return`\n${e.url}\n referer=${t.referer}\n user-agent=${UserAgent}\n out=${t.title}.flv\n split=${i}\n`.trim()})}).join("\n")}\n`.trim()}}}class s extends r{static async test(){if(!document.URL.includes("/www.bilibili.com/video/av")){return false}return await SpinQuery.select("#multi_page")!==null}async getItemList(){if(this.itemList.length>0){return this.itemList}const t=`https://api.bilibili.com/x/web-interface/view?aid=${unsafeWindow.aid}`;const e=await Ajax.getJson(t);if(e.code!==0){Toast.error(`获取视频选集列表失败, message=${e.message}`,"批量下载");return[]}const i=e.data.pages;if(i===undefined){Toast.error(`获取视频选集列表失败, 没有找到选集信息.`,"批量下载");return[]}this.itemList=i.map(t=>{return{title:`P${t.page} ${t.part}`,cid:t.cid,aid:unsafeWindow.aid}});return this.itemList}async collectData(t){const e=[];for(const i of(await this.getItemList()).filter(this.itemFilter)){const r=`https://api.bilibili.com/x/player/playurl?avid=${i.aid}&cid=${i.cid}&qn=${t}&otype=json`;const s=await Ajax.getJsonWithCredentials(r);const n=s.data||s.result||s;if(n.quality!==t){console.warn(`${i.title} 不支持所选画质, 已回退到较低画质. (quality=${n.quality})`)}const a=n.durl.map(t=>{return{length:t.length,size:t.size,url:t.url}});e.push({fragments:a,title:i.title.replace(/[\/\\:\*\?"<>\|]/g," "),totalSize:a.map(t=>t.size).reduce((t,e)=>t+e),cid:i.cid,referer:document.URL.replace(window.location.search,"")})}return JSON.stringify(e)}}class n extends r{static async test(){return document.URL.includes("/www.bilibili.com/bangumi")}async getItemList(){if(this.itemList.length>0){return this.itemList}const t=document.querySelector("meta[property='og:url']");if(t===null){Toast.error("获取番剧数据失败: 无法找到 Season ID","批量下载");return[]}const e=t.getAttribute("content").match(/play\/ss(\d+)/)[1];if(e===undefined){Toast.error("获取番剧数据失败: 无法解析 Season ID","批量下载");return[]}const i=await Ajax.getJson(`https://api.bilibili.com/pgc/web/season/section?season_id=${e}`);if(i.code!==0){Toast.error(`获取番剧数据失败: 无法获取番剧集数列表, message=${i.message}`,"批量下载");return[]}this.itemList=i.result.main_section.episodes.map((t,e)=>{return{aid:t.aid,cid:t.cid,title:t.long_title?`${t.title} - ${t.long_title}`:`${e+1} - ${t.title}`}});return this.itemList}async collectData(t){const e=[];for(const i of(await this.getItemList()).filter(this.itemFilter)){const r=`https://api.bilibili.com/pgc/player/web/playurl?avid=${i.aid}&cid=${i.cid}&qn=${t}&otype=json`;const s=await Ajax.getJsonWithCredentials(r);const n=s.data||s.result||s;if(n.quality!==t){console.warn(`${i.title} 不支持所选画质, 已回退到较低画质. (quality=${n.quality})`)}const a=n.durl.map(t=>{return{length:t.length,size:t.size,url:t.url}});e.push({fragments:a,title:i.title.replace(/[\/\\:\*\?"<>\|]/g," "),totalSize:a.map(t=>t.size).reduce((t,e)=>t+e),cid:i.cid,referer:document.URL.replace(window.location.search,"")})}return JSON.stringify(e)}}const a=[n,s];let o;class c{constructor(){this.itemFilter=(()=>true)}static async test(){for(const t of a){if(await t.test()===true){o=t;return true}}return false}getExtractor(){if(o===null){logError("[批量下载] 未找到合适的解析模块.");throw new Error(`[Batch Download] module not found.`)}const t=new o;t.itemFilter=this.itemFilter;return t}async getItemList(){const t=this.getExtractor();return await t.getItemList()}async collectData(t,e){const i=this.getExtractor();const r=await i.collectData(t.quality);e.dismiss();return r}async collectAria2(t,e,i){const r=this.getExtractor();const s=await r.collectAria2(t.quality,i);e.dismiss();return s}}return{export:{BatchExtractor:c}}}})();
|
||||
@ -10,7 +10,7 @@
|
||||
"auto-continue.min.js": "96CD47C367D7397CE1467A69764409BAEFF16E8F535BC8E15523CD0B8A86687E",
|
||||
"auto-draw.min.js": "AE72CF2623DF2D15AD4AF82D125FFAF7EF5B1E6B36D9E9AC646AC98AA6AA8698",
|
||||
"auto-play.min.js": "DC9938AC15DADDC9D88DCA0C9BE64BE142C37D32CB85E42E23DAB2A7378531E5",
|
||||
"batch-download.min.js": "A0F979F33A06E23D3800525CF70700170565EBD98579CE394ECE4CC66AAEEB5A",
|
||||
"batch-download.min.js": "2937E9622D79F7512405902587DDDA3C647453ADAE7B18245CE934808FA02712",
|
||||
"biliplus-redirect.min.js": "9882D14DAC5C103212A101A5168883C41B4B3B2737FD5F222DC343D0304FF8AF",
|
||||
"blackboard.min.js": "8BB9CCDE72302AE7DE39D66F9231EFB82BA27DDA799BA4AAAC06F6DE802661BF",
|
||||
"blackboard.vue.min.js": "8F1984AB69B029701DC88F33568EF1881D0C54EEC72D65EA3029CF2F68BD6674",
|
||||
@ -47,7 +47,7 @@
|
||||
"download-video-package.min.js": "B9BFBA2E5E9267ED901359BEE10E0B92619A429F4B09F7E96B15EE6B9CD81A30",
|
||||
"download-video.min.css": "95FF9DA3C1FFF1C1134D70AE3BB609049B7D5D3FBB71CAAAE84C8742C51AD841",
|
||||
"download-video.min.html": "DDC359FD5551C01572CDE93CF7A8C63D4DD956568930AA97AA2C6FFEA7A6FC2A",
|
||||
"download-video.min.js": "0066CA903974E0F70EB4588D9A4E9FE697C7D63815C4A95056B95A2D79EBA246",
|
||||
"download-video.min.js": "BB4E9B82DB9109ACC45488F081BE5875CB3BE2EC351E8DFE3192CA911AEB6A1F",
|
||||
"dpi-img.vue.min.js": "3D9D93079365DCABA8DC8970F051AD1A7F25C3EFFF850CD1000C4312E9B910DD",
|
||||
"expand-danmaku.min.js": "B21658C40085AEA8DC49652AE62EB8610BB2EBEAF7A9C9AF69EF3B11E08EEC8D",
|
||||
"expand-description.min.css": "58C7710A50521B80F7D872BDC4C652610D84C4FABC6874BA66DA37B4F8759224",
|
||||
|
||||
BIN
min/bundle.zip
BIN
min/bundle.zip
Binary file not shown.
2
min/download-video.min.js
vendored
2
min/download-video.min.js
vendored
File diff suppressed because one or more lines are too long
13
src/video/download-video/batch-download.d.ts
vendored
13
src/video/download-video/batch-download.d.ts
vendored
@ -1,13 +0,0 @@
|
||||
export interface BatchItem {
|
||||
title: string
|
||||
aid: string
|
||||
cid: string
|
||||
}
|
||||
export class BatchExtractor {
|
||||
itemList: BatchItem[]
|
||||
itemFilter: (item: BatchItem) => boolean
|
||||
static test(): Promise<boolean>
|
||||
getItemList(): Promise<BatchItem[]>
|
||||
collectData(format: any, toast: Toast): Promise<string>
|
||||
collectAria2(format: any, toast: Toast, rpc?: boolean): Promise<string>
|
||||
}
|
||||
@ -1,18 +1,29 @@
|
||||
const fragmentSplitFactor = 12
|
||||
class Batch {
|
||||
constructor () {
|
||||
this.itemList = []
|
||||
this.itemFilter = () => true
|
||||
interface BatchItem {
|
||||
aid: number | string
|
||||
cid: number | string
|
||||
title: string
|
||||
}
|
||||
async getItemList() {}
|
||||
async collectData () {}
|
||||
async collectAria2 (quality, rpc) {
|
||||
const json = JSON.parse(await this.collectData(quality))
|
||||
interface RawItemFragment { length: number, size: number, url: string }
|
||||
interface RawItem {
|
||||
fragments: RawItemFragment[]
|
||||
title: string
|
||||
totalSize: number
|
||||
cid: number | string
|
||||
referer: string
|
||||
}
|
||||
abstract class Batch {
|
||||
itemList: BatchItem[] = []
|
||||
itemFilter: (item: BatchItem) => boolean = () => true
|
||||
abstract async getItemList(): Promise<BatchItem[]>
|
||||
abstract async collectData(quality: number | string): Promise<string>
|
||||
async collectAria2(quality: number | string, rpc: boolean) {
|
||||
const json: RawItem[] = JSON.parse(await this.collectData(quality))
|
||||
if (rpc) {
|
||||
const option = settings.aria2RpcOption
|
||||
const { sendRpc } = await import('./aria2-rpc')
|
||||
for (const item of json) {
|
||||
const params = item.fragments.map((fragment, index) => {
|
||||
const params = item.fragments.map((fragment: { url: string }, index: number) => {
|
||||
let indexNumber = ''
|
||||
if (item.fragments.length > 1) {
|
||||
indexNumber = ' - ' + (index + 1)
|
||||
@ -72,14 +83,14 @@ class VideoEpisodeBatch extends Batch {
|
||||
const json = await Ajax.getJson(api)
|
||||
if (json.code !== 0) {
|
||||
Toast.error(`获取视频选集列表失败, message=${json.message}`, '批量下载')
|
||||
return ''
|
||||
return []
|
||||
}
|
||||
const pages = json.data.pages
|
||||
if (pages === undefined) {
|
||||
Toast.error(`获取视频选集列表失败, 没有找到选集信息.`, '批量下载')
|
||||
return ''
|
||||
return []
|
||||
}
|
||||
this.itemList = pages.map(page => {
|
||||
this.itemList = pages.map((page: any) => {
|
||||
return {
|
||||
title: `P${page.page} ${page.part}`,
|
||||
cid: page.cid,
|
||||
@ -88,7 +99,7 @@ class VideoEpisodeBatch extends Batch {
|
||||
})
|
||||
return this.itemList
|
||||
}
|
||||
async collectData (quality) {
|
||||
async collectData(quality: number | string) {
|
||||
const result = []
|
||||
for (const item of (await this.getItemList()).filter(this.itemFilter)) {
|
||||
const url = `https://api.bilibili.com/x/player/playurl?avid=${item.aid}&cid=${item.cid}&qn=${quality}&otype=json`
|
||||
@ -97,7 +108,7 @@ class VideoEpisodeBatch extends Batch {
|
||||
if (data.quality !== quality) {
|
||||
console.warn(`${item.title} 不支持所选画质, 已回退到较低画质. (quality=${data.quality})`)
|
||||
}
|
||||
const fragments = data.durl.map(it => {
|
||||
const fragments: RawItemFragment[] = data.durl.map((it: any) => {
|
||||
return {
|
||||
length: it.length,
|
||||
size: it.size,
|
||||
@ -126,19 +137,19 @@ class BangumiBatch extends Batch {
|
||||
const metaUrl = document.querySelector("meta[property='og:url']")
|
||||
if (metaUrl === null) {
|
||||
Toast.error('获取番剧数据失败: 无法找到 Season ID', '批量下载')
|
||||
return ''
|
||||
return []
|
||||
}
|
||||
const seasonId = metaUrl.getAttribute('content').match(/play\/ss(\d+)/)[1]
|
||||
const seasonId = metaUrl.getAttribute('content')!.match(/play\/ss(\d+)/)![1]
|
||||
if (seasonId === undefined) {
|
||||
Toast.error('获取番剧数据失败: 无法解析 Season ID', '批量下载')
|
||||
return ''
|
||||
return []
|
||||
}
|
||||
const json = await Ajax.getJson(`https://api.bilibili.com/pgc/web/season/section?season_id=${seasonId}`)
|
||||
if (json.code !== 0) {
|
||||
Toast.error(`获取番剧数据失败: 无法获取番剧集数列表, message=${json.message}`, '批量下载')
|
||||
return ''
|
||||
return []
|
||||
}
|
||||
this.itemList = json.result.main_section.episodes.map((it, index) => {
|
||||
this.itemList = json.result.main_section.episodes.map((it: any, index: number) => {
|
||||
return {
|
||||
aid: it.aid,
|
||||
cid: it.cid,
|
||||
@ -147,7 +158,7 @@ class BangumiBatch extends Batch {
|
||||
})
|
||||
return this.itemList
|
||||
}
|
||||
async collectData (quality) {
|
||||
async collectData(quality: string | number) {
|
||||
const result = []
|
||||
for (const item of (await this.getItemList()).filter(this.itemFilter)) {
|
||||
const url = `https://api.bilibili.com/pgc/player/web/playurl?avid=${item.aid}&cid=${item.cid}&qn=${quality}&otype=json`
|
||||
@ -156,7 +167,7 @@ class BangumiBatch extends Batch {
|
||||
if (data.quality !== quality) {
|
||||
console.warn(`${item.title} 不支持所选画质, 已回退到较低画质. (quality=${data.quality})`)
|
||||
}
|
||||
const fragments = data.durl.map(it => {
|
||||
const fragments: RawItemFragment[] = data.durl.map((it: any) => {
|
||||
return {
|
||||
length: it.length,
|
||||
size: it.size,
|
||||
@ -175,11 +186,9 @@ class BangumiBatch extends Batch {
|
||||
}
|
||||
}
|
||||
const extractors = [BangumiBatch, VideoEpisodeBatch]
|
||||
let ExtractorClass = null
|
||||
let ExtractorClass: new() => Batch
|
||||
export class BatchExtractor {
|
||||
constructor() {
|
||||
this.itemFilter = () => true
|
||||
}
|
||||
itemFilter: (item: BatchItem) => boolean = () => true
|
||||
static async test() {
|
||||
for (const e of extractors) {
|
||||
if (await e.test() === true) {
|
||||
@ -187,7 +196,6 @@ export class BatchExtractor {
|
||||
return true
|
||||
}
|
||||
}
|
||||
ExtractorClass = null
|
||||
return false
|
||||
}
|
||||
getExtractor() {
|
||||
@ -203,13 +211,13 @@ export class BatchExtractor {
|
||||
const extractor = this.getExtractor()
|
||||
return await extractor.getItemList()
|
||||
}
|
||||
async collectData (format, toast) {
|
||||
async collectData(format: { quality: number | string }, toast: Toast) {
|
||||
const extractor = this.getExtractor()
|
||||
const result = await extractor.collectData(format.quality)
|
||||
toast.dismiss()
|
||||
return result
|
||||
}
|
||||
async collectAria2 (format, toast, rpc) {
|
||||
async collectAria2(format: { quality: number | string }, toast: Toast, rpc: boolean) {
|
||||
const extractor = this.getExtractor()
|
||||
const result = await extractor.collectAria2(format.quality, rpc)
|
||||
toast.dismiss()
|
||||
@ -834,16 +834,16 @@ async function loadPanel() {
|
||||
this.episodeList = []
|
||||
return
|
||||
}
|
||||
this.batchExtractor = new BatchExtractor()
|
||||
const batchExtractor = this.batchExtractor = new BatchExtractor()
|
||||
this.batch = true
|
||||
this.episodeList = (await this.batchExtractor.getItemList()).map((item: EpisodeItem, index: number) => {
|
||||
this.episodeList = (await batchExtractor.getItemList()).map((item, index) => {
|
||||
return {
|
||||
aid: item.aid,
|
||||
cid: item.cid,
|
||||
title: item.title,
|
||||
index,
|
||||
checked: true,
|
||||
}
|
||||
} as EpisodeItem
|
||||
})
|
||||
},
|
||||
cancelDownload() {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user