Skip to content

Commit 2600d73

Browse files
authored
Merge pull request #54 from ScottSloan/dev
更新 Version 1.55.0
2 parents 3f20e04 + 8b290b9 commit 2600d73

23 files changed

+1478
-363
lines changed

README.md

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Bili23 Downloader GUI
99

1010
### **导航**
1111
+ [下载地址](https://github.com/ScottSloan/Bili23-Downloader/releases)
12-
+ [使用说明](#使用说明)
12+
+ [使用说明](#使用说明)
1313
+ [更新日志](#更新日志)
1414
+ [免责声明](#免责声明)
1515
+ [开源许可](#开源许可)
@@ -32,7 +32,7 @@ Bili23 Downloader GUI
3232
| 剧集(含番剧、电影、纪录片、国创、电视剧、综艺) | 解析下载 | https://www.bilibili.com/bangumi/play/ss45574 |
3333
| 课程 | 解析下载 | https://www.bilibili.com/cheese/play/ep69165 |
3434
| 直播 | m3u8直链解析、录制 | https://live.bilibili.com/1 |
35-
| b23.tv 短链接 | 解析下载 | https://b23.tv/BV1UG411f7K1 |
35+
| b23.tv、bili2233.cn 短链接 | 解析下载 | https://b23.tv/BV1UG411f7K1 |
3636
| 活动页链接 | 解析下载 | https://www.bilibili.com/blackboard/topic/activity-jjR1nNRUF.html
3737

3838
**注意:本程序不提供付费视频解析服务,请自行登录大会员账号后再进行使用。**
@@ -62,22 +62,22 @@ Bili23 Downloader GUI
6262
程序支持多线程下载(最多 8 线程)、并行下载(上限为 8 个,支持动态调整)、断点续传、下载限速、出错重试等功能。
6363

6464
# 更新日志
65-
### **Version 1.54 (2025/01/01)**
66-
Version 1.54.0 正式版 发布
65+
### **Version 1.55 (2025/01/28)**
66+
Version 1.55.0 正式版发布
6767

6868
本次更新内容:
69-
* 新增剧集列表显示菜单,可快速修改剧集列表显示方式
70-
* 支持解析下载课程类链接
71-
* 支持替换音视频流 CDN,可自动切换或手动指定
72-
* 支持下载视频字幕,可保存为 srt, txt, json 三种格式
73-
* 支持解析合集中的分 P 视频
74-
* 部分接口添加 wbi 签名,避免账号被风控
69+
* 剧集列表添加右键菜单,支持复制视频标题
70+
* 支持查看视频详细信息
71+
* 支持解析 bili2233.cn 短链接
72+
* 支持 flv 格式下载,解决部分远古视频无法下载的问题
73+
* 设置页新增“高级”选项卡
74+
* 优化 CDN 切换相关体验
75+
* 自动添加的序号调整为补零序号,长度随下载总数而变化,例如:01、001
7576
* 优化部分界面显示效果
76-
* 优化异常处理机制
77-
* 程序配置文件采用 json 格式存储
78-
* 修复设置合成页面的修改无法保存的问题
79-
* 修复手动指定播放器无效的问题
80-
* 修复部分情况下视频下载失败的问题
77+
* 下载失败时支持自动重试
78+
* 修复图标缩放异常的问题
79+
* 修复默认情况下剧集列表显示为单个的问题
80+
* 修复跳转链接无法解析的问题
8181

8282
# 免责声明
8383
本项目仅供个人学习与研究用途,任何通过本项目下载的内容仅限于个人使用,用户自行承担使用本项目可能带来的所有风险。

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "bili23-downloader"
3-
version = "1.54.0"
3+
version = "1.55.0"
44
description = "跨平台的 B 站视频下载工具,支持 Windows、Linux、macOS 三平台,下载 B 站视频/番剧/电影/纪录片 等资源 "
55
readme = "README.md"
66
license = "MIT License"

src/gui/dialog/cdn.py

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
import re
2+
import wx
3+
import subprocess
4+
from concurrent.futures import ThreadPoolExecutor
5+
6+
from utils.config import Config
7+
from utils.common.map import cdn_map
8+
9+
class ChangeCDNDialog(wx.Dialog):
10+
def __init__(self, parent):
11+
wx.Dialog.__init__(self, parent, -1, "更改 CDN")
12+
13+
self.init_UI()
14+
15+
self.Bind_EVT()
16+
17+
self.init_utils()
18+
19+
self.CenterOnParent()
20+
21+
def init_UI(self):
22+
def _get_scale_size(_size: tuple):
23+
match Config.Sys.platform:
24+
case "windows":
25+
return self.FromDIP(_size)
26+
27+
case "linux" | "darwin":
28+
return wx.DefaultSize
29+
30+
cdn_lab = wx.StaticText(self, -1, "CDN 列表")
31+
32+
self.cdn_list = wx.ListCtrl(self, -1, size = self.FromDIP((650, 250)), style = wx.LC_REPORT | wx.LC_SINGLE_SEL)
33+
self.cdn_list.EnableCheckBoxes(True)
34+
35+
custom_lab = wx.StaticText(self, -1, "自定义")
36+
self.custom_box = wx.TextCtrl(self, -1, size = _get_scale_size((240, 24)))
37+
self.add_btn = wx.Button(self, -1, "添加", size = _get_scale_size((80, 28)))
38+
self.delete_btn = wx.Button(self, -1, "删除", size = _get_scale_size((80, 28)))
39+
40+
self.ping_btn = wx.Button(self, -1, "Ping 测试", size = _get_scale_size((100, 28)))
41+
42+
action_hbox = wx.BoxSizer(wx.HORIZONTAL)
43+
action_hbox.Add(custom_lab, 0, wx.ALL & (~wx.TOP) & (~wx.BOTTOM) | wx.ALIGN_CENTER, 10)
44+
action_hbox.Add(self.custom_box, 0, wx.ALL & (~wx.TOP) & (~wx.BOTTOM) & (~wx.LEFT) | wx.ALIGN_CENTER, 10)
45+
action_hbox.Add(self.add_btn, 0, wx.ALL & (~wx.TOP) & (~wx.BOTTOM) & (~wx.LEFT), 10)
46+
action_hbox.Add(self.delete_btn, 0, wx.ALL & (~wx.TOP) & (~wx.BOTTOM) & (~wx.LEFT), 10)
47+
action_hbox.AddStretchSpacer()
48+
action_hbox.Add(self.ping_btn, 0, wx.ALL & (~wx.TOP) & (~wx.BOTTOM), 10)
49+
50+
bottom_line = wx.StaticLine(self, -1)
51+
52+
self.ok_btn = wx.Button(self, wx.ID_OK, "确定", size = _get_scale_size((80, 30)))
53+
self.cancel_btn = wx.Button(self, wx.ID_CANCEL, "取消", size = _get_scale_size((80, 30)))
54+
55+
bottom_hbox = wx.BoxSizer(wx.HORIZONTAL)
56+
bottom_hbox.AddStretchSpacer(1)
57+
bottom_hbox.Add(self.ok_btn, 0, wx.ALL & (~wx.TOP), 10)
58+
bottom_hbox.Add(self.cancel_btn, 0, wx.ALL & (~wx.TOP) & (~wx.LEFT), 10)
59+
60+
vbox = wx.BoxSizer(wx.VERTICAL)
61+
vbox.Add(cdn_lab, 0, wx.ALL & (~wx.BOTTOM), 10)
62+
vbox.Add(self.cdn_list, 0, wx.ALL | wx.EXPAND, 10)
63+
vbox.Add(action_hbox, 0, wx.EXPAND)
64+
vbox.Add(bottom_line, 0, wx.ALL | wx.EXPAND, 10)
65+
vbox.Add(bottom_hbox, 0, wx.EXPAND)
66+
67+
self.SetSizerAndFit(vbox)
68+
69+
def Bind_EVT(self):
70+
self.cdn_list.Bind(wx.EVT_LIST_ITEM_CHECKED, self.onCheckEVT)
71+
self.cdn_list.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.onItemActivateEVT)
72+
73+
self.ping_btn.Bind(wx.EVT_BUTTON, self.onPingTestEVT)
74+
self.add_btn.Bind(wx.EVT_BUTTON, self.onAddCDNEVT)
75+
self.delete_btn.Bind(wx.EVT_BUTTON, self.onDeleteCDNEVT)
76+
self.ok_btn.Bind(wx.EVT_BUTTON, self.onConfirm)
77+
78+
def init_utils(self):
79+
def init_listctrl():
80+
self.cdn_list.AppendColumn("序号", width = self.FromDIP(75))
81+
self.cdn_list.AppendColumn("CDN", width = self.FromDIP(280))
82+
self.cdn_list.AppendColumn("提供商", width = self.FromDIP(140))
83+
self.cdn_list.AppendColumn("延迟", width = self.FromDIP(100))
84+
85+
def init_cdn_list():
86+
for key, value in cdn_map.items():
87+
index = self.cdn_list.Append([str(key + 1), value["cdn"], value["provider"], "未知"])
88+
89+
if value["cdn"] == Config.Advanced.custom_cdn:
90+
self.cdn_list.CheckItem(index)
91+
92+
self.update_index()
93+
94+
self._last_index = -1
95+
96+
init_listctrl()
97+
init_cdn_list()
98+
99+
def get_cdn(self):
100+
return self.cdn_list.GetItemText(self._last_index, 1)
101+
102+
def onConfirm(self, event):
103+
if self._last_index == -1 or not self.cdn_list.IsItemChecked(self._last_index):
104+
wx.MessageDialog(self, "更改失败\n\n请选择需要更改的 CDN", "警告", wx.ICON_WARNING).ShowModal()
105+
return
106+
107+
event.Skip()
108+
109+
def onPingTestEVT(self, event):
110+
def worker(index: int, cdn: str):
111+
def update(value):
112+
self.cdn_list.SetItem(index, 3, value)
113+
114+
def get_ping_cmd() -> str:
115+
match Config.Sys.platform:
116+
case "windows":
117+
return f"ping {cdn}"
118+
119+
case "linux" | "darwin":
120+
return f"ping {cdn} -c 4"
121+
122+
def get_latency():
123+
match Config.Sys.platform:
124+
case "windows":
125+
return re.findall(r"Average = ([0-9]*)", process.stdout)
126+
127+
case "linux" | "darwin":
128+
_temp = re.findall(r"time=([0-9]*)", process.stdout)
129+
130+
if _temp:
131+
return [int(sum(list(map(int, _temp))) / len(_temp))]
132+
else:
133+
return None
134+
135+
wx.CallAfter(update, "正在检测...")
136+
137+
process = subprocess.run(get_ping_cmd(), stdout = subprocess.PIPE, stderr = subprocess.STDOUT, shell = True, text = True, encoding = "utf-8")
138+
latency = get_latency()
139+
140+
if latency:
141+
result = f"{latency[0]}ms"
142+
else:
143+
result = "请求超时"
144+
145+
wx.CallAfter(update, result)
146+
147+
thread_pool = ThreadPoolExecutor(max_workers = 5)
148+
149+
for i in range(self.cdn_list.GetItemCount()):
150+
item: wx.ListItem = self.cdn_list.GetItem(i, 1)
151+
152+
thread_pool.submit(worker, i, item.GetText())
153+
154+
def onCheckEVT(self, event: wx.ListEvent):
155+
index = event.GetIndex()
156+
157+
if self._last_index != -1 and self._last_index != index:
158+
self.cdn_list.CheckItem(self._last_index, False)
159+
160+
self.cdn_list.Select(index)
161+
162+
self._last_index = index
163+
164+
def onItemActivateEVT(self, event: wx.ListEvent):
165+
index = event.GetIndex()
166+
167+
self.cdn_list.CheckItem(index)
168+
169+
def onAddCDNEVT(self, event):
170+
if not self.custom_box.GetValue():
171+
wx.MessageDialog(self, "添加失败\n\n请输入要添加的 CDN", "警告", wx.ICON_WARNING).ShowModal()
172+
self.custom_box.SetFocus()
173+
return
174+
175+
for i in range(self.cdn_list.GetItemCount()):
176+
item: wx.ListItem = self.cdn_list.GetItem(i, 1)
177+
178+
if item.GetText() == self.custom_box.GetValue():
179+
wx.MessageDialog(self, "添加失败\n\n已存在相同的 CDN,无法重复添加", "警告", wx.ICON_WARNING).ShowModal()
180+
self.custom_box.SetFocus()
181+
return
182+
183+
self.cdn_list.SetFocus()
184+
185+
index = self.cdn_list.Append(["-", self.custom_box.GetValue(), "自定义", "未知"])
186+
187+
Config.Advanced.custom_cdn_list.append(self.custom_box.GetValue())
188+
189+
self.cdn_list.Focus(index)
190+
self.cdn_list.Select(index)
191+
192+
self.update_index()
193+
194+
def onDeleteCDNEVT(self, event):
195+
if self._last_index == -1 or not self.cdn_list.IsItemChecked(self._last_index):
196+
wx.MessageDialog(self, "删除失败\n\n请选择要删除的 CDN", "警告", wx.ICON_WARNING).ShowModal()
197+
self.cdn_list.SetFocus()
198+
return
199+
200+
cdn = self.cdn_list.GetItemText(self._last_index, 1)
201+
202+
if self.cdn_list.GetItemText(self._last_index, 2) != "自定义":
203+
wx.MessageDialog(self, "删除失败\n\n仅支持删除自定义的 CDN", "警告", wx.ICON_WARNING).ShowModal()
204+
self.cdn_list.SetFocus()
205+
return
206+
207+
self.cdn_list.DeleteItem(self._last_index)
208+
209+
Config.Advanced.custom_cdn_list.remove(cdn)
210+
211+
self._last_index = -1
212+
213+
self.update_index()
214+
215+
def update_index(self):
216+
for i in range(self.cdn_list.GetItemCount()):
217+
self.cdn_list.SetItem(i, 0, str(i + 1))

0 commit comments

Comments
 (0)