工作流,更新双GPU加速

This commit is contained in:
Jerry Yan 2022-05-03 11:07:45 +08:00
parent a43b47c0b8
commit 12504b9cf5
3 changed files with 427 additions and 98 deletions

107
config.py Normal file
View File

@ -0,0 +1,107 @@
import configparser
import os.path
# [danmaku]
# exec
DANMAKU_FACTORY_EXEC = "DanmakuFactory"
# speed
DANMAKU_SPEED = 12
# font
DEFAULT_FONT_NAME = "Sarasa Term SC"
# resolution
VIDEO_RESOLUTION = "1280x720"
# [ffmpeg]
# exec
FFMPEG_EXEC = "ffmpeg"
# nvidia_gpu
FFMPEG_USE_NVIDIA_GPU = True
# intel_gpu
FFMPEG_USE_INTEL_GPU = True
# bitrate
VIDEO_BITRATE = "2.5M"
# [video]
# title
VIDEO_TITLE = "【永恒de草薙直播录播】直播于 {}"
# [clip]
# each_sec
VIDEO_CLIP_EACH_SEC = 6000
# overflow_sec
VIDEO_CLIP_OVERFLOW_SEC = 5
# [recorder]
# bili_dir
BILILIVE_RECORDER_DIRECTORY = "./"
# xigua_dir
XIGUALIVE_RECORDER_DIRECTORY = "./"
def load_config():
if not os.path.exists("config.ini"):
write_config()
return False
config = configparser.ConfigParser()
config.read("config.ini", encoding="utf-8")
if config.has_section("danmaku"):
section = config['danmaku']
global DANMAKU_FACTORY_EXEC, DANMAKU_SPEED, DEFAULT_FONT_NAME, VIDEO_RESOLUTION
DANMAKU_FACTORY_EXEC = section.get('exec', DANMAKU_FACTORY_EXEC)
DANMAKU_SPEED = section.getfloat('speed', DANMAKU_SPEED)
DEFAULT_FONT_NAME = section.get('font', DEFAULT_FONT_NAME)
VIDEO_RESOLUTION = section.get('resolution', VIDEO_RESOLUTION)
if config.has_section("video"):
section = config['video']
global VIDEO_TITLE
VIDEO_TITLE = section.get('title', VIDEO_TITLE)
if config.has_section("clip"):
section = config['clip']
global VIDEO_CLIP_EACH_SEC, VIDEO_CLIP_OVERFLOW_SEC
VIDEO_CLIP_EACH_SEC = section.getfloat('each_sec', VIDEO_CLIP_EACH_SEC)
VIDEO_CLIP_OVERFLOW_SEC = section.getfloat('overflow_sec', VIDEO_CLIP_OVERFLOW_SEC)
if config.has_section("ffmpeg"):
section = config['ffmpeg']
global FFMPEG_EXEC, FFMPEG_USE_NVIDIA_GPU, FFMPEG_USE_INTEL_GPU, VIDEO_BITRATE
FFMPEG_EXEC = section.get('exec', FFMPEG_EXEC)
FFMPEG_USE_NVIDIA_GPU = section.getboolean('nvidia_gpu', FFMPEG_USE_NVIDIA_GPU)
FFMPEG_USE_INTEL_GPU = section.getboolean('intel_gpu', FFMPEG_USE_INTEL_GPU)
VIDEO_BITRATE = section.get('bitrate', VIDEO_BITRATE)
if config.has_section("recorder"):
global BILILIVE_RECORDER_DIRECTORY, XIGUALIVE_RECORDER_DIRECTORY
section = config['recorder']
BILILIVE_RECORDER_DIRECTORY = section.get('bili_dir', BILILIVE_RECORDER_DIRECTORY)
XIGUALIVE_RECORDER_DIRECTORY = section.get('xigua_dir', XIGUALIVE_RECORDER_DIRECTORY)
return True
def get_config():
config = {
'danmaku': {
'exec': DANMAKU_FACTORY_EXEC,
'speed': DANMAKU_SPEED,
'font': DEFAULT_FONT_NAME,
'resolution': VIDEO_RESOLUTION,
},
'clip': {
'each_sec': VIDEO_CLIP_EACH_SEC,
'overflow_sec': VIDEO_CLIP_OVERFLOW_SEC,
},
'ffmpeg': {
'exec': FFMPEG_EXEC,
'nvidia_gpu': FFMPEG_USE_NVIDIA_GPU,
'intel_gpu': FFMPEG_USE_INTEL_GPU,
'bitrate': VIDEO_BITRATE,
},
}
return config
def write_config():
config = configparser.ConfigParser()
_config = get_config()
for _i in _config:
config[_i] = _config[_i]
with open("config.ini", "w", encoding="utf-8") as f:
config.write(f)
return True
load_config()

