Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion videocaptioner/core/asr/faster_whisper.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
GPUtil = None # type: ignore[assignment]

from ..utils.logger import setup_logger
from ..utils.platform_utils import get_subprocess_kwargs
from ..utils.subprocess_helper import StreamReader
from .asr_data import ASRData, ASRDataSeg
from .base import BaseASR
Expand Down Expand Up @@ -258,7 +259,7 @@ def _default_callback(x, y):
text=True,
encoding="utf-8",
errors="ignore",
creationflags=subprocess.CREATE_NO_WINDOW if os.name == "nt" else 0,
**get_subprocess_kwargs(), # Windows 隐藏控制台窗口
)

# 使用 StreamReader 处理输出
Expand Down
4 changes: 3 additions & 1 deletion videocaptioner/core/asr/whisper_cpp.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

from ...config import MODEL_PATH
from ..utils.logger import setup_logger
from ..utils.platform_utils import get_subprocess_kwargs
from ..utils.subprocess_helper import StreamReader
from .asr_data import ASRData, ASRDataSeg
from .base import BaseASR
Expand Down Expand Up @@ -155,6 +156,7 @@ def _default_callback(_progress: int, _message: str) -> None:
text=True,
encoding="utf-8",
bufsize=1,
**get_subprocess_kwargs(), # Windows 隐藏控制台窗口
)

logger.debug(f"Whisper.cpp process started, PID: {self.process.pid}")
Expand Down Expand Up @@ -247,7 +249,7 @@ def get_audio_duration(self, filepath: str) -> int:
text=True,
encoding="utf-8",
errors="replace",
creationflags=subprocess.CREATE_NO_WINDOW if os.name == "nt" else 0,
**get_subprocess_kwargs(), # Windows 隐藏控制台窗口
)
info = result.stderr
if duration_match := re.search(r"Duration: (\d+):(\d+):(\d+\.\d+)", info):
Expand Down
19 changes: 5 additions & 14 deletions videocaptioner/core/subtitle/ass_renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from videocaptioner.config import CACHE_PATH, FONTS_PATH, RESOURCE_PATH
from videocaptioner.core.entities import SubtitleLayoutEnum
from videocaptioner.core.utils.logger import setup_logger
from videocaptioner.core.utils.platform_utils import get_subprocess_kwargs

from .ass_utils import auto_wrap_ass_file

Expand Down Expand Up @@ -172,11 +173,7 @@ def render_ass_preview(
str(default_bg),
],
capture_output=True,
creationflags=(
getattr(subprocess, "CREATE_NO_WINDOW", 0)
if os.name == "nt"
else 0
),
**get_subprocess_kwargs(), # Windows 隐藏控制台窗口
)
bg_path_obj = default_bg

Expand Down Expand Up @@ -205,9 +202,7 @@ def render_ass_preview(
result = subprocess.run(
cmd,
capture_output=True,
creationflags=(
getattr(subprocess, "CREATE_NO_WINDOW", 0) if os.name == "nt" else 0
),
**get_subprocess_kwargs(), # Windows 隐藏控制台窗口
)

if result.returncode != 0:
Expand All @@ -228,9 +223,7 @@ def _get_video_resolution(video_path: str) -> Tuple[int, int]:
["ffmpeg", "-i", video_path],
capture_output=True,
text=True,
creationflags=(
getattr(subprocess, "CREATE_NO_WINDOW", 0) if os.name == "nt" else 0
),
**get_subprocess_kwargs(), # Windows 隐藏控制台窗口
)

# 从 ffmpeg 输出中解析分辨率
Expand Down Expand Up @@ -350,9 +343,7 @@ def render_ass_video(
text=True,
encoding="utf-8",
errors="replace",
creationflags=(
getattr(subprocess, "CREATE_NO_WINDOW", 0) if os.name == "nt" else 0
),
**get_subprocess_kwargs(), # Windows 隐藏控制台窗口
)

# 实时Reading输出并调用回调
Expand Down
7 changes: 3 additions & 4 deletions videocaptioner/core/subtitle/rounded_renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

from videocaptioner.core.entities import SubtitleLayoutEnum
from videocaptioner.core.utils.logger import setup_logger
from videocaptioner.core.utils.platform_utils import get_subprocess_kwargs

from .font_utils import FontType, get_font
from .styles import RoundedBgStyle
Expand All @@ -31,7 +32,7 @@ def _get_video_info(video_path: str) -> Tuple[int, int, float]:
text=True,
encoding="utf-8",
errors="replace",
creationflags=(getattr(subprocess, "CREATE_NO_WINDOW", 0) if os.name == "nt" else 0),
**get_subprocess_kwargs(), # Windows 隐藏控制台窗口
)

