260106 from nuc

This commit is contained in:
liubiren 2026-01-06 17:54:56 +08:00
parent 7379c992bf
commit 0426a77764
7 changed files with 98 additions and 90 deletions

View File

@ -2,4 +2,4 @@ from mysql import MySQL
from sqlite import SQLite from sqlite import SQLite
from request import restrict, Authenticator, Request from request import restrict, Authenticator, Request
from feishu import Feishu from feishu import Feishu
from rules_engine import RulesEngine from rules_engine import RulesEngine

View File

@ -17,9 +17,15 @@ from requests import Response, Session
from requests.adapters import HTTPAdapter from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry from urllib3.util.retry import Retry
from sqlite import SQLite
# 基于令牌桶限流算法的装饰器
def restrict(refill_rate: float = 5.0, max_tokens: int = 5): def restrict(refill_rate: float = 5.0, max_tokens: int = 5):
"""
请求限速装饰器
:param refill_rate: 令牌填充速率单位为个/
:param max_tokens: 最大令牌数单位为个
"""
class TokenBucket: class TokenBucket:
@ -178,15 +184,15 @@ class Request:
raise ValueError("上传文件和使用流式传输不能同时使用") raise ValueError("上传文件和使用流式传输不能同时使用")
return self return self
class CacheClient(SQLiteClient): class Caches(SQLite):
"""缓存客户端""" """请求缓存"""
def __init__(self, cache_ttl: int): def __init__(self, cache_ttl: int):
""" """
初始化缓存数据库 初始化
:param cache_ttl: 缓存生存时间单位为秒 :param cache_ttl: 缓存生存时间单位为秒
""" """
# 初始化SQLite客户端 # 初始化
super().__init__(database=Path(__file__).parent.resolve() / "caches.db") super().__init__(database=Path(__file__).parent.resolve() / "caches.db")
# 初始化缓存生存时间,单位为秒 # 初始化缓存生存时间,单位为秒
self.cache_ttl = cache_ttl self.cache_ttl = cache_ttl
@ -293,11 +299,10 @@ class Request:
# 初始化缓存生存时间,单位由天转为秒 # 初始化缓存生存时间,单位由天转为秒
self.cache_ttl = cache_ttl * 86400 self.cache_ttl = cache_ttl * 86400
self.cache_client: Optional[HTTPClient.CacheClient] = None self.caches: Optional[Request.Caches] = None
# 若使用缓存则实例化缓存客户端 # 若使用缓存则实例化缓存
if self.cache_enabled: if self.cache_enabled:
# 初始化缓存客户端 self.caches = Request.Caches(cache_ttl=self.cache_ttl)
self.cache_client = self.CacheClient(cache_ttl=self.cache_ttl)
def __del__(self): def __del__(self):
"""析构时关闭请求会话""" """析构时关闭请求会话"""
@ -346,26 +351,19 @@ class Request:
def get( def get(
self, **kwargs self, **kwargs
) -> Union[str, Tuple[str, bytes], Dict[str, Any], ElementTree.Element, None]: ) -> Any:
"""发送GET请求""" """发送GET请求"""
return self._request(method="GET", parameters=self.Parameters(**kwargs)) return self._request(method="GET", parameters=self.Parameters(**kwargs))
def post( def post(
self, **kwargs self, **kwargs
) -> Union[str, Tuple[str, bytes], Dict[str, Any], ElementTree.Element, None]: ) -> Any:
"""发送POST请求""" """发送POST请求"""
return self._request(method="POST", parameters=self.Parameters(**kwargs)) return self._request(method="POST", parameters=self.Parameters(**kwargs))
def download( def download(
self, stream_enabled: bool = False, chunk_size: int = 1024, **kwargs self, stream_enabled: bool = False, chunk_size: int = 1024, **kwargs
) -> Union[ ) -> Any:
str,
Tuple[str, bytes],
Dict[str, Any],
ElementTree.Element,
Generator[bytes, None, None],
None,
]:
""" """
下载文件 下载文件
:param stream_enabled: 使用流式传输 :param stream_enabled: 使用流式传输

View File