View File

@ -1,22 +1,19 @@
# 工作流
import datetime
import os.path
import platform
import subprocess
import sys
from typing import Optional, IO, AnyStr
import threading
from typing import Optional, IO, Union
from datetime import datetime, timedelta
from PyQt5 import QtGui
from PyQt5.QtCore import Qt, QThread, pyqtSignal
from PyQt5.QtWidgets import QWidget, QLabel, QApplication, QFrame, QVBoxLayout, QPushButton, \
QSizePolicy, QMessageBox
from danmaku_xml_helper import get_file_start, diff_danmaku_files
DANMAKU_PATH = "workspace"
DANMAKU_FACTORY_CLI = "DFCLI.exe"
FFMPEG_TOOL = "ffmpeg.exe"
SPLIT_PATH = "split"
SPLIT_TOOL = "quick_split_video.exe"
HOME_PATH = os.path.realpath(".")
QSizePolicy, QMessageBox, QProgressBar
from danmaku_xml_helper import get_file_start, diff_danmaku_files, danmaku_to_subtitle
from config import load_config, FFMPEG_EXEC, DANMAKU_FACTORY_EXEC, FFMPEG_USE_INTEL_GPU, FFMPEG_USE_NVIDIA_GPU, \
VIDEO_BITRATE, VIDEO_CLIP_EACH_SEC, VIDEO_CLIP_OVERFLOW_SEC
class Job:
@ -55,11 +52,14 @@ class WorkLabel(QLabel):
self.adjustSize()
def mouseDoubleClickEvent(self, a0: QtGui.QMouseEvent) -> None:
if self.workVideo is None or self.finished:
if self.finished:
self.finished = False
elif self.workVideo is None:
self.parent().labelDestroy.emit(self)
return self.deleteLater()
self.workVideo = None
self.workDanmaku = []
else:
self.workVideo = None
self.workDanmaku = []
self.init_ui()
def init_ui(self):
@ -149,19 +149,26 @@ class HomePage(QWidget):
labelDestroy = pyqtSignal(WorkLabel)
processCurTime = pyqtSignal(str)
processSpeed = pyqtSignal(str)
processCurTime2 = pyqtSignal(str)
processSpeed2 = pyqtSignal(str)
def __init__(self):
super(HomePage, self).__init__()
self.layout = None
self.labels: list[WorkLabel] = []
self.worker: Optional[WorkerThread] = None
self.btn_start: Optional[QPushButton] = None
self.worker: WorkerThread
self.btn_start: QPushButton
self.showMessageBox.connect(self.on_show_message_box_info)
self.labelDestroy.connect(self.on_label_destroy)
self.processCurTime.connect(self.on_process_cur_time_change)
self.processSpeed.connect(self.on_process_speed_change)
self.processCurTime2.connect(self.on_process_cur_time2_change)
self.processSpeed2.connect(self.on_process_speed2_change)
self.process_cur_time = "-"
self.process_speed = "-"
self.process_cur_time2 = ""
self.process_speed2 = ""
self.cur_clip_duration = 0
self.init_ui()
def init_ui(self):
@ -225,21 +232,46 @@ class HomePage(QWidget):
self.btn_start.setDisabled(True)
self.btn_start.setText("正在处理")
def on_process_cur_time_change(self, s: str)->None:
def on_process_cur_time_change(self, s: str) -> None:
if self.process_cur_time == s:
return
self.process_cur_time = s
self.update_btn_process_text()
def on_process_speed_change(self, s: str)->None:
def on_process_speed_change(self, s: str) -> None:
if self.process_speed == s:
return
self.process_speed = s
self.update_btn_process_text()
def on_process_cur_time2_change(self, s: str) -> None:
if self.process_cur_time2 == s:
return
self.process_cur_time2 = s
self.update_btn_process_text()
def on_process_speed2_change(self, s: str) -> None:
if self.process_speed2 == s:
return
self.process_speed2 = s
self.update_btn_process_text()
def update_btn_process_text(self):
self.btn_start.setText("{}@{}".format(self.process_cur_time, self.process_speed))
_t = []
if self.process_cur_time != "" and self.process_speed != "":
_t.append("{}@{}".format(self.process_cur_time, self.process_speed))
if self.process_cur_time2 != "" and self.process_speed2 != "":
_t.append("{}@{}".format(self.process_cur_time2, self.process_speed2))
if len(_t) == 0:
self.btn_start.setText("Working")
else:
self.btn_start.setText("|".join(_t))
def on_worker_stop(self):
self.btn_start.setDisabled(False)
self.btn_start.setText("开始")
self.worker = None
self.handle_do()
class WorkerThread(QThread):
@ -248,89 +280,257 @@ class WorkerThread(QThread):
self.app = app
self.label = label
def get_video_real_duration(self, filename) -> float:
ffmpeg_process = subprocess.Popen([
FFMPEG_EXEC, "-hide_banner", "-progress", "-", "-loglevel", "error", "-i", filename, "-c", "copy", "-f",
"null", "-"
], **subprocess_args(True))
_duration_str = self.handle_ffmpeg_output(ffmpeg_process.stdout)
ffmpeg_process.wait()
return duration_str_to_float(_duration_str)
def run(self) -> None:
self.label.start_running()
job = self.label.get_job()
if job.type == Job.DANMAKU_ENCODE:
self.run_danmaku_encode(job)
elif job.type == Job.PURE_SPLIT:
self.run_pure_split(job)
self.quick_split_video(job.video)
self.label.stop_running()
def run_danmaku_encode(self, job: Job):
os.chdir(DANMAKU_PATH)
base_danmaku = job.danmaku.pop(0)
time_shift = 0
base_start_ts = get_file_start(base_danmaku)
new_subtitle_name = os.path.basename(base_danmaku) + ".ass"
subprocess.Popen((
DANMAKU_FACTORY_CLI,
"-r", "1280x720", "-s", "10", "-f", "5",
"-S", "40", "-N", "\"Sarasa Term SC\"", "--showmsgbox", "FALSE",
"-O", "255", "-L", "1", "-D", "0",
"-o", "ass", new_subtitle_name, "-i", base_danmaku, "-t", str(time_shift)
), **subprocess_args()).wait()
print(new_subtitle_name)
new_subtitle_name = danmaku_to_subtitle(base_danmaku, time_shift)
job.subtitles.append(new_subtitle_name)
for danmaku in job.danmaku:
time_shift = diff_danmaku_files(base_danmaku, danmaku)
new_subtitle_name = os.path.basename(danmaku) + ".ass"
subprocess.Popen((
DANMAKU_FACTORY_CLI,
"-r", "1280x720", "-s", "10", "-f", "5",
"-S", "40", "-N", "\"Sarasa Term SC\"", "--showmsgbox", "FALSE",
"-O", "255", "-L", "1", "-D", "0",
"-o", "ass", new_subtitle_name, "-i", danmaku, "-t", str(time_shift)
), **subprocess_args()).wait()
print(new_subtitle_name)
new_subtitle_name = danmaku_to_subtitle(danmaku, time_shift)
job.subtitles.append(new_subtitle_name)
# 压制
base_start = datetime.datetime.fromtimestamp(base_start_ts)
new_file_name = base_start.strftime("%Y%m%d_%H%M.flv")
encode_process = subprocess.Popen([
FFMPEG_TOOL, "-hide_banner", "-progress", "-", "-v", "0", "-y",
"-i", job.video, "-vf", ",".join("subtitles=%s" % i for i in job.subtitles) + ",hwupload_cuda",
"-c:a", "copy", "-c:v", "h264_nvenc", "-f", "mp4",
"-preset:v", "fast", "-profile:v", "high", "-level", "4.1",
"-b:v", "2.5M", "-bufsize:v", "5M", "-rc:v", "vbr_hq", "-bf:v", "3",
"-qmin", "10", "-qmax", "52", "-crf", "16",
# "-t", "10",
os.path.join(HOME_PATH, SPLIT_PATH, new_file_name)
], **subprocess_args(True))
self.handle_ffmpeg_output(encode_process.stdout)
encode_process.wait()
split_files = self.multi_gpu_encode_video_with_subtitles(job.video, job.subtitles, base_start_ts)
for file in split_files:
self.quick_split_video(file)
for _f in job.subtitles:
if os.path.isfile(_f):
os.remove(_f)
os.chdir(HOME_PATH)
os.chdir(SPLIT_PATH)
split_process = subprocess.Popen([
SPLIT_TOOL, new_file_name
], **subprocess_args(True))
self.handle_ffmpeg_output(split_process.stdout)
split_process.wait()
os.chdir(HOME_PATH)
def run_pure_split(self, job: Job):
os.chdir(SPLIT_PATH)
split_process = subprocess.Popen([
SPLIT_TOOL, job.video
], **subprocess_args(True))
self.handle_ffmpeg_output(split_process.stdout)
split_process.wait()
os.chdir(HOME_PATH)
def encode_video_with_subtitles(self, orig_filename: str, subtitles: list[str], new_filename: str):
if FFMPEG_USE_NVIDIA_GPU:
print("[+]Use Nvidia NvEnc Acceleration")
encode_process = subprocess.Popen([
FFMPEG_EXEC, "-hide_banner", "-progress", "-", "-loglevel", "error", "-y",
"-i", orig_filename, "-vf",
",".join("subtitles=%s" % i for i in subtitles) + ",hwupload_cuda",
"-c:a", "copy", "-c:v", "h264_nvenc",
"-f", "mp4", "-preset:v", "fast", "-profile:v", "high", "-level", "4.1",
"-b:v", VIDEO_BITRATE, "-rc:v", "vbr", "-tune:v", "hq",
"-qmin", "10", "-qmax", "32", "-crf", "16",
# "-t", "10",
new_filename
], stdout=subprocess.PIPE)
elif FFMPEG_USE_INTEL_GPU:
if platform.system().lower() == "windows":
print("[+]Use Intel QSV Acceleration")
encode_process = subprocess.Popen([
FFMPEG_EXEC, "-hide_banner", "-progress", "-", "-loglevel", "error", "-y",
"-hwaccel", "qsv", "-i", orig_filename, "-vf",
",".join("subtitles=%s" % i for i in subtitles),
"-c:a", "copy", "-c:v", "h264_qsv",
"-f", "mp4", "-preset:v", "fast", "-profile:v", "high", "-level", "4.1",
"-b:v", VIDEO_BITRATE, "-rc:v", "vbr", "-tune:v", "hq",
"-qmin", "10", "-qmax", "32", "-crf", "16",
# "-t", "10",
new_filename
], stdout=subprocess.PIPE)
else:
print("[+]Use Intel VAAPI Acceleration")
encode_process = subprocess.Popen([
FFMPEG_EXEC, "-hide_banner", "-progress", "-", "-loglevel", "error", "-y",
"-hwaccel", "vaapi", "-i", orig_filename, "-vf",
",".join("subtitles=%s" % i for i in subtitles) + ",hwupload",
"-c:a", "copy", "-c:v", "h264_vaapi",
"-f", "mp4", "-preset:v", "fast", "-profile:v", "high", "-level", "4.1",
"-b:v", VIDEO_BITRATE, "-rc:v", "vbr", "-tune:v", "hq",
"-qmin", "10", "-qmax", "32", "-crf", "16",
# "-t", "10",
new_filename
], stdout=subprocess.PIPE)
else:
print("[+]Use CPU Encode")
encode_process = subprocess.Popen([
FFMPEG_EXEC, "-hide_banner", "-progress", "-", "-loglevel", "error", "-y",
"-i", orig_filename, "-vf",
",".join("subtitles=%s" % i for i in subtitles),
"-c:a", "copy", "-c:v", "h264",
"-f", "mp4", "-preset:v", "fast", "-profile:v", "high", "-level", "4.1",
"-b:v", VIDEO_BITRATE, "-rc:v", "vbr",
"-qmin", "10", "-qmax", "32", "-crf", "16",
# "-t", "10",
new_filename
], stdout=subprocess.PIPE)
self.handle_ffmpeg_output(encode_process.stdout)
return encode_process.wait()
def handle_ffmpeg_output(self, stderr: IO[bytes]) -> None:
def multi_gpu_encode_video_with_subtitles(self, orig_filename: str, subtitles: list[str], base_ts: float):
new_filename = base_ts_to_filename(base_ts)
if not (FFMPEG_USE_NVIDIA_GPU and FFMPEG_USE_INTEL_GPU):
print("[!]Not Enabled Both GPU")
self.encode_video_with_subtitles(orig_filename, subtitles, new_filename)
return [new_filename]
duration = self.get_video_real_duration(orig_filename)
print("[>]Duration:", duration)
if duration > (VIDEO_CLIP_EACH_SEC * 5):
# qsv 压制前2段剩余交由nvenc压制
new_filename0 = base_ts_to_filename(base_ts)
new_filename1 = base_ts_to_filename(base_ts + VIDEO_CLIP_EACH_SEC * 2)
print("[+]Use Intel QSV Acceleration")
encode_process0 = subprocess.Popen([
FFMPEG_EXEC, "-hide_banner", "-progress", "-", "-loglevel", "error", "-y",
"-hwaccel", "qsv", "-t", str(VIDEO_CLIP_EACH_SEC * 2 + (VIDEO_CLIP_OVERFLOW_SEC * 0.8)), "-i",
orig_filename, "-vf",
",".join("subtitles=%s" % i for i in subtitles),
"-c:a", "copy", "-c:v", "h264_qsv",
"-f", "mp4", "-preset:v", "fast", "-profile:v", "high", "-level", "4.1",
"-b:v", VIDEO_BITRATE, "-rc:v", "vbr", "-tune:v", "hq",
"-qmin", "10", "-qmax", "32", "-crf", "16",
# "-t", "10",
new_filename0
], **subprocess_args(True))
print("[+]Use Nvidia NvEnc Acceleration")
encode_process1 = subprocess.Popen([
FFMPEG_EXEC, "-hide_banner", "-progress", "-", "-loglevel", "error", "-y",
"-ss", str(VIDEO_CLIP_EACH_SEC * 2), "-copyts", "-i", orig_filename, "-vf",
",".join("subtitles=%s" % i for i in subtitles),
"-c:a", "copy", "-c:v", "h264_nvenc", "-ss", str(VIDEO_CLIP_EACH_SEC * 2),
"-f", "mp4", "-preset:v", "fast", "-profile:v", "high", "-level", "4.1",
"-b:v", VIDEO_BITRATE, "-rc:v", "vbr", "-tune:v", "hq",
"-qmin", "10", "-qmax", "32", "-crf", "16",
# "-t", "10",
new_filename1
], **subprocess_args(True))
threading.Thread(target=self.handle_ffmpeg_output, args=(encode_process0.stdout, True,)).start()
threading.Thread(target=self.handle_ffmpeg_output, args=(encode_process1.stdout,)).start()
encode_process0.wait()
encode_process1.wait()
return [new_filename0, new_filename1]
elif duration > (VIDEO_CLIP_EACH_SEC * 3):
# 至少也要能切2片才用双GPU加速
new_filename0 = base_ts_to_filename(base_ts, True)
new_filename1 = base_ts_to_filename(base_ts + VIDEO_CLIP_EACH_SEC)
print("[+]Use Intel QSV Acceleration")
encode_process0 = subprocess.Popen([
FFMPEG_EXEC, "-hide_banner", "-progress", "-", "-loglevel", "error", "-y",
"-hwaccel", "qsv", "-t", str(VIDEO_CLIP_EACH_SEC + (VIDEO_CLIP_OVERFLOW_SEC * 0.8)), "-i",
orig_filename, "-vf",
",".join("subtitles=%s" % i for i in subtitles),
"-c:a", "copy", "-c:v", "h264_qsv",
"-f", "mp4", "-preset:v", "fast", "-profile:v", "high", "-level", "4.1",
"-b:v", VIDEO_BITRATE, "-rc:v", "vbr", "-tune:v", "hq",
"-qmin", "10", "-qmax", "32", "-crf", "16",
# "-t", "10",
new_filename0
], **subprocess_args(True))
print("[+]Use Nvidia NvEnc Acceleration")
encode_process1 = subprocess.Popen([
FFMPEG_EXEC, "-hide_banner", "-progress", "-", "-loglevel", "error", "-y",
"-ss", str(VIDEO_CLIP_EACH_SEC), "-copyts", "-i", orig_filename, "-vf",
",".join("subtitles=%s" % i for i in subtitles),
"-c:a", "copy", "-c:v", "h264_nvenc", "-ss", str(VIDEO_CLIP_EACH_SEC),
"-f", "mp4", "-preset:v", "fast", "-profile:v", "high", "-level", "4.1",
"-b:v", VIDEO_BITRATE, "-rc:v", "vbr", "-tune:v", "hq",
"-qmin", "10", "-qmax", "32", "-crf", "16",
# "-t", "10",
new_filename1
], **subprocess_args(True))
threading.Thread(target=self.handle_ffmpeg_output, args=(encode_process0.stdout, True,)).start()
threading.Thread(target=self.handle_ffmpeg_output, args=(encode_process1.stdout,)).start()
encode_process0.wait()
encode_process1.wait()
return [new_filename1]
else:
self.encode_video_with_subtitles(orig_filename, subtitles, new_filename)
return [new_filename]
def quick_split_video(self, file):
if not os.path.isfile(file):
raise FileNotFoundError(file)
file_name = os.path.split(file)[-1]
_create_dt = os.path.splitext(file_name)[0]
create_dt = datetime.strptime(_create_dt, "%Y%m%d_%H%M")
duration = self.get_video_real_duration(file)
current_sec = 0
while current_sec < duration:
current_dt = (create_dt + timedelta(seconds=current_sec)).strftime("%Y%m%d_%H%M_")
print("CUR_DT", current_dt)
print("BIAS_T", current_sec)
split_process = subprocess.Popen([
FFMPEG_EXEC, "-y", "-hide_banner", "-progress", "-", "-loglevel", "error",
"-ss", str(current_sec),
"-i", file_name, "-c", "copy", "-f", "mp4",
"-t", str(VIDEO_CLIP_EACH_SEC + VIDEO_CLIP_OVERFLOW_SEC),
"{}.mp4".format(current_dt)
], **subprocess_args(True))
self.handle_ffmpeg_output(split_process.stdout)
current_sec += VIDEO_CLIP_EACH_SEC
def handle_ffmpeg_output(self, stdout: Optional[IO[bytes]], second=False) -> str:
out_time = "0:0:0.0"
if stdout is None:
return out_time
speed = "0"
while True:
line = stderr.readline()
line = stdout.readline()
if line == b"":
break
if line.strip() == b"progress=end":
# 处理完毕
break
if line.startswith(b"out_time="):
cur_time = line.replace(b"out_time=", b"").decode()
self.app.processCurTime.emit(cur_time.strip())
out_time = line.replace(b"out_time=", b"").decode().strip()
if second:
self.app.processCurTime2.emit(out_time)
else:
self.app.processCurTime.emit(out_time)
if line.startswith(b"speed="):
speed = line.replace(b"speed=", b"").decode()
self.app.processSpeed.emit(speed.strip())
speed = line.replace(b"speed=", b"").decode().strip()
if second:
self.app.processSpeed2.emit(speed)
else:
self.app.processSpeed.emit(speed)
if second:
self.app.processSpeed2.emit("")
self.app.processCurTime2.emit("")
else:
self.app.processSpeed.emit("")
self.app.processCurTime.emit("")
return out_time
class NvencWorkerThread(QThread):
...
class IntelWorkerThread(QThread):
...
class SplitWorkerThread(QThread):
...
def duration_str_to_float(duration_str) -> float:
_duration = datetime.strptime(duration_str, "%H:%M:%S.%f") - datetime(1900, 1, 1)
return _duration.total_seconds()
def base_ts_to_filename(start_ts: float, is_mp4=False) -> str:
base_start = datetime.fromtimestamp(start_ts)
if is_mp4:
return base_start.strftime("%Y%m%d_%H%M.mp4")
else:
return base_start.strftime("%Y%m%d_%H%M.flv")
# Create a set of arguments which make a ``subprocess.Popen`` (and
@ -384,34 +584,38 @@ def subprocess_args(include_stdout=True):
return ret
def check_exec(name: Union[os.PathLike[str], str]) -> bool:
if is_windows():
check_process = subprocess.Popen([
"where.exe", name
], stdout=subprocess.PIPE)
check_process.wait()
return len(check_process.stdout.readlines()) > 0
elif is_linux():
check_process = subprocess.Popen([
"which", name
])
check_process.wait()
return check_process.returncode == 0
else:
return False
def is_windows() -> bool:
return platform.system().lower() == "windows"
def is_linux() -> bool:
return platform.system().lower() == "linux"
def check_all_prerequisite():
if not os.path.isdir(DANMAKU_PATH):
os.mkdir(DANMAKU_PATH)
if not os.path.isdir(SPLIT_PATH):
os.mkdir(SPLIT_PATH)
os.chdir(DANMAKU_PATH)
validate_process = subprocess.Popen([
"where.exe", DANMAKU_FACTORY_CLI
], **subprocess_args(True))
if len(validate_process.stdout.readlines()) == 0:
if not check_exec(DANMAKU_FACTORY_EXEC):
input("弹幕处理工具不存在")
exit(1)
os.chdir(HOME_PATH)
os.chdir(SPLIT_PATH)
validate_process = subprocess.Popen([
"where.exe", SPLIT_TOOL
], **subprocess_args(True))
if len(validate_process.stdout.readlines()) == 0:
input("视频分割工具不存在")
exit(1)
os.chdir(HOME_PATH)
validate_process = subprocess.Popen([
"where.exe", FFMPEG_TOOL
], **subprocess_args(True))
if len(validate_process.stdout.readlines()) == 0:
if not check_exec(FFMPEG_EXEC):
input("FFMPEG工具不存在")
exit(1)
os.chdir(HOME_PATH)
def main():
@ -422,4 +626,5 @@ def main():
if __name__ == '__main__':
load_config()
main()

