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
|
||||
}
|
||||
async getItemList() {}
|
||||
async collectData () {}
|
||||
async collectAria2 (quality, rpc) {
|
||||
const json = JSON.parse(await this.collectData(quality))
|
||||
interface BatchItem {
|
||||
aid: number | string
|
||||
cid: number | string
|
||||
title: string
|
||||
}
|
||||
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)
|
||||
@ -43,8 +54,8 @@ class Batch {
|
||||
# Generated by Bilibili Evolved Video Export
|
||||
# https://github.com/the1812/Bilibili-Evolved/
|
||||
${json.map(item => {
|
||||
return item.fragments.map(f => {
|
||||
return `
|
||||
return item.fragments.map(f => {
|
||||
return `
|
||||
${f.url}
|
||||
referer=${item.referer}
|
||||
user-agent=${UserAgent}
|
||||
@ -58,7 +69,7 @@ ${f.url}
|
||||
}
|
||||
}
|
||||
class VideoEpisodeBatch extends Batch {
|
||||
static async test () {
|
||||
static async test() {
|
||||
if (!document.URL.includes('/www.bilibili.com/video/av')) {
|
||||
return false
|
||||
}
|
||||
@ -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,
|
||||
@ -116,7 +127,7 @@ class VideoEpisodeBatch extends Batch {
|
||||
}
|
||||
}
|
||||
class BangumiBatch extends Batch {
|
||||
static async test () {
|
||||
static async test() {
|
||||
return document.URL.includes('/www.bilibili.com/bangumi')
|
||||
}
|
||||
async getItemList() {
|
||||
@ -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,22 +186,19 @@ class BangumiBatch extends Batch {
|
||||
}
|
||||
}
|
||||
const extractors = [BangumiBatch, VideoEpisodeBatch]
|
||||
let ExtractorClass = null
|
||||
let ExtractorClass: new() => Batch
|
||||
export class BatchExtractor {
|
||||
constructor() {
|
||||
this.itemFilter = () => true
|
||||
}
|
||||
static async test () {
|
||||
itemFilter: (item: BatchItem) => boolean = () => true
|
||||
static async test() {
|
||||
for (const e of extractors) {
|
||||
if (await e.test() === true) {
|
||||
ExtractorClass = e
|
||||
return true
|
||||
}
|
||||
}
|
||||
ExtractorClass = null
|
||||
return false
|
||||
}
|
||||
getExtractor () {
|
||||
getExtractor() {
|
||||
if (ExtractorClass === null) {
|
||||
logError('[批量下载] 未找到合适的解析模块.')
|
||||
throw new Error(`[Batch Download] module not found.`)
|
||||
@ -199,17 +207,17 @@ export class BatchExtractor {
|
||||
extractor.itemFilter = this.itemFilter
|
||||
return extractor
|
||||
}
|
||||
async getItemList () {
|
||||
async getItemList() {
|
||||
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