feat(core): 语音功能上线

This commit is contained in:
DLmaster361
2025-06-01 03:16:56 +08:00
parent 3f8e2fbe6b
commit 9a0e7265c6
55 changed files with 182 additions and 29 deletions

View File

@@ -32,6 +32,7 @@ __license__ = "GPL-3.0 license"
from .config import QueueConfig, MaaConfig, MaaUserConfig, MaaPlanConfig, Config
from .main_info_bar import MainInfoBar
from .network import Network
from .sound_player import SoundPlayer
from .task_manager import Task, TaskManager
from .timer import MainTimer
@@ -43,6 +44,7 @@ __all__ = [
"MaaPlanConfig",
"MainInfoBar",
"Network",
"SoundPlayer",
"Task",
"TaskManager",
"MainTimer",

View File

@@ -185,6 +185,11 @@ class GlobalConfig(LQConfig):
"Function", "IfSkipMumuSplashAds", False, BoolValidator()
)
self.voice_Enabled = ConfigItem("Voice", "Enabled", False, BoolValidator())
self.voice_Type = OptionsConfigItem(
"Voice", "Type", "simple", OptionsValidator(["simple", "noisy"])
)
self.start_IfSelfStart = ConfigItem(
"Start", "IfSelfStart", False, BoolValidator()
)

View File

@@ -30,6 +30,7 @@ from PySide6.QtCore import Qt
from qfluentwidgets import InfoBar, InfoBarPosition
from .config import Config
from .sound_player import SoundPlayer
class _MainInfoBar:
@@ -79,5 +80,10 @@ class _MainInfoBar:
if info_bar_item not in Config.info_bar_list:
Config.info_bar_list.append(info_bar_item)
if mode == "warning":
SoundPlayer.play("发生异常")
if mode == "error":
SoundPlayer.play("发生错误")
MainInfoBar = _MainInfoBar()

70
app/core/sound_player.py Normal file
View File

@@ -0,0 +1,70 @@
# AUTO_MAA:A MAA Multi Account Management and Automation Tool
# Copyright © 2024-2025 DLmaster361
# This file is part of AUTO_MAA.
# AUTO_MAA is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published
# by the Free Software Foundation, either version 3 of the License,
# or (at your option) any later version.
# AUTO_MAA is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty
# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
# the GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with AUTO_MAA. If not, see <https://www.gnu.org/licenses/>.
# Contact: DLmaster_361@163.com
"""
AUTO_MAA
AUTO_MAA音效播放器
v4.3
作者DLmaster_361
"""
from loguru import logger
from PySide6.QtCore import QObject, QUrl
from PySide6.QtMultimedia import QSoundEffect
from pathlib import Path
from .config import Config
class _SoundPlayer(QObject):
def __init__(self):
super().__init__()
self.sounds_path = Config.app_path / "resources/sounds"
def play(self, sound_name: str, parent: QObject = None):
if not Config.get(Config.voice_Enabled):
return
if (self.sounds_path / f"both/{sound_name}.wav").exists():
self.play_voice(self.sounds_path / f"both/{sound_name}.wav", parent)
elif (
self.sounds_path / Config.get(Config.voice_Type) / f"{sound_name}.wav"
).exists():
self.play_voice(
self.sounds_path / Config.get(Config.voice_Type) / f"{sound_name}.wav",
parent,
)
def play_voice(self, sound_path: Path, parent: QObject):
effect = QSoundEffect(self if parent is None else parent)
effect.setVolume(1)
effect.setSource(QUrl.fromLocalFile(sound_path))
effect.play()
SoundPlayer = _SoundPlayer()

View File

@@ -35,6 +35,7 @@ from typing import Dict, Union
from .config import Config
from .main_info_bar import MainInfoBar
from .network import Network
from .sound_player import SoundPlayer
from app.models import MaaManager
from app.services import System
@@ -191,6 +192,7 @@ class _TaskManager(QObject):
logger.info(f"任务开始:{name}")
MainInfoBar.push_info_bar("info", "任务开始", name, 3000)
SoundPlayer.play("任务开始")
Config.running_list.append(name)
self.task_dict[name] = Task(mode, name, info)
@@ -239,6 +241,7 @@ class _TaskManager(QObject):
logger.info(f"任务结束:{name}")
MainInfoBar.push_info_bar("info", "任务结束", name, 3000)
SoundPlayer.play("任务结束")
self.task_dict[name].deleteLater()
self.task_dict.pop(name)

View File