@ -1,4 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""
draft模块
"""
from pathlib import Path from pathlib import Path
from typing import Any, Dict, List, Optional, Tuple, Union from typing import Any, Dict, List, Optional, Tuple, Union
@ -19,7 +22,8 @@ class JianYingDraft:
6将草稿保存至剪映草稿文件夹内 6将草稿保存至剪映草稿文件夹内
""" """
# noinspection PyShadowingNames # pylint: disable=too-many-arguments
# pylint: disable=too-many-positional-arguments
def __init__( def __init__(
self, self,
drafts_folder: pyJianYingDraft.DraftFolder, drafts_folder: pyJianYingDraft.DraftFolder,
@ -40,7 +44,6 @@ class JianYingDraft:
:param video_fps: 视频帧率单位为帧/默认为 30 :param video_fps: 视频帧率单位为帧/默认为 30
:param materials_folder_path: 素材文件夹路径 :param materials_folder_path: 素材文件夹路径
""" """
# noinspection PyBroadException
try: try:
# 新建草稿 # 新建草稿
self.draft = drafts_folder.create_draft( self.draft = drafts_folder.create_draft(
@ -56,14 +59,14 @@ class JianYingDraft:
self.materials_folder_path = materials_folder_path self.materials_folder_path = materials_folder_path
except Exception as exception: except Exception as exception:
raise RuntimeError(f"发生异常:{str(exception)}") raise RuntimeError(f"发生异常:{str(exception)}") from exception
def add_text_segment( def add_text_segment(
self, self,
track_name: str, track_name: str,
text: str, text: str,
add_track: bool = True, add_track: bool = True,
timerange: Optional[Tuple[Optional[int, str], Optional[int, str]]] = None, timerange: Optional[Tuple[Union[int, str], Union[int, str]]] = None,
font: Optional[str] = None, font: Optional[str] = None,
style: Optional[Dict[str, Any]] = None, style: Optional[Dict[str, Any]] = None,
border: Optional[Dict[str, Any]] = None, border: Optional[Dict[str, Any]] = None,
@ -132,20 +135,18 @@ class JianYingDraft:
# 向指定文本轨道添加文本片段 # 向指定文本轨道添加文本片段
self.draft.add_segment(segment=text_segment, track_name=track_name) self.draft.add_segment(segment=text_segment, track_name=track_name)
except Exception: except Exception as exception:
raise raise RuntimeError(str(exception)) from exception
def add_audio_segment( def add_audio_segment(
self, self,
track_name: str, track_name: str,
material_path: Path, material_path: Path,
add_track: bool = True, add_track: bool = True,
target_timerange: Optional[ target_timerange: Optional[Tuple[Union[int, str], Union[int, str]]] = None,
Tuple[Optional[int, str], Optional[int, str]]
] = None,
source_timerange: Optional[Tuple[str, str]] = None, source_timerange: Optional[Tuple[str, str]] = None,
speed: Optional[float] = 1.0, speed: float = 1.0,
volume: Optional[float] = 1.0, volume: float = 1.0,
fade: Optional[Tuple[str, str]] = None, fade: Optional[Tuple[str, str]] = None,
) -> None: ) -> None:
""" """
@ -193,28 +194,25 @@ class JianYingDraft:
# 向指定音频轨道添加音频片段 # 向指定音频轨道添加音频片段
self.draft.add_segment(segment=audio_segment, track_name=track_name) self.draft.add_segment(segment=audio_segment, track_name=track_name)
except Exception: except Exception as exception:
raise raise RuntimeError(str(exception)) from exception
# pylint: disable=too-many-locals
def add_video_segment( def add_video_segment(
self, self,
track_name: str, track_name: str,
material_path: Path, material_path: Path,
target_timerange: Optional[ target_timerange: Optional[Tuple[Union[int, str], Union[int, str]]] = None,
Tuple[Optional[int, str], Optional[int, str]] source_timerange: Optional[Tuple[Union[int, str], Union[int, str]]] = None,
] = None, speed: float = 1.0,
source_timerange: Optional[ volume: float = 1.0,
Tuple[Optional[int, str], Optional[int, str]],
] = None,
speed: Optional[float] = 1.0,
volume: Optional[float] = 1.0,
clip_settings: Optional[Dict[str, Any]] = None, clip_settings: Optional[Dict[str, Any]] = None,
keyframes: Optional[ keyframes: Optional[
List[Tuple[pyJianYingDraft.keyframe, Union[str, int], float]] List[Tuple[pyJianYingDraft.KeyframeProperty, Union[str, int], float]]
] = None, ] = None,
animation: Optional[Dict[str, Any]] = None, animation: Optional[Dict[str, Any]] = None,
transition: Optional[Dict[str, Any]] = None, transition: Optional[Dict[str, Any]] = None,
background_filling: Optional[Tuple[str, Any]] = None, background_filling: Optional[Dict[str, Any]] = None,
) -> None: ) -> None:
""" """
向指定视频轨道添加视频或图片片段 向指定视频轨道添加视频或图片片段
@ -238,9 +236,9 @@ class JianYingDraft:
track_name=track_name, track_name=track_name,
) )
# 解析开始时间和持续时间 # 获取持续时间
target_start, target_duration = ( target_duration = pyJianYingDraft.time_util.tim(
target_timerange if target_timerange else (0, self.draft_duration) (target_timerange if target_timerange else (0, self.draft_duration))[1]
) )
# 视频素材 # 视频素材
@ -276,9 +274,8 @@ class JianYingDraft:
) )
# 添加关键帧 # 添加关键帧
if keyframes: if keyframes:
# noinspection PyShadowingBuiltins for _property, offset, value in keyframes:
for property, offset, value in keyframes: video_segment.add_keyframe(_property, offset, value)
video_segment.add_keyframe(property, offset, value)
# 添加动画 # 添加动画
if animation: if animation:
@ -290,23 +287,21 @@ class JianYingDraft:
# 添加背景填充 # 添加背景填充
if background_filling: if background_filling:
video_segment.add_background_filling(*background_filling) video_segment.add_background_filling(**background_filling)
# 向指定视频轨道添加视频或图片片段 # 向指定视频轨道添加视频或图片片段
self.draft.add_segment(segment=video_segment, track_name=track_name) self.draft.add_segment(segment=video_segment, track_name=track_name)
duration += video_material_duration duration += video_material_duration
except Exception: except Exception as exception:
raise raise RuntimeError(str(exception)) from exception
def add_sticker( def add_sticker(
self, self,
track_name: str, track_name: str,
resource_id: str, resource_id: str,
target_timerange: Optional[ target_timerange: Optional[Tuple[Union[int, str], Union[int, str]]] = None,
Tuple[Optional[int, str], Optional[int, str]]
] = None,
clip_settings: Optional[Dict[str, Any]] = None, clip_settings: Optional[Dict[str, Any]] = None,
) -> None: ) -> None:
""" """
@ -344,13 +339,13 @@ class JianYingDraft:
# 向指定贴纸轨道添加贴纸片段 # 向指定贴纸轨道添加贴纸片段
self.draft.add_segment(segment=sticker_segment, track_name=track_name) self.draft.add_segment(segment=sticker_segment, track_name=track_name)
except Exception: except Exception as exception:
raise raise RuntimeError(str(exception)) from exception
def add_subtitles( def add_subtitles(
self, self,
text: str, text: str,
timbre: Optional[str] = "女声-晓晓", timbre: str = "女声-晓晓",
rate: str = "+25%", rate: str = "+25%",
volume: str = "+0%", volume: str = "+0%",
font: Optional[str] = None, font: Optional[str] = None,
@ -429,5 +424,5 @@ class JianYingDraft:
try: try:
self.draft.save() self.draft.save()
except Exception: except Exception as exception:
raise raise RuntimeError(str(exception)) from exception

View File

@ -1,17 +1,21 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""
EdgeTTS模块
"""
import asyncio import asyncio
from _md5 import md5
from pathlib import Path from pathlib import Path
from typing import Tuple, Union from typing import Tuple, Union
from hashlib import md5
import edge_tts import edge_tts
from mutagen.mp3 import MP3 from mutagen.mp3 import MP3
# pylint: disable=too-few-public-methods
class EdgeTTS: class EdgeTTS:
""" """
封装EdgeTTS支持 EdgeTTS模块支持
1根据文本合成语音并将语音文件保存至指定文件夹内 1根据文本合成语音并将语音文件保存至指定文件夹内
""" """
@ -41,10 +45,8 @@ class EdgeTTS:
:param timbre: 音色名称 :param timbre: 音色名称
:param rate: 语速 :param rate: 语速
:param volume: 音量 :param volume: 音量
:return 语音文件路径path对象和持续时长单位为微秒 :return: 语音文件路径path对象和持续时长单位为微秒
""" """
# noinspection PyBroadException
try: try:
# 异步处理方法 # 异步处理方法
async def _async_synthetize(): async def _async_synthetize():
@ -69,4 +71,4 @@ class EdgeTTS:
except Exception as exception: except Exception as exception:
raise RuntimeError( raise RuntimeError(
f"根据文本合成语音并将语音文件保存至指定文件夹内发生异常:{str(exception)}" f"根据文本合成语音并将语音文件保存至指定文件夹内发生异常:{str(exception)}"
) ) from exception

