From 88f0ce7e516e6d1cb248c0ce106b8dd6bfe7ae42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B6=B5=E6=9B=A6?= Date: Fri, 14 Jun 2024 01:58:10 +0000 Subject: [PATCH] use ruff lint and fmt code --- pdm.lock | 29 +++++++++++++- pyproject.toml | 26 +++++++++++++ requirements.txt | 18 +++++++++ xiaomusic/config.py | 5 +-- xiaomusic/httpserver.py | 30 ++++++++------ xiaomusic/utils.py | 11 +++--- xiaomusic/xiaomusic.py | 86 +++++++++++++++++++++-------------------- 7 files changed, 142 insertions(+), 63 deletions(-) diff --git a/pdm.lock b/pdm.lock index eeed407..5557fa6 100644 --- a/pdm.lock +++ b/pdm.lock @@ -2,10 +2,10 @@ # It is not intended for manual editing. [metadata] -groups = ["default"] +groups = ["default", "lint"] strategy = ["cross_platform"] lock_version = "4.4.1" -content_hash = "sha256:d771311a452ca58665efe3b74af341cb202d75d83a250896c293ea9c696e5696" +content_hash = "sha256:fa83f8134ccb4a432304dead5fe1303899418558634abaa0824e8d9fdfb1f490" [[package]] name = "aiohttp" @@ -648,6 +648,31 @@ files = [ {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]] name = "typing-extensions" version = "4.9.0" diff --git a/pyproject.toml b/pyproject.toml index 4598214..148e88d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,3 +24,29 @@ requires = ["pdm-backend"] build-backend = "pdm.backend" [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 ." diff --git a/requirements.txt b/requirements.txt index bfec243..df4ecf6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -374,6 +374,24 @@ requests==2.31.0 \ rich==13.7.1 \ --hash=sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222 \ --hash=sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432 +ruff==0.4.8 \ + --hash=sha256:14019a06dbe29b608f6b7cbcec300e3170a8d86efaddb7b23405cb7f7dcaf780 \ + --hash=sha256:16d717b1d57b2e2fd68bd0bf80fb43931b79d05a7131aa477d66fc40fbd86268 \ + --hash=sha256:284c2e3f3396fb05f5f803c9fffb53ebbe09a3ebe7dda2929ed8d73ded736deb \ + --hash=sha256:384154a1c3f4bf537bac69f33720957ee49ac8d484bfc91720cc94172026ceed \ + --hash=sha256:6d795d7639212c2dfd01991259460101c22aabf420d9b943f153ab9d9706e6a9 \ + --hash=sha256:6ea874950daca5697309d976c9afba830d3bf0ed66887481d6bca1673fc5b66a \ + --hash=sha256:704977a658131651a22b5ebeb28b717ef42ac6ee3b11e91dc87b633b5d83142b \ + --hash=sha256:72584676164e15a68a15778fd1b17c28a519e7a0622161eb2debdcdabdc71883 \ + --hash=sha256:7663a6d78f6adb0eab270fa9cf1ff2d28618ca3a652b60f2a234d92b9ec89066 \ + --hash=sha256:9678d5c9b43315f323af2233a04d747409d1e3aa6789620083a82d1066a35199 \ + --hash=sha256:a7354f921e3fbe04d2a62d46707e569f9315e1a613307f7311a935743c51a764 \ + --hash=sha256:aad360893e92486662ef3be0a339c5ca3c1b109e0134fcd37d534d4be9fb8de3 \ + --hash=sha256:d05f8d6f0c3cce5026cecd83b7a143dcad503045857bc49662f736437380ad45 \ + --hash=sha256:e14a3a095d07560a9d6769a72f781d73259655919d9b396c650fc98a8157555d \ + --hash=sha256:e9d5ce97cacc99878aa0d084c626a15cd21e6b3d53fd6f9112b7fc485918e1fa \ + --hash=sha256:eeceb78da8afb6de0ddada93112869852d04f1cd0f6b80fe464fd4e35c330913 \ + --hash=sha256:fc95aac2943ddf360376be9aa3107c8cf9640083940a8c5bd824be692d2216dc typing-extensions==4.9.0; python_version < "3.11" \ --hash=sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783 \ --hash=sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd diff --git a/xiaomusic/config.py b/xiaomusic/config.py index 3ac940a..017febf 100644 --- a/xiaomusic/config.py +++ b/xiaomusic/config.py @@ -3,8 +3,7 @@ from __future__ import annotations import argparse import json import os -from dataclasses import dataclass, field -from typing import Any, Iterable +from dataclasses import dataclass from xiaomusic.utils import validate_proxy @@ -13,7 +12,7 @@ COOKIE_TEMPLATE = "deviceId={device_id}; serviceToken={service_token}; userId={u HARDWARE_COMMAND_DICT = { # hardware: (tts_command, wakeup_command, volume_command) "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 "S12A": ("5-1", "5-5", "2-1"), "LX01": ("5-1", "5-5", "2-1"), diff --git a/xiaomusic/httpserver.py b/xiaomusic/httpserver.py index 42b6ee2..8aa59a5 100644 --- a/xiaomusic/httpserver.py +++ b/xiaomusic/httpserver.py @@ -1,25 +1,21 @@ #!/usr/bin/env python3 import os -import sys -import traceback -import asyncio +from threading import Thread from flask import Flask, request, send_from_directory from waitress import serve -from threading import Thread - -from xiaomusic.config import ( - KEY_WORD_DICT, -) from xiaomusic import ( __version__, ) +from xiaomusic.config import ( + KEY_WORD_DICT, +) # 隐藏 flask 启动告警 # https://gist.github.com/jerblack/735b9953ba1ab6234abb43174210d356 -#from flask import cli -#cli.show_server_banner = lambda *_: None +# from flask import cli +# cli.show_server_banner = lambda *_: None app = Flask(__name__) host = "0.0.0.0" @@ -33,6 +29,7 @@ log = None def allcmds(): return KEY_WORD_DICT + @app.route("/getversion", methods=["GET"]) def getversion(): log.debug("getversion %s", __version__) @@ -40,6 +37,7 @@ def getversion(): "version": __version__, } + @app.route("/getvolume", methods=["GET"]) def getvolume(): volume = xiaomusic.get_volume_ret() @@ -47,15 +45,18 @@ def getvolume(): "volume": volume, } + @app.route("/searchmusic", methods=["GET"]) def searchmusic(): - name = request.args.get('name') + name = request.args.get("name") return xiaomusic.searchmusic(name) + @app.route("/playingmusic", methods=["GET"]) def playingmusic(): return xiaomusic.playingmusic() + @app.route("/", methods=["GET"]) def redirect_to_index(): return send_from_directory("static", "index.html") @@ -71,6 +72,7 @@ async def do_cmd(): return {"ret": "OK"} return {"ret": "Unknow cmd"} + @app.route("/getsetting", methods=["GET"]) async def getsetting(): config = xiaomusic.getconfig() @@ -88,6 +90,7 @@ async def getsetting(): } return data + @app.route("/savesetting", methods=["POST"]) async def savesetting(): data = request.get_json() @@ -95,14 +98,17 @@ async def savesetting(): await xiaomusic.saveconfig(data) return "save success" + @app.route("/musiclist", methods=["GET"]) async def musiclist(): return xiaomusic.get_music_list() + @app.route("/curplaylist", methods=["GET"]) async def curplaylist(): return xiaomusic.get_cur_play_list() + def static_path_handler(filename): log.debug(filename) log.debug(static_path) @@ -110,9 +116,11 @@ def static_path_handler(filename): log.debug(absolute_path) return send_from_directory(absolute_path, filename) + def run_app(): serve(app, host=host, port=port) + def StartHTTPServer(_port, _static_path, _xiaomusic): global port, static_path, xiaomusic, log port = _port diff --git a/xiaomusic/utils.py b/xiaomusic/utils.py index 661c31c..1056270 100644 --- a/xiaomusic/utils.py +++ b/xiaomusic/utils.py @@ -1,13 +1,11 @@ #!/usr/bin/env python3 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 re +from collections.abc import AsyncIterator +from http.cookies import SimpleCookie +from urllib.parse import urlparse from requests.utils import cookiejar_from_dict @@ -62,6 +60,7 @@ def validate_proxy(proxy_str: str) -> bool: return True + # 模糊搜索 def fuzzyfinder(user_input, collection): return difflib.get_close_matches(user_input, collection, 10, cutoff=0.1) diff --git a/xiaomusic/xiaomusic.py b/xiaomusic/xiaomusic.py index 927281a..d222465 100644 --- a/xiaomusic/xiaomusic.py +++ b/xiaomusic/xiaomusic.py @@ -3,45 +3,43 @@ import asyncio import json import logging import os +import queue import random import re import time -import urllib.parse import traceback -import mutagen -import queue -from xiaomusic.httpserver import StartHTTPServer - +import urllib.parse from pathlib import Path +import mutagen from aiohttp import ClientSession, ClientTimeout from miservice import MiAccount, MiIOService, MiNAService, miio_command -from rich import print 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 ( __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() PLAY_TYPE_ONE = 0 # 单曲循环 PLAY_TYPE_ALL = 1 # 全部循环 + class XiaoMusic: def __init__(self, config: Config): self.config = config @@ -76,7 +74,7 @@ class XiaoMusic: self._all_music = {} self._play_list = [] self._cur_play_list = "" - self._music_list = {} # 播放列表 key 为目录名, value 为 play_list + self._music_list = {} # 播放列表 key 为目录名, value 为 play_list self._playing = False # 关机定时器 @@ -86,7 +84,7 @@ class XiaoMusic: logging.basicConfig( format=f"[{__version__}]\t%(message)s", datefmt="[%X]", - handlers=[RichHandler(rich_tracebacks=True)] + handlers=[RichHandler(rich_tracebacks=True)], ) self.log = logging.getLogger("xiaomusic") self.log.setLevel(logging.DEBUG if config.verbose else logging.INFO) @@ -290,7 +288,10 @@ class XiaoMusic: def is_downloading(self): if not self.download_proc: 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 True @@ -379,7 +380,7 @@ class XiaoMusic: self._music_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.log.debug("dir_name:%s, list:%s", dir_name, self._music_list[dir_name]) pass @@ -396,7 +397,7 @@ class XiaoMusic: def get_next_music(self): play_list_len = len(self._play_list) if play_list_len == 0: - self.log.warning(f"没有随机到歌曲") + self.log.warning("没有随机到歌曲") return "" # 随机选择一个文件 index = 0 @@ -411,7 +412,7 @@ class XiaoMusic: filename = self.get_filename(name) if len(filename) <= 0: self._play_list.pop(next_index) - self.log.info(f'pop not exist music:{name}') + self.log.info(f"pop not exist music:{name}") return self.get_next_music() return name @@ -430,7 +431,7 @@ class XiaoMusic: self.log.info(f"歌曲 {self.cur_music} : {filename} 的时长 {sec} 秒") if self._next_timer: self._next_timer.cancel() - self.log.info(f"定时器已取消") + self.log.info("定时器已取消") self._timeout = sec async def _do_next(): @@ -450,11 +451,11 @@ class XiaoMusic: await self.init_all_data(session) task = asyncio.create_task(self.poll_latest_ask()) 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) - self.log.info( - f"Running xiaomusic now, 用`{joined_keywords}`开头来控制" - ) + self.log.info(f"Running xiaomusic now, 用`{joined_keywords}`开头来控制") while True: self.polling_event.set() @@ -561,30 +562,30 @@ class XiaoMusic: if self.play_type == PLAY_TYPE_ALL or name == "": name = self.get_next_music() if name == "": - await self.do_tts(f"本地没有歌曲") + await self.do_tts("本地没有歌曲") return await self.play(arg1=name) # 单曲循环 async def set_play_type_one(self, **kwargs): self.play_type = PLAY_TYPE_ONE - await self.do_tts(f"已经设置为单曲循环") + await self.do_tts("已经设置为单曲循环") # 全部循环 async def set_play_type_all(self, **kwargs): self.play_type = PLAY_TYPE_ALL - await self.do_tts(f"已经设置为全部循环") + await self.do_tts("已经设置为全部循环") # 随机播放 async def random_play(self, **kwargs): self.play_type = PLAY_TYPE_ALL random.shuffle(self._play_list) - await self.do_tts(f"已经设置为随机播放") + await self.do_tts("已经设置为随机播放") # 刷新列表 async def gen_music_list(self, **kwargs): self._gen_all_music_list() - await self.do_tts(f"生成播放列表完毕") + await self.do_tts("生成播放列表完毕") # 播放一个播放列表 async def play_music_list(self, **kwargs): @@ -608,14 +609,14 @@ class XiaoMusic: self._playing = False if self._next_timer: self._next_timer.cancel() - self.log.info(f"定时器已取消") + self.log.info("定时器已取消") self.cur_music = "" await self.force_stop_xiaoai() async def stop_after_minute(self, **kwargs): if self._stop_timer: self._stop_timer.cancel() - self.log.info(f"关机定时器已取消") + self.log.info("关机定时器已取消") minute = int(kwargs["arg1"]) async def _do_stop(): @@ -635,7 +636,9 @@ class XiaoMusic: async def get_volume(self, **kwargs): playing_info = await self.mina_service.player_get_status(self.device_id) 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) def get_volume_ret(self): @@ -680,7 +683,7 @@ class XiaoMusic: async def saveconfig(self, data): # 默认暂时配置保存到 music 目录下 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) self.update_config_from_setting(data) await self.call_main_thread_function(self.reinit) @@ -725,12 +728,13 @@ class XiaoMusic: async def call_main_thread_function(self, func, arg1=None): loop = asyncio.get_event_loop() future = loop.create_future() + def callback(ret): nonlocal future loop.call_soon_threadsafe(future.set_result, ret) + self.queue.put((func, callback, arg1)) self.last_record = None self.new_record_event.set() result = await future return result -