From def63b24077bf372f7c56c263d0553088df295c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B6=B5=E6=9B=A6?= Date: Thu, 28 Nov 2024 17:58:34 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E5=8D=95=E6=9B=B2?= =?UTF-8?q?=E6=92=AD=E6=94=BE=E5=92=8C=E9=A1=BA=E5=BA=8F=E6=92=AD=E6=94=BE?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=20close=20#277?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pdm.lock | 94 ++++++++-------------- pyproject.toml | 2 +- xiaomusic/config.py | 20 ++++- xiaomusic/const.py | 2 + xiaomusic/static/default/app.js | 20 +++-- xiaomusic/static/default/debug.html | 4 +- xiaomusic/static/default/downloadtool.html | 4 +- xiaomusic/static/default/index.html | 6 +- xiaomusic/static/default/m3u.html | 2 +- xiaomusic/static/default/setting.html | 10 ++- xiaomusic/xiaomusic.py | 23 +++++- 11 files changed, 108 insertions(+), 79 deletions(-) diff --git a/pdm.lock b/pdm.lock index 6379ef3d3..4c288fc39 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default", "dev", "lint"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:10d18ad4e1cc30b6683ac4c5fcdf73ab7aa3e6a5910b94e5ded3183e108bbbe1" +content_hash = "sha256:37be8846356b5717495cac7252d78b1554985d607e286cf38ab863e307e5e25e" [[metadata.targets]] requires_python = "==3.10.12" @@ -39,23 +39,24 @@ files = [ [[package]] name = "aiohttp" -version = "3.10.10" -requires_python = ">=3.8" +version = "3.11.8" +requires_python = ">=3.9" summary = "Async http client/server framework (asyncio)" groups = ["default"] marker = "python_full_version == \"3.10.12\"" dependencies = [ "aiohappyeyeballs>=2.3.0", "aiosignal>=1.1.2", - "async-timeout<5.0,>=4.0; python_version < \"3.11\"", + "async-timeout<6.0,>=4.0; python_version < \"3.11\"", "attrs>=17.3.0", "frozenlist>=1.1.1", "multidict<7.0,>=4.5", - "yarl<2.0,>=1.12.0", + "propcache>=0.2.0", + "yarl<2.0,>=1.17.0", ] files = [ - {file = "aiohttp-3.10.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab5a5a0c7a7991d90446a198689c0535be89bbd6b410a1f9a66688f0880ec026"}, - {file = "aiohttp-3.10.10.tar.gz", hash = "sha256:0631dd7c9f0822cc61c88586ca76d5b5ada26538097d0f1df510b082bad3411a"}, + {file = "aiohttp-3.11.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df9bf08eb93611b1d4d6245b6fecf88728e90eece00e00d554e1b0c445557d83"}, + {file = "aiohttp-3.11.8.tar.gz", hash = "sha256:7bc9d64a2350cbb29a9732334e1a0743cbb6844de1731cbdf5949b235653f3fd"}, ] [[package]] @@ -108,20 +109,18 @@ files = [ [[package]] name = "apscheduler" -version = "3.10.4" -requires_python = ">=3.6" +version = "3.11.0" +requires_python = ">=3.8" summary = "In-process task scheduler with Cron-like capabilities" groups = ["default"] marker = "python_full_version == \"3.10.12\"" dependencies = [ - "importlib-metadata>=3.6.0; python_version < \"3.8\"", - "pytz", - "six>=1.4.0", - "tzlocal!=3.*,>=2.0", + "backports-zoneinfo; python_version < \"3.9\"", + "tzlocal>=3.0", ] files = [ - {file = "APScheduler-3.10.4-py3-none-any.whl", hash = "sha256:fb91e8a768632a4756a585f79ec834e0e27aad5860bac7eaa523d9ccefd87661"}, - {file = "APScheduler-3.10.4.tar.gz", hash = "sha256:e6df071b27d9be898e486bc7940a7be50b4af2e9da7c08f0744a96d4bd4cef4a"}, + {file = "APScheduler-3.11.0-py3-none-any.whl", hash = "sha256:fc134ca32e50f5eadcc4938e3a4545ab19131435e851abb40b34d63d5141c6da"}, + {file = "apscheduler-3.11.0.tar.gz", hash = "sha256:4c622d250b0955a65d5d0eb91c33e6d43fd879834bf541e0a18661ae60460133"}, ] [[package]] @@ -265,8 +264,8 @@ files = [ [[package]] name = "commitizen" -version = "3.30.0" -requires_python = ">=3.8" +version = "4.0.0" +requires_python = ">=3.9" summary = "Python commitizen client tool" groups = ["dev"] marker = "python_full_version == \"3.10.12\"" @@ -282,11 +281,11 @@ dependencies = [ "questionary<3.0,>=2.0", "termcolor<3,>=1.1", "tomlkit<1.0.0,>=0.5.3", - "typing-extensions<5.0.0,>=4.0.1; python_version < \"3.8\"", + "typing-extensions<5.0.0,>=4.0.1; python_version < \"3.11\"", ] files = [ - {file = "commitizen-3.30.0-py3-none-any.whl", hash = "sha256:8dc226a136aee61207e396101fcd89e73de67a57c06e066db982310863caaf65"}, - {file = "commitizen-3.30.0.tar.gz", hash = "sha256:ae67a47c1a700b4f35ac12de0c35c7ba96f152b9377d22b6226bb87372c527b0"}, + {file = "commitizen-4.0.0-py3-none-any.whl", hash = "sha256:52873ee589a64cf77fc55570dbd3f987c6ffcd33132d179eb625c4d06ae935f7"}, + {file = "commitizen-4.0.0.tar.gz", hash = "sha256:16aff27e01b43015eab1c74eabbca3e284b4988dd1b146a0963282db241dc2c0"}, ] [[package]] @@ -315,7 +314,7 @@ files = [ [[package]] name = "fastapi" -version = "0.115.4" +version = "0.115.5" requires_python = ">=3.8" summary = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" groups = ["default"] @@ -326,8 +325,8 @@ dependencies = [ "typing-extensions>=4.8.0", ] files = [ - {file = "fastapi-0.115.4-py3-none-any.whl", hash = "sha256:0b504a063ffb3cf96a5e27dc1bc32c80ca743a2528574f9cdc77daa2d31b4742"}, - {file = "fastapi-0.115.4.tar.gz", hash = "sha256:db653475586b091cb8b2fec2ac54a680ac6a158e07406e1abae31679e8826349"}, + {file = "fastapi-0.115.5-py3-none-any.whl", hash = "sha256:596b95adbe1474da47049e802f9a65ab2ffa9c2b07e7efee70eb8a66c9f2f796"}, + {file = "fastapi-0.115.5.tar.gz", hash = "sha256:0e7a4d0dc0d01c68df21887cce0945e72d3c48b9f4f79dfe7a7d53aa08fbb289"}, ] [[package]] @@ -771,17 +770,6 @@ files = [ {file = "python_multipart-0.0.17.tar.gz", hash = "sha256:41330d831cae6e2f22902704ead2826ea038d0419530eadff3ea80175aec5538"}, ] -[[package]] -name = "pytz" -version = "2024.2" -summary = "World timezone definitions, modern and historical" -groups = ["default"] -marker = "python_full_version == \"3.10.12\"" -files = [ - {file = "pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725"}, - {file = "pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a"}, -] - [[package]] name = "pyyaml" version = "6.0.1" @@ -868,26 +856,14 @@ files = [ [[package]] name = "ruff" -version = "0.7.2" +version = "0.8.0" requires_python = ">=3.7" summary = "An extremely fast Python linter and code formatter, written in Rust." groups = ["lint"] marker = "python_full_version == \"3.10.12\"" files = [ - {file = "ruff-0.7.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dba53ed84ac19ae4bfb4ea4bf0172550a2285fa27fbb13e3746f04c80f7fa088"}, - {file = "ruff-0.7.2.tar.gz", hash = "sha256:2b14e77293380e475b4e3a7a368e14549288ed2931fce259a6f99978669e844f"}, -] - -[[package]] -name = "six" -version = "1.16.0" -requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -summary = "Python 2 and 3 compatibility utilities" -groups = ["default"] -marker = "python_full_version == \"3.10.12\"" -files = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, + {file = "ruff-0.8.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87a8e86bae0dbd749c815211ca11e3a7bd559b9710746c559ed63106d382bd9c"}, + {file = "ruff-0.8.0.tar.gz", hash = "sha256:a7ccfe6331bf8c8dad715753e157457faf7351c2b69f62f32c165c2dbcbacd44"}, ] [[package]] @@ -904,7 +880,7 @@ files = [ [[package]] name = "starlette" -version = "0.41.2" +version = "0.41.3" requires_python = ">=3.8" summary = "The little ASGI library that shines." groups = ["default"] @@ -914,8 +890,8 @@ dependencies = [ "typing-extensions>=3.10.0; python_version < \"3.10\"", ] files = [ - {file = "starlette-0.41.2-py3-none-any.whl", hash = "sha256:fbc189474b4731cf30fcef52f18a8d070e3f3b46c6a04c97579e85e6ffca942d"}, - {file = "starlette-0.41.2.tar.gz", hash = "sha256:9834fd799d1a87fd346deb76158668cfa0b0d56f85caefe8268e2d97c3468b62"}, + {file = "starlette-0.41.3-py3-none-any.whl", hash = "sha256:44cedb2b7c77a9de33a8b74b2b90e9f50d11fcf25d8270ea525ad71a25374ff7"}, + {file = "starlette-0.41.3.tar.gz", hash = "sha256:0e4ab3d16522a255be6b28260b938eae2482f98ce5cc934cb08dce8dc3ba5835"}, ] [[package]] @@ -947,7 +923,7 @@ name = "typing-extensions" version = "4.12.2" requires_python = ">=3.8" summary = "Backported and Experimental Type Hints for Python 3.8+" -groups = ["default"] +groups = ["default", "dev"] marker = "python_full_version == \"3.10.12\"" files = [ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, @@ -984,7 +960,7 @@ files = [ [[package]] name = "uvicorn" -version = "0.32.0" +version = "0.32.1" requires_python = ">=3.8" summary = "The lightning-fast ASGI server." groups = ["default"] @@ -995,8 +971,8 @@ dependencies = [ "typing-extensions>=4.0; python_version < \"3.11\"", ] files = [ - {file = "uvicorn-0.32.0-py3-none-any.whl", hash = "sha256:60b8f3a5ac027dcd31448f411ced12b5ef452c646f76f02f8cc3f25d8d26fd82"}, - {file = "uvicorn-0.32.0.tar.gz", hash = "sha256:f78b36b143c16f54ccdb8190d0a26b5f1901fe5a3c777e1ab29f26391af8551e"}, + {file = "uvicorn-0.32.1-py3-none-any.whl", hash = "sha256:82ad92fd58da0d12af7482ecdb5f2470a04c9c9a53ced65b9bbb4a205377602e"}, + {file = "uvicorn-0.32.1.tar.gz", hash = "sha256:ee9519c246a72b1c084cea8d3b44ed6026e78a4a309cbedae9c37e4cb9fbb175"}, ] [[package]] @@ -1033,12 +1009,12 @@ files = [ [[package]] name = "yt-dlp" -version = "2024.11.4" +version = "2024.11.18" requires_python = ">=3.9" summary = "A feature-rich command-line audio/video downloader" groups = ["default"] marker = "python_full_version == \"3.10.12\"" files = [ - {file = "yt_dlp-2024.11.4-py3-none-any.whl", hash = "sha256:589d51ed9f154624a45c1f0ceb3d68d0d1e2031460e8dbc62139be631c20b388"}, - {file = "yt_dlp-2024.11.4.tar.gz", hash = "sha256:ed204c1b61bc563e134447766d1ab343173540799e13ebb953e887ce7dcf6865"}, + {file = "yt_dlp-2024.11.18-py3-none-any.whl", hash = "sha256:b9741695911dc566498b5f115cdd6b1abbc5be61cb01fd98abe649990a41656c"}, + {file = "yt_dlp-2024.11.18.tar.gz", hash = "sha256:b8a4c23d3c9afd7e476bcdb87f38b6c0e8e12e3a239d7988f13acb434200f54d"}, ] diff --git a/pyproject.toml b/pyproject.toml index 5a7c098bc..5a6d050a0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ dependencies = [ "aiohttp>=3.8.6", "miservice-fork>=2.7.0", "mutagen>=1.47.0", - "yt-dlp>=2024.07.01", + "yt-dlp>=2024.11.18", "uvicorn>=0.30.1", "fastapi>=0.115.4", "starlette>=0.37.2", diff --git a/xiaomusic/config.py b/xiaomusic/config.py index ca1dd6aa5..2aa955253 100644 --- a/xiaomusic/config.py +++ b/xiaomusic/config.py @@ -10,6 +10,8 @@ PLAY_TYPE_ALL, PLAY_TYPE_ONE, PLAY_TYPE_RND, + PLAY_TYPE_SEQ, + PLAY_TYPE_SIN, ) from xiaomusic.utils import validate_proxy @@ -21,7 +23,9 @@ def default_key_word_dict(): "上一首": "play_prev", "单曲循环": "set_play_type_one", "全部循环": "set_play_type_all", - "随机播放": "set_random_play", + "随机播放": "set_play_type_rnd", + "单曲播放": "set_play_type_sin", + "顺序播放": "set_play_type_seq", "分钟后关机": "stop_after_minute", "刷新列表": "gen_music_list", "加入收藏": "add_to_favorites", @@ -53,6 +57,8 @@ def default_key_match_order(): "单曲循环", "全部循环", "随机播放", + "单曲播放", + "顺序播放", "关机", "刷新列表", "播放列表第", @@ -98,7 +104,7 @@ class Config: ffmpeg_location: str = os.getenv("XIAOMUSIC_FFMPEG_LOCATION", "./ffmpeg/bin") active_cmd: str = os.getenv( "XIAOMUSIC_ACTIVE_CMD", - "play,set_random_play,playlocal,play_music_list,play_music_list_index,stop_after_minute,stop", + "play,set_play_type_rnd,playlocal,play_music_list,play_music_list_index,stop_after_minute,stop", ) exclude_dirs: str = os.getenv("XIAOMUSIC_EXCLUDE_DIRS", "@eaDir,tmp") music_path_depth: int = int(os.getenv("XIAOMUSIC_MUSIC_PATH_DEPTH", "10")) @@ -175,6 +181,12 @@ class Config: play_type_rnd_tts_msg: str = os.getenv( "XIAOMUSIC_PLAY_TYPE_RND_TTS_MSG", "已经设置为随机播放" ) + play_type_sin_tts_msg: str = os.getenv( + "XIAOMUSIC_PLAY_TYPE_SIN_TTS_MSG", "已经设置为单曲播放" + ) + play_type_seq_tts_msg: str = os.getenv( + "XIAOMUSIC_PLAY_TYPE_SEQ_TTS_MSG", "已经设置为顺序播放" + ) def append_keyword(self, keys, action): for key in keys.split(","): @@ -302,4 +314,8 @@ def get_play_type_tts(self, play_type): return self.play_type_all_tts_msg if play_type == PLAY_TYPE_RND: return self.play_type_rnd_tts_msg + if play_type == PLAY_TYPE_SIN: + return self.play_type_sin_tts_msg + if play_type == PLAY_TYPE_SEQ: + return self.play_type_seq_tts_msg return "" diff --git a/xiaomusic/const.py b/xiaomusic/const.py index e8954d1c1..ed89ed0cc 100644 --- a/xiaomusic/const.py +++ b/xiaomusic/const.py @@ -13,6 +13,8 @@ PLAY_TYPE_ONE = 0 # 单曲循环 PLAY_TYPE_ALL = 1 # 全部循环 PLAY_TYPE_RND = 2 # 随机播放 +PLAY_TYPE_SIN = 3 # 单曲播放 +PLAY_TYPE_SEQ = 4 # 顺序播放 # 需要采用 mina 获取对话记录的设备型号 GET_ASK_BY_MINA = { diff --git a/xiaomusic/static/default/app.js b/xiaomusic/static/default/app.js index e97af147b..8014b77e1 100644 --- a/xiaomusic/static/default/app.js +++ b/xiaomusic/static/default/app.js @@ -4,16 +4,20 @@ $(function(){ append_op_button_name("加入收藏"); append_op_button_name("取消收藏"); + append_op_button_name("上一首"); + append_op_button_name("关机"); + append_op_button_name("下一首"); + const PLAY_TYPE_ONE = 0; // 单曲循环 const PLAY_TYPE_ALL = 1; // 全部循环 const PLAY_TYPE_RND = 2; // 随机播放 + const PLAY_TYPE_SIN = 3; // 单曲播放 + const PLAY_TYPE_SEQ = 4; // 顺序播放 append_op_button("play_type_all", "全部循环", "全部循环"); append_op_button("play_type_one", "单曲循环", "单曲循环"); append_op_button("play_type_rnd", "随机播放", "随机播放"); - - append_op_button_name("上一首"); - append_op_button_name("关机"); - append_op_button_name("下一首"); + append_op_button("play_type_sin", "单曲播放", "单曲播放"); + append_op_button("play_type_seq", "顺序播放", "顺序播放"); append_op_button_name("刷新列表"); @@ -71,6 +75,12 @@ $(function(){ } else if (cur_device.play_type == PLAY_TYPE_RND) { $("#play_type_rnd").css('background-color', '#b1a8f3'); $("#play_type_rnd").text('✔️ 随机播放'); + } else if (cur_device.play_type == PLAY_TYPE_SIN) { + $("#play_type_sin").css('background-color', '#b1a8f3'); + $("#play_type_sin").text('✔️ 单曲播放'); + } else if (cur_device.play_type == PLAY_TYPE_SEQ) { + $("#play_type_seq").css('background-color', '#b1a8f3'); + $("#play_type_seq").text('✔️ 顺序播放'); } } } @@ -298,7 +308,7 @@ $(function(){ if (cmd == "刷新列表") { check_status_refresh_music_list(3); // 最多重试3次 } - if (["全部循环", "单曲循环", "随机播放"].includes(cmd)) { + if (["全部循环", "单曲循环", "随机播放", "单曲播放", "顺序播放"].includes(cmd)) { location.reload(); } }, diff --git a/xiaomusic/static/default/debug.html b/xiaomusic/static/default/debug.html index b94c480ef..10a6f66b0 100644 --- a/xiaomusic/static/default/debug.html +++ b/xiaomusic/static/default/debug.html @@ -6,9 +6,9 @@ Debug For XiaoMusic - + - + diff --git a/xiaomusic/static/default/downloadtool.html b/xiaomusic/static/default/downloadtool.html index 644740996..d17c275b5 100644 --- a/xiaomusic/static/default/downloadtool.html +++ b/xiaomusic/static/default/downloadtool.html @@ -4,8 +4,8 @@ 歌曲下载工具 - - + + diff --git a/xiaomusic/static/default/index.html b/xiaomusic/static/default/index.html index 5f19c63b8..96d081204 100644 --- a/xiaomusic/static/default/index.html +++ b/xiaomusic/static/default/index.html @@ -4,9 +4,9 @@ 小爱音箱操控面板 - - - + + + diff --git a/xiaomusic/static/default/m3u.html b/xiaomusic/static/default/m3u.html index e3d682ec7..fb2f576ca 100644 --- a/xiaomusic/static/default/m3u.html +++ b/xiaomusic/static/default/m3u.html @@ -5,7 +5,7 @@ M3U to JSON Converter - + diff --git a/xiaomusic/static/default/setting.html b/xiaomusic/static/default/setting.html index 3a8259c72..42f87909f 100644 --- a/xiaomusic/static/default/setting.html +++ b/xiaomusic/static/default/setting.html @@ -4,9 +4,9 @@ 小爱音箱操控面板 - - - + + + @@ -176,6 +176,10 @@

