Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
15 changes: 14 additions & 1 deletion backend/config.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@

import os
import platform
from pathlib import Path
from qfluentwidgets import (qconfig, ConfigItem, QConfig, OptionsValidator, BoolValidator, OptionsConfigItem,
EnumSerializer, RangeValidator, RangeConfigItem, ConfigValidator)
Expand Down Expand Up @@ -97,7 +98,19 @@ class Config(QConfig):
# VideoSubFinder 视频解码组件
videoSubFinderDecoder = OptionsConfigItem("Main", "VideoSubFinderDecoder", VideoSubFinderDecoder.OPENCV, OptionsValidator(VideoSubFinderDecoder), EnumSerializer(VideoSubFinderDecoder))

CONFIG_FILE = 'config/config.json'
def get_config_file():
system = platform.system()

if system == 'Windows':
return os.path.join('config', 'config.json')

else:
config_dir = Path.home() / '.config' / 'video-subtitle-extractor'

config_dir.mkdir(parents=True, exist_ok=True)
return str(config_dir / 'config.json')

CONFIG_FILE = get_config_file()
config = Config()
qconfig.load(CONFIG_FILE, config)

Expand Down
29 changes: 25 additions & 4 deletions backend/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,10 @@ def __init__(self, vd_path):
# 通过视频路径获取视频名称
self.vd_name = Path(self.video_path).stem
# 临时存储文件夹
self.temp_output_dir = os.path.join(os.path.dirname(BASE_DIR), 'output', str(self.vd_name))
if platform.system() == 'Windows':
self.temp_output_dir = os.path.join(os.path.dirname(BASE_DIR), 'output', str(self.vd_name))
else:
self.temp_output_dir = os.path.join(os.path.expanduser('~'), '.cache', 'video-subtitle-extractor', str(self.vd_name))
# 视频帧总数
self.frame_count = self.video_cap.get(cv2.CAP_PROP_FRAME_COUNT)
# 视频帧率
Expand Down Expand Up @@ -452,8 +455,26 @@ def vsf_output(out, ):
if platform.system() == 'Windows':
path_vsf = os.path.join(BASE_DIR, 'subfinder', 'windows', 'VideoSubFinderWXW.exe')
else:
path_vsf = os.path.join(BASE_DIR, 'subfinder', 'linux', 'VideoSubFinderCli.run')
os.chmod(path_vsf, 0o775)
# Create a writable copy of subfinder in cache directory
import shutil
cache_subfinder_dir = os.path.join(os.path.expanduser('~'), '.cache', 'video-subtitle-extractor', 'subfinder', 'linux')
original_subfinder_dir = os.path.join(BASE_DIR, 'subfinder', 'linux')

# To ensure it's a clean copy with correct permissions, remove old one.
if os.path.exists(cache_subfinder_dir):
shutil.rmtree(cache_subfinder_dir)

shutil.copytree(original_subfinder_dir, cache_subfinder_dir)

# Make copied directory and all its contents writable by user
os.chmod(cache_subfinder_dir, 0o775)
for root, dirs, files in os.walk(cache_subfinder_dir):
for name in dirs:
os.chmod(os.path.join(root, name), 0o775)
for name in files:
os.chmod(os.path.join(root, name), 0o775)

