1
0
mirror of https://github.com/hanxi/xiaomusic.git synced 2025-12-07 15:02:55 +08:00

Compare commits

...

25 Commits

Author SHA1 Message Date
涵曦
aa2992b5d7 bump: version 0.3.41 → 0.3.42 2024-10-24 11:00:00 +08:00
涵曦
7fcd3eeae5 build: update static version 2024-10-24 10:59:59 +08:00
涵曦
30194272d9 Update ci.yml 2024-10-23 19:01:53 +08:00
涵曦
1b71301b06 Update Dockerfile 2024-10-23 18:26:14 +08:00
涵曦
13bbff8d67 Update ci.yml 2024-10-23 17:22:47 +08:00
涵曦
ae3507b811 Update ci.yml 2024-10-23 16:53:37 +08:00
涵曦
bdfb0a4127 ci: update ci 2024-10-23 15:31:04 +08:00
涵曦
3d0a38cbb8 build: 尝试修复 arm 平台的容器下不能启动的问题 2024-10-23 14:08:29 +08:00
涵曦
f469f63d97 fix: 尝试修复缺少 libtiff.so.6 文件的问题 #244 2024-10-23 06:22:00 +08:00
涵曦
6544bb2ff1 fix: 修复默认主题播放歌曲输入框空的情况 2024-10-21 22:59:38 +08:00
涵曦
668237401e fix: 尝试修复停止后自动播放的问题 2024-10-21 22:53:37 +08:00
涵曦
139ebf37c4 Update README.md 2024-10-21 19:40:33 +08:00
涵曦
7146d61fcb Update README.md 2024-10-18 01:14:41 +08:00
涵曦
6033c1a6fc Update README.md 2024-10-18 01:14:02 +08:00
涵曦
e77a4fc10d bump: version 0.3.40 → 0.3.41 2024-10-17 23:49:21 +08:00
涵曦
bf2909d35a build: update static version 2024-10-17 23:49:20 +08:00
涵曦
b3255a17ce fix: 修复获取标签信息报错问题 2024-10-17 23:35:38 +08:00
Gao, Ruiyuan
a3140ff23a fix: remove_id3_tags return None if no id3 tag (#238)
* fix: remove_id3_tags return None if no id3 tag

* Auto-format code 🧹🌟🤖

---------

Co-authored-by: Formatter [BOT] <runner@fv-az885-171.m43iyx0u3v4e1h2pvvlabjukza.cx.internal.cloudapp.net>
2024-10-17 11:36:48 +08:00
Gao, Ruiyuan
becfdbf338 fix: bug in del_music (#237) 2024-10-17 10:25:25 +08:00
涵曦
8b74b664f0 feat: 设置默认时区为东八区 closed #236 2024-10-17 08:18:54 +08:00
涵曦
0daba20885 bump: version 0.3.39 → 0.3.40 2024-10-16 21:44:05 +08:00
涵曦
8ac39af8cd build: update static version 2024-10-16 21:44:04 +08:00
涵曦
d6fb62eb8e feat: 默认主题的播放列表上显示歌曲数量 2024-10-16 13:00:09 +08:00
涵曦
24ac876632 fix: 修复播放卡顿问题(谷歌统计地址无法访问的情况) 2024-10-16 12:42:22 +08:00
涵曦
fcdb7bf035 Update README.md 2024-10-16 01:14:22 +08:00
16 changed files with 187 additions and 68 deletions

View File

@@ -6,6 +6,9 @@ on:
- "*"
workflow_dispatch:
env:
TEST_TAG: ${{ secrets.DOCKERHUB_USERNAME }}/xiaomusic:${{ github.ref_name }}
jobs:
build-image:
runs-on: ubuntu-latest
@@ -14,21 +17,70 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v4
# We have to build each platform separately because when using multi-arch
# builds, only one platform is being loaded into the cache. This would
# prevent us from testing the other platforms.
- name: Build Docker image (linux/amd64)
uses: docker/build-push-action@v6
with:
platforms: linux/amd64
context: .
push: false
load: true
tags: ${{ env.TEST_TAG }}-linux-amd64
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache-new
- name: Build Docker image (linux/arm64)
uses: docker/build-push-action@v6
with:
platforms: linux/arm64
context: .
push: false
load: true
tags: ${{ env.TEST_TAG }}-linux-arm64
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache-new
- name: Build Docker image (linux/arm/v7)
uses: docker/build-push-action@v6
with:
platforms: linux/arm/v7
context: .
push: false
load: true
tags: ${{ env.TEST_TAG }}-linux-arm-v7
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache-new
# We test all the images on amd64 host here. This uses QEMU to emulate
# the other platforms.
- run: docker run --rm ${TEST_TAG}-linux-amd64 -h
- run: docker run --rm ${TEST_TAG}-linux-arm64 -h
- run: docker run --rm ${TEST_TAG}-linux-arm-v7 -h
# This will only push the previously built images.
- name: Publish to Docker Hub
uses: docker/build-push-action@v6
with:
platforms: linux/amd64,linux/arm64,linux/arm/v7
context: .
push: true
tags: ${{ secrets.DOCKERHUB_USERNAME }}/xiaomusic:${{ github.ref_name }}
tags: ${{ env.TEST_TAG }}
cache-from: type=local,src=/tmp/.buildx-cache-new
cache-to: type=local,dest=/tmp/.buildx-cache-new
- name: Docker Hub Description
uses: peter-evans/dockerhub-description@v4
with:

View File

@@ -1,9 +1,39 @@
## v0.3.42 (2024-10-24)
### Fix
- 尝试修复缺少 libtiff.so.6 文件的问题 #244
- 修复默认主题播放歌曲输入框空的情况
- 尝试修复停止后自动播放的问题
## v0.3.41 (2024-10-17)
### Feat
- 设置默认时区为东八区 closed #236
### Fix
- 修复获取标签信息报错问题
- remove_id3_tags return None if no id3 tag (#238)
- bug in del_music (#237)
## v0.3.40 (2024-10-16)
### Feat
- 默认主题的播放列表上显示歌曲数量
### Fix
- 修复播放卡顿问题(谷歌统计地址无法访问的情况)
## v0.3.39 (2024-10-15)
### Feat
- 固定的播放列表全部初始化
- 生产环境与开发环境接口分离、关于页面增加返回到主页的链接
- 生产环境与开发环境接口分离、关于页面增加返回到主页的链接
update: 支持https页面未及时更新的问题
### Fix

View File

@@ -12,6 +12,13 @@ COPY install_dependencies.sh .
RUN bash install_dependencies.sh
FROM python:3.10-slim
RUN apt-get update && apt-get install -y \
libtiff6 \
libopenjp2-7 \
libxcb1 \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY --from=builder /app/.venv /app/.venv
COPY --from=builder /app/ffmpeg /app/ffmpeg
@@ -23,5 +30,6 @@ ENV XIAOMUSIC_PORT=8090
VOLUME /app/conf
VOLUME /app/music
EXPOSE 8090
ENV TZ=Asia/Shanghai
ENV PATH=/app/.venv/bin:$PATH
ENTRYPOINT [".venv/bin/python3","xiaomusic.py"]

View File

@@ -24,6 +24,12 @@
docker run -p 8090:8090 -v /xiaomusic/music:/app/music -v /xiaomusic/conf:/app/conf hanxi/xiaomusic
```
🔥 国内:
```bash
docker run -p 8090:8090 -v /xiaomusic/music:/app/music -v /xiaomusic/conf:/app/conf m.daocloud.io/docker.io/hanxi/xiaomusic
```
对应的 docker compose 配置如下:
```yaml
@@ -39,6 +45,21 @@ services:
- /xiaomusic/conf:/app/conf
```
🔥 国内:
```yaml
services:
xiaomusic:
image: m.daocloud.io/docker.io/hanxi/xiaomusic
container_name: xiaomusic
restart: unless-stopped
ports:
- 8090:8090
volumes:
- /xiaomusic/music:/app/music
- /xiaomusic/conf:/app/conf
```
其中 conf 目录为配置文件存放目录music 目录为音乐存放目录,建议分开配置为不同的目录。
> [!NOTE]
@@ -98,8 +119,6 @@ services:
> [!TIP]
> 隐藏玩法: 对小爱同学说播放歌曲小猪佩奇的故事,会先下载小猪佩奇的故事,然后再播放小猪佩奇的故事。
更多功能见 [📝 文档汇总](https://github.com/hanxi/xiaomusic/issues/211)
## 🛠️ pip 方式安装运行
```shell
@@ -281,22 +300,13 @@ docker build -t xiaomusic .
- [微信小程序: XIAO晓音](https://github.com/F-loat/xiaoplayer)
- [pure 主题 xiaomusicUI](https://github.com/52fisher/xiaomusicUI)
- [移动端的播放器主题](https://github.com/52fisher/XMusicPlayer)
- [一个第三方的主题](https://github.com/DarrenWen/xiaomusicui)
- 所有帮忙调试和测试的朋友
- 所有反馈问题和建议的朋友
### 👉 其他教程
> [!NOTE]
> 下面教程可能比较旧,只供参考
- [NAS部署教程](https://post.m.smzdm.com/p/avpe7n99/)
- [群晖部署教程](https://post.m.smzdm.com/p/a7px7dol/)
- [QNAS部署教程](https://post.smzdm.com/p/a5xz5x63/)
- [视频教程](https://www.bilibili.com/video/BV1ZZpweHEtT/)
- [TechHive](https://mp.weixin.qq.com/s/4a41muFtPaFKtHeZYu795w)
- [弹个AI](https://mp.weixin.qq.com/s/sIsKxB7Y8b83AhnvaWiMog)
- [简单免费教你用绿联NAS联动小爱音箱私人音乐库也能语音点播](https://post.m.smzdm.com/p/a8pldgg7/)
- [飞牛教程](https://mp.weixin.qq.com/s?t=pages/image_detail&__biz=MzkxODc1NDMwOA==&mid=2247483725&idx=1&sn=2d615f14733b9bf989557fa766b4e1fc)
更多功能见 [📝 文档汇总](https://github.com/hanxi/xiaomusic/issues/211)
## 🚨 免责声明

View File

@@ -1,6 +1,6 @@
[project]
name = "xiaomusic"
version = "0.3.39"
version = "0.3.42"
description = "Play Music with xiaomi AI speaker"
authors = [
{name = "涵曦", email = "im.hanxi@gmail.com"},

View File

@@ -1 +1 @@
__version__ = "0.3.39"
__version__ = "0.3.42"

View File

@@ -11,6 +11,7 @@ class Analytics:
self.gtag = None
self.current_date = None
self.log = log
self.task = None
self.init()
def init(self):
@@ -27,16 +28,19 @@ class Analytics:
self.gtag = gtag
self.log.info("analytics init ok")
async def run_with_timeout(self, func, *args, **kwargs):
async def run_with_cancel(self, func, *args, **kwargs):
try:
return await asyncio.wait_for(func(*args, **kwargs), 3)
except asyncio.TimeoutError as e:
self.log.warning(f"analytics run_with_timeout failed {e}")
if self.task:
self.log.warning(f"analytics run_with_cancel old : {self.task}")
self.task.cancel()
self.task = asyncio.create_task(func(*args, **kwargs))
except Exception as e:
self.log.warning(f"analytics run_with_cancel failed {e}")
return None
async def send_startup_event(self):
try:
await self.run_with_timeout(self._send_startup_event)
await self.run_with_cancel(self._send_startup_event)
except Exception as e:
self.log.warning(f"analytics send_startup_event failed {e}")
self.init()
@@ -49,7 +53,7 @@ class Analytics:
async def send_daily_event(self):
try:
await self.run_with_timeout(self._send_daily_event)
await self.run_with_cancel(self._send_daily_event)
except Exception as e:
self.log.warning(f"analytics send_daily_event failed {e}")
self.init()
@@ -68,7 +72,7 @@ class Analytics:
async def send_play_event(self, name, sec):
try:
await self.run_with_timeout(self._send_play_event, name, sec)
await self.run_with_cancel(self._send_play_event, name, sec)
except Exception as e:
self.log.warning(f"analytics send_play_event failed {e}")
self.init()

View File

@@ -316,9 +316,9 @@ class MusicItem(BaseModel):
@app.post("/delmusic")
def delmusic(data: MusicItem, Verifcation=Depends(verification)):
async def delmusic(data: MusicItem, Verifcation=Depends(verification)):
log.info(data)
xiaomusic.del_music(data.name)
await xiaomusic.del_music(data.name)
return "success"

View File

@@ -122,7 +122,8 @@ $(function(){
$.get("/musiclist", function(data, status) {
console.log(data, status);
$.each(data, function(key, value) {
$('#music_list').append($('<option></option>').val(key).text(key));
let cnt = value.length;
$('#music_list').append($('<option></option>').val(key).text(`${key} (${cnt})`));
});
$('#music_list').change(function() {
@@ -250,7 +251,13 @@ $(function(){
$("#play").on("click", () => {
var search_key = $("#music-name").val();
if (search_key == null) {
search_key = "";
}
var filename = $("#music-filename").val();
if (filename == null) {
filename = "";
}
let cmd = "播放歌曲" + search_key + "|" + filename;
sendcmd(cmd);
});

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="./style.css?version=1729000721">
<link rel="stylesheet" type="text/css" href="./style.css?version=1729738799">
<script src="https://unpkg.com/vconsole@latest/dist/vconsole.min.js"></script>
<script src="./jquery-3.7.1.min.js?version=1729000721"></script>
<script src="./jquery-3.7.1.min.js?version=1729738799"></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="./style.css?version=1729000721">
<script src="./jquery-3.7.1.min.js?version=1729000721"></script>
<link rel="stylesheet" type="text/css" href="./style.css?version=1729738799">
<script src="./jquery-3.7.1.min.js?version=1729738799"></script>
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-Z09NC1K7ZW"></script>

View File

@@ -4,9 +4,9 @@
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width">
<title>小爱音箱操控面板</title>
<script src="./jquery-3.7.1.min.js?version=1729000721"></script>
<script src="./app.js?version=1729000721"></script>
<link rel="stylesheet" type="text/css" href="./style.css?version=1729000721">
<script src="./jquery-3.7.1.min.js?version=1729738799"></script>
<script src="./app.js?version=1729738799"></script>
<link rel="stylesheet" type="text/css" href="./style.css?version=1729738799">
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-Z09NC1K7ZW"></script>

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="./style.css?version=1729000721">
<link rel="stylesheet" type="text/css" href="./style.css?version=1729738799">
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-Z09NC1K7ZW"></script>

View File

@@ -4,9 +4,9 @@
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width">
<title>小爱音箱操控面板</title>
<script src="./jquery-3.7.1.min.js?version=1729000721"></script>
<script src="./setting.js?version=1729000721"></script>
<link rel="stylesheet" type="text/css" href="./style.css?version=1729000721">
<script src="./jquery-3.7.1.min.js?version=1729738799"></script>
<script src="./setting.js?version=1729738799"></script>
<link rel="stylesheet" type="text/css" href="./style.css?version=1729738799">
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-Z09NC1K7ZW"></script>
@@ -59,7 +59,7 @@ var vConsole = new window.VConsole();
<option value="false">false</option>
</select>
<label for="group_list">设备分组配置:</label>
<label for="group_list">设备分组配置:<a href="https://github.com/hanxi/xiaomusic/issues/65#issuecomment-2215736529" target="_blank">文档</a></label>
<input id="group_list" type="text" placeholder="did1:组名1,did2:组名1,did3:组名2"></input>
<label for="music_path">音乐目录:</label>
@@ -111,7 +111,7 @@ var vConsole = new window.VConsole();
<option value="false" selected>false</option>
</select>
<label for="miio_tts_command">MiIO sst 指令:</label>
<label for="miio_tts_command">MiIO tts 指令(解决部分型号没有提示音的问题):</label>
<input id="miio_tts_command" type="text" placeholder="如5 或者 5-3"></input>
<label for="disable_httpauth">关闭控制台密码验证:</label>

View File

@@ -416,6 +416,15 @@ def get_temp_dir(music_path: str):
def remove_id3_tags(input_file: str, config) -> str:
audio = MP3(input_file, ID3=ID3)
# 检查是否存在ID3 v2.3或v2.4标签
if not (
audio.tags
and (audio.tags.version == (2, 3, 0) or audio.tags.version == (2, 4, 0))
):
return None
music_path = config.music_path
temp_dir = get_temp_dir(music_path)
@@ -436,22 +445,15 @@ def remove_id3_tags(input_file: str, config) -> str:
log.info(f"File {out_file_path} already exists. Skipping remove_id3_tags.")
return relative_path
audio = MP3(input_file, ID3=ID3)
# 检查是否存在ID3 v2.3或v2.4标签
if audio.tags and (
audio.tags.version == (2, 3, 0) or audio.tags.version == (2, 4, 0)
):
# 拷贝文件
shutil.copy(input_file, out_file_path)
outaudio = MP3(out_file_path, ID3=ID3)
# 删除ID3标签
outaudio.delete()
# 保存修改后的文件
outaudio.save(padding=no_padding)
log.info(f"File {out_file_path} remove_id3_tags ok.")
return relative_path
# 开始去除(不再需要检查)
# 拷贝文件
shutil.copy(input_file, out_file_path)
outaudio = MP3(out_file_path, ID3=ID3)
# 删除ID3标签
outaudio.delete()
# 保存修改后的文件
outaudio.save(padding=no_padding)
log.info(f"File {out_file_path} remove_id3_tags ok.")
return relative_path
@@ -591,7 +593,7 @@ def _to_utf8(v):
return ts
return old_ts
elif isinstance(v, list):
return "".join(v)
return "".join(str(item) for item in v)
return str(v)

View File

@@ -892,7 +892,7 @@ class XiaoMusic:
self.log.info("gen_music_list ok")
# 删除歌曲
def del_music(self, name):
async def del_music(self, name):
filename = self.get_filename(name)
if filename == "":
self.log.info(f"${name} not exist")
@@ -1187,7 +1187,6 @@ class XiaoMusicDevice:
self._download_proc = None # 下载对象
self._next_timer = None
self._timeout = 0
self._playing = False
# 播放进度
self._start_time = 0
@@ -1677,14 +1676,17 @@ class XiaoMusicDevice:
# 设置下一首歌曲的播放定时器
async def set_next_music_timeout(self, sec):
self.cancel_next_timer()
self._timeout = sec
async def _do_next():
await asyncio.sleep(self._timeout)
await asyncio.sleep(sec)
try:
self.log.info("定时器时间到了")
self._next_timer = None
await self._play_next()
if self._next_timer:
self._next_timer = None
await self._play_next()
else:
self.log.info("定时器时间到了但是不见了")
except Exception as e:
self.log.error(f"Execption {e}")
@@ -1760,13 +1762,17 @@ class XiaoMusicDevice:
await self.do_tts(f"收到,{minute}分钟后将关机")
def cancel_next_timer(self):
self.log.info("cancel_next_timer")
if self._next_timer:
self._next_timer.cancel()
self.log.info(f"下一曲定时器已取消 {self.device_id}")
self._next_timer = None
else:
self.log.info("下一曲定时器不见了")
def cancel_group_next_timer(self):
devices = self.xiaomusic.get_group_devices(self.group_name)
self.log.info(f"cancel_group_next_timer {devices}")
for device in devices.values():
device.cancel_next_timer()