1
0
mirror of https://github.com/hanxi/xiaomusic.git synced 2025-12-11 15:38:14 +08:00

Compare commits

...

20 Commits

Author SHA1 Message Date
涵曦
a178278576 new version v0.1.58 2024-06-25 01:03:13 +00:00
涵曦
474fea8434 优化播放被打断的问题 2024-06-25 01:03:01 +00:00
涵曦
d271f7b0f7 代码优化 2024-06-24 16:03:10 +00:00
涵曦
7e2af515ed fix: 登陆失败不阻塞启动 2024-06-24 15:38:41 +00:00
涵曦
2d403ff18c new version v0.1.57 2024-06-24 14:28:08 +00:00
涵曦
f08244a990 update readme 2024-06-24 14:28:05 +00:00
涵曦
4869e5cf80 新增ape和cue格式文件 2024-06-24 14:27:31 +00:00
涵曦
b887504f9f Update README.md 2024-06-24 12:24:47 +08:00
涵曦
ad43a4f732 new version v0.1.56 2024-06-24 01:04:13 +00:00
涵曦
8699938b61 删除用不上的配置参数 2024-06-24 00:45:41 +00:00
涵曦
40bd099153 支持wav格式文件 2024-06-24 00:19:49 +00:00
涵曦
750923d5ca Update README.md 2024-06-24 00:38:33 +08:00
涵曦
6d99b30e2d Update README.md 2024-06-24 00:35:02 +08:00
涵曦
a155f16560 Update README.md 2024-06-24 00:30:42 +08:00
涵曦
d4aa045487 Update README.md 2024-06-23 20:05:20 +08:00
涵曦
8080dd9822 Update Dockerfile 2024-06-23 19:05:13 +08:00
涵曦
2eab8d8113 update dockerfile 2024-06-23 10:23:46 +00:00
涵曦
b51e56718a update dockerfile 2024-06-23 10:18:07 +00:00
涵曦
088d448e10 优化 Dockerfile 2024-06-23 17:40:28 +08:00
涵曦
4a89b4bce5 Update README.md 2024-06-23 16:27:22 +08:00
8 changed files with 82 additions and 114 deletions

View File

@@ -6,7 +6,6 @@ COPY install_dependencies.sh .
RUN bash install_dependencies.sh RUN bash install_dependencies.sh
FROM python:3.10-slim FROM python:3.10-slim
WORKDIR /app WORKDIR /app
COPY --from=builder /app/.venv /app/.venv COPY --from=builder /app/.venv /app/.venv
COPY --from=builder /app/ffmpeg /app/ffmpeg COPY --from=builder /app/ffmpeg /app/ffmpeg

View File

