mirror of
https://github.com/hanxi/xiaomusic.git
synced 2025-12-06 14:52:50 +08:00
Compare commits
41 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a316053d01 | ||
|
|
0bfa4e6251 | ||
|
|
6ef7d4c6c2 | ||
|
|
fff35783c2 | ||
|
|
c8fad5ba1d | ||
|
|
956a4af3c9 | ||
|
|
4cf0ef2fdd | ||
|
|
2eb6e8035b | ||
|
|
1679eedb94 | ||
|
|
5b20b3c8eb | ||
|
|
4d07b48c63 | ||
|
|
fb48ed13c0 | ||
|
|
fe25ad6b81 | ||
|
|
e98ec4f8e4 | ||
|
|
80c4ca332e | ||
|
|
9ceda0d524 | ||
|
|
b34a0fdc1e | ||
|
|
9f2dbb2457 | ||
|
|
684df86688 | ||
|
|
a62061f4ae | ||
|
|
be4028b235 | ||
|
|
8f5fe0df98 | ||
|
|
6fb5917047 | ||
|
|
a3da3523e5 | ||
|
|
5f944b8c5c | ||
|
|
c72b19ffc0 | ||
|
|
acab694adc | ||
|
|
3cd1627c83 | ||
|
|
7dfac1a71f | ||
|
|
784054b3e9 | ||
|
|
6125d65fad | ||
|
|
17e7fc566f | ||
|
|
3193141653 | ||
|
|
01b678f4fc | ||
|
|
c7be019b56 | ||
|
|
b005a340e1 | ||
|
|
e4bdae4834 | ||
|
|
e5eb57cf05 | ||
|
|
eb28a328ef | ||
|
|
e06b7a6e6c | ||
|
|
bc34c677af |
3
.github/workflows/build-base-image.yml
vendored
3
.github/workflows/build-base-image.yml
vendored
@@ -10,6 +10,9 @@ on:
|
||||
- '.github/workflows/build-base-image.yml'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build-image:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
18
CHANGELOG.md
18
CHANGELOG.md
@@ -1,3 +1,21 @@
|
||||
## v0.3.83 (2025-06-12)
|
||||
|
||||
### Feat
|
||||
|
||||
- 新增开关控制是否开始谷歌统计 see #473
|
||||
- 支持b站合集和收藏下载 (#487)
|
||||
|
||||
### Fix
|
||||
|
||||
- 修复安全问题
|
||||
- 修复安全问题
|
||||
|
||||
## v0.3.82 (2025-05-30)
|
||||
|
||||
### Fix
|
||||
|
||||
- 修复节假日文件没有打包到 docker 镜像里的问题
|
||||
|
||||
## v0.3.81 (2025-05-28)
|
||||
|
||||
### Feat
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
FROM hanxi/xiaomusic:builder AS builder
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
RUN pip install -U pdm
|
||||
ENV PDM_CHECK_UPDATE=false
|
||||
WORKDIR /app
|
||||
COPY pyproject.toml README.md .
|
||||
COPY xiaomusic/ ./xiaomusic/
|
||||
COPY plugins/ ./plugins/
|
||||
COPY holiday/ ./holiday/
|
||||
COPY xiaomusic.py .
|
||||
RUN pdm install --prod --no-editable
|
||||
|
||||
@@ -14,6 +14,7 @@ WORKDIR /app
|
||||
COPY --from=builder /app/.venv ./.venv
|
||||
COPY --from=builder /app/xiaomusic/ ./xiaomusic/
|
||||
COPY --from=builder /app/plugins/ ./plugins/
|
||||
COPY --from=builder /app/holiday/ ./holiday/
|
||||
COPY --from=builder /app/xiaomusic.py .
|
||||
COPY --from=builder /app/xiaomusic/__init__.py /base_version.py
|
||||
RUN touch /app/.dockerenv
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
FROM python:3.10
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
FROM python:3.12-alpine3.22
|
||||
|
||||
RUN apk add --no-cache --virtual .build-deps build-base python3-dev libffi-dev openssl-dev zlib-dev jpeg-dev libc6-compat gcc musl-dev
|
||||
RUN pip install -U pdm
|
||||
ENV PDM_CHECK_UPDATE=false
|
||||
WORKDIR /app
|
||||
COPY pyproject.toml README.md .
|
||||
COPY xiaomusic/ ./xiaomusic/
|
||||
COPY plugins/ ./plugins/
|
||||
COPY xiaomusic.py .
|
||||
COPY pyproject.toml README.md ./
|
||||
|
||||
RUN pdm install --prod --no-editable
|
||||
|
||||
COPY xiaomusic/ ./xiaomusic/
|
||||
COPY plugins/ ./plugins/
|
||||
COPY holiday/ ./holiday/
|
||||
COPY xiaomusic.py .
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
FROM python:3.10-slim
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
FROM python:3.12-alpine3.22
|
||||
|
||||
RUN apt-get update && apt-get install -y \
|
||||
RUN apk add --no-cache bash\
|
||||
wget \
|
||||
xz-utils \
|
||||
libtiff6 \
|
||||
libopenjp2-7 \
|
||||
libxcb1 \
|
||||
xz \
|
||||
tiff \
|
||||
openjpeg \
|
||||
libxcb \
|
||||
supervisor \
|
||||
vim \
|
||||
libc6-compat \
|
||||
ffmpeg \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /app
|
||||
COPY install_dependencies.sh .
|
||||
RUN bash install_dependencies.sh
|
||||
RUN mkdir -p /app/ffmpeg/bin \
|
||||
&& ln -s /usr/bin/ffmpeg /app/ffmpeg/bin/ffmpeg \
|
||||
&& ln -s /usr/bin/ffprobe /app/ffmpeg/bin/ffprobe
|
||||
|
||||
@@ -77,7 +77,7 @@ services:
|
||||
> docker 和 docker compose 二选一即可,启动成功后,在 web 页面可以配置其他参数,带有 `*` 号的配置是必须要配置的,其他的用不上时不用修改。初次配置时需要在页面上输入小米账号和密码保存后才能获取到设备列表。
|
||||
|
||||
> [!TIP]
|
||||
> 目前安装步骤已经是最简化了,如果还是嫌安装麻烦,可以微信或者 QQ 约我远程安装,我一般周末和晚上才有时间,收个辛苦费 :moneybag: 50 元一次,安装失败不收费。
|
||||
> 目前安装步骤已经是最简化了,如果还是嫌安装麻烦,可以微信或者 QQ 约我远程安装,我一般周末和晚上才有时间,需要赞助个辛苦费 :moneybag: 50 元一次。
|
||||
|
||||
遇到问题可以去 web 设置页面底部点击【下载日志文件】按钮,然后搜索一下日志文件内容确保里面没有账号密码信息后(有就删除这些敏感信息),然后在提 issues 反馈问题时把下载的日志文件带上。
|
||||
|
||||
@@ -94,6 +94,11 @@ services:
|
||||
> - 🔥【广告: 搭建您的专属大模型主页
|
||||
告别繁琐配置难题,一键即可畅享稳定流畅的AI体验!】<https://university.aliyun.com/mobile?userCode=szqvatm6>
|
||||
|
||||
> [!TIP]
|
||||
> - 免费主机
|
||||
> - <a href="https://dartnode.com"><img src="https://dartnode.com/branding/DN-Open-Source-sm.png" alt="Powered by DartNode - Free VPS for Open Source" width="320"></a>
|
||||
|
||||
|
||||
### 🤐 支持语音口令
|
||||
|
||||
- 【播放歌曲】,播放本地的歌曲
|
||||
|
||||
@@ -10,5 +10,10 @@ title: 同步网易云歌单
|
||||
|
||||
## 评论
|
||||
|
||||
没有评论。
|
||||
|
||||
### 评论 1 - Ghost-Sam1222
|
||||
|
||||
什么时候可以同步apple music歌单就真好了
|
||||
|
||||
---
|
||||
[链接到 GitHub Issue](https://github.com/hanxi/xiaomusic/issues/312)
|
||||
|
||||
@@ -105,5 +105,37 @@ services:
|
||||
EOF
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 评论 3 - worrywast
|
||||
|
||||
为啥我的播放完一首音乐之后,还会播放该歌曲开头一点点才会放下一首呢?
|
||||
|
||||
---
|
||||
|
||||
### 评论 4 - hanxi
|
||||
|
||||
> 为啥我的播放完一首音乐之后,还会播放该歌曲开头一点点才会放下一首呢?
|
||||
|
||||
正常现象。可以把延迟设置为0试试。
|
||||
|
||||
|
||||
---
|
||||
|
||||
### 评论 5 - worrywast
|
||||
|
||||
> > 为啥我的播放完一首音乐之后,还会播放该歌曲开头一点点才会放下一首呢?
|
||||
>
|
||||
> 正常现象。可以把延迟设置为0试试。
|
||||
|
||||
是这个选项吗:“下一首歌延迟播放秒数:”
|
||||
我设置为0,也会有这个现象,只是没有之前播放开头那么多
|
||||
|
||||
---
|
||||
|
||||
### 评论 6 - hanxi
|
||||
|
||||
@worrywast 等下个版本我优化一下,允许设置成负数吧。
|
||||
|
||||
---
|
||||
[链接到 GitHub Issue](https://github.com/hanxi/xiaomusic/issues/360)
|
||||
|
||||
@@ -4,7 +4,7 @@ title: 微信交流群二维码
|
||||
|
||||
# 微信交流群二维码
|
||||
|
||||

|
||||

|
||||
|
||||
如果你刚好在买流量卡,可以在我的微信卡店里看看有没有合适的。
|
||||

|
||||
|
||||
@@ -201,6 +201,24 @@ docker run --rm -it browsh/browsh --startup-url https://mi.com
|
||||
4. 查看容器日志中的歌曲链接是否正常,点击后台页面上的播放歌曲时,容器中会有歌曲链接,一般是 http 开头的链接,复制完整链接到浏览器试试看能否打开,能打开说明网络没问题,继续下一步排查。不能打开有可能是 ip 和端口配置错误,请使用设置页面的自动填按钮自动填 ip 和端口。
|
||||
5. 歌曲文件格式是否是 mp3 格式,有些型号无法播放 flac 格式的歌曲,请使用 mp3 格式的歌曲文件测试,一个不行就多找几个文件测试。
|
||||
|
||||
## 在安装好第一次用语音正常,第二天就不能用语音了
|
||||
|
||||
拉取对话记录的问题。拉取对话记录太频繁了,可以使用[定时任务功能](/issues/182.html),设置晚上关闭拉取对话记录。比如这样设置是早上6点开启拉取对话记录,晚上12点关闭:
|
||||
```
|
||||
[
|
||||
{
|
||||
"expression": "0 6 * * *",
|
||||
"name": "set_pull_ask",
|
||||
"arg1": "enable"
|
||||
},
|
||||
{
|
||||
"expression": "0 0 * * *",
|
||||
"name": "set_pull_ask",
|
||||
"arg1": "disable"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## 评论
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,22 @@
|
||||
# 版本日志
|
||||
|
||||
## v0.3.82 (2025-05-30)
|
||||
|
||||
### Fix
|
||||
|
||||
- 修复节假日文件没有打包到 docker 镜像里的问题
|
||||
|
||||
## v0.3.81 (2025-05-28)
|
||||
|
||||
### Feat
|
||||
|
||||
- 定时任务支持工作日和休息日 see #182
|
||||
|
||||
### Fix
|
||||
|
||||
- 动态小程序码生成接口 (#478)
|
||||
- 指定日志编码,避免中文乱码 (#475)
|
||||
|
||||
## v0.3.80 (2025-05-18)
|
||||
|
||||
### Feat
|
||||
|
||||
@@ -77,7 +77,7 @@ services:
|
||||
> docker 和 docker compose 二选一即可,启动成功后,在 web 页面可以配置其他参数,带有 `*` 号的配置是必须要配置的,其他的用不上时不用修改。初次配置时需要在页面上输入小米账号和密码保存后才能获取到设备列表。
|
||||
|
||||
> [!TIP]
|
||||
> 目前安装步骤已经是最简化了,如果还是嫌安装麻烦,可以微信或者 QQ 约我远程安装,我一般周末和晚上才有时间,收个辛苦费 :moneybag: 50 元一次,安装失败不收费。
|
||||
> 目前安装步骤已经是最简化了,如果还是嫌安装麻烦,可以微信或者 QQ 约我远程安装,我一般周末和晚上才有时间,需要赞助个辛苦费 :moneybag: 50 元一次。
|
||||
|
||||
遇到问题可以去 web 设置页面底部点击【下载日志文件】按钮,然后搜索一下日志文件内容确保里面没有账号密码信息后(有就删除这些敏感信息),然后在提 issues 反馈问题时把下载的日志文件带上。
|
||||
|
||||
@@ -94,6 +94,11 @@ services:
|
||||
> - 🔥【广告: 搭建您的专属大模型主页
|
||||
告别繁琐配置难题,一键即可畅享稳定流畅的AI体验!】<https://university.aliyun.com/mobile?userCode=szqvatm6>
|
||||
|
||||
> [!TIP]
|
||||
> - 免费主机
|
||||
> - <a href="https://dartnode.com"><img src="https://dartnode.com/branding/DN-Open-Source-sm.png" alt="Powered by DartNode - Free VPS for Open Source" width="320"></a>
|
||||
|
||||
|
||||
### 🤐 支持语音口令
|
||||
|
||||
- 【播放歌曲】,播放本地的歌曲
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "xiaomusic"
|
||||
version = "0.3.81"
|
||||
version = "0.3.83"
|
||||
description = "Play Music with xiaomi AI speaker"
|
||||
authors = [
|
||||
{name = "涵曦", email = "im.hanxi@gmail.com"},
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = "0.3.81"
|
||||
__version__ = "0.3.83"
|
||||
|
||||
@@ -57,8 +57,11 @@ class Analytics:
|
||||
await self._send(event)
|
||||
|
||||
async def _send(self, event):
|
||||
asyncio.create_task(self.post_to_umami(event))
|
||||
await self.run_with_cancel(self._google_send, [event])
|
||||
if self.config.enable_analytics:
|
||||
asyncio.create_task(self.post_to_umami(event))
|
||||
await self.run_with_cancel(self._google_send, [event])
|
||||
else:
|
||||
self.log.info("analytics is disabled, skip sending event")
|
||||
|
||||
def _google_send(self, events):
|
||||
try:
|
||||
|
||||
@@ -191,6 +191,9 @@ class Config:
|
||||
enable_save_tag: bool = (
|
||||
os.getenv("XIAOMUSIC_ENABLE_SAVE_TAG", "false").lower() == "true"
|
||||
)
|
||||
enable_analytics: bool = (
|
||||
os.getenv("XIAOMUSIC_ENABLE_ANALYTICS", "true").lower() == "true"
|
||||
)
|
||||
get_ask_by_mina: bool = (
|
||||
os.getenv("XIAOMUSIC_GET_ASK_BY_MINA", "false").lower() == "true"
|
||||
)
|
||||
|
||||
@@ -42,6 +42,7 @@ from starlette.responses import FileResponse, Response
|
||||
|
||||
from xiaomusic import __version__
|
||||
from xiaomusic.utils import (
|
||||
check_bili_fav_list,
|
||||
chmoddir,
|
||||
convert_file_to_mp3,
|
||||
deepcopy_data_no_sensitive_info,
|
||||
@@ -53,6 +54,7 @@ from xiaomusic.utils import (
|
||||
remove_common_prefix,
|
||||
remove_id3_tags,
|
||||
restart_xiaomusic,
|
||||
safe_join_path,
|
||||
try_add_access_control_param,
|
||||
update_version,
|
||||
)
|
||||
@@ -548,14 +550,32 @@ class DownloadPlayList(BaseModel):
|
||||
@app.post("/downloadplaylist")
|
||||
async def downloadplaylist(data: DownloadPlayList, Verifcation=Depends(verification)):
|
||||
try:
|
||||
download_proc = await download_playlist(config, data.url, data.dirname)
|
||||
bili_fav_list = await check_bili_fav_list(data.url)
|
||||
download_proc_list = []
|
||||
if bili_fav_list:
|
||||
for bvid, title in bili_fav_list.items():
|
||||
bvurl = f"https://www.bilibili.com/video/{bvid}"
|
||||
download_proc_list[title] = await download_one_music(
|
||||
config, bvurl, os.path.join(data.dirname, title)
|
||||
)
|
||||
for title, download_proc_sigle in download_proc_list.items():
|
||||
exit_code = await download_proc_sigle.wait()
|
||||
log.info(f"Download completed {title} with exit code {exit_code}")
|
||||
dir_path = safe_join_path(config.download_path, data.dirname)
|
||||
log.debug(f"Download dir_path: {dir_path}")
|
||||
# 可能只是部分失败,都需要整理下载目录
|
||||
remove_common_prefix(dir_path)
|
||||
chmoddir(dir_path)
|
||||
return {"ret": "OK"}
|
||||
else:
|
||||
download_proc = await download_playlist(config, data.url, data.dirname)
|
||||
|
||||
async def check_download_proc():
|
||||
# 等待子进程完成
|
||||
exit_code = await download_proc.wait()
|
||||
log.info(f"Download completed with exit code {exit_code}")
|
||||
|
||||
dir_path = os.path.join(config.download_path, data.dirname)
|
||||
dir_path = safe_join_path(config.download_path, data.dirname)
|
||||
log.debug(f"Download dir_path: {dir_path}")
|
||||
# 可能只是部分失败,都需要整理下载目录
|
||||
remove_common_prefix(dir_path)
|
||||
|
||||
4
xiaomusic/static/default/debug.html
vendored
4
xiaomusic/static/default/debug.html
vendored
@@ -6,9 +6,9 @@
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<title>Debug For XiaoMusic</title>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="./main.css?version=1748447212">
|
||||
<link rel="stylesheet" type="text/css" href="./main.css?version=1749666240">
|
||||
<script src="https://unpkg.com/vconsole@latest/dist/vconsole.min.js"></script>
|
||||
<script src="./jquery-3.7.1.min.js?version=1748447212"></script>
|
||||
<script src="./jquery-3.7.1.min.js?version=1749666240"></script>
|
||||
|
||||
<!-- Google tag (gtag.js) -->
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-Z09NC1K7ZW"></script>
|
||||
|
||||
4
xiaomusic/static/default/downloadtool.html
vendored
4
xiaomusic/static/default/downloadtool.html
vendored
@@ -4,8 +4,8 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<title>歌曲下载工具</title>
|
||||
<link rel="stylesheet" type="text/css" href="./main.css?version=1748447212">
|
||||
<script src="./jquery-3.7.1.min.js?version=1748447212"></script>
|
||||
<link rel="stylesheet" type="text/css" href="./main.css?version=1749666240">
|
||||
<script src="./jquery-3.7.1.min.js?version=1749666240"></script>
|
||||
|
||||
<!-- Google tag (gtag.js) -->
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-Z09NC1K7ZW"></script>
|
||||
|
||||
6
xiaomusic/static/default/index.html
vendored
6
xiaomusic/static/default/index.html
vendored
@@ -6,8 +6,8 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>小爱音箱操控面板</title>
|
||||
<link href="https://fonts.googleapis.com/css?family=Material+Icons|Material+Icons+Outlined" rel="stylesheet">
|
||||
<script src="./jquery-3.7.1.min.js?version=1748447212"></script>
|
||||
<link rel="stylesheet" href="./main.css?version=1748447212">
|
||||
<script src="./jquery-3.7.1.min.js?version=1749666240"></script>
|
||||
<link rel="stylesheet" href="./main.css?version=1749666240">
|
||||
<link rel="icon" href="./favicon.ico">
|
||||
|
||||
<!-- Google tag (gtag.js) -->
|
||||
@@ -219,7 +219,7 @@
|
||||
Powered by XiaoMusic
|
||||
</div>
|
||||
|
||||
<script src="./md.js?version=1748447212">
|
||||
<script src="./md.js?version=1749666240">
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
2
xiaomusic/static/default/m3u.html
vendored
2
xiaomusic/static/default/m3u.html
vendored
@@ -5,7 +5,7 @@
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<title>M3U to JSON Converter</title>
|
||||
<link rel="stylesheet" type="text/css" href="./main.css?version=1748447212">
|
||||
<link rel="stylesheet" type="text/css" href="./main.css?version=1749666240">
|
||||
|
||||
<!-- Google tag (gtag.js) -->
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-Z09NC1K7ZW"></script>
|
||||
|
||||
13
xiaomusic/static/default/setting.html
vendored
13
xiaomusic/static/default/setting.html
vendored
@@ -5,9 +5,9 @@
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<title>小爱音箱操控面板</title>
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||
<script src="./jquery-3.7.1.min.js?version=1748447212"></script>
|
||||
<script src="./setting.js?version=1748447212"></script>
|
||||
<link rel="stylesheet" type="text/css" href="./main.css?version=1748447212">
|
||||
<script src="./jquery-3.7.1.min.js?version=1749666240"></script>
|
||||
<script src="./setting.js?version=1749666240"></script>
|
||||
<link rel="stylesheet" type="text/css" href="./main.css?version=1749666240">
|
||||
|
||||
<!-- Google tag (gtag.js) -->
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-Z09NC1K7ZW"></script>
|
||||
@@ -260,6 +260,13 @@ var vConsole = new window.VConsole();
|
||||
<option value="false" selected>false</option>
|
||||
</select>
|
||||
|
||||
<label for="enable_analytics">开启谷歌数据统计(无敏感数据):</label>
|
||||
<select id="enable_analytics">
|
||||
<option value="true" selected>true</option>
|
||||
<option value="false">false</option>
|
||||
</select>
|
||||
|
||||
|
||||
<label for="music_list_url" class="setting-label">歌单地址:
|
||||
<button class="option-inline mini-button" id="get_music_list">
|
||||
<span class="material-icons">sync_alt</span>
|
||||
|
||||
@@ -22,7 +22,8 @@ import urllib.parse
|
||||
from collections.abc import AsyncIterator
|
||||
from dataclasses import asdict, dataclass
|
||||
from http.cookies import SimpleCookie
|
||||
from urllib.parse import urlparse
|
||||
from time import sleep
|
||||
from urllib.parse import parse_qs, urlparse
|
||||
|
||||
import aiohttp
|
||||
import mutagen
|
||||
@@ -941,6 +942,80 @@ def _set_wave_tags(audio, info):
|
||||
audio["Artist"] = info.artist
|
||||
|
||||
|
||||
async def check_bili_fav_list(url):
|
||||
bvid_info = {}
|
||||
parsed_url = urlparse(url)
|
||||
path = parsed_url.path
|
||||
# 提取查询参数
|
||||
query_params = parse_qs(parsed_url.query)
|
||||
if parsed_url.hostname == "space.bilibili.com":
|
||||
if "/favlist" in path:
|
||||
lid = query_params.get("fid", [None])[0]
|
||||
type = query_params.get("ctype", [None])[0]
|
||||
if type == "11":
|
||||
type = "create"
|
||||
elif type == "21":
|
||||
type = "collect"
|
||||
else:
|
||||
raise ValueError("当前只支持合集和收藏夹")
|
||||
elif "/lists/" in path:
|
||||
parts = path.split("/")
|
||||
if len(parts) >= 4 and "?" in url:
|
||||
lid = parts[3] # 提取 lid
|
||||
type = query_params.get("type", [None])[0]
|
||||
# https://api.bilibili.com/x/polymer/web-space/seasons_archives_list?season_id={lid}&page_size=30&page_num=1
|
||||
page_size = 100
|
||||
page_num = 1
|
||||
headers = {
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0 Safari/537.36",
|
||||
"Accept": "application/json, text/plain, */*",
|
||||
"Referer": url,
|
||||
"Origin": "https://space.bilibili.com",
|
||||
}
|
||||
async with aiohttp.ClientSession(headers=headers) as session:
|
||||
if type == "season" or type == "collect":
|
||||
while True:
|
||||
list_url = f"https://api.bilibili.com/x/polymer/web-space/seasons_archives_list?season_id={lid}&page_size={page_size}&page_num={page_num}"
|
||||
async with session.get(list_url) as response:
|
||||
if response.status != 200:
|
||||
raise Exception(f"Failed to fetch data from {list_url}")
|
||||
data = await response.json()
|
||||
archives = data.get("data", {}).get("archives", [])
|
||||
if not archives:
|
||||
break
|
||||
for archive in archives:
|
||||
bvid = archive.get("bvid", None)
|
||||
title = archive.get("title", None)
|
||||
bvid_info[bvid] = title
|
||||
|
||||
if len(archives) < page_size:
|
||||
break
|
||||
page_num += 1
|
||||
sleep(1)
|
||||
elif type == "create":
|
||||
while True:
|
||||
list_url = f"https://api.bilibili.com/x/v3/fav/resource/list?media_id={lid}&pn={page_num}&ps={page_size}&order=mtime"
|
||||
async with session.get(list_url) as response:
|
||||
if response.status != 200:
|
||||
raise Exception(f"Failed to fetch data from {list_url}")
|
||||
data = await response.json()
|
||||
medias = data.get("data", {}).get("medias", [])
|
||||
if not medias:
|
||||
break
|
||||
for media in medias:
|
||||
bvid = media.get("bvid", None)
|
||||
title = media.get("title", None)
|
||||
bvurl = f"https://www.bilibili.com/video/{bvid}"
|
||||
bvid_info[bvurl] = title
|
||||
|
||||
if len(medias) < page_size:
|
||||
break
|
||||
page_num += 1
|
||||
else:
|
||||
raise ValueError("当前只支持合集和收藏夹")
|
||||
return bvid_info
|
||||
|
||||
|
||||
# 下载播放列表
|
||||
async def download_playlist(config, url, dirname):
|
||||
title = f"{dirname}/%(title)s.%(ext)s"
|
||||
@@ -1032,6 +1107,16 @@ def _longest_common_prefix(file_names):
|
||||
return prefix
|
||||
|
||||
|
||||
def safe_join_path(safe_root, directory):
|
||||
directory = os.path.join(safe_root, directory)
|
||||
# Normalize the directory path
|
||||
normalized_directory = os.path.normpath(directory)
|
||||
# Ensure the directory is within the safe root
|
||||
if not normalized_directory.startswith(os.path.normpath(safe_root)):
|
||||
raise ValueError(f"Access to directory '{directory}' is not allowed.")
|
||||
return normalized_directory
|
||||
|
||||
|
||||
# 移除目录下文件名前缀相同的
|
||||
def remove_common_prefix(directory):
|
||||
files = os.listdir(directory)
|
||||
|
||||
Reference in New Issue
Block a user