小爱音箱设置面板 + + + + diff --git a/xiaomusic/xiaomusic.py b/xiaomusic/xiaomusic.py index c4b943369..7c6f503a6 100644 --- a/xiaomusic/xiaomusic.py +++ b/xiaomusic/xiaomusic.py @@ -31,6 +31,8 @@ PLAY_TYPE_ALL, PLAY_TYPE_ONE, PLAY_TYPE_RND, + PLAY_TYPE_SEQ, + PLAY_TYPE_SIN, SUPPORT_MUSIC_TYPE, ) from xiaomusic.crontab import Crontab @@ -891,9 +893,17 @@ async def set_play_type_all(self, did="", **kwargs): await self.set_play_type(did, PLAY_TYPE_ALL) # 设置为随机播放 - async def set_random_play(self, did="", **kwargs): + async def set_play_type_rnd(self, did="", **kwargs): await self.set_play_type(did, PLAY_TYPE_RND) + # 设置为单曲播放 + async def set_play_type_sin(self, did="", **kwargs): + await self.set_play_type(did, PLAY_TYPE_SIN) + + # 设置为顺序播放 + async def set_play_type_seq(self, did="", **kwargs): + await self.set_play_type(did, PLAY_TYPE_SEQ) + async def set_play_type(self, did="", play_type=PLAY_TYPE_RND, dotts=True): await self.devices[did].set_play_type(play_type, dotts) @@ -1347,6 +1357,7 @@ async def _play_next(self): if ( self.device.play_type == PLAY_TYPE_ALL or self.device.play_type == PLAY_TYPE_RND + or self.device.play_type == PLAY_TYPE_SEQ or name == "" or ( (name not in self._play_list) and self.device.play_type != PLAY_TYPE_ONE @@ -1433,6 +1444,10 @@ async def _playmusic(self, name): self.log.info(f"【{name}】已经开始播放了") await self.xiaomusic.analytics.send_play_event(name, sec) + if self.device.play_type == PLAY_TYPE_SIN: + self.log.info(f"【{name}】单曲播放时不会设置下一首歌的定时器") + return + # 设置下一首歌曲的播放定时器 if sec <= 1: self.log.info(f"【{name}】不会设置下一首歌的定时器") @@ -1585,6 +1600,12 @@ def get_music(self, direction="next"): else: if direction == "next": new_index = index + 1 + if ( + self.device.play_type == PLAY_TYPE_SEQ + and new_index >= play_list_len + ): + self.log.info("顺序播放结束") + return "" if new_index >= play_list_len: new_index = 0 elif direction == "prev":