mirror of
https://github.com/hanxi/xiaomusic.git
synced 2025-12-09 15:18:15 +08:00
Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aa2992b5d7 | ||
|
|
7fcd3eeae5 | ||
|
|
30194272d9 | ||
|
|
1b71301b06 | ||
|
|
13bbff8d67 | ||
|
|
ae3507b811 | ||
|
|
bdfb0a4127 | ||
|
|
3d0a38cbb8 | ||
|
|
f469f63d97 | ||
|
|
6544bb2ff1 | ||
|
|
668237401e | ||
|
|
139ebf37c4 | ||
|
|
7146d61fcb | ||
|
|
6033c1a6fc | ||
|
|
e77a4fc10d | ||
|
|
bf2909d35a | ||
|
|
b3255a17ce | ||
|
|
a3140ff23a | ||
|
|
becfdbf338 | ||
|
|
8b74b664f0 | ||
|
|
0daba20885 | ||
|
|
8ac39af8cd | ||
|
|
d6fb62eb8e | ||
|
|
24ac876632 | ||
|
|
fcdb7bf035 | ||
|
|
4c927c56c0 | ||
|
|
3a7672982f | ||
|
|
a3ddca05a3 | ||
|
|
e9dba716b7 | ||
|
|
6ed5d0cb5f | ||
|
|
44819de3a5 | ||
|
|
043ad12dec | ||
|
|
cc5facdf4f |
64
.github/workflows/ci.yml
vendored
64
.github/workflows/ci.yml
vendored
@@ -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:
|
||||
|
||||
47
CHANGELOG.md
47
CHANGELOG.md
@@ -1,3 +1,50 @@
|
||||
## 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
|
||||
|
||||
- pure主题 当前设备与远程设备未正确区分的问题 (#234)
|
||||
- static和doc添加basic auth (#231)
|
||||
|
||||
### Refactor
|
||||
|
||||
- 修改默认UI播放提示词 (#233)
|
||||
|
||||
## v0.3.38 (2024-10-14)
|
||||
|
||||
### Feat
|
||||
|
||||
@@ -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"]
|
||||
|
||||
36
README.md
36
README.md
@@ -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)
|
||||
|
||||
## 🚨 免责声明
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "xiaomusic"
|
||||
version = "0.3.38"
|
||||
version = "0.3.42"
|
||||
description = "Play Music with xiaomi AI speaker"
|
||||
authors = [
|
||||
{name = "涵曦", email = "im.hanxi@gmail.com"},
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = "0.3.38"
|
||||
__version__ = "0.3.42"
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -24,6 +24,8 @@ from fastapi import (
|
||||
status,
|
||||
)
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.openapi.docs import get_redoc_html, get_swagger_ui_html
|
||||
from fastapi.openapi.utils import get_openapi
|
||||
from fastapi.responses import RedirectResponse, StreamingResponse
|
||||
from fastapi.security import HTTPBasic, HTTPBasicCredentials
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
@@ -92,6 +94,9 @@ def no_verification():
|
||||
app = FastAPI(
|
||||
lifespan=app_lifespan,
|
||||
version=__version__,
|
||||
docs_url=None,
|
||||
redoc_url=None,
|
||||
openapi_url=None,
|
||||
)
|
||||
|
||||
app.add_middleware(
|
||||
@@ -111,6 +116,17 @@ def reset_http_server():
|
||||
app.dependency_overrides = {}
|
||||
|
||||
|
||||
class AuthStaticFiles(StaticFiles):
|
||||
def __init__(self, *args, **kwargs) -> None:
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
async def __call__(self, scope, receive, send) -> None:
|
||||
request = Request(scope, receive)
|
||||
if not config.disable_httpauth:
|
||||
assert verification(await security(request))
|
||||
await super().__call__(scope, receive, send)
|
||||
|
||||
|
||||
def HttpInit(_xiaomusic):
|
||||
global xiaomusic, config, log
|
||||
xiaomusic = _xiaomusic
|
||||
@@ -118,7 +134,7 @@ def HttpInit(_xiaomusic):
|
||||
log = xiaomusic.log
|
||||
|
||||
folder = os.path.dirname(__file__)
|
||||
app.mount("/static", StaticFiles(directory=f"{folder}/static"), name="static")
|
||||
app.mount("/static", AuthStaticFiles(directory=f"{folder}/static"), name="static")
|
||||
reset_http_server()
|
||||
|
||||
|
||||
@@ -300,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"
|
||||
|
||||
|
||||
@@ -598,3 +614,18 @@ async def get_picture(request: Request, file_path: str, key: str = "", code: str
|
||||
if mime_type is None:
|
||||
mime_type = "image/jpeg"
|
||||
return FileResponse(absolute_file_path, media_type=mime_type)
|
||||
|
||||
|
||||
@app.get("/docs", include_in_schema=False)
|
||||
async def get_swagger_documentation(Verifcation=Depends(verification)):
|
||||
return get_swagger_ui_html(openapi_url="/openapi.json", title="docs")
|
||||
|
||||
|
||||
@app.get("/redoc", include_in_schema=False)
|
||||
async def get_redoc_documentation(Verifcation=Depends(verification)):
|
||||
return get_redoc_html(openapi_url="/openapi.json", title="docs")
|
||||
|
||||
|
||||
@app.get("/openapi.json", include_in_schema=False)
|
||||
async def openapi(Verifcation=Depends(verification)):
|
||||
return get_openapi(title=app.title, version=app.version, routes=app.routes)
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
@@ -344,7 +351,7 @@ $(function(){
|
||||
// 添加用户输入作为一个选项
|
||||
const userOption = document.createElement('option');
|
||||
userOption.value = query;
|
||||
userOption.textContent = `使用关键词联网搜索: ${query}`;
|
||||
userOption.textContent = `使用关键词播放: ${query}`;
|
||||
musicSelect.appendChild(userOption);
|
||||
|
||||
// 提示没找到
|
||||
|
||||
@@ -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=1728858679">
|
||||
<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=1728858679"></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>
|
||||
|
||||
@@ -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=1728858679">
|
||||
<script src="./jquery-3.7.1.min.js?version=1728858679"></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>
|
||||
|
||||
@@ -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=1728858679"></script>
|
||||
<script src="./app.js?version=1728858679"></script>
|
||||
<link rel="stylesheet" type="text/css" href="./style.css?version=1728858679">
|
||||
<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>
|
||||
|
||||
@@ -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=1728858679">
|
||||
<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>
|
||||
|
||||
@@ -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=1728858679"></script>
|
||||
<script src="./setting.js?version=1728858679"></script>
|
||||
<link rel="stylesheet" type="text/css" href="./style.css?version=1728858679">
|
||||
<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>
|
||||
@@ -99,19 +99,19 @@ var vConsole = new window.VConsole();
|
||||
<label for="proxy">XIAOMUSIC_PROXY(ytsearch需要):</label>
|
||||
<input id="proxy" type="text" placeholder="http://192.168.2.5:8080"></input>
|
||||
|
||||
<label for="remove_id3tag">去除MP3 ID3v2和填充,减少播放前延迟:</label>
|
||||
<label for="remove_id3tag">去除MP3 ID3v2和填充:</label>
|
||||
<select id="remove_id3tag">
|
||||
<option value="true">true</option>
|
||||
<option value="false" selected>false</option>
|
||||
</select>
|
||||
|
||||
<label for="convert_to_mp3">转换为MP3</label>
|
||||
<label for="convert_to_mp3">转换为MP3:</label>
|
||||
<select id="convert_to_mp3">
|
||||
<option value="true">true</option>
|
||||
<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>
|
||||
|
||||
41
xiaomusic/static/pure/assets/index-B19OeAC1.js
Normal file
41
xiaomusic/static/pure/assets/index-B19OeAC1.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
xiaomusic/static/pure/assets/index-co2Wfzfa.css
Normal file
1
xiaomusic/static/pure/assets/index-co2Wfzfa.css
Normal file
File diff suppressed because one or more lines are too long
@@ -6,8 +6,8 @@
|
||||
<link rel="icon" href="/static/pure/favicon.ico">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>小爱音箱操控面板</title>
|
||||
<script type="module" crossorigin src="/static/pure/assets/index-C0SgXQ6G.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/static/pure/assets/index-DuznNR7f.css">
|
||||
<script type="module" crossorigin src="/static/pure/assets/index-B19OeAC1.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/static/pure/assets/index-co2Wfzfa.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
@@ -116,12 +116,19 @@ def keyword_detection(user_input, str_list, n):
|
||||
else:
|
||||
remains.append(item)
|
||||
|
||||
matched = sorted(
|
||||
matched,
|
||||
key=lambda s: difflib.SequenceMatcher(None, s, user_input).ratio(),
|
||||
reverse=True, # 降序排序,越相似的越靠前
|
||||
)
|
||||
|
||||
# 如果 n 是 -1,如果 n 大于匹配的数量,返回所有匹配的结果
|
||||
if n == -1 or n > len(matched):
|
||||
return matched, remains
|
||||
|
||||
# 随机选择 n 个匹配的结果
|
||||
return random.sample(matched, n), remains
|
||||
# 选择前 n 个匹配的结果
|
||||
remains = matched[n:] + remains
|
||||
return matched[:n], remains
|
||||
|
||||
|
||||
def real_search(prompt, candidates, cutoff, n):
|
||||
@@ -409,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)
|
||||
|
||||
@@ -429,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
|
||||
|
||||
|
||||
@@ -584,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)
|
||||
|
||||
|
||||
|
||||
@@ -602,7 +602,17 @@ class XiaoMusic:
|
||||
|
||||
# self.log.debug(self.all_music)
|
||||
|
||||
self.music_list = OrderedDict({"临时搜索列表": []})
|
||||
self.music_list = OrderedDict(
|
||||
{
|
||||
"临时搜索列表": [],
|
||||
"所有歌曲": [],
|
||||
"所有电台": [],
|
||||
"收藏": [],
|
||||
"全部": [], # 包含所有歌曲和所有电台
|
||||
"下载": [], # 下载目录下的
|
||||
"其他": [], # 主目录下的
|
||||
}
|
||||
)
|
||||
# 全部,所有,自定义歌单(收藏)
|
||||
self.music_list["全部"] = list(self.all_music.keys())
|
||||
self.music_list["所有歌曲"] = [
|
||||
@@ -882,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")
|
||||
@@ -1177,7 +1187,6 @@ class XiaoMusicDevice:
|
||||
|
||||
self._download_proc = None # 下载对象
|
||||
self._next_timer = None
|
||||
self._timeout = 0
|
||||
self._playing = False
|
||||
# 播放进度
|
||||
self._start_time = 0
|
||||
@@ -1667,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}")
|
||||
|
||||
@@ -1750,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()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user