From 485a42a9a0587acfe9ebf4872f4a0d9daf3f4904 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B6=B5=E6=9B=A6?= Date: Thu, 4 Jul 2024 09:36:06 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2=EF=BC=8C=E6=96=B0=E5=A2=9E=E6=9B=B4=E5=A4=9A?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- xiaomusic/config.py | 34 ++++++++++- xiaomusic/httpserver.py | 21 +++---- xiaomusic/static/setting.html | 109 +++++++++++++++++++++++++++++----- xiaomusic/static/setting.js | 77 ++++++++++++------------ xiaomusic/utils.py | 28 +++++++++ xiaomusic/xiaomusic.py | 55 +++++++++-------- 6 files changed, 231 insertions(+), 93 deletions(-) diff --git a/xiaomusic/config.py b/xiaomusic/config.py index 0b4374c..bcda511 100644 --- a/xiaomusic/config.py +++ b/xiaomusic/config.py @@ -4,6 +4,7 @@ import argparse import json import os from dataclasses import dataclass +from typing import get_type_hints from xiaomusic.utils import validate_proxy @@ -58,7 +59,7 @@ class Config: port: int = int(os.getenv("XIAOMUSIC_PORT", "8090")) proxy: str | None = os.getenv("XIAOMUSIC_PROXY", None) search_prefix: str = os.getenv( - "XIAOMUSIC_SEARCH", "ytsearch:" + "XIAOMUSIC_SEARCH", "bilisearch:" ) # "bilisearch:" or "ytsearch:" ffmpeg_location: str = os.getenv("XIAOMUSIC_FFMPEG_LOCATION", "./ffmpeg/bin") active_cmd: str = os.getenv( @@ -137,3 +138,34 @@ class Config: if value is not None and key in cls.__dataclass_fields__: result[key] = value return result + + def update_config(self, data): + # 获取类型提示 + type_hints = get_type_hints(self) + + for k, v in data.items(): + if v and k in type_hints: + # 获取字段的类型 + expected_type = type_hints[k] + + # 根据期望的类型进行转换 + if isinstance(v, expected_type): + # 如果v已经是正确的类型,则直接赋值 + setattr(self, k, v) + else: + # 尝试转换类型 + try: + # 特殊情况处理(例如对布尔值的转换) + if expected_type is bool: + converted_value = False + if v and v.lower() == "true": + converted_value = True + else: + # 使用期望类型的构造函数进行转换 + converted_value = expected_type(v) + except (ValueError, TypeError) as e: + print(f"Error converting {v} to {expected_type}: {e}") + continue + + # 设置转换后的值 + setattr(self, k, converted_value) diff --git a/xiaomusic/httpserver.py b/xiaomusic/httpserver.py index 9dbc24d..3503f9f 100644 --- a/xiaomusic/httpserver.py +++ b/xiaomusic/httpserver.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 import os +from dataclasses import asdict from threading import Thread from flask import Flask, request, send_file, send_from_directory @@ -10,6 +11,7 @@ from xiaomusic import ( __version__, ) from xiaomusic.utils import ( + deepcopy_data_no_sensitive_info, downloadfile, ) @@ -98,19 +100,11 @@ async def do_cmd(): @auth.login_required async def getsetting(): config = xiaomusic.getconfig() - + data = asdict(config) alldevices = await xiaomusic.call_main_thread_function(xiaomusic.getalldevices) - log.info(alldevices) - data = { - "mi_did": config.mi_did, - "mi_did_list": alldevices["did_list"], - "mi_hardware": config.hardware, - "mi_hardware_list": alldevices["hardware_list"], - "xiaomusic_search": config.search_prefix, - "xiaomusic_proxy": config.proxy, - "xiaomusic_music_list_url": config.music_list_url, - "xiaomusic_music_list_json": config.music_list_json, - } + log.info(f"getsetting alldevices: {alldevices}") + data["mi_did_list"] = alldevices["did_list"] + data["mi_hardware_list"] = alldevices["hardware_list"] return data @@ -118,7 +112,8 @@ async def getsetting(): @auth.login_required async def savesetting(): data = request.get_json() - log.info(data) + debug_data = deepcopy_data_no_sensitive_info(data) + log.info(f"saveconfig: {debug_data}") await xiaomusic.saveconfig(data) return "save success" diff --git a/xiaomusic/static/setting.html b/xiaomusic/static/setting.html index a77ec8e..e07676a 100644 --- a/xiaomusic/static/setting.html +++ b/xiaomusic/static/setting.html @@ -14,22 +14,103 @@ )
-
- +
+ + + + + + + - - - - + + + + + + - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

