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

Compare commits

...

9 Commits

Author SHA1 Message Date
涵曦
15ee6c4dd1 new version v0.1.44 2024-06-14 15:47:02 +00:00
涵曦
aeaa8f8925 fmt 2024-06-14 15:46:47 +00:00
涵曦
59d7e056c4 new version v0.1.43 2024-06-14 15:14:56 +00:00
涵曦
e79afa46b3 新增删除歌曲按钮 2024-06-14 15:14:34 +00:00
涵曦
9714f3d064 new version v0.1.41 2024-06-14 14:11:00 +00:00
涵曦
2c35c6cfd6 add XIAOMUSIC_VERBOSE env 2024-06-14 14:10:56 +00:00
涵曦
88f0ce7e51 use ruff lint and fmt code 2024-06-14 01:58:10 +00:00
涵曦
e484164fad 修复刷新列表问题 2024-06-13 14:49:36 +00:00
涵曦
aa6bce75cd update readme 2024-06-12 17:26:00 +00:00
11 changed files with 200 additions and 86 deletions

View File

@@ -49,6 +49,11 @@ pdm run xiaomusic.py
- 下一首 - 下一首
- 单曲循环 - 单曲循环
- 全部循环 - 全部循环
- 随机播放
- 关机
- 停止播放
- 刷新列表
- 播放列表+列表名 比如:播放列表其他
> 隐藏玩法: 对小爱同学说播放歌曲小猪佩奇的故事,会播放小猪佩奇的故事。 > 隐藏玩法: 对小爱同学说播放歌曲小猪佩奇的故事,会播放小猪佩奇的故事。

29
pdm.lock generated
View File

@@ -2,10 +2,10 @@
# It is not intended for manual editing. # It is not intended for manual editing.
[metadata] [metadata]
groups = ["default"] groups = ["default", "lint"]
strategy = ["cross_platform"] strategy = ["cross_platform"]
lock_version = "4.4.1" lock_version = "4.4.1"
content_hash = "sha256:d771311a452ca58665efe3b74af341cb202d75d83a250896c293ea9c696e5696" content_hash = "sha256:fa83f8134ccb4a432304dead5fe1303899418558634abaa0824e8d9fdfb1f490"
[[package]] [[package]]
name = "aiohttp" name = "aiohttp"
@@ -648,6 +648,31 @@ files = [
{file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"}, {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"},
] ]
[[package]]
name = "ruff"
version = "0.4.8"
requires_python = ">=3.7"
summary = "An extremely fast Python linter and code formatter, written in Rust."
files = [
{file = "ruff-0.4.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:7663a6d78f6adb0eab270fa9cf1ff2d28618ca3a652b60f2a234d92b9ec89066"},
{file = "ruff-0.4.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:eeceb78da8afb6de0ddada93112869852d04f1cd0f6b80fe464fd4e35c330913"},
{file = "ruff-0.4.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aad360893e92486662ef3be0a339c5ca3c1b109e0134fcd37d534d4be9fb8de3"},
{file = "ruff-0.4.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:284c2e3f3396fb05f5f803c9fffb53ebbe09a3ebe7dda2929ed8d73ded736deb"},
{file = "ruff-0.4.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7354f921e3fbe04d2a62d46707e569f9315e1a613307f7311a935743c51a764"},
{file = "ruff-0.4.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:72584676164e15a68a15778fd1b17c28a519e7a0622161eb2debdcdabdc71883"},
{file = "ruff-0.4.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9678d5c9b43315f323af2233a04d747409d1e3aa6789620083a82d1066a35199"},
{file = "ruff-0.4.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704977a658131651a22b5ebeb28b717ef42ac6ee3b11e91dc87b633b5d83142b"},
{file = "ruff-0.4.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d05f8d6f0c3cce5026cecd83b7a143dcad503045857bc49662f736437380ad45"},
{file = "ruff-0.4.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:6ea874950daca5697309d976c9afba830d3bf0ed66887481d6bca1673fc5b66a"},
{file = "ruff-0.4.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:fc95aac2943ddf360376be9aa3107c8cf9640083940a8c5bd824be692d2216dc"},
{file = "ruff-0.4.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:384154a1c3f4bf537bac69f33720957ee49ac8d484bfc91720cc94172026ceed"},
{file = "ruff-0.4.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e9d5ce97cacc99878aa0d084c626a15cd21e6b3d53fd6f9112b7fc485918e1fa"},
{file = "ruff-0.4.8-py3-none-win32.whl", hash = "sha256:6d795d7639212c2dfd01991259460101c22aabf420d9b943f153ab9d9706e6a9"},
{file = "ruff-0.4.8-py3-none-win_amd64.whl", hash = "sha256:e14a3a095d07560a9d6769a72f781d73259655919d9b396c650fc98a8157555d"},
{file = "ruff-0.4.8-py3-none-win_arm64.whl", hash = "sha256:14019a06dbe29b608f6b7cbcec300e3170a8d86efaddb7b23405cb7f7dcaf780"},
{file = "ruff-0.4.8.tar.gz", hash = "sha256:16d717b1d57b2e2fd68bd0bf80fb43931b79d05a7131aa477d66fc40fbd86268"},
]
[[package]] [[package]]
name = "typing-extensions" name = "typing-extensions"
version = "4.9.0" version = "4.9.0"