@@ -76,7 +76,7 @@ pdm run xiaomusic.py
## 在 Docker 里使用 ## 在 Docker 里使用
```shell ```shell
docker run -e MI_USER=<your-xiaomi-account> \ docker run -e MI_USER='your-xiaomi-account' \
-e MI_PASS='your-xiaomi-password' \ -e MI_PASS='your-xiaomi-password' \
-e MI_DID='your-xiaomi-speaker-mid' \ -e MI_DID='your-xiaomi-speaker-mid' \
-e MI_HARDWARE='L07A' \ -e MI_HARDWARE='L07A' \
@@ -186,6 +186,17 @@ services:
- XIAOMUSIC_SEARCH - XIAOMUSIC_SEARCH
- XIAOMUSIC_PROXY - XIAOMUSIC_PROXY
## 更多其他可选配置
- XIAOMUSIC_ACTIVE_CMD 配置唤醒命令,具体见 <https://github.com/hanxi/xiaomusic/pull/43>
- XIAOMUSIC_EXCLUDE_DIRS 配置歌曲目录里需要忽略的目录
- XIAOMUSIC_MUSIC_PATH_DEPTH 配置歌曲目录搜索深度,具体见 <https://github.com/hanxi/xiaomusic/issues/76>
- XIAOMUSIC_DISABLE_HTTPAUTH 配置成 false 表示开启密码访问web控制台具体见 <https://github.com/hanxi/xiaomusic/issues/47>
- XIAOMUSIC_HTTPAUTH_USERNAME 配置 web 控制台用户
- XIAOMUSIC_HTTPAUTH_PASSWORD 配置 web 控制台密码
- XIAOMUSIC_CONF_PATH 用来存放配置文件的目录记得把目录映射到主机默认情况会把配置存放在music目录具体见 <https://github.com/hanxi/xiaomusic/issues/74>
- XIAOMUSIC_VERBOSE 设置为 true 时开启 debug 日志,用于排查问题
## 讨论区 ## 讨论区
- [点击链接加入QQ频道【xiaomusic】](https://pd.qq.com/s/e2jybz0ss) - [点击链接加入QQ频道【xiaomusic】](https://pd.qq.com/s/e2jybz0ss)
@@ -206,3 +217,6 @@ services:
## Star History ## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=hanxi/xiaomusic&type=Date)](https://star-history.com/#hanxi/xiaomusic&Date) [![Star History Chart](https://api.star-history.com/svg?repos=hanxi/xiaomusic&type=Date)](https://star-history.com/#hanxi/xiaomusic&Date)
## 赞赏
谢谢就够了

View File

@@ -1,6 +1,6 @@
[project] [project]
name = "xiaomusic" name = "xiaomusic"
version = "0.1.55" version = "0.1.58"
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"},

View File

@@ -1 +1 @@
__version__ = "0.1.55" __version__ = "0.1.58"

View File

@@ -27,20 +27,6 @@ def main():
dest="cookie", dest="cookie",
help="xiaomi cookie", help="xiaomi cookie",
) )
parser.add_argument(
"--use_command",
dest="use_command",
action="store_true",
default=None,
help="use command to tts",
)
parser.add_argument(
"--mute_xiaoai",
dest="mute_xiaoai",
action="store_true",
default=None,
help="try to mute xiaoai answer",
)
parser.add_argument( parser.add_argument(
"--verbose", "--verbose",
dest="verbose", dest="verbose",

View File

@@ -9,28 +9,6 @@ from xiaomusic.utils import validate_proxy
LATEST_ASK_API = "https://userprofile.mina.mi.com/device_profile/v2/conversation?source=dialogu&hardware={hardware}&timestamp={timestamp}&limit=2" LATEST_ASK_API = "https://userprofile.mina.mi.com/device_profile/v2/conversation?source=dialogu&hardware={hardware}&timestamp={timestamp}&limit=2"
COOKIE_TEMPLATE = "deviceId={device_id}; serviceToken={service_token}; userId={user_id}" COOKIE_TEMPLATE = "deviceId={device_id}; serviceToken={service_token}; userId={user_id}"
HARDWARE_COMMAND_DICT = {
# hardware: (tts_command, wakeup_command, volume_command)
"LX06": ("5-1", "5-5", "2-1"),
"L05B": ("5-3", "5-4", "2-1"),
"S12": ("5-1", "5-5", "2-1"), # 第一代小爱型号MDZ-25-DA
"S12A": ("5-1", "5-5", "2-1"),
"LX01": ("5-1", "5-5", "2-1"),
"L06A": ("5-1", "5-5", "2-1"),
"LX04": ("5-1", "5-4", "2-1"),
"L05C": ("5-3", "5-4", "2-1"),
"L17A": ("7-3", "7-4", "2-1"),
"X08E": ("7-3", "7-4", "2-1"),
"LX05A": ("5-1", "5-5", "2-1"), # 小爱红外版
"LX5A": ("5-1", "5-5", "2-1"), # 小爱红外版
"L07A": ("5-1", "5-5", "2-1"), # Redmi小爱音箱Play(l7a)
"L15A": ("7-3", "7-4", "2-1"),
"X6A": ("7-3", "7-4", "2-1"), # 小米智能家庭屏6
"X10A": ("7-3", "7-4", "2-1"), # 小米智能家庭屏10
# add more here
}
DEFAULT_COMMAND = ("5-1", "5-5", "2-1")
KEY_WORD_DICT = { KEY_WORD_DICT = {
"播放歌曲": "play", "播放歌曲": "play",
@@ -73,6 +51,9 @@ KEY_MATCH_ORDER = [
SUPPORT_MUSIC_TYPE = [ SUPPORT_MUSIC_TYPE = [
".mp3", ".mp3",
".flac", ".flac",
".wav",
".ape",
".cue",
] ]
@@ -82,9 +63,7 @@ class Config:
account: str = os.getenv("MI_USER", "") account: str = os.getenv("MI_USER", "")
password: str = os.getenv("MI_PASS", "") password: str = os.getenv("MI_PASS", "")
mi_did: str = os.getenv("MI_DID", "") mi_did: str = os.getenv("MI_DID", "")
mute_xiaoai: bool = True
cookie: str = "" cookie: str = ""
use_command: bool = False
verbose: bool = os.getenv("XIAOMUSIC_VERBOSE", "").lower() == "true" 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")
conf_path: str = os.getenv("XIAOMUSIC_CONF_PATH", None) conf_path: str = os.getenv("XIAOMUSIC_CONF_PATH", None)
@@ -108,18 +87,6 @@ class Config:
if self.proxy: if self.proxy:
validate_proxy(self.proxy) validate_proxy(self.proxy)
@property
def tts_command(self) -> str:
return HARDWARE_COMMAND_DICT.get(self.hardware, DEFAULT_COMMAND)[0]
@property
def wakeup_command(self) -> str:
return HARDWARE_COMMAND_DICT.get(self.hardware, DEFAULT_COMMAND)[1]
@property
def volume_command(self) -> str:
return HARDWARE_COMMAND_DICT.get(self.hardware, DEFAULT_COMMAND)[2]
@classmethod @classmethod
def from_options(cls, options: argparse.Namespace) -> Config: def from_options(cls, options: argparse.Namespace) -> Config:
config = {} config = {}

View File

@@ -71,6 +71,12 @@ def playingmusic():
return xiaomusic.playingmusic() return xiaomusic.playingmusic()
@app.route("/isplaying", methods=["GET"])
@auth.login_required
def isplaying():
return xiaomusic.isplaying()
@app.route("/", methods=["GET"]) @app.route("/", methods=["GET"])
def index(): def index():
return send_from_directory("static", "index.html") return send_from_directory("static", "index.html")

View File

@@ -13,7 +13,7 @@ from pathlib import Path
import mutagen 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
from xiaomusic import ( from xiaomusic import (
__version__, __version__,
@@ -127,7 +127,9 @@ class XiaoMusic:
async def init_all_data(self, session): async def init_all_data(self, session):
await self.login_miboy(session) await self.login_miboy(session)
await self._init_data_hardware() await self._init_data_hardware()
session.cookie_jar.update_cookies(self.get_cookie()) cookie_jar = self.get_cookie()
if cookie_jar:
session.cookie_jar.update_cookies(cookie_jar)
self.cookie_jar = session.cookie_jar self.cookie_jar = session.cookie_jar
async def login_miboy(self, session): async def login_miboy(self, session):
@@ -143,13 +145,14 @@ class XiaoMusic:
self.miio_service = MiIOService(account) self.miio_service = MiIOService(account)
async def try_update_device_id(self): async def try_update_device_id(self):
hardware_data = await self.mina_service.device_list()
# fix multi xiaoai problems we check did first # fix multi xiaoai problems we check did first
# why we use this way to fix? # why we use this way to fix?
# some videos and articles already in the Internet # some videos and articles already in the Internet
# we do not want to change old way, so we check if miotDID in `env` first # we do not want to change old way, so we check if miotDID in `env` first
# to set device id # to set device id
try:
hardware_data = await self.mina_service.device_list()
for h in hardware_data: for h in hardware_data:
if did := self.config.mi_did: if did := self.config.mi_did:
if h.get("miotDID", "") == str(did): if h.get("miotDID", "") == str(did):
@@ -164,6 +167,9 @@ class XiaoMusic:
self.log.error( self.log.error(
f"we have no hardware: {self.config.hardware} please use `micli mina` to check" f"we have no hardware: {self.config.hardware} please use `micli mina` to check"
) )
except Exception as e:
self.log.error(f"Execption {e}")
pass
async def _init_data_hardware(self): async def _init_data_hardware(self):
if self.config.cookie: if self.config.cookie:
@@ -193,7 +199,11 @@ class XiaoMusic:
cookie_dict = cookie_jar.get_dict() cookie_dict = cookie_jar.get_dict()
self.device_id = cookie_dict["deviceId"] self.device_id = cookie_dict["deviceId"]
return cookie_jar return cookie_jar
else:
if not os.path.exists(self.mi_token_home):
self.log.error(f"{self.mi_token_home} file not exist")
return None
with open(self.mi_token_home) as f: with open(self.mi_token_home) as f:
user_data = json.loads(f.read()) user_data = json.loads(f.read())
user_id = user_data.get("userId") user_id = user_data.get("userId")
@@ -221,8 +231,8 @@ class XiaoMusic:
continue continue
try: try:
data = await r.json() data = await r.json()
except Exception: except Exception as e:
self.log.warning("get latest ask from xiaoai error, retry") self.log.warning(f"get latest ask from xiaoai error {e}, retry")
if i == 2: if i == 2:
# tricky way to fix #282 #272 # if it is the third time we re init all data # tricky way to fix #282 #272 # if it is the third time we re init all data
self.log.info("Maybe outof date trying to re init it") self.log.info("Maybe outof date trying to re init it")
@@ -253,42 +263,25 @@ class XiaoMusic:
async def do_tts(self, value): async def do_tts(self, value):
self.log.info("do_tts: %s", value) self.log.info("do_tts: %s", value)
if self.config.mute_xiaoai:
await self.force_stop_xiaoai() await self.force_stop_xiaoai()
else:
# waiting for xiaoai speaker done
await asyncio.sleep(8)
if not self.config.use_command:
try: try:
await self.mina_service.text_to_speech(self.device_id, value) await self.mina_service.text_to_speech(self.device_id, value)
except Exception: except Exception as e:
self.log.error(f"Execption {e}")
pass pass
else: if self._playing:
await miio_command( # 继续播放歌曲
self.miio_service, await self.play()
self.config.mi_did,
f"{self.config.tts_command} {value}",
)
async def do_set_volume(self, value): async def do_set_volume(self, value):
value = int(value) value = int(value)
self._volume = value self._volume = value
self.log.info(f"声音设置为{value}") self.log.info(f"声音设置为{value}")
if not self.config.use_command:
try: try:
self.log.debug("do_set_volume not use_command value:%d", value)
await self.mina_service.player_set_volume(self.device_id, value) await self.mina_service.player_set_volume(self.device_id, value)
except Exception: except Exception as e:
self.log.error(f"Execption {e}")
pass pass
else:
self.log.debug("do_set_volume use_command value:%d", value)
await miio_command(
self.miio_service,
self.config.mi_did,
f"{self.config.volume_command}=#{value}",
)
async def force_stop_xiaoai(self): async def force_stop_xiaoai(self):
await self.mina_service.player_stop(self.device_id) await self.mina_service.player_stop(self.device_id)
@@ -379,7 +372,7 @@ class XiaoMusic:
name, name,
extension, extension,
) )
if extension not in SUPPORT_MUSIC_TYPE: if extension.lower() not in SUPPORT_MUSIC_TYPE:
continue continue
# 歌曲名字相同会覆盖 # 歌曲名字相同会覆盖
@@ -544,7 +537,7 @@ class XiaoMusic:
return ("stop", {}) return ("stop", {})
return (None, None) return (None, None)
# 判断是否播放一下私募歌曲 # 判断是否播放下一首歌曲
def check_play_next(self): def check_play_next(self):
# 当前没我在播放的歌曲 # 当前没我在播放的歌曲
if self.cur_music == "": if self.cur_music == "":
@@ -629,7 +622,6 @@ class XiaoMusic:
# 刷新列表 # 刷新列表
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("生成播放列表完毕")
# 删除歌曲 # 删除歌曲
def del_music(self, name): def del_music(self, name):
@@ -722,6 +714,10 @@ class XiaoMusic:
self.log.debug("playingmusic. cur_music:%s", self.cur_music) self.log.debug("playingmusic. cur_music:%s", self.cur_music)
return self.cur_music return self.cur_music
# 当前是否正在播放歌曲
def isplaying(self):
return self._playing
# 获取当前配置 # 获取当前配置
def getconfig(self): def getconfig(self):
return self.config return self.config