View File

@ -1,4 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""
export模块
"""
import random import random
import subprocess import subprocess
@ -13,20 +16,23 @@ import win32gui
from draft import JianYingDraft from draft import JianYingDraft
# pylint: disable=too-few-public-methods
# pylint: disable=too-many-instance-attributes
class JianYingExport: class JianYingExport:
""" """
封装 pyJianYing中导出草稿的相关功能支持 封装 pyJianYingDraft.JianyingController库支持
1初始化素材文件夹内所有素材 1初始化素材文件夹内所有素材
2就工作流添加工作 2初始化工作流和工作配置
3基于工作流生成草稿 3导出草稿
""" """
# noinspection PyShadowingNames # pylint: disable=too-many-arguments
# pylint: disable=too-many-positional-arguments
def __init__( def __init__(
self, self,
materials_folder_path: str, materials_folder_path: str,
program_path: str = r"E:\JianyingPro\5.9.0.11632\JianyingPro.exe", program_path: str = "E:\\JianYingPro\\5.9.0.11632\\JianYingPro.exe", # 仅可在windows运行该脚本
drafts_folder_path: str = r"E:\JianyingPro Drafts", drafts_folder_path: str = "E:\\JianYingPro Drafts",
draft_counts: int = 10, draft_counts: int = 10,
video_width: int = 1080, video_width: int = 1080,
video_height: int = 1920, video_height: int = 1920,
@ -34,7 +40,7 @@ class JianYingExport:
): ):
""" """
初始化 初始化
:param program_path: 剪映执行程序路径 :param program_path: 剪映程序路径
:param drafts_folder_path: 剪映草稿文件夹路径 :param drafts_folder_path: 剪映草稿文件夹路径
:param materials_folder_path: 素材文件夹路径 :param materials_folder_path: 素材文件夹路径
:param draft_counts: 草稿数默认为 10 :param draft_counts: 草稿数默认为 10
@ -42,11 +48,10 @@ class JianYingExport:
:param video_height: 视频高度默认为 1920像素 :param video_height: 视频高度默认为 1920像素
:param video_fps: 视频帧率单位为帧/默认为 30 :param video_fps: 视频帧率单位为帧/默认为 30
""" """
# noinspection PyBroadException
try: try:
self.program_path = Path(program_path) self.program_path = Path(program_path)
if not self.program_path.exists(): if not self.program_path.exists():
raise RuntimeError("剪映执行程序路径不存在") raise RuntimeError("剪映程序路径不存在")
# 初始化剪映专业版进程 # 初始化剪映专业版进程
self.jianying_process = None self.jianying_process = None
@ -309,7 +314,7 @@ class JianYingExport:
self.draft_names = [] self.draft_names = []
except Exception as exception: except Exception as exception:
raise RuntimeError(f"发生异常:{str(exception)}") raise RuntimeError(f"发生异常:{str(exception)}") from exception
def _init_materials(self) -> None: def _init_materials(self) -> None:
""" """
@ -500,7 +505,7 @@ class JianYingExport:
start_time = time.time() start_time = time.time()
while time.time() - start_time < timeout: while time.time() - start_time < timeout:
# 定位剪映执行程序窗口 # 定位剪映程序窗口
if self._locate_window() is not None: if self._locate_window() is not None:
print(f"已启动剪映专业版进程PID {self.jianying_process.pid}") print(f"已启动剪映专业版进程PID {self.jianying_process.pid}")
return return
@ -508,7 +513,9 @@ class JianYingExport:
raise RuntimeError("启动超时") raise RuntimeError("启动超时")
except Exception as exception: except Exception as exception:
raise RuntimeError(f"启动剪映专业版进程发生异常:{str(exception)}") raise RuntimeError(
f"启动剪映专业版进程发生异常:{str(exception)}"
) from exception
def _close_process(self, timeout: int = 60) -> None: def _close_process(self, timeout: int = 60) -> None:
""" """
@ -517,29 +524,32 @@ class JianYingExport:
:return: :return:
""" """
try: try:
# 定位剪映执行程序窗口 # 定位剪映程序窗口
window_handle = self._locate_window() window_handle = self._locate_window()
if window_handle is not None: if window_handle is not None:
# 请求关闭剪映执行程序窗口 # 请求关闭剪映程序窗口
# pylint: disable=c-extension-no-member
win32gui.SendMessage(window_handle, win32con.WM_CLOSE, 0, 0) win32gui.SendMessage(window_handle, win32con.WM_CLOSE, 0, 0)
start_time = time.time() start_time = time.time()
while time.time() - start_time < timeout: while time.time() - start_time < timeout:
# pylint: disable=c-extension-no-member
if not win32gui.IsWindow(window_handle): if not win32gui.IsWindow(window_handle):
print("已关闭剪映专业版进程") print("已关闭剪映专业版进程")
return return
else: time.sleep(2)
time.sleep(2)
raise RuntimeError("关闭超时") raise RuntimeError("关闭超时")
except Exception as exception: except Exception as exception:
raise RuntimeError(f"关闭剪映专业版进程发生异常:{str(exception)}") raise RuntimeError(
f"关闭剪映专业版进程发生异常:{str(exception)}"
) from exception
@staticmethod @staticmethod
def _locate_window() -> Optional[int]: def _locate_window() -> Optional[int]:
""" """
定位剪映执行程序窗口 定位剪映程序窗口
:return: 剪映执行程序窗口句柄 :return: 剪映程序窗口句柄
""" """
window_handle = None window_handle = None
@ -550,13 +560,16 @@ class JianYingExport:
# 初始化窗口句柄 # 初始化窗口句柄
nonlocal window_handle nonlocal window_handle
# 获取窗口标题 # 获取窗口标题
# pylint: disable=c-extension-no-member
window_text = win32gui.GetWindowText(handle) window_text = win32gui.GetWindowText(handle)
# 检查窗口是否可见且窗口标题为剪映专业版 # 检查窗口是否可见且窗口标题为剪映专业版
# pylint: disable=c-extension-no-member
if win32gui.IsWindowVisible(handle) and window_text == "剪映专业版": if win32gui.IsWindowVisible(handle) and window_text == "剪映专业版":
window_handle = handle window_handle = handle
return False return False
return True return True
# 遍历所有顶层窗口 # 遍历所有顶层窗口
# pylint: disable=c-extension-no-member
win32gui.EnumWindows(callback, None) win32gui.EnumWindows(callback, None)
return window_handle return window_handle

View File

@ -5,10 +5,10 @@ import re
from base64 import b64encode from base64 import b64encode
from datetime import datetime from datetime import datetime
from decimal import Decimal, ROUND_HALF_UP from decimal import Decimal, ROUND_HALF_UP
from hashlib import md5
from pathlib import Path from pathlib import Path
from typing import Optional, Tuple, Dict, Any from typing import Optional, Tuple, Dict, Any
from hashlib import md5
import cv2 import cv2
import numpy import numpy
import pandas import pandas
@ -17,8 +17,6 @@ from jionlp import parse_location
from common import dossier, master_data, rule_engine from common import dossier, master_data, rule_engine
print(1)
exit()
from utils import Authenticator, Request from utils import Authenticator, Request
# 实例化认证器 # 实例化认证器

View File

@ -4,6 +4,8 @@ from datetime import datetime
from decimal import Decimal, ROUND_HALF_UP from decimal import Decimal, ROUND_HALF_UP
from typing import Any, Dict, List, Optional from typing import Any, Dict, List, Optional
import sys
sys.path.append(".")
from utils import SQLite from utils import SQLite