View File

@ -1,10 +1,14 @@
import datetime
import os
import argparse
import subprocess
from hashlib import md5
from typing import Union
from bs4 import BeautifulSoup
from config import DANMAKU_FACTORY_EXEC, VIDEO_RESOLUTION, DANMAKU_SPEED, DEFAULT_FONT_NAME
class NoDanmakuException(Exception):
...
@ -39,6 +43,19 @@ def diff_danmaku_files(base_file: Union[os.PathLike[str], str], file: Union[os.P
return get_file_start(file) - get_file_start(base_file)
def danmaku_to_subtitle(file: Union[os.PathLike[str], str], time_shift: float):
new_subtitle_name = md5(file.encode("utf-8")).hexdigest() + ".ass"
process = subprocess.Popen((
DANMAKU_FACTORY_EXEC, "--ignore-warnings",
"-r", str(VIDEO_RESOLUTION), "-s", str(DANMAKU_SPEED), "-f", "5",
"-S", "40", "-N", str(DEFAULT_FONT_NAME), "--showmsgbox", "FALSE",
"-O", "255", "-L", "1", "-D", "0",
"-o", "ass", new_subtitle_name, "-i", file, "-t", str(time_shift)
))
process.wait()
return new_subtitle_name
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument("base", help="以此为标准")