feat: 新增定时任务功能 #182
This commit is contained in:
parent
c5e0d4f3ca
commit
ec3dc578b8
59
pdm.lock
59
pdm.lock
@ -5,7 +5,7 @@
|
||||
groups = ["default", "dev", "lint"]
|
||||
strategy = ["inherit_metadata"]
|
||||
lock_version = "4.5.0"
|
||||
content_hash = "sha256:d78c6aed8ee11387663e36ade149f06fd493f984e253a1936163f85542ab5a52"
|
||||
content_hash = "sha256:743f0a2ac59e1902f4f5389375ec5df7e2469502b0dff7cef40d391febd1ad92"
|
||||
|
||||
[[metadata.targets]]
|
||||
requires_python = "==3.10.12"
|
||||
@ -106,6 +106,24 @@ files = [
|
||||
{file = "anyio-4.4.0.tar.gz", hash = "sha256:5aadc6a1bbb7cdb0bede386cac5e2940f5e2ff3aa20277e991cf028e0585ce94"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "apscheduler"
|
||||
version = "3.10.4"
|
||||
requires_python = ">=3.6"
|
||||
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",
|
||||
]
|
||||
files = [
|
||||
{file = "APScheduler-3.10.4-py3-none-any.whl", hash = "sha256:fb91e8a768632a4756a585f79ec834e0e27aad5860bac7eaa523d9ccefd87661"},
|
||||
{file = "APScheduler-3.10.4.tar.gz", hash = "sha256:e6df071b27d9be898e486bc7940a7be50b4af2e9da7c08f0744a96d4bd4cef4a"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "argcomplete"
|
||||
version = "3.4.0"
|
||||
@ -915,6 +933,17 @@ files = [
|
||||
{file = "python_multipart-0.0.9.tar.gz", hash = "sha256:03f54688c663f1b7977105f021043b0793151e4cb1c1a9d4a11fc13d622c4026"},
|
||||
]
|
||||
|
||||
[[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"
|
||||
@ -1023,6 +1052,18 @@ files = [
|
||||
{file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"},
|
||||
]
|
||||
|
||||
[[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"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sniffio"
|
||||
version = "1.3.1"
|
||||
@ -1105,6 +1146,22 @@ files = [
|
||||
{file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tzlocal"
|
||||
version = "5.2"
|
||||
requires_python = ">=3.8"
|
||||
summary = "tzinfo object for the local timezone"
|
||||
groups = ["default"]
|
||||
marker = "python_full_version == \"3.10.12\""
|
||||
dependencies = [
|
||||
"backports-zoneinfo; python_version < \"3.9\"",
|
||||
"tzdata; platform_system == \"Windows\"",
|
||||
]
|
||||
files = [
|
||||
{file = "tzlocal-5.2-py3-none-any.whl", hash = "sha256:49816ef2fe65ea8ac19d19aa7a1ae0551c834303d5014c6d5a62e4cbda8047b8"},
|
||||
{file = "tzlocal-5.2.tar.gz", hash = "sha256:8d399205578f1a9342816409cc1e46a93ebd5755e39ea2d85334bea911bf0e6e"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "urllib3"
|
||||
version = "2.2.2"
|
||||
|
@ -15,6 +15,7 @@ dependencies = [
|
||||
"starlette>=0.37.2",
|
||||
"aiofiles>=24.1.0",
|
||||
"ga4mp>=2.0.4",
|
||||
"apscheduler>=3.10.4",
|
||||
]
|
||||
requires-python = ">=3.10,<3.12"
|
||||
readme = "README.md"
|
||||
|
@ -149,6 +149,7 @@ class Config:
|
||||
os.getenv("XIAOMUSIC_CONTINUE_PLAY", "false").lower() == "true"
|
||||
)
|
||||
pull_ask_sec: int = int(os.getenv("XIAOMUSIC_PULL_ASK_SEC", "1"))
|
||||
crontab_json: str = os.getenv("XIAOMUSIC_CRONTAB_JSON", "") # 定时任务
|
||||
|
||||
def append_keyword(self, keys, action):
|
||||
for key in keys.split(","):
|
||||
|
91
xiaomusic/crontab.py
Normal file
91
xiaomusic/crontab.py
Normal file
@ -0,0 +1,91 @@
|
||||
import json
|
||||
|
||||
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
||||
from apscheduler.triggers.cron import CronTrigger
|
||||
|
||||
|
||||
class Crontab:
|
||||
def __init__(self, log):
|
||||
self.log = log
|
||||
self.scheduler = AsyncIOScheduler()
|
||||
|
||||
def start(self):
|
||||
self.scheduler.start()
|
||||
|
||||
def add_job(self, expression, job):
|
||||
try:
|
||||
trigger = CronTrigger.from_crontab(expression)
|
||||
self.scheduler.add_job(job, trigger)
|
||||
except ValueError as e:
|
||||
self.log.error(f"Invalid crontab expression {e}")
|
||||
except Exception as e:
|
||||
self.log.exception(f"Execption {e}")
|
||||
|
||||
# 添加关机任务
|
||||
def add_job_stop(self, expression, xiaomusic, did, **kwargs):
|
||||
async def job():
|
||||
await xiaomusic.stop(did, "notts")
|
||||
|
||||
self.add_job(expression, job)
|
||||
|
||||
# 添加播放任务
|
||||
def add_job_play(self, expression, xiaomusic, did, arg1, **kwargs):
|
||||
async def job():
|
||||
await xiaomusic.play(did, arg1)
|
||||
|
||||
self.add_job(expression, job)
|
||||
|
||||
# 添加播放列表任务
|
||||
def add_job_play_music_list(self, expression, xiaomusic, did, arg1, **kwargs):
|
||||
async def job():
|
||||
await xiaomusic.play_music_list(did, arg1)
|
||||
|
||||
self.add_job(expression, job)
|
||||
|
||||
# 添加语音播放任务
|
||||
def add_job_tts(self, expression, xiaomusic, did, arg1, **kwargs):
|
||||
async def job():
|
||||
xiaomusic.do_tts(did, arg1)
|
||||
|
||||
self.add_job(expression, job)
|
||||
|
||||
def add_job_cron(self, xiaomusic, cron):
|
||||
expression = cron["expression"] # cron 计划格式
|
||||
name = cron["name"] # stop, play, play_music_list, tts
|
||||
did = cron["did"]
|
||||
arg1 = cron.get("arg1", "")
|
||||
jobname = f"add_job_{name}"
|
||||
func = getattr(self, jobname, None)
|
||||
if callable(func):
|
||||
func(expression, xiaomusic, did=did, arg1=arg1)
|
||||
self.log.info(
|
||||
f"crontab add_job_cron ok. did:{did}, name:{name}, arg1:{arg1}"
|
||||
)
|
||||
else:
|
||||
self.log.error(
|
||||
f"'{self.__class__.__name__}' object has no attribute '{jobname}'"
|
||||
)
|
||||
|
||||
# 清空任务
|
||||
def clear_jobs(self):
|
||||
for job in self.scheduler.get_jobs():
|
||||
try:
|
||||
job.remove()
|
||||
except Exception as e:
|
||||
self.log.exception(f"Execption {e}")
|
||||
|
||||
# 重新加载计划任务
|
||||
def reload_config(self, xiaomusic):
|
||||
self.clear_jobs()
|
||||
|
||||
crontab_json = xiaomusic.config.crontab_json
|
||||
if not crontab_json:
|
||||
return
|
||||
|
||||
try:
|
||||
cron_list = json.loads(crontab_json)
|
||||
for cron in cron_list:
|
||||
self.add_job_cron(xiaomusic, cron)
|
||||
self.log.info("crontab reload_config ok")
|
||||
except Exception as e:
|
||||
self.log.exception(f"Execption {e}")
|
@ -172,9 +172,11 @@ var vConsole = new window.VConsole();
|
||||
<label for="music_list_url">歌单地址:</label>
|
||||
<input id="music_list_url" type="text" value="https://gist.githubusercontent.com/hanxi/dda82d964a28f8110f8fba81c3ff8314/raw/example.json"></input>
|
||||
|
||||
<label for="music_list_json">歌单内容:</label>
|
||||
<label for="music_list_json">歌单内容:<a href="https://github.com/hanxi/xiaomusic/issues/78" target="_blank">格式文档</a></label>
|
||||
<textarea id="music_list_json" type="text"></textarea>
|
||||
|
||||
<label for="crontab_json">定时任务:<a href="https://github.com/hanxi/xiaomusic/issues/182" target="_blank">格式文档</a></label>
|
||||
<textarea id="crontab_json" type="text"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
|
@ -32,6 +32,7 @@ from xiaomusic.const import (
|
||||
PLAY_TYPE_TTS,
|
||||
SUPPORT_MUSIC_TYPE,
|
||||
)
|
||||
from xiaomusic.crontab import Crontab
|
||||
from xiaomusic.plugin import PluginManager
|
||||
from xiaomusic.utils import (
|
||||
convert_file_to_mp3,
|
||||
@ -73,6 +74,9 @@ class XiaoMusic:
|
||||
# 初始化日志
|
||||
self.setup_logger()
|
||||
|
||||
# 计划任务
|
||||
self.crontab = Crontab(self.log)
|
||||
|
||||
# 尝试从设置里加载配置
|
||||
self.try_init_setting()
|
||||
|
||||
@ -532,6 +536,7 @@ class XiaoMusic:
|
||||
await asyncio.sleep(3600)
|
||||
|
||||
async def run_forever(self):
|
||||
self.crontab.start()
|
||||
await self.analytics.send_startup_event()
|
||||
analytics_task = asyncio.create_task(self.analytics_task_daily())
|
||||
assert (
|
||||
@ -897,6 +902,9 @@ class XiaoMusic:
|
||||
self.log.info(f"语音控制已启动, 用【{joined_keywords}】开头来控制")
|
||||
self.log.debug(f"key_word_dict: {self.config.key_word_dict}")
|
||||
|
||||
# 重新加载计划任务
|
||||
self.crontab.reload_config(self)
|
||||
|
||||
# 重新初始化
|
||||
async def reinit(self, **kwargs):
|
||||
for handler in self.log.handlers:
|
||||
|
Loading…
Reference in New Issue
Block a user