View File

@@ -1,6 +1,6 @@
[project] [project]
name = "xiaomusic" name = "xiaomusic"
version = "0.1.40" version = "0.1.44"
description = "Play Music with xiaomi AI speaker" description = "Play Music with xiaomi AI speaker"
authors = [ authors = [
{name = "涵曦", email = "im.hanxi@gmail.com"}, {name = "涵曦", email = "im.hanxi@gmail.com"},
@@ -24,3 +24,29 @@ requires = ["pdm-backend"]
build-backend = "pdm.backend" build-backend = "pdm.backend"
[tool.pdm] [tool.pdm]
[tool.pdm.dev-dependencies]
lint = [
"ruff>=0.4.8",
]
[tool.ruff]
select = [
"B", # flake8-bugbear
"C4", # flake8-comprehensions
"E", # pycodestyle - Error
"F", # Pyflakes
"I", # isort
"W", # pycodestyle - Warning
"UP", # pyupgrade
]
ignore = [
"E501", # line-too-long
"W191", # tab-indentation
]
include = ["**/*.py", "**/*.pyi", "**/pyproject.toml"]
[tool.ruff.pydocstyle]
convention = "google"
[tool.pdm.scripts]
lint = "ruff check ."
fmt = "ruff format ."

View File

@@ -1 +1 @@
pdm export -o requirements.txt pdm export --prod -o requirements.txt

View File

@@ -1 +1 @@
__version__ = "0.1.40" __version__ = "0.1.44"

View File

@@ -3,8 +3,7 @@ from __future__ import annotations
import argparse import argparse
import json import json
import os import os
from dataclasses import dataclass, field from dataclasses import dataclass
from typing import Any, Iterable
from xiaomusic.utils import validate_proxy from xiaomusic.utils import validate_proxy
@@ -13,7 +12,7 @@ COOKIE_TEMPLATE = "deviceId={device_id}; serviceToken={service_token}; userId={u
HARDWARE_COMMAND_DICT = { HARDWARE_COMMAND_DICT = {
# hardware: (tts_command, wakeup_command, volume_command) # hardware: (tts_command, wakeup_command, volume_command)
"LX06": ("5-1", "5-5", "2-1"), "LX06": ("5-1", "5-5", "2-1"),
"L05B": ("5-3", "5-4", "2-1"), "L05B": ("5-3", "5-4", "2-1"),
"S12": ("5-1", "5-5", "2-1"), # 第一代小爱型号MDZ-25-DA "S12": ("5-1", "5-5", "2-1"), # 第一代小爱型号MDZ-25-DA
"S12A": ("5-1", "5-5", "2-1"), "S12A": ("5-1", "5-5", "2-1"),
"LX01": ("5-1", "5-5", "2-1"), "LX01": ("5-1", "5-5", "2-1"),
@@ -86,7 +85,7 @@ class Config:
mute_xiaoai: bool = True mute_xiaoai: bool = True
cookie: str = "" cookie: str = ""
use_command: bool = False use_command: bool = False
verbose: bool = False verbose: bool = os.getenv("XIAOMUSIC_VERBOSE", "").lower() == "true"
music_path: str = os.getenv("XIAOMUSIC_MUSIC_PATH", "music") music_path: str = os.getenv("XIAOMUSIC_MUSIC_PATH", "music")
hostname: str = os.getenv("XIAOMUSIC_HOSTNAME", "192.168.2.5") hostname: str = os.getenv("XIAOMUSIC_HOSTNAME", "192.168.2.5")
port: int = int(os.getenv("XIAOMUSIC_PORT", "8090")) port: int = int(os.getenv("XIAOMUSIC_PORT", "8090"))

View File

@@ -1,25 +1,21 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import os import os
import sys from threading import Thread
import traceback
import asyncio
from flask import Flask, request, send_from_directory from flask import Flask, request, send_from_directory
from waitress import serve from waitress import serve
from threading import Thread
from xiaomusic.config import (
KEY_WORD_DICT,
)
from xiaomusic import ( from xiaomusic import (
__version__, __version__,
) )
from xiaomusic.config import (
KEY_WORD_DICT,
)
# 隐藏 flask 启动告警 # 隐藏 flask 启动告警
# https://gist.github.com/jerblack/735b9953ba1ab6234abb43174210d356 # https://gist.github.com/jerblack/735b9953ba1ab6234abb43174210d356
#from flask import cli # from flask import cli
#cli.show_server_banner = lambda *_: None # cli.show_server_banner = lambda *_: None
app = Flask(__name__) app = Flask(__name__)
host = "0.0.0.0" host = "0.0.0.0"
@@ -33,6 +29,7 @@ log = None
def allcmds(): def allcmds():
return KEY_WORD_DICT return KEY_WORD_DICT
@app.route("/getversion", methods=["GET"]) @app.route("/getversion", methods=["GET"])
def getversion(): def getversion():
log.debug("getversion %s", __version__) log.debug("getversion %s", __version__)
@@ -40,6 +37,7 @@ def getversion():
"version": __version__, "version": __version__,
} }
@app.route("/getvolume", methods=["GET"]) @app.route("/getvolume", methods=["GET"])
def getvolume(): def getvolume():
volume = xiaomusic.get_volume_ret() volume = xiaomusic.get_volume_ret()
@@ -47,15 +45,18 @@ def getvolume():
"volume": volume, "volume": volume,
} }
@app.route("/searchmusic", methods=["GET"]) @app.route("/searchmusic", methods=["GET"])
def searchmusic(): def searchmusic():
name = request.args.get('name') name = request.args.get("name")
return xiaomusic.searchmusic(name) return xiaomusic.searchmusic(name)
@app.route("/playingmusic", methods=["GET"]) @app.route("/playingmusic", methods=["GET"])
def playingmusic(): def playingmusic():
return xiaomusic.playingmusic() return xiaomusic.playingmusic()
@app.route("/", methods=["GET"]) @app.route("/", methods=["GET"])
def redirect_to_index(): def redirect_to_index():
return send_from_directory("static", "index.html") return send_from_directory("static", "index.html")
@@ -71,6 +72,7 @@ async def do_cmd():
return {"ret": "OK"} return {"ret": "OK"}
return {"ret": "Unknow cmd"} return {"ret": "Unknow cmd"}
@app.route("/getsetting", methods=["GET"]) @app.route("/getsetting", methods=["GET"])
async def getsetting(): async def getsetting():
config = xiaomusic.getconfig() config = xiaomusic.getconfig()
@@ -88,6 +90,7 @@ async def getsetting():
} }
return data return data
@app.route("/savesetting", methods=["POST"]) @app.route("/savesetting", methods=["POST"])
async def savesetting(): async def savesetting():
data = request.get_json() data = request.get_json()
@@ -95,14 +98,25 @@ async def savesetting():
await xiaomusic.saveconfig(data) await xiaomusic.saveconfig(data)
return "save success" return "save success"
@app.route("/musiclist", methods=["GET"]) @app.route("/musiclist", methods=["GET"])
async def musiclist(): async def musiclist():
return xiaomusic.get_music_list() return xiaomusic.get_music_list()
@app.route("/curplaylist", methods=["GET"]) @app.route("/curplaylist", methods=["GET"])
async def curplaylist(): async def curplaylist():
return xiaomusic.get_cur_play_list() return xiaomusic.get_cur_play_list()
@app.route("/delmusic", methods=["POST"])
def delmusic():
data = request.get_json()
log.info(data)
xiaomusic.del_music(data["name"])
return "success"
def static_path_handler(filename): def static_path_handler(filename):
log.debug(filename) log.debug(filename)
log.debug(static_path) log.debug(static_path)
@@ -110,9 +124,11 @@ def static_path_handler(filename):
log.debug(absolute_path) log.debug(absolute_path)
return send_from_directory(absolute_path, filename) return send_from_directory(absolute_path, filename)
def run_app(): def run_app():
serve(app, host=host, port=port) serve(app, host=host, port=port)
def StartHTTPServer(_port, _static_path, _xiaomusic): def StartHTTPServer(_port, _static_path, _xiaomusic):
global port, static_path, xiaomusic, log global port, static_path, xiaomusic, log
port = _port port = _port

