mirror of
https://github.com/lanyeeee/bilibili-video-downloader.git
synced 2026-09-08 17:16:52 +08:00
feat: 给受版权保护(DRM)的视频添加提示,并跳过视频处理
This commit is contained in:
@@ -447,6 +447,7 @@ impl BiliClient {
|
|||||||
"ep_id": ep_id,
|
"ep_id": ep_id,
|
||||||
"qn": 127,
|
"qn": 127,
|
||||||
"fnval": 4048,
|
"fnval": 4048,
|
||||||
|
"drm_tech_type": 2,
|
||||||
});
|
});
|
||||||
// 发送获取课程url的请求
|
// 发送获取课程url的请求
|
||||||
let request = self
|
let request = self
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ pub struct DownloadProgress {
|
|||||||
pub json_task: JsonTask,
|
pub json_task: JsonTask,
|
||||||
pub create_ts: u64,
|
pub create_ts: u64,
|
||||||
pub completed_ts: Option<u64>,
|
pub completed_ts: Option<u64>,
|
||||||
|
pub is_drm: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DownloadProgress {
|
impl DownloadProgress {
|
||||||
@@ -138,6 +139,7 @@ impl DownloadProgress {
|
|||||||
json_task: tasks.json,
|
json_task: tasks.json,
|
||||||
create_ts,
|
create_ts,
|
||||||
completed_ts: None,
|
completed_ts: None,
|
||||||
|
is_drm: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(progress)
|
Ok(progress)
|
||||||
@@ -185,6 +187,7 @@ impl DownloadProgress {
|
|||||||
json_task: tasks.json,
|
json_task: tasks.json,
|
||||||
create_ts,
|
create_ts,
|
||||||
completed_ts: None,
|
completed_ts: None,
|
||||||
|
is_drm: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(progress)
|
Ok(progress)
|
||||||
@@ -240,7 +243,16 @@ impl DownloadProgress {
|
|||||||
tracing::debug!("{ids_string} `{filename}`音频下载任务完成");
|
tracing::debug!("{ids_string} `{filename}`音频下载任务完成");
|
||||||
}
|
}
|
||||||
|
|
||||||
if !video_process_task.is_completed() {
|
let video_process_task_is_completed = video_process_task.is_completed();
|
||||||
|
if self.is_drm && !video_process_task_is_completed {
|
||||||
|
download_task.update_progress(|p| {
|
||||||
|
p.video_process_task.skipped = true;
|
||||||
|
p.video_process_task.completed = true;
|
||||||
|
});
|
||||||
|
tracing::debug!(
|
||||||
|
"{ids_string} `{filename}`受版权保护(DRM),无法处理,已跳过视频处理任务"
|
||||||
|
);
|
||||||
|
} else if !video_process_task_is_completed {
|
||||||
video_process_task
|
video_process_task
|
||||||
.process(download_task, self, &mut player_info)
|
.process(download_task, self, &mut player_info)
|
||||||
.await
|
.await
|
||||||
@@ -359,6 +371,8 @@ impl DownloadProgress {
|
|||||||
.await
|
.await
|
||||||
.context("获取课程视频链接失败")?;
|
.context("获取课程视频链接失败")?;
|
||||||
|
|
||||||
|
self.is_drm = media_url.is_drm;
|
||||||
|
|
||||||
if video_selected && !video_completed {
|
if video_selected && !video_completed {
|
||||||
// 如果视频被选中且未完成,则准备视频任务
|
// 如果视频被选中且未完成,则准备视频任务
|
||||||
self.video_task.prepare_cheese(app, &media_url).await?;
|
self.video_task.prepare_cheese(app, &media_url).await?;
|
||||||
@@ -507,6 +521,7 @@ fn create_normal_progresses_for_single(
|
|||||||
json_task: tasks.json,
|
json_task: tasks.json,
|
||||||
create_ts,
|
create_ts,
|
||||||
completed_ts: None,
|
completed_ts: None,
|
||||||
|
is_drm: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
return Ok(vec![progress]);
|
return Ok(vec![progress]);
|
||||||
@@ -543,6 +558,7 @@ fn create_normal_progresses_for_single(
|
|||||||
json_task: tasks.json,
|
json_task: tasks.json,
|
||||||
create_ts,
|
create_ts,
|
||||||
completed_ts: None,
|
completed_ts: None,
|
||||||
|
is_drm: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
return Ok(vec![progress]);
|
return Ok(vec![progress]);
|
||||||
@@ -579,6 +595,7 @@ fn create_normal_progresses_for_single(
|
|||||||
json_task: tasks.json.clone(),
|
json_task: tasks.json.clone(),
|
||||||
create_ts,
|
create_ts,
|
||||||
completed_ts: None,
|
completed_ts: None,
|
||||||
|
is_drm: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
progresses.push(progress);
|
progresses.push(progress);
|
||||||
@@ -647,6 +664,7 @@ fn create_normal_progresses_for_season(
|
|||||||
json_task: tasks.json,
|
json_task: tasks.json,
|
||||||
create_ts,
|
create_ts,
|
||||||
completed_ts: None,
|
completed_ts: None,
|
||||||
|
is_drm: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
return Ok(vec![progress]);
|
return Ok(vec![progress]);
|
||||||
@@ -683,6 +701,7 @@ fn create_normal_progresses_for_season(
|
|||||||
json_task: tasks.json,
|
json_task: tasks.json,
|
||||||
create_ts,
|
create_ts,
|
||||||
completed_ts: None,
|
completed_ts: None,
|
||||||
|
is_drm: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
return Ok(vec![progress]);
|
return Ok(vec![progress]);
|
||||||
@@ -720,6 +739,7 @@ fn create_normal_progresses_for_season(
|
|||||||
json_task: tasks.json.clone(),
|
json_task: tasks.json.clone(),
|
||||||
create_ts,
|
create_ts,
|
||||||
completed_ts: None,
|
completed_ts: None,
|
||||||
|
is_drm: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
progresses.push(progress);
|
progresses.push(progress);
|
||||||
@@ -766,6 +786,7 @@ impl Tasks {
|
|||||||
embed_chapter_selected: config.embed_chapter,
|
embed_chapter_selected: config.embed_chapter,
|
||||||
embed_skip_selected: config.embed_skip,
|
embed_skip_selected: config.embed_skip,
|
||||||
completed: false,
|
completed: false,
|
||||||
|
skipped: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let danmaku = DanmakuTask {
|
let danmaku = DanmakuTask {
|
||||||
|
|||||||
@@ -24,11 +24,13 @@ pub struct VideoProcessTask {
|
|||||||
pub embed_chapter_selected: bool,
|
pub embed_chapter_selected: bool,
|
||||||
pub embed_skip_selected: bool,
|
pub embed_skip_selected: bool,
|
||||||
pub completed: bool,
|
pub completed: bool,
|
||||||
|
pub skipped: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VideoProcessTask {
|
impl VideoProcessTask {
|
||||||
pub fn mark_uncompleted(&mut self) {
|
pub fn mark_uncompleted(&mut self) {
|
||||||
self.completed = false;
|
self.completed = false;
|
||||||
|
self.skipped = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_completed(&self) -> bool {
|
pub fn is_completed(&self) -> bool {
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ pub struct CheeseMediaUrl {
|
|||||||
pub seek_type: String,
|
pub seek_type: String,
|
||||||
pub from: String,
|
pub from: String,
|
||||||
pub video_codecid: i64,
|
pub video_codecid: i64,
|
||||||
|
pub is_drm: bool,
|
||||||
pub no_rexcode: i64,
|
pub no_rexcode: i64,
|
||||||
pub format: String,
|
pub format: String,
|
||||||
pub support_formats: Vec<SupportFormatInCheese>,
|
pub support_formats: Vec<SupportFormatInCheese>,
|
||||||
|
|||||||
+2
-2
@@ -284,7 +284,7 @@ export type Dimension = { width: number; height: number; rotate: number }
|
|||||||
export type DimensionInBangumi = { height: number; rotate: number; width: number }
|
export type DimensionInBangumi = { height: number; rotate: number; width: number }
|
||||||
export type DimensionInWatchLater = { width: number; height: number; rotate: number }
|
export type DimensionInWatchLater = { width: number; height: number; rotate: number }
|
||||||
export type DownloadEvent = { event: "Speed"; data: { speed: string } } | { event: "TaskCreate"; data: { state: DownloadTaskState; progress: DownloadProgress } } | { event: "TaskStateUpdate"; data: { task_id: string; state: DownloadTaskState } } | { event: "TaskSleeping"; data: { task_id: string; remaining_sec: number } } | { event: "TaskDelete"; data: { task_id: string } } | { event: "ProgressPreparing"; data: { task_id: string } } | { event: "ProgressUpdate"; data: { progress: DownloadProgress } }
|
export type DownloadEvent = { event: "Speed"; data: { speed: string } } | { event: "TaskCreate"; data: { state: DownloadTaskState; progress: DownloadProgress } } | { event: "TaskStateUpdate"; data: { task_id: string; state: DownloadTaskState } } | { event: "TaskSleeping"; data: { task_id: string; remaining_sec: number } } | { event: "TaskDelete"; data: { task_id: string } } | { event: "ProgressPreparing"; data: { task_id: string } } | { event: "ProgressUpdate"; data: { progress: DownloadProgress } }
|
||||||
export type DownloadProgress = { task_id: string; episode_type: EpisodeType; aid: number; bvid: string | null; cid: number; ep_id: number | null; duration: number; pub_ts: number; collection_title: string; part_title: string | null; part_order: number | null; episode_title: string; episode_order: number; up_name: string | null; up_uid: number | null; up_avatar: string | null; episode_dir: string; filename: string; video_task: VideoTask; audio_task: AudioTask; video_process_task: VideoProcessTask; subtitle_task: SubtitleTask; danmaku_task: DanmakuTask; cover_task: CoverTask; nfo_task: NfoTask; json_task: JsonTask; create_ts: number; completed_ts: number | null }
|
export type DownloadProgress = { task_id: string; episode_type: EpisodeType; aid: number; bvid: string | null; cid: number; ep_id: number | null; duration: number; pub_ts: number; collection_title: string; part_title: string | null; part_order: number | null; episode_title: string; episode_order: number; up_name: string | null; up_uid: number | null; up_avatar: string | null; episode_dir: string; filename: string; video_task: VideoTask; audio_task: AudioTask; video_process_task: VideoProcessTask; subtitle_task: SubtitleTask; danmaku_task: DanmakuTask; cover_task: CoverTask; nfo_task: NfoTask; json_task: JsonTask; create_ts: number; completed_ts: number | null; is_drm: boolean }
|
||||||
export type DownloadTaskState = "Pending" | "Downloading" | "Paused" | "Completed" | "Failed"
|
export type DownloadTaskState = "Pending" | "Downloading" | "Paused" | "Completed" | "Failed"
|
||||||
export type Ed = { end: number; start: number }
|
export type Ed = { end: number; start: number }
|
||||||
export type EpInBangumi = { aid: number; badge: string; badge_info: BadgeInfoInBangumi; badge_type: number | null; bvid: string | null; cid: number; cover: string; dimension: DimensionInBangumi | null; duration: number | null; enable_vt: boolean; ep_id: number; from: string | null; id: number; is_view_hide: boolean; link: string; link_type: string | null; long_title: string | null; pub_time: number; pv: number; release_date: string | null; rights: RightsInBangumiEp | null; section_type: number; share_copy: string | null; share_url: string | null; short_link: string | null; showDrmLoginDialog: boolean; show_title: string | null; skip: Skip | null; status: number; subtitle: string | null; title: string; vid: string | null; icon_font: IconFont | null }
|
export type EpInBangumi = { aid: number; badge: string; badge_info: BadgeInfoInBangumi; badge_type: number | null; bvid: string | null; cid: number; cover: string; dimension: DimensionInBangumi | null; duration: number | null; enable_vt: boolean; ep_id: number; from: string | null; id: number; is_view_hide: boolean; link: string; link_type: string | null; long_title: string | null; pub_time: number; pv: number; release_date: string | null; rights: RightsInBangumiEp | null; section_type: number; share_copy: string | null; share_url: string | null; short_link: string | null; showDrmLoginDialog: boolean; show_title: string | null; skip: Skip | null; status: number; subtitle: string | null; title: string; vid: string | null; icon_font: IconFont | null }
|
||||||
@@ -420,7 +420,7 @@ export type UserStatusInCheese = { bp: number; expire_at: number; favored: numbe
|
|||||||
export type UserVideoInfo = { list: UserVideoList; page: PageInUserVideo }
|
export type UserVideoInfo = { list: UserVideoList; page: PageInUserVideo }
|
||||||
export type UserVideoList = { vlist: EpInUserVideo[] }
|
export type UserVideoList = { vlist: EpInUserVideo[] }
|
||||||
export type UserVideoSearchResult = UserVideoInfo
|
export type UserVideoSearchResult = UserVideoInfo
|
||||||
export type VideoProcessTask = { merge_selected: boolean; embed_chapter_selected: boolean; embed_skip_selected: boolean; completed: boolean }
|
export type VideoProcessTask = { merge_selected: boolean; embed_chapter_selected: boolean; embed_skip_selected: boolean; completed: boolean; skipped: boolean }
|
||||||
export type VideoQuality = "Unknown" | "240P" | "360P" | "480P" | "720P" | "720P60" | "1080P" | "AiRepair" | "1080P+" | "1080P60" | "4K" | "HDR" | "Dolby" | "8K"
|
export type VideoQuality = "Unknown" | "240P" | "360P" | "480P" | "720P" | "720P60" | "1080P" | "AiRepair" | "1080P+" | "1080P60" | "4K" | "HDR" | "Dolby" | "8K"
|
||||||
export type VideoQualityAndCodecType = { video_quality: VideoQuality; codec_type: CodecType }
|
export type VideoQualityAndCodecType = { video_quality: VideoQuality; codec_type: CodecType }
|
||||||
export type VideoTask = { selected: boolean; url: string; video_quality: VideoQuality; codec_type: CodecType; content_length: number; chunks: MediaChunk[]; completed: boolean; skipped: boolean }
|
export type VideoTask = { selected: boolean; url: string; video_quality: VideoQuality; codec_type: CodecType; content_length: number; chunks: MediaChunk[]; completed: boolean; skipped: boolean }
|
||||||
|
|||||||
@@ -149,12 +149,20 @@ function handleModifyClick() {
|
|||||||
@contextmenu="handleProgressContextMenu"
|
@contextmenu="handleProgressContextMenu"
|
||||||
@dblclick="handleProgressDoubleClick">
|
@dblclick="handleProgressDoubleClick">
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
|
<div class="relative">
|
||||||
<img
|
<img
|
||||||
class="w-224px h-140px rounded-lg object-cover lazyload"
|
class="w-224px h-140px rounded-lg object-cover lazyload"
|
||||||
:data-src="`${ensureHttps(p.cover_task.url)}@672w_378h_1c.webp`"
|
:data-src="`${ensureHttps(p.cover_task.url)}@672w_378h_1c.webp`"
|
||||||
:key="p.cover_task.url"
|
:key="p.cover_task.url"
|
||||||
alt=""
|
alt=""
|
||||||
draggable="false" />
|
draggable="false" />
|
||||||
|
<div
|
||||||
|
v-if="p.is_drm"
|
||||||
|
class="absolute top-3 left-3 z-1 bg-red-5 text-white px-1 rounded"
|
||||||
|
title="该资源受版权保护(DRM)">
|
||||||
|
🔒DRM
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="ml-2 flex flex-col w-full overflow-hidden">
|
<div class="ml-2 flex flex-col w-full overflow-hidden">
|
||||||
<div class="text-lg font-bold line-clamp-2" :title="p.episode_title">{{ p.episode_title }}</div>
|
<div class="text-lg font-bold line-clamp-2" :title="p.episode_title">{{ p.episode_title }}</div>
|
||||||
@@ -187,9 +195,24 @@ function handleModifyClick() {
|
|||||||
</span>
|
</span>
|
||||||
</ColorfulTag>
|
</ColorfulTag>
|
||||||
|
|
||||||
<ColorfulTag v-if="p.video_process_task.merge_selected" color="purple">自动合并</ColorfulTag>
|
<ColorfulTag v-if="p.video_process_task.merge_selected" color="purple">
|
||||||
<ColorfulTag v-if="p.video_process_task.embed_chapter_selected" color="purple">标记章节</ColorfulTag>
|
<span :class="{ 'text-gray': p.video_process_task.skipped }">
|
||||||
<ColorfulTag v-if="p.video_process_task.embed_skip_selected" color="purple">标记广告</ColorfulTag>
|
<span>自动合并</span>
|
||||||
|
<span v-if="p.video_process_task.skipped">(跳过)</span>
|
||||||
|
</span>
|
||||||
|
</ColorfulTag>
|
||||||
|
<ColorfulTag v-if="p.video_process_task.embed_chapter_selected" color="purple">
|
||||||
|
<span :class="{ 'text-gray': p.video_process_task.skipped }">
|
||||||
|
<span>标记章节</span>
|
||||||
|
<span v-if="p.video_process_task.skipped">(跳过)</span>
|
||||||
|
</span>
|
||||||
|
</ColorfulTag>
|
||||||
|
<ColorfulTag v-if="p.video_process_task.embed_skip_selected" color="purple">
|
||||||
|
<span :class="{ 'text-gray': p.video_process_task.skipped }">
|
||||||
|
<span>标记广告</span>
|
||||||
|
<span v-if="p.video_process_task.skipped">(跳过)</span>
|
||||||
|
</span>
|
||||||
|
</ColorfulTag>
|
||||||
|
|
||||||
<ColorfulTag v-if="p.danmaku_task.xml_selected" color="green">
|
<ColorfulTag v-if="p.danmaku_task.xml_selected" color="green">
|
||||||
<span :class="{ 'text-gray': p.danmaku_task.skipped }">
|
<span :class="{ 'text-gray': p.danmaku_task.skipped }">
|
||||||
|
|||||||
Reference in New Issue
Block a user