path_vsf = os.path.join(cache_subfinder_dir, 'VideoSubFinderCli.run')
# :图像上半部分所占百分比,取值【0-1】
top_end = 1 - self.sub_area.ymin / self.frame_height
# bottom_end:图像下半部分所占百分比,取值【0-1】
Expand Down Expand Up @@ -496,7 +517,7 @@ def vsf_output(out, ):
self.vsf_running = True
try:
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=1,
close_fds='posix' in sys.builtin_module_names, shell=True, creationflags=subprocess.CREATE_NEW_PROCESS_GROUP)
close_fds='posix' in sys.builtin_module_names, shell=True)
Thread(target=vsf_output, daemon=True, args=(p.stderr,)).start()
ProcessManager.instance().add_process(p)
self.manage_process(p.pid)
Expand Down
1 change: 0 additions & 1 deletion backend/subfinder/linux/VideoSubFinderCli.run
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#!/bin/sh
cd ${0%/*}
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD:/lib64
chmod +x ./VideoSubFinderCli
./VideoSubFinderCli "$@"
21 changes: 16 additions & 5 deletions backend/tools/process_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,20 @@ def terminate_by_pid(self, pid):
subprocess.run(['taskkill', '/F', '/T', '/PID', str(pid)],
stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=3)
else:
subprocess.run(['pkill', '-9', '-P', str(pid)],
stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=2)
subprocess.run(['kill', '-9', str(pid)],
stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=3)
# NixOS requires absolute paths for commands in subprocess
pkill_path = '/run/current-system/sw/bin/pkill'
kill_path = '/run/current-system/sw/bin/kill'
if os.path.exists(pkill_path):
subprocess.run([pkill_path, '-9', '-P', str(pid)],
stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=2)
if os.path.exists(kill_path):
subprocess.run([kill_path, '-9', str(pid)],
stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=3)
else:
# Fallback for linux
subprocess.run(['pkill', '-9', '-P', str(pid)],
stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=2)
subprocess.run(['kill', '-9', str(pid)],
stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=3)
except Exception as e:
print(f"Error forcibly terminating process with PID {pid}: {str(e)}")
print(f"Error forcibly terminating process with PID {pid}: {str(e)}")
47 changes: 39 additions & 8 deletions backend/tools/reformat.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
import pysrt
import wordsegment as ws
import re
import shutil
import platform

def execute(path, lang='en'):
try:
Expand Down Expand Up @@ -43,14 +45,43 @@ def execute(path, lang='en'):
"needn't", "oughtn't", "shan't", "shouldn't", "usedn't", "won't", "wouldn't", "that's", "what's", "it'll"]
verb_form_map = {}

typo_map_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'configs', 'typoMap.json')
try:
with open(typo_map_path, 'r', encoding='utf-8') as load_f:
typo_map = json.load(load_f)
except Exception as e:
print(f"Error: Failed to load typoMap.json: {str(e)}")
print(traceback.format_exc())
typo_map = {}
if platform.system() == 'Windows':
typo_map_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'configs', 'typoMap.json')
try:
with open(typo_map_path, 'r', encoding='utf-8') as load_f:
typo_map = json.load(load_f)
except Exception as e:
print(f"Error: Failed to load typoMap.json: {str(e)}")
print(traceback.format_exc())
typo_map = {}
else:
config_dir = os.path.join(os.path.expanduser('~'), '.config', 'video-subtitle-extractor')
user_typo_map_path = os.path.join(config_dir, 'typoMap.json')
default_typo_map_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'configs', 'typoMap.json')

if os.path.exists(user_typo_map_path):
typo_map_path = user_typo_map_path
elif os.path.exists(default_typo_map_path):
os.makedirs(config_dir, exist_ok=True)
import shutil
shutil.copy2(default_typo_map_path, user_typo_map_path)
os.chmod(user_typo_map_path, 0o664) # Make the file writable for the user
typo_map_path = user_typo_map_path
print(f"Created user typoMap.json at: {user_typo_map_path}")
else:
typo_map_path = None

if typo_map_path:
try:
with open(typo_map_path, 'r', encoding='utf-8') as load_f:
typo_map = json.load(load_f)
print(f"Loaded typoMap.json from: {typo_map_path}")
except Exception as e:
print(f"Error: Failed to load typoMap.json: {str(e)}")
print(traceback.format_exc())
typo_map = {}
else:
print("No typoMap.json found, using empty typo corrections")

for verb in verb_forms:
verb_form_map[verb.replace("'", "").lower()] = verb
Expand Down
Binary file added design/vse.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 4 additions & 2 deletions gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ def __init__(self):
# 任何尺寸下都悬浮展开, 防止窗口撑大
self.navigationInterface.panel.minimumExpandWidth = 999999
# 设置窗口图标
self.setWindowIcon(QtGui.QIcon("design/vse.ico"))
script_dir = os.path.dirname(os.path.abspath(__file__))
icon_path = os.path.abspath(os.path.join(script_dir, "design", "vse.png"))
self.setWindowIcon(QtGui.QIcon(icon_path))
self.setWindowTitle(tr['SubtitleExtractorGUI']['Title'] + " v" + VERSION)
# 创建界面布局
self._create_layout()
Expand Down Expand Up @@ -187,4 +189,4 @@ def keyPressEvent(self, event):
animation.setStartValue(0.0)
animation.setEndValue(1.0)
animation.start()
app.exec()
app.exec()
3 changes: 2 additions & 1 deletion ui/icon/my_fluent_icon.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import os
from enum import Enum

from qfluentwidgets import getIconColor, Theme, FluentIconBase
Expand All @@ -8,4 +9,4 @@ class MyFluentIcon(FluentIconBase, Enum):

def path(self, theme=Theme.AUTO):
# getIconColor() return "white" or "black" according to current theme
return f'./ui/icon/{self.value}_{getIconColor(theme)}.svg'
return os.path.abspath(os.path.join(os.path.dirname(__file__), f'{self.value}_{getIconColor(theme)}.svg'))