@@ -38,7 +38,7 @@ from pathlib import Path
from jinja2 import Environment, FileSystemLoader
from typing import Union, List, Dict
from app.core import Config, MaaConfig, MaaUserConfig
from app.core import Config, MaaConfig, MaaUserConfig, SoundPlayer
from app.services import Notify, System
@@ -88,6 +88,8 @@ class MaaManager(QObject):
self.question_response.connect(self.__capture_response)
self.question_response.connect(self.question_loop.quit)
self.wait_loop = QEventLoop()
self.interrupt.connect(self.quit_monitor)
self.maa_version = None
@@ -511,10 +513,7 @@ class MaaManager(QObject):
"检测到MAA进程完成代理任务\n正在等待相关程序结束\n请等待10s"
)
for _ in range(10):
if self.isInterruptionRequested:
break
time.sleep(1)
self.sleep(10)
else:
logger.error(
f"{self.name} | 用户: {user[0]} - 代理任务异常: {self.maa_result}"
@@ -564,10 +563,11 @@ class MaaManager(QObject):
f"{user[0].replace("_", " ")}{mode_book[mode][5:7]}出现异常",
1,
)
for _ in range(10):
if self.isInterruptionRequested:
break
time.sleep(1)
if i == self.set["RunSet"]["RunTimesLimit"] - 1:
SoundPlayer.play("子任务失败", self)
else:
SoundPlayer.play(self.maa_result, self)
self.sleep(10)
# 任务结束后释放ADB
try:
@@ -619,6 +619,7 @@ class MaaManager(QObject):
},
user_data,
)
SoundPlayer.play("六星喜报", self)
# 执行MAA解压更新动作
if self.maa_update_package:
@@ -630,15 +631,13 @@ class MaaManager(QObject):
self.update_log_text.emit(
f"检测到MAA存在更新\nMAA正在执行更新动作\n请等待10s"
)
SoundPlayer.play("MAA更新", self)
self.set_maa("更新MAA", None)
subprocess.Popen(
[self.maa_exe_path],
creationflags=subprocess.CREATE_NO_WINDOW,
)
for _ in range(10):
if self.isInterruptionRequested:
break
time.sleep(1)
self.sleep(10)
System.kill_process(self.maa_exe_path)
logger.info(f"{self.name} | 更新动作结束")
@@ -745,10 +744,7 @@ class MaaManager(QObject):
# 无命令行中止MAA与其子程序
System.kill_process(self.maa_exe_path)
self.if_open_emulator = True
for _ in range(10):
if self.isInterruptionRequested:
break
time.sleep(1)
self.sleep(10)
# 登录成功,结束循环
if run_book[0]:
@@ -756,6 +752,7 @@ class MaaManager(QObject):
# 登录失败,询问是否结束循环
elif not self.isInterruptionRequested:
SoundPlayer.play("排查重试", self)
if not self.push_question(
"操作提示", "MAA未能正确登录到PRTS是否重试"
):
@@ -764,6 +761,7 @@ class MaaManager(QObject):
# 登录成功,录入人工排查情况
if run_book[0] and not self.isInterruptionRequested:
SoundPlayer.play("排查录入", self)
if self.push_question(
"操作提示", "请检查用户代理情况,该用户是否正确完成代理任务?"
):
@@ -885,6 +883,7 @@ class MaaManager(QObject):
self.maa_result = "任务被手动中止"
self.isInterruptionRequested = True
self.wait_loop.quit()
def push_question(self, title: str, message: str) -> bool:
@@ -895,6 +894,12 @@ class MaaManager(QObject):
def __capture_response(self, response: bool) -> None:
self.response = response
def sleep(self, time: int) -> None:
"""非阻塞型等待"""
QTimer.singleShot(time * 1000, self.wait_loop.quit)
self.wait_loop.exec()
def search_ADB_address(self) -> None:
"""搜索ADB实际地址"""
@@ -902,10 +907,7 @@ class MaaManager(QObject):
f"即将搜索ADB实际地址\n正在等待模拟器完成启动\n请等待{self.wait_time}s"
)
for _ in range(self.wait_time):
if self.isInterruptionRequested:
break
time.sleep(1)
self.sleep(self.wait_time)
# 移除静默进程标记
Config.silence_list.remove(self.emulator_path)
@@ -968,6 +970,7 @@ class MaaManager(QObject):
with self.maa_set_path.open(mode="w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=4)
SoundPlayer.play("ADB成功", self)
return None
else:
@@ -975,6 +978,8 @@ class MaaManager(QObject):
else:
logger.info(f"{self.name} | 无法连接到ADB地址{ADB_address}")
SoundPlayer.play("ADB失败", self)
def refresh_maa_log(self) -> None:
"""刷新MAA日志"""

View File

@@ -48,7 +48,7 @@ from PySide6.QtGui import QTextCursor
from typing import List, Dict
from app.core import Config, TaskManager, Task, MainInfoBar
from app.core import Config, TaskManager, Task, MainInfoBar, SoundPlayer
from .Widget import StatefulItemCard, ComboBoxMessageBox, PivotArea

View File

@@ -51,7 +51,7 @@ from pathlib import Path
from typing import Union, List, Dict
from app.core import Config
from app.core import Config, SoundPlayer
from .Widget import StatefulItemCard, QuantifiedItemCard, QuickExpandGroupCard
@@ -83,6 +83,8 @@ class History(QWidget):
def reload_history(self, mode: str, start_date: QDate, end_date: QDate) -> None:
"""加载历史记录界面"""
SoundPlayer.play("历史记录查询")
while self.content_layout.count() > 0:
item = self.content_layout.takeAt(0)
if item.spacerItem():

View File

@@ -45,7 +45,7 @@ from datetime import datetime, timedelta
import shutil
import darkdetect
from app.core import Config, TaskManager, MainTimer, MainInfoBar
from app.core import Config, TaskManager, MainTimer, MainInfoBar, SoundPlayer
from app.services import Notify, Crypto, System
from .home import Home
from .member_manager import MemberManager
@@ -285,6 +285,7 @@ class AUTO_MAA(MSFluentWindow):
and not self.window().isMaximized()
):
self.titleBar.maxBtn.click()
SoundPlayer.play("欢迎回来")
self.show_ui("配置托盘")
elif if_start:
if Config.get(Config.ui_maximized) and not self.window().isMaximized():
@@ -378,6 +379,8 @@ class AUTO_MAA(MSFluentWindow):
self.titleBar.minBtn.click()
SoundPlayer.play("MAA在完成任务前退出")
def clean_old_logs(self):
"""
删除超过用户设定天数的日志文件(基于目录日期)

View File

@@ -58,7 +58,15 @@ from typing import List
import shutil
import json
from app.core import Config, MainInfoBar, TaskManager, MaaConfig, MaaUserConfig, Network
from app.core import (
Config,
MainInfoBar,
TaskManager,
MaaConfig,
MaaUserConfig,
Network,
SoundPlayer,
)
from app.services import Crypto
from .downloader import DownloadManager
from .Widget import (
@@ -181,6 +189,7 @@ class MemberManager(QWidget):
MainInfoBar.push_info_bar(
"success", "操作成功", f"添加脚本实例 脚本_{index}", 3000
)
SoundPlayer.play("添加脚本实例")
def del_setting_box(self):
"""删除一个脚本实例"""
@@ -221,6 +230,7 @@ class MemberManager(QWidget):
MainInfoBar.push_info_bar(
"success", "操作成功", f"删除脚本实例 {name}", 3000
)
SoundPlayer.play("删除脚本实例")
def left_setting_box(self):
"""向左移动脚本实例"""
@@ -885,6 +895,7 @@ class MemberManager(QWidget):
MainInfoBar.push_info_bar(
"success", "操作成功", f"{self.name} 添加 用户_{index}", 3000
)
SoundPlayer.play("添加用户")
def del_user(self):
"""删除一个用户"""
@@ -944,6 +955,7 @@ class MemberManager(QWidget):
MainInfoBar.push_info_bar(
"success", "操作成功", f"{self.name} 删除 {name}", 3000
)
SoundPlayer.play("删除用户")
def left_user(self):
"""向前移动用户"""

View File

@@ -43,7 +43,7 @@ from qfluentwidgets import (
from typing import List, Dict, Union
import shutil
from app.core import Config, MainInfoBar, MaaPlanConfig
from app.core import Config, MainInfoBar, MaaPlanConfig, SoundPlayer
from .Widget import (
ComboBoxMessageBox,
LineEditSettingCard,
@@ -129,6 +129,7 @@ class PlanManager(QWidget):
MainInfoBar.push_info_bar(
"success", "操作成功", f"添加计划表 计划_{index}", 3000
)
SoundPlayer.play("添加计划表")
def del_setting_box(self):
"""删除一个计划表"""
@@ -167,6 +168,7 @@ class PlanManager(QWidget):
logger.success(f"计划表 {name} 删除成功")
MainInfoBar.push_info_bar("success", "操作成功", f"删除计划表 {name}", 3000)
SoundPlayer.play("删除计划表")
def left_setting_box(self):
"""向左移动计划表"""

View File

@@ -42,7 +42,7 @@ from qfluentwidgets import (
)
from typing import List
from app.core import QueueConfig, Config, MainInfoBar
from app.core import QueueConfig, Config, MainInfoBar, SoundPlayer
from .Widget import (
SwitchSettingCard,
ComboBoxSettingCard,
@@ -116,6 +116,7 @@ class QueueManager(QWidget):
logger.success(f"调度队列_{index} 添加成功")
MainInfoBar.push_info_bar("success", "操作成功", f"添加 调度队列_{index}", 3000)
SoundPlayer.play("添加调度队列")
def del_setting_box(self):
"""删除一个调度队列实例"""
@@ -154,6 +155,7 @@ class QueueManager(QWidget):
logger.success(f"{name} 删除成功")
MainInfoBar.push_info_bar("success", "操作成功", f"删除 {name}", 3000)
SoundPlayer.play("删除调度队列")
def left_setting_box(self):
"""向左移动调度队列实例"""

View File

@@ -49,7 +49,7 @@ from packaging import version
from pathlib import Path
from typing import Dict, Union
from app.core import Config, MainInfoBar, Network
from app.core import Config, MainInfoBar, Network, SoundPlayer
from app.services import Crypto, System, Notify
from .downloader import DownloadManager
from .Widget import (
@@ -71,6 +71,7 @@ class Setting(QWidget):
self.setObjectName("设置")
self.function = FunctionSettingCard(self)
self.voice = VoiceSettingCard(self)
self.start = StartSettingCard(self)
self.ui = UiSettingCard(self)
self.notification = NotifySettingCard(self)
@@ -94,6 +95,7 @@ class Setting(QWidget):
content_layout = QVBoxLayout(content_widget)
content_layout.setContentsMargins(0, 0, 11, 0)
content_layout.addWidget(self.function)
content_layout.addWidget(self.voice)
content_layout.addWidget(self.start)
content_layout.addWidget(self.ui)
content_layout.addWidget(self.notification)
@@ -376,6 +378,7 @@ class Setting(QWidget):
all_version_info[key] = value.copy()
# 询问是否开始版本更新
SoundPlayer.play("有新版本")
choice = NoticeMessageBox(
self.window(),
"版本更新",
@@ -461,8 +464,10 @@ class Setting(QWidget):
3600000,
if_force=True,
)
SoundPlayer.play("有新版本")
else:
MainInfoBar.push_info_bar("success", "更新检查", "已是最新版本~", 3000)
SoundPlayer.play("无新版本")
def start_setup(self) -> None:
subprocess.Popen(
@@ -530,6 +535,7 @@ class Setting(QWidget):
choice = NoticeMessageBox(self.window(), "公告", notice["notice_dict"])
choice.button_cancel.hide()
choice.button_layout.insertStretch(0, 1)
SoundPlayer.play("公告展示")
if choice.exec():
with (Config.app_path / "resources/notice.json").open(
mode="w", encoding="utf-8"
@@ -545,6 +551,7 @@ class Setting(QWidget):
MainInfoBar.push_info_bar(
"info", "有新公告", "请前往设置界面查看公告", 3600000, if_force=True
)
SoundPlayer.play("公告通知")
return None
@@ -653,6 +660,36 @@ class FunctionSettingCard(HeaderCardWidget):
self.addGroupWidget(widget)
class VoiceSettingCard(HeaderCardWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setTitle("音效")
self.card_Enabled = SwitchSettingCard(
icon=FluentIcon.PAGE_RIGHT,
title="音效开关",
content="是否启用音效",
qconfig=Config,
configItem=Config.voice_Enabled,
parent=self,
)
self.card_Type = ComboBoxSettingCard(
icon=FluentIcon.PAGE_RIGHT,
title="音效模式",
content="选择音效的播放模式",
texts=["简洁", "聒噪"],
qconfig=Config,
configItem=Config.voice_Type,
parent=self,
)
Layout = QVBoxLayout()
Layout.addWidget(self.card_Enabled)
Layout.addWidget(self.card_Type)
self.viewLayout.addLayout(Layout)
class StartSettingCard(HeaderCardWidget):
def __init__(self, parent=None):