# 解析分辨率
Expand Down Expand Up @@ -442,9 +443,7 @@ def render_rounded_video(
text=True,
encoding="utf-8",
errors="replace",
creationflags=(
getattr(subprocess, "CREATE_NO_WINDOW", 0) if os.name == "nt" else 0
),
**get_subprocess_kwargs(), # Windows 隐藏控制台窗口
)

if result.returncode != 0:
Expand Down
24 changes: 22 additions & 2 deletions videocaptioner/core/utils/platform_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,16 +83,36 @@ def get_subprocess_kwargs():
"""
获取跨平台的subprocess参数

在 Windows GUI 应用中,子进程(如 ffmpeg、ASR 解码器等)启动时会弹出控制台窗口,
这会干扰用户体验。本函数返回 Windows 下用于隐藏控制台窗口的参数:
- creationflags = CREATE_NO_WINDOW:最常用的隐藏方式
- startupinfo.wShowWindow = SW_HIDE:备用方案,确保窗口完全不可见

非 Windows 系统返回空字典,不影响正常行为。

Usage:
subprocess.run(cmd, **get_subprocess_kwargs())
subprocess.Popen(cmd, **get_subprocess_kwargs())

Returns:
dict: subprocess参数字典
dict: subprocess参数字典,Windows 下包含 creationflags/startupinfo,其他系统为空
"""
kwargs = {}

# 仅在Windows上添加CREATE_NO_WINDOW标志
# 仅在Windows上添加静默启动参数
if platform.system() == "Windows":
if hasattr(subprocess, "CREATE_NO_WINDOW"):
kwargs["creationflags"] = getattr(subprocess, "CREATE_NO_WINDOW", 0)

if hasattr(subprocess, "STARTUPINFO") and hasattr(
subprocess, "STARTF_USESHOWWINDOW"
):
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
if hasattr(subprocess, "SW_HIDE"):
startupinfo.wShowWindow = subprocess.SW_HIDE
kwargs["startupinfo"] = startupinfo

return kwargs
Comment thread
hjkl950217 marked this conversation as resolved.


Expand Down
33 changes: 8 additions & 25 deletions videocaptioner/core/utils/video_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from ..subtitle.ass_utils import auto_wrap_ass_file
from ..subtitle.rounded_renderer import render_rounded_video
from ..utils.logger import setup_logger
from ..utils.platform_utils import get_subprocess_kwargs

if TYPE_CHECKING:
from videocaptioner.core.asr.asr_data import ASRData
Expand Down Expand Up @@ -104,9 +105,7 @@ def video2audio(input_file: str, output: str = "", audio_track_index: int = 0) -
check=True,
encoding="utf-8",
errors="replace",
creationflags=(
getattr(subprocess, "CREATE_NO_WINDOW", 0) if os.name == "nt" else 0
),
**get_subprocess_kwargs(), # Windows 隐藏控制台窗口
)
if result.returncode == 0 and Path(output).is_file():
logger.debug("Audio conversion complete")
Expand Down Expand Up @@ -136,9 +135,7 @@ def check_cuda_available() -> bool:
["ffmpeg", "-hwaccels"],
capture_output=True,
text=True,
creationflags=(
getattr(subprocess, "CREATE_NO_WINDOW", 0) if os.name == "nt" else 0
),
**get_subprocess_kwargs(), # Windows 隐藏控制台窗口
)
if "cuda" not in result.stdout.lower():
logger.debug("CUDA not in FFmpeg hwaccels list")
Expand All @@ -149,9 +146,7 @@ def check_cuda_available() -> bool:
["ffmpeg", "-hide_banner", "-init_hw_device", "cuda"],
capture_output=True,
text=True,
creationflags=(
getattr(subprocess, "CREATE_NO_WINDOW", 0) if os.name == "nt" else 0
),
**get_subprocess_kwargs(), # Windows 隐藏控制台窗口
)