diff --git a/xiaomusic/static/setting.js b/xiaomusic/static/setting.js index 14d4f8b..e0e5692 100644 --- a/xiaomusic/static/setting.js +++ b/xiaomusic/static/setting.js @@ -5,6 +5,17 @@ $(function(){ $("#version").text(`${data.version}`); }); + // 遍历所有的select元素,默认选中只有1个选项的 + const autoSelectOne = () => { + $('select').each(function() { + // 如果select元素仅有一个option子元素 + if ($(this).children('option').length === 1) { + // 选中这个option + $(this).find('option').prop('selected', true); + } + }); + }; + const updateSelectOptions = (selectId, optionsList, selectedOption) => { const select = $(selectId); select.empty(); @@ -42,51 +53,37 @@ $(function(){ // 拉取现有配置 $.get("/getsetting", function(data, status) { console.log(data, status); - updateSelectOptions("#mi_did", data.mi_did_list, data.mi_did); - updateSelectOptions("#mi_hardware", data.mi_hardware_list, data.mi_hardware); + updateSelectOptions("#hardware", data.mi_hardware_list, data.hardware); // 初始化联动 - linkSelects('#mi_did', data.mi_did_list, '#mi_hardware', data.mi_hardware_list); + linkSelects('#mi_did', data.mi_did_list, '#hardware', data.mi_hardware_list); - if (data.xiaomusic_search != "") { - $("#xiaomusic_search").val(data.xiaomusic_search); + // 初始化显示 + for (const key in data) { + if (data.hasOwnProperty(key) && data[key] != "") { + const $element = $("#" + key); + if ($element.length) { + $element.val(data[key]); + } + } } - if (data.xiaomusic_proxy != "") { - $("#xiaomusic_proxy").val(data.xiaomusic_proxy); - } - - if (data.xiaomusic_music_list_url != "") { - $("#xiaomusic_music_list_url").val(data.xiaomusic_music_list_url); - } - - if (data.xiaomusic_music_list_json != "") { - $("#xiaomusic_music_list_json").val(data.xiaomusic_music_list_json); - } + autoSelectOne(); }); $("#save").on("click", () => { - var mi_did = $("#mi_did").val(); - var mi_hardware = $("#mi_hardware").val(); - var xiaomusic_search = $("#xiaomusic_search").val(); - var xiaomusic_proxy = $("#xiaomusic_proxy").val(); - var xiaomusic_music_list_url = $("#xiaomusic_music_list_url").val(); - var xiaomusic_music_list_json = $("#xiaomusic_music_list_json").val(); - console.log("mi_did", mi_did); - console.log("mi_hardware", mi_hardware); - console.log("xiaomusic_search", xiaomusic_search); - console.log("xiaomusic_proxy", xiaomusic_proxy); - console.log("xiaomusic_music_list_url", xiaomusic_music_list_url); - console.log("xiaomusic_music_list_json", xiaomusic_music_list_json); - var data = { - mi_did: mi_did, - mi_hardware: mi_hardware, - xiaomusic_search: xiaomusic_search, - xiaomusic_proxy: xiaomusic_proxy, - xiaomusic_music_list_url: xiaomusic_music_list_url, - xiaomusic_music_list_json: xiaomusic_music_list_json, - }; + var setting = $('#setting'); + var inputs = setting.find('input, select, textarea'); + var data = {}; + inputs.each(function() { + var id = this.id; + if (id) { + data[id] = $(this).val(); + } + }); + console.log(data) + $.ajax({ type: "POST", url: "/savesetting", @@ -102,10 +99,10 @@ $(function(){ }); $("#get_music_list").on("click", () => { - var xiaomusic_music_list_url = $("#xiaomusic_music_list_url").val(); - console.log("xiaomusic_music_list_url", xiaomusic_music_list_url); + var music_list_url = $("#music_list_url").val(); + console.log("music_list_url", music_list_url); var data = { - url: xiaomusic_music_list_url, + url: music_list_url, }; $.ajax({ type: "POST", @@ -114,7 +111,7 @@ $(function(){ data: JSON.stringify(data), success: (res) => { if (res.ret == "OK") { - $("#xiaomusic_music_list_json").val(res.content); + $("#music_list_json").val(res.content); } else { console.log(res); alert(res.ret); diff --git a/xiaomusic/utils.py b/xiaomusic/utils.py index 556a085..2a21057 100644 --- a/xiaomusic/utils.py +++ b/xiaomusic/utils.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 from __future__ import annotations +import copy import difflib import os import random @@ -240,3 +241,30 @@ def get_local_music_duration(filename): def get_random(length): return "".join(random.sample(string.ascii_letters + string.digits, length)) + + +# 深拷贝把敏感数据设置位* +def deepcopy_data_no_sensitive_info(data, fields_to_anonymize=None): + if fields_to_anonymize is None: + fields_to_anonymize = [ + "account", + "password", + "httpauth_username", + "httpauth_password", + ] + + copy_data = copy.deepcopy(data) + + # 检查copy_data是否是字典或具有属性的对象 + if isinstance(copy_data, dict): + # 对字典进行处理 + for field in fields_to_anonymize: + if field in copy_data: + copy_data[field] = "******" + else: + # 对对象进行处理 + for field in fields_to_anonymize: + if hasattr(copy_data, field): + setattr(copy_data, field, "******") + + return copy_data diff --git a/xiaomusic/xiaomusic.py b/xiaomusic/xiaomusic.py index d2861cc..5402ef7 100644 --- a/xiaomusic/xiaomusic.py +++ b/xiaomusic/xiaomusic.py @@ -1,6 +1,5 @@ #!/usr/bin/env python3 import asyncio -import copy import json import logging import os @@ -31,6 +30,7 @@ from xiaomusic.const import ( from xiaomusic.httpserver import StartHTTPServer from xiaomusic.utils import ( custom_sort_key, + deepcopy_data_no_sensitive_info, find_best_match, fuzzyfinder, get_local_music_duration, @@ -61,20 +61,6 @@ class XiaoMusic: self.new_record_event = asyncio.Event() self.queue = queue.Queue() - self.music_path = config.music_path - self.conf_path = config.conf_path - if not self.conf_path: - self.conf_path = config.music_path - - self.hostname = config.hostname - self.port = config.port - self.proxy = config.proxy - self.search_prefix = config.search_prefix - self.ffmpeg_location = config.ffmpeg_location - self.active_cmd = config.active_cmd.split(",") - self.exclude_dirs = set(config.exclude_dirs.split(",")) - self.music_path_depth = config.music_path_depth - # 下载对象 self.download_proc = None # 单曲循环,全部循环 @@ -93,6 +79,9 @@ class XiaoMusic: # 关机定时器 self._stop_timer = None + # 初始化配置 + self.init_config() + # 初始化日志 self.setup_logger() @@ -105,6 +94,24 @@ class XiaoMusic: # 启动时初始化获取声音 self.set_last_record("get_volume#") + debug_config = deepcopy_data_no_sensitive_info(self.config) + self.log.info(f"Startup OK. {debug_config}") + + def init_config(self): + self.music_path = self.config.music_path + self.conf_path = self.config.conf_path + if not self.conf_path: + self.conf_path = self.config.music_path + + self.hostname = self.config.hostname + self.port = self.config.port + self.proxy = self.config.proxy + self.search_prefix = self.config.search_prefix + self.ffmpeg_location = self.config.ffmpeg_location + self.active_cmd = self.config.active_cmd.split(",") + self.exclude_dirs = set(self.config.exclude_dirs.split(",")) + self.music_path_depth = self.config.music_path_depth + def setup_logger(self): log_format = f"%(asctime)s [{__version__}] [%(levelname)s] %(message)s" date_format = "[%X]" @@ -127,12 +134,6 @@ class XiaoMusic: self.log = logging.getLogger("xiaomusic") self.log.addHandler(handler) self.log.setLevel(logging.DEBUG if self.config.verbose else logging.INFO) - debug_config = copy.deepcopy(self.config) - debug_config.account = "******" - debug_config.password = "******" - debug_config.httpauth_username = "******" - debug_config.httpauth_password = "******" - self.log.info(debug_config) async def poll_latest_ask(self): async with ClientSession() as session: @@ -982,19 +983,23 @@ class XiaoMusic: await self.call_main_thread_function(self.reinit) def update_config_from_setting(self, data): - self.config.mi_did = data.get("mi_did") + # 兼容旧配置:一段时间后清理这里的旧代码 self.config.hardware = data.get("mi_hardware") self.config.search_prefix = data.get("xiaomusic_search") self.config.proxy = data.get("xiaomusic_proxy") self.config.music_list_url = data.get("xiaomusic_music_list_url") self.config.music_list_json = data.get("xiaomusic_music_list_json") - self.search_prefix = self.config.search_prefix - self.proxy = self.config.proxy - self.log.debug("update_config_from_setting ok. data:%s", data) + # 自动赋值相同字段的配置 + self.config.update_config(data) + + self.init_config() + debug_config = deepcopy_data_no_sensitive_info(self.config) + self.log.info("update_config_from_setting ok. data:%s", debug_config) # 重新初始化 async def reinit(self, **kwargs): + self.setup_logger() await self.init_all_data(self.session) self._gen_all_music_list() self.log.info("reinit success")