View File

@@ -27,35 +27,59 @@ $(function(){
}); });
// 拉取播放列表 // 拉取播放列表
$.get("/musiclist", function(data, status) { function refresh_music_list() {
console.log(data, status); $('#music_list').empty();
$.each(data, function(key, value) { $.get("/musiclist", function(data, status) {
$('#music_list').append($('<option></option>').val(key).text(key)); console.log(data, status);
}); $.each(data, function(key, value) {
$('#music_list').append($('<option></option>').val(key).text(key));
$('#music_list').change(function() {
const selectedValue = $(this).val();
$('#music_name').empty();
$.each(data[selectedValue], function(index, item) {
$('#music_name').append($('<option></option>').val(item).text(item));
}); });
});
$('#music_list').trigger('change'); $('#music_list').change(function() {
const selectedValue = $(this).val();
$('#music_name').empty();
$.each(data[selectedValue], function(index, item) {
$('#music_name').append($('<option></option>').val(item).text(item));
});
});
// 获取当前播放列表
$.get("curplaylist", function(data, status) {
$('#music_list').val(data);
$('#music_list').trigger('change'); $('#music_list').trigger('change');
// 获取当前播放列表
$.get("curplaylist", function(data, status) {
$('#music_list').val(data);
$('#music_list').trigger('change');
})
}) })
}) }
refresh_music_list();
$("#play_music_list").on("click", () => { $("#play_music_list").on("click", () => {
var music_list = $("#music_list").val(); var music_list = $("#music_list").val();
var music_name = $("#music_name").val(); var music_name = $("#music_name").val();
let cmd = "播放列表" + music_list + "|" + music_name; let cmd = "播放列表" + music_list + "|" + music_name;
sendcmd(cmd); sendcmd(cmd);
}) });
$("#del_music").on("click", () => {
var del_music_name = $("#music_name").val();
if (confirm(`确定删除歌曲 ${del_music_name} 吗?`)) {
console.log(`删除歌曲 ${del_music_name}`);
$.ajax({
type: 'POST',
url: '/delmusic',
data: JSON.stringify({"name": del_music_name}),
contentType: "application/json; charset=utf-8",
success: () => {
alert(`删除 ${del_music_name} 成功`);
refresh_music_list();
},
error: () => {
alert(`删除 ${del_music_name} 失败`);
}
});
}
});
function append_op_button_name(name) { function append_op_button_name(name) {
append_op_button(name, name); append_op_button(name, name);
@@ -92,11 +116,11 @@ $(function(){
$.ajax({ $.ajax({
type: "POST", type: "POST",
url: "/cmd", url: "/cmd",
contentType: "application/json", contentType: "application/json; charset=utf-8",
data: JSON.stringify({cmd: cmd}), data: JSON.stringify({cmd: cmd}),
success: () => { success: () => {
if (cmd == "刷新列表") { if (cmd == "刷新列表") {
location.reload(); setTimeout(refresh_music_list, 3000);
} }
}, },
error: () => { error: () => {

View File

@@ -41,6 +41,7 @@
<select id="music_name"></select> <select id="music_name"></select>
</div> </div>
<button id="play_music_list">播放列表歌曲</button> <button id="play_music_list">播放列表歌曲</button>
<button id="del_music">删除选中歌曲</button>
<footer> <footer>
<p>Powered by <a href="https://github.com/hanxi/xiaomusic" target="_blank">xiaomusic</a></p> <p>Powered by <a href="https://github.com/hanxi/xiaomusic" target="_blank">xiaomusic</a></p>

View File

@@ -1,13 +1,11 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from __future__ import annotations from __future__ import annotations
import os
import re
import socket
from http.cookies import SimpleCookie
from typing import AsyncIterator
from urllib.parse import urlparse
import difflib import difflib
import re
from collections.abc import AsyncIterator
from http.cookies import SimpleCookie
from urllib.parse import urlparse
from requests.utils import cookiejar_from_dict from requests.utils import cookiejar_from_dict
@@ -62,6 +60,7 @@ def validate_proxy(proxy_str: str) -> bool:
return True return True
# 模糊搜索 # 模糊搜索
def fuzzyfinder(user_input, collection): def fuzzyfinder(user_input, collection):
return difflib.get_close_matches(user_input, collection, 10, cutoff=0.1) return difflib.get_close_matches(user_input, collection, 10, cutoff=0.1)

View File

@@ -3,45 +3,43 @@ import asyncio
import json import json
import logging import logging
import os import os
import queue
import random import random
import re import re
import time import time
import urllib.parse
import traceback import traceback
import mutagen import urllib.parse
import queue
from xiaomusic.httpserver import StartHTTPServer
from pathlib import Path from pathlib import Path
import mutagen
from aiohttp import ClientSession, ClientTimeout from aiohttp import ClientSession, ClientTimeout
from miservice import MiAccount, MiIOService, MiNAService, miio_command from miservice import MiAccount, MiIOService, MiNAService, miio_command
from rich import print
from rich.logging import RichHandler from rich.logging import RichHandler
from xiaomusic.config import (
COOKIE_TEMPLATE,
LATEST_ASK_API,
KEY_WORD_DICT,
KEY_WORD_ARG_BEFORE_DICT,
KEY_MATCH_ORDER,
SUPPORT_MUSIC_TYPE,
Config,
)
from xiaomusic.utils import (
parse_cookie_string,
fuzzyfinder,
)
from xiaomusic import ( from xiaomusic import (
__version__, __version__,
) )
from xiaomusic.config import (
COOKIE_TEMPLATE,
KEY_MATCH_ORDER,
KEY_WORD_ARG_BEFORE_DICT,
KEY_WORD_DICT,
LATEST_ASK_API,
SUPPORT_MUSIC_TYPE,
Config,
)
from xiaomusic.httpserver import StartHTTPServer
from xiaomusic.utils import (
fuzzyfinder,
parse_cookie_string,
)
EOF = object() EOF = object()
PLAY_TYPE_ONE = 0 # 单曲循环 PLAY_TYPE_ONE = 0 # 单曲循环
PLAY_TYPE_ALL = 1 # 全部循环 PLAY_TYPE_ALL = 1 # 全部循环
class XiaoMusic: class XiaoMusic:
def __init__(self, config: Config): def __init__(self, config: Config):
self.config = config self.config = config
@@ -76,7 +74,7 @@ class XiaoMusic:
self._all_music = {} self._all_music = {}
self._play_list = [] self._play_list = []
self._cur_play_list = "" self._cur_play_list = ""
self._music_list = {} # 播放列表 key 为目录名, value 为 play_list self._music_list = {} # 播放列表 key 为目录名, value 为 play_list
self._playing = False self._playing = False
# 关机定时器 # 关机定时器
@@ -86,7 +84,7 @@ class XiaoMusic:
logging.basicConfig( logging.basicConfig(
format=f"[{__version__}]\t%(message)s", format=f"[{__version__}]\t%(message)s",
datefmt="[%X]", datefmt="[%X]",
handlers=[RichHandler(rich_tracebacks=True)] handlers=[RichHandler(rich_tracebacks=True)],
) )
self.log = logging.getLogger("xiaomusic") self.log = logging.getLogger("xiaomusic")
self.log.setLevel(logging.DEBUG if config.verbose else logging.INFO) self.log.setLevel(logging.DEBUG if config.verbose else logging.INFO)
@@ -290,7 +288,10 @@ class XiaoMusic:
def is_downloading(self): def is_downloading(self):
if not self.download_proc: if not self.download_proc:
return False return False
if self.download_proc.returncode != None and self.download_proc.returncode < 0: if (
self.download_proc.returncode is not None
and self.download_proc.returncode < 0
):
return False return False
return True return True
@@ -379,7 +380,7 @@ class XiaoMusic:
self._music_list = {} self._music_list = {}
self._music_list["全部"] = self._play_list self._music_list["全部"] = self._play_list
for dir_name,musics in all_music_by_dir.items(): for dir_name, musics in all_music_by_dir.items():
self._music_list[dir_name] = list(musics.keys()) self._music_list[dir_name] = list(musics.keys())
self.log.debug("dir_name:%s, list:%s", dir_name, self._music_list[dir_name]) self.log.debug("dir_name:%s, list:%s", dir_name, self._music_list[dir_name])
pass pass
@@ -396,7 +397,7 @@ class XiaoMusic:
def get_next_music(self): def get_next_music(self):
play_list_len = len(self._play_list) play_list_len = len(self._play_list)
if play_list_len == 0: if play_list_len == 0:
self.log.warning(f"没有随机到歌曲") self.log.warning("没有随机到歌曲")
return "" return ""
# 随机选择一个文件 # 随机选择一个文件
index = 0 index = 0
@@ -411,6 +412,7 @@ class XiaoMusic:
filename = self.get_filename(name) filename = self.get_filename(name)
if len(filename) <= 0: if len(filename) <= 0:
self._play_list.pop(next_index) self._play_list.pop(next_index)
self.log.info(f"pop not exist music:{name}")
return self.get_next_music() return self.get_next_music()
return name return name
@@ -429,7 +431,7 @@ class XiaoMusic:
self.log.info(f"歌曲 {self.cur_music} : {filename} 的时长 {sec}") self.log.info(f"歌曲 {self.cur_music} : {filename} 的时长 {sec}")
if self._next_timer: if self._next_timer:
self._next_timer.cancel() self._next_timer.cancel()
self.log.info(f"定时器已取消") self.log.info("定时器已取消")
self._timeout = sec self._timeout = sec
async def _do_next(): async def _do_next():
@@ -449,11 +451,11 @@ class XiaoMusic:
await self.init_all_data(session) await self.init_all_data(session)
task = asyncio.create_task(self.poll_latest_ask()) task = asyncio.create_task(self.poll_latest_ask())
assert task is not None # to keep the reference to task, do not remove this assert task is not None # to keep the reference to task, do not remove this
filtered_keywords = [keyword for keyword in KEY_MATCH_ORDER if "#" not in keyword] filtered_keywords = [
keyword for keyword in KEY_MATCH_ORDER if "#" not in keyword
]
joined_keywords = "/".join(filtered_keywords) joined_keywords = "/".join(filtered_keywords)
self.log.info( self.log.info(f"Running xiaomusic now, 用`{joined_keywords}`开头来控制")
f"Running xiaomusic now, 用`{joined_keywords}`开头来控制"
)
while True: while True:
self.polling_event.set() self.polling_event.set()
@@ -560,30 +562,44 @@ class XiaoMusic:
if self.play_type == PLAY_TYPE_ALL or name == "": if self.play_type == PLAY_TYPE_ALL or name == "":
name = self.get_next_music() name = self.get_next_music()
if name == "": if name == "":
await self.do_tts(f"本地没有歌曲") await self.do_tts("本地没有歌曲")
return return
await self.play(arg1=name) await self.play(arg1=name)
# 单曲循环 # 单曲循环
async def set_play_type_one(self, **kwargs): async def set_play_type_one(self, **kwargs):
self.play_type = PLAY_TYPE_ONE self.play_type = PLAY_TYPE_ONE
await self.do_tts(f"已经设置为单曲循环") await self.do_tts("已经设置为单曲循环")
# 全部循环 # 全部循环
async def set_play_type_all(self, **kwargs): async def set_play_type_all(self, **kwargs):
self.play_type = PLAY_TYPE_ALL self.play_type = PLAY_TYPE_ALL
await self.do_tts(f"已经设置为全部循环") await self.do_tts("已经设置为全部循环")
# 随机播放 # 随机播放
async def random_play(self, **kwargs): async def random_play(self, **kwargs):
self.play_type = PLAY_TYPE_ALL self.play_type = PLAY_TYPE_ALL
random.shuffle(self._play_list) random.shuffle(self._play_list)
await self.do_tts(f"已经设置为随机播放") await self.do_tts("已经设置为随机播放")
# 刷新列表 # 刷新列表
async def gen_music_list(self, **kwargs): async def gen_music_list(self, **kwargs):
self._gen_all_music_list() self._gen_all_music_list()
await self.do_tts(f"生成播放列表完毕") await self.do_tts("生成播放列表完毕")
# 删除歌曲
def del_music(self, name):
filename = self.get_filename(name)
if filename == "":
self.log.info(f"${name} not exist")
return
try:
os.remove(filename)
self.log.info(f"del ${filename} success")
except OSError:
self.log.error(f"del ${filename} failed")
pass
self._gen_all_music_list()
# 播放一个播放列表 # 播放一个播放列表
async def play_music_list(self, **kwargs): async def play_music_list(self, **kwargs):
@@ -607,14 +623,14 @@ class XiaoMusic:
self._playing = False self._playing = False
if self._next_timer: if self._next_timer:
self._next_timer.cancel() self._next_timer.cancel()
self.log.info(f"定时器已取消") self.log.info("定时器已取消")
self.cur_music = "" self.cur_music = ""
await self.force_stop_xiaoai() await self.force_stop_xiaoai()
async def stop_after_minute(self, **kwargs): async def stop_after_minute(self, **kwargs):
if self._stop_timer: if self._stop_timer:
self._stop_timer.cancel() self._stop_timer.cancel()
self.log.info(f"关机定时器已取消") self.log.info("关机定时器已取消")
minute = int(kwargs["arg1"]) minute = int(kwargs["arg1"])
async def _do_stop(): async def _do_stop():
@@ -634,7 +650,9 @@ class XiaoMusic:
async def get_volume(self, **kwargs): async def get_volume(self, **kwargs):
playing_info = await self.mina_service.player_get_status(self.device_id) playing_info = await self.mina_service.player_get_status(self.device_id)
self.log.debug("get_volume. playing_info:%s", playing_info) self.log.debug("get_volume. playing_info:%s", playing_info)
self._volume = json.loads(playing_info.get("data", {}).get("info", "{}")).get("volume", 5) self._volume = json.loads(playing_info.get("data", {}).get("info", "{}")).get(
"volume", 5
)
self.log.info("get_volume. volume:%s", self._volume) self.log.info("get_volume. volume:%s", self._volume)
def get_volume_ret(self): def get_volume_ret(self):
@@ -679,7 +697,7 @@ class XiaoMusic:
async def saveconfig(self, data): async def saveconfig(self, data):
# 默认暂时配置保存到 music 目录下 # 默认暂时配置保存到 music 目录下
filename = os.path.join(self.music_path, "setting.json") filename = os.path.join(self.music_path, "setting.json")
with open(filename, 'w', encoding='utf-8') as f: with open(filename, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=4) json.dump(data, f, ensure_ascii=False, indent=4)
self.update_config_from_setting(data) self.update_config_from_setting(data)
await self.call_main_thread_function(self.reinit) await self.call_main_thread_function(self.reinit)
@@ -724,12 +742,13 @@ class XiaoMusic:
async def call_main_thread_function(self, func, arg1=None): async def call_main_thread_function(self, func, arg1=None):
loop = asyncio.get_event_loop() loop = asyncio.get_event_loop()
future = loop.create_future() future = loop.create_future()
def callback(ret): def callback(ret):
nonlocal future nonlocal future
loop.call_soon_threadsafe(future.set_result, ret) loop.call_soon_threadsafe(future.set_result, ret)
self.queue.put((func, callback, arg1)) self.queue.put((func, callback, arg1))
self.last_record = None self.last_record = None
self.new_record_event.set() self.new_record_event.set()
result = await future result = await future
return result return result