# 如果stderr中包含"Cannot load cuda" 或 "Failed to load"等Error output,说明CUDA不可用
Expand Down Expand Up @@ -232,11 +227,7 @@ def add_subtitles(
text=True,
encoding="utf-8",
errors="replace",
creationflags=(
getattr(subprocess, "CREATE_NO_WINDOW", 0)
if os.name == "nt"
else 0
),
**get_subprocess_kwargs(), # Windows 隐藏控制台窗口
)
logger.debug("Soft subtitle added")
except subprocess.CalledProcessError as e:
Expand Down Expand Up @@ -301,11 +292,7 @@ def add_subtitles(
text=True,
encoding="utf-8",
errors="replace",
creationflags=(
getattr(subprocess, "CREATE_NO_WINDOW", 0)
if os.name == "nt"
else 0
),
**get_subprocess_kwargs(), # Windows 隐藏控制台窗口
)

# 实时Reading输出并调用回调函数
Expand Down Expand Up @@ -390,9 +377,7 @@ def get_video_info(
text=True,
encoding="utf-8",
errors="replace",
creationflags=(
getattr(subprocess, "CREATE_NO_WINDOW", 0) if os.name == "nt" else 0
),
**get_subprocess_kwargs(), # Windows 隐藏控制台窗口
)
info = result.stderr

Expand Down Expand Up @@ -514,9 +499,7 @@ def _extract_thumbnail(video_path: str, seek_time: float, thumbnail_path: str) -
text=True,
encoding="utf-8",
errors="replace",
creationflags=(
getattr(subprocess, "CREATE_NO_WINDOW", 0) if os.name == "nt" else 0
),
**get_subprocess_kwargs(), # Windows 隐藏控制台窗口
)
return result.returncode == 0

Expand Down
4 changes: 2 additions & 2 deletions videocaptioner/ui/components/FasterWhisperSettingWidget.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
TranscribeLanguageEnum,
VadMethodEnum,
)
from videocaptioner.core.utils.platform_utils import open_folder
from videocaptioner.core.utils.platform_utils import get_subprocess_kwargs, open_folder
from videocaptioner.ui.common.config import cfg
from videocaptioner.ui.components.LineEditSettingCard import LineEditSettingCard
from videocaptioner.ui.components.SpinBoxSettingCard import DoubleSpinBoxSettingCard
Expand Down Expand Up @@ -166,7 +166,7 @@ def run(self):
subprocess.run(
["7z", "x", self.zip_file, f"-o{self.extract_path}", "-y"],
check=True,
creationflags=subprocess.CREATE_NO_WINDOW if os.name == "nt" else 0,
**get_subprocess_kwargs(), # Windows 隐藏控制台窗口
)
# 删除压缩包
os.remove(self.zip_file)
Expand Down
13 changes: 3 additions & 10 deletions videocaptioner/ui/view/batch_process_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,10 @@ def __init__(self, parent=None):
super().__init__(parent=parent)
self.setObjectName("batchProcessInterface")
self.setWindowTitle(self.tr("批量处理"))

# 启用拖放支持,窗口级拖放由 MainWindow 统一处理
self.setAcceptDrops(True)
Comment thread
hjkl950217 marked this conversation as resolved.
Outdated

self.batch_thread = BatchProcessThread()

self.init_ui()
Expand Down Expand Up @@ -163,16 +166,6 @@ def on_add_file_clicked(self):
if files:
self.add_files(files)

def dragEnterEvent(self, event):
if event.mimeData().hasUrls():
event.accept()
else:
event.ignore()

def dropEvent(self, event):
files = [url.toLocalFile() for url in event.mimeData().urls()]
self.add_files(files)

def add_files(self, file_paths):
task_type = BatchTaskType(self.task_type_combo.currentText())

Expand Down
17 changes: 17 additions & 0 deletions videocaptioner/ui/view/main_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ def __init__(self):
# 注册退出处理, 清理进程
atexit.register(self.stop)

# 启用拖放支持
self.setAcceptDrops(True)

def initNavigation(self):
"""初始化导航栏"""
# 添加导航项
Expand Down Expand Up @@ -119,6 +122,20 @@ def initWindow(self):
self.show()
QApplication.processEvents()

def dragEnterEvent(self, event):
"""窗口拖放进入事件,接受文件 URL"""
if event.mimeData().hasUrls():
event.accept()
else:
event.ignore()

def dropEvent(self, event):
"""窗口拖放放下事件,转发文件给当前子界面处理"""
current = self.stackedWidget.currentWidget()
if hasattr(current, "add_files"):
files = [url.toLocalFile() for url in event.mimeData().urls()]
current.add_files(files)
Comment thread
hjkl950217 marked this conversation as resolved.
Outdated
Comment thread
hjkl950217 marked this conversation as resolved.
Outdated

def onGithubDialog(self):
"""打开GitHub"""
w = MessageBox(
Expand Down
Loading