1
0
mirror of https://github.com/hanxi/xiaomusic.git synced 2025-12-06 14:52:50 +08:00

Compare commits

...

41 Commits

Author SHA1 Message Date
涵曦
a316053d01 bump: version 0.3.82 → 0.3.83 2025-06-12 02:24:04 +08:00
涵曦
0bfa4e6251 build: update static version 2025-06-12 02:24:03 +08:00
涵曦
6ef7d4c6c2 feat: 新增开关控制是否开始谷歌统计 see #473 2025-06-12 01:07:33 +08:00
Issues Docs [BOT]
fff35783c2 Auto-Generate docs 🤖 2025-06-11 16:56:35 +00:00
涵曦
c8fad5ba1d Update Dockerfile.runtime 2025-06-12 00:47:56 +08:00
涵曦
956a4af3c9 fix: 修复安全问题 2025-06-10 18:46:15 +08:00
涵曦
4cf0ef2fdd Potential fix for code scanning alert no. 47: Incomplete URL substring sanitization (#497)
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2025-06-10 17:24:48 +08:00
涵曦
2eb6e8035b fix: 修复安全问题 2025-06-10 16:43:43 +08:00
涵曦
1679eedb94 Potential fix for code scanning alert no. 40: Uncontrolled data used in path expression (#493)
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2025-06-10 16:09:32 +08:00
涵曦
5b20b3c8eb Update Dockerfile.runtime 2025-06-10 15:13:02 +08:00
涵曦
4d07b48c63 Update Dockerfile.builder 2025-06-10 14:06:34 +08:00
涵曦
fb48ed13c0 Update Dockerfile.builder 2025-06-10 13:13:37 +08:00
涵曦
fe25ad6b81 Update Dockerfile.builder 2025-06-10 13:09:49 +08:00
涵曦
e98ec4f8e4 Update Dockerfile.builder 2025-06-10 11:24:37 +08:00
涵曦
80c4ca332e Update Dockerfile.builder 2025-06-10 08:43:46 +08:00
涵曦
9ceda0d524 Potential fix for code scanning alert no. 39: Workflow does not contain permissions (#492)
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2025-06-09 23:19:49 +08:00
涵曦
b34a0fdc1e Update Dockerfile.runtime 2025-06-09 23:19:01 +08:00
涵曦
9f2dbb2457 Update Dockerfile.builder 2025-06-09 23:18:13 +08:00
Issues Docs [BOT]
684df86688 Auto-Generate docs 🤖 2025-06-09 15:02:22 +00:00
Formatter [BOT]
a62061f4ae Auto-format code 🧹🌟🤖 2025-06-09 14:57:17 +00:00
涵曦
be4028b235 Update README.md 2025-06-09 22:56:44 +08:00
Moruy
8f5fe0df98 Update Dockerfile.builder (#490) 2025-06-09 14:58:53 +08:00
Issues Docs [BOT]
6fb5917047 Auto-Generate docs 🤖 2025-06-09 04:38:17 +00:00
涵曦
a3da3523e5 Update Dockerfile 2025-06-09 12:34:31 +08:00
Moruy
5f944b8c5c feature: Migrate debian to alpine linux (#489)
* Update Dockerfile

change debian to alpine linux, reduce the image size by 50%

* Update Dockerfile.builder

change debian to alpine linux, reduce the image size by 50%

* Update Dockerfile.runtime

change debian to alpine linux, reduce the image size by 50%
2025-06-09 12:32:30 +08:00
nfzsh
c72b19ffc0 feat: 支持b站合集和收藏下载 (#487)
* feat:支持收藏夹和合集下载

* feat:支持收藏夹和合集下载

* feat:支持收藏夹和合集下载
2025-06-09 00:37:26 +08:00
Issues Docs [BOT]
acab694adc Auto-Generate docs 🤖 2025-06-08 04:45:44 +00:00
Issues Docs [BOT]
3cd1627c83 Auto-Generate docs 🤖 2025-06-07 10:15:55 +00:00
涵曦
7dfac1a71f Update README.md 2025-06-07 18:14:20 +08:00
Issues Docs [BOT]
784054b3e9 Auto-Generate docs 🤖 2025-06-07 09:53:20 +00:00
涵曦
6125d65fad Update README.md 2025-06-07 17:51:36 +08:00
Issues Docs [BOT]
17e7fc566f Auto-Generate docs 🤖 2025-06-05 03:39:22 +00:00
Issues Docs [BOT]
3193141653 Auto-Generate docs 🤖 2025-06-05 03:35:30 +00:00
Issues Docs [BOT]
01b678f4fc Auto-Generate docs 🤖 2025-06-04 04:28:07 +00:00
涵曦
c7be019b56 Update README.md 2025-06-04 12:26:25 +08:00
Issues Docs [BOT]
b005a340e1 Auto-Generate docs 🤖 2025-05-29 23:51:27 +00:00
涵曦
e4bdae4834 bump: version 0.3.81 → 0.3.82 2025-05-30 07:49:12 +08:00
涵曦
e5eb57cf05 build: update static version 2025-05-30 07:49:11 +08:00
涵曦
eb28a328ef fix: 修复节假日文件没有打包到 docker 镜像里的问题 2025-05-30 07:13:54 +08:00
Issues Docs [BOT]
e06b7a6e6c Auto-Generate docs 🤖 2025-05-29 16:28:03 +00:00
Issues Docs [BOT]
bc34c677af Auto-Generate docs 🤖 2025-05-28 15:49:21 +00:00
23 changed files with 265 additions and 38 deletions

View File

@@ -10,6 +10,9 @@ on:
- '.github/workflows/build-base-image.yml'
workflow_dispatch:
permissions:
contents: read
jobs:
build-image:
runs-on: ubuntu-latest

View File

@@ -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

View File

@@ -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

View File

@@ -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 .

View File

@@ -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

View File

@@ -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>
### 🤐 支持语音口令
- 【播放歌曲】,播放本地的歌曲

View File

@@ -10,5 +10,10 @@ title: 同步网易云歌单
## 评论
没有评论。
### 评论 1 - Ghost-Sam1222
什么时候可以同步apple music歌单就真好了
---
[链接到 GitHub Issue](https://github.com/hanxi/xiaomusic/issues/312)

View File

@@ -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)

View File

@@ -4,7 +4,7 @@ title: 微信交流群二维码
# 微信交流群二维码
![Image](https://gproxy.hanxi.cc/proxy/user-attachments/assets/bcfc59d4-dace-4aa8-bfcc-07118b10c29f)
![Image](https://gproxy.hanxi.cc/proxy/user-attachments/assets/8ae5af48-cb76-45c1-ba32-33f03aa6f7fe)
如果你刚好在买流量卡,可以在我的微信卡店里看看有没有合适的。
![mmexportc6bd050862507d2806a2da710a82cb28_1735878113870](https://gproxy.hanxi.cc/proxy/user-attachments/assets/ca5a86e0-f753-42d3-8dcb-8583d50d64aa)

View File

@@ -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"
}
]
```
## 评论

View File

@@ -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

View File

@@ -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>
### 🤐 支持语音口令
- 【播放歌曲】,播放本地的歌曲

View File

@@ -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"},

View File

@@ -1 +1 @@
__version__ = "0.3.81"
__version__ = "0.3.83"

View File

@@ -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:

View File

@@ -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"
)

View File

@@ -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)

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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)