diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..7a4ebe0
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+/build/
+/dist/
+/venv/
diff --git a/config.py b/config.py
index 6132e75..696e3c6 100644
--- a/config.py
+++ b/config.py
@@ -3,8 +3,12 @@ import os.path
 
 
 # [danmaku]
+# use_danmu2ass
+DANMAKU_USE_DANMU2ASS = False
+# use_danmakufactory
+DANMAKU_USE_DANMAKUFACTORY = True
 # exec
-DANMAKU_FACTORY_EXEC = "DanmakuFactory"
+DANMAKU_EXEC = "DanmakuFactory"
 # speed
 DANMAKU_SPEED = 12
 # font
@@ -16,18 +20,15 @@ VIDEO_RESOLUTION = "1280x720"
 # [ffmpeg]
 # exec
 FFMPEG_EXEC = "ffmpeg"
-# hevc
-FFMPEG_USE_HEVC = False
-# nvidia_gpu
-FFMPEG_USE_NVIDIA_GPU = False
-# intel_gpu
-FFMPEG_USE_INTEL_GPU = False
-# vaapi
-FFMPEG_USE_VAAPI = False
-# bitrate
-VIDEO_BITRATE = "2.5M"
-# gop
-VIDEO_GOP = 60
+# [handbrake]
+# exec
+HANDBRAKE_EXEC = "HandBrakeCli"
+# preset_file
+HANDBRAKE_PRESET_FILE = "handbrake.json"
+# preset
+HANDBRAKE_PRESET = "NvEnc"
+# encopt
+HANDBRAKE_ENCOPT = ""
 # [video]
 # enabled
 VIDEO_ENABLED = False
@@ -66,8 +67,11 @@ def load_config():
     config.read("config.ini", encoding="utf-8")
     if config.has_section("danmaku"):
         section = config['danmaku']
-        global DANMAKU_FACTORY_EXEC, DANMAKU_SPEED, DANMAKU_FONT_NAME, VIDEO_RESOLUTION, DANMAKU_FONT_SIZE
-        DANMAKU_FACTORY_EXEC = section.get('exec', DANMAKU_FACTORY_EXEC)
+        global DANMAKU_EXEC, DANMAKU_SPEED, DANMAKU_FONT_NAME, VIDEO_RESOLUTION, DANMAKU_FONT_SIZE, \
+            DANMAKU_USE_DANMU2ASS, DANMAKU_USE_DANMAKUFACTORY
+        DANMAKU_USE_DANMU2ASS = section.getboolean('use_danmu2ass', DANMAKU_USE_DANMU2ASS)
+        DANMAKU_USE_DANMAKUFACTORY = section.getboolean('use_danmakufactory', DANMAKU_USE_DANMAKUFACTORY)
+        DANMAKU_EXEC = section.get('exec', DANMAKU_EXEC)
         DANMAKU_SPEED = section.getfloat('speed', DANMAKU_SPEED)
         DANMAKU_FONT_NAME = section.get('font', DANMAKU_FONT_NAME)
         DANMAKU_FONT_SIZE = section.getint('font_size', DANMAKU_FONT_SIZE)
@@ -87,15 +91,15 @@ def load_config():
         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_HEVC, FFMPEG_USE_NVIDIA_GPU, FFMPEG_USE_INTEL_GPU, VIDEO_BITRATE, VIDEO_CRF, \
-            VIDEO_GOP, FFMPEG_USE_VAAPI
+        global FFMPEG_EXEC
         FFMPEG_EXEC = section.get('exec', FFMPEG_EXEC)
-        FFMPEG_USE_HEVC = section.getboolean('hevc', FFMPEG_USE_HEVC)
-        FFMPEG_USE_NVIDIA_GPU = section.getboolean('nvidia_gpu', FFMPEG_USE_NVIDIA_GPU)
-        FFMPEG_USE_INTEL_GPU = section.getboolean('intel_gpu', FFMPEG_USE_INTEL_GPU)
-        FFMPEG_USE_VAAPI = section.getboolean('vaapi', FFMPEG_USE_VAAPI)
-        VIDEO_BITRATE = section.get('bitrate', VIDEO_BITRATE)
-        VIDEO_GOP = section.getfloat('gop', VIDEO_GOP)
+    if config.has_section("handbrake"):
+        section = config['handbrake']
+        global HANDBRAKE_EXEC, HANDBRAKE_PRESET_FILE, HANDBRAKE_PRESET, HANDBRAKE_ENCOPT
+        HANDBRAKE_EXEC = section.get('exec', HANDBRAKE_EXEC)
+        HANDBRAKE_PRESET_FILE = section.get('preset_file', HANDBRAKE_PRESET_FILE)
+        HANDBRAKE_PRESET = section.get('preset', HANDBRAKE_PRESET)
+        HANDBRAKE_ENCOPT = section.get('encopt', HANDBRAKE_ENCOPT)
     if config.has_section("recorder"):
         global BILILIVE_RECORDER_DIRECTORY, XIGUALIVE_RECORDER_DIRECTORY, VIDEO_OUTPUT_DIR
         section = config['recorder']
@@ -108,7 +112,9 @@ def load_config():
 def get_config():
     config = {
         'danmaku': {
-            'exec': DANMAKU_FACTORY_EXEC,
+            'exec': DANMAKU_EXEC,
+            'use_danmu2ass': DANMAKU_USE_DANMU2ASS,
+            'use_danmakufactory': DANMAKU_USE_DANMAKUFACTORY,
             'speed': DANMAKU_SPEED,
             'font': DANMAKU_FONT_NAME,
             'font_size': DANMAKU_FONT_SIZE,
@@ -127,12 +133,12 @@ def get_config():
         },
         'ffmpeg': {
             'exec': FFMPEG_EXEC,
-            'hevc': FFMPEG_USE_HEVC,
-            'nvidia_gpu': FFMPEG_USE_NVIDIA_GPU,
-            'intel_gpu': FFMPEG_USE_INTEL_GPU,
-            'vaapi': FFMPEG_USE_VAAPI,
-            'bitrate': VIDEO_BITRATE,
-            'gop': VIDEO_GOP,
+        },
+        'handbrake': {
+            'exec': HANDBRAKE_EXEC,
+            'preset_file': HANDBRAKE_PRESET_FILE,
+            'preset': HANDBRAKE_PRESET,
+            'encopt': HANDBRAKE_ENCOPT,
         },
         'recorder': {
             'bili_dir': BILILIVE_RECORDER_DIRECTORY,
diff --git a/danmaku_workflow.py b/danmaku_workflow.py
index 7b1061a..462f1cc 100644
--- a/danmaku_workflow.py
+++ b/danmaku_workflow.py
@@ -1,4 +1,5 @@
 # 工作流
+import json
 import os.path
 import platform
 import subprocess
@@ -13,9 +14,10 @@ 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, NoDanmakuException
-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, VIDEO_RESOLUTION, DANMAKU_SPEED, DANMAKU_FONT_NAME, \
-    VIDEO_OUTPUT_DIR, VIDEO_GOP, FFMPEG_USE_HEVC, DANMAKU_FONT_SIZE
+from config import load_config, \
+    DANMAKU_EXEC, DANMAKU_SPEED, DANMAKU_FONT_NAME, DANMAKU_FONT_SIZE, \
+    VIDEO_CLIP_EACH_SEC, VIDEO_CLIP_OVERFLOW_SEC, VIDEO_RESOLUTION, VIDEO_OUTPUT_DIR, \
+    FFMPEG_EXEC, HANDBRAKE_EXEC, HANDBRAKE_PRESET_FILE, HANDBRAKE_PRESET, HANDBRAKE_ENCOPT
 
 
 class Job:
@@ -313,21 +315,8 @@ class WorkerThread(QThread):
     def encode_video_with_subtitles(self, orig_filename: str, subtitles: list[str], base_ts: float):
         new_filename = base_ts_to_filename(base_ts, False)
         new_fullpath = os.path.join(VIDEO_OUTPUT_DIR, new_filename)
-        if FFMPEG_USE_HEVC:
-            if FFMPEG_USE_NVIDIA_GPU:
-                process = get_encode_hevc_process_use_nvenc(orig_filename, subtitles, new_fullpath)
-            elif FFMPEG_USE_INTEL_GPU:
-                process = get_encode_hevc_process_use_intel(orig_filename, subtitles, new_fullpath)
-            else:
-                process = get_encode_hevc_process_use_cpu(orig_filename, subtitles, new_fullpath)
-        else:
-            if FFMPEG_USE_NVIDIA_GPU:
-                process = get_encode_process_use_nvenc(orig_filename, subtitles, new_fullpath)
-            elif FFMPEG_USE_INTEL_GPU:
-                process = get_encode_process_use_intel(orig_filename, subtitles, new_fullpath)
-            else:
-                process = get_encode_process_use_cpu(orig_filename, subtitles, new_fullpath)
-        self.handle_ffmpeg_output(process.stdout)
+        process = get_encode_process_use_handbrake(orig_filename, subtitles, new_fullpath)
+        self.handle_handbrake_output(process.stdout)
         process.wait()
         return [new_fullpath]
 
@@ -362,6 +351,33 @@ class WorkerThread(QThread):
             current_sec += VIDEO_CLIP_EACH_SEC
         return True
 
+    def handle_handbrake_output(self, stdout: Optional[IO[bytes]]):
+        if stdout is None:
+            print("[!]STDOUT is null")
+            return
+        json_body = ""
+        json_start = False
+        while True:
+            line = stdout.readline()
+            if line == b"":
+                break
+            if json_start:
+                json_body += line.strip().decode("UTF-8")
+                if line.startswith(b"}"):
+                    json_start = False
+                    status_payload = json.loads(json_body)
+                    if status_payload["State"] == "WORKING":
+                        self.app.processCurTime.emit("ETA: {Hours:02d}:{Minutes:02d}:{Seconds:02d}".format_map(status_payload["Working"]))
+                        self.app.processSpeed.emit("{Rate:.2f}FPS".format_map(status_payload["Working"]))
+                    elif status_payload["State"] == "WORKDONE":
+                        break
+                continue
+            if line.startswith(b"Progress:"):
+                json_start = True
+                json_body = "{"
+        self.app.processSpeed.emit("")
+        self.app.processCurTime.emit("")
+
     def handle_ffmpeg_output(self, stdout: Optional[IO[bytes]]) -> str:
         out_time = "0:0:0.0"
         if stdout is None:
@@ -403,7 +419,7 @@ def base_ts_to_filename(start_ts: float, is_mp4=False) -> str:
 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",
+        DANMAKU_EXEC, "--ignore-warnings",
         "-r", str(VIDEO_RESOLUTION), "-s", str(DANMAKU_SPEED), "-f", "5",
         "-S", str(DANMAKU_FONT_SIZE), "-N", str(DANMAKU_FONT_NAME), "--showmsgbox", "FALSE",
         "-O", "255", "-L", "1", "-D", "0",
@@ -413,85 +429,15 @@ def danmaku_to_subtitle(file: Union[os.PathLike[str], str], time_shift: float):
     return new_subtitle_name
 
 
-def get_encode_process_use_nvenc(orig_filename: str, subtitles: list[str], new_filename: str):
-    print("[+]Use Nvidia NvEnc Acceleration")
+def get_encode_process_use_handbrake(orig_filename: str, subtitles: list[str], new_filename: str):
+    print("[+]Use HandBrakeCli")
     encode_process = subprocess.Popen([
-        FFMPEG_EXEC, *_common_ffmpeg_setting(),
-        "-i", orig_filename, "-vf",
-        ",".join("subtitles=%s" % i for i in subtitles) + ",hwupload_cuda",
-        "-c:v", "h264_nvenc",
-        *_common_ffmpeg_params(),
-        # "-t", "10",
-        new_filename
-    ], **subprocess_args(True))
-    return encode_process
-
-
-def get_encode_process_use_intel(orig_filename: str, subtitles: list[str], new_filename: str):
-    print("[+]Use Intel QSV Acceleration")
-    encode_process = subprocess.Popen([
-        FFMPEG_EXEC, *_common_ffmpeg_setting(),
-        "-hwaccel", "qsv", "-i", orig_filename, "-vf",
-        ",".join("subtitles=%s" % i for i in subtitles),
-        "-c:v", "h264_qsv",
-        *_common_ffmpeg_params(),
-        # "-t", "10",
-        new_filename
-    ], **subprocess_args(True))
-    return encode_process
-
-
-def get_encode_process_use_cpu(orig_filename: str, subtitles: list[str], new_filename: str):
-    print("[+]Use CPU Encode")
-    encode_process = subprocess.Popen([
-        FFMPEG_EXEC, *_common_ffmpeg_setting(),
-        "-i", orig_filename, "-vf",
-        ",".join("subtitles=%s" % i for i in subtitles),
-        "-c:v", "h264",
-        *_common_ffmpeg_params(),
-        # "-t", "10",
-        new_filename
-    ], **subprocess_args(True))
-    return encode_process
-
-
-def get_encode_hevc_process_use_nvenc(orig_filename: str, subtitles: list[str], new_filename: str):
-    print("[+]Use Nvidia NvEnc Acceleration")
-    encode_process = subprocess.Popen([
-        FFMPEG_EXEC, *_common_ffmpeg_setting(),
-        "-i", orig_filename, "-vf",
-        ",".join("subtitles=%s" % i for i in subtitles) + ",hwupload_cuda",
-        "-c:v", "hevc_nvenc",
-        *_common_ffmpeg_params(),
-        # "-t", "10",
-        new_filename
-    ], **subprocess_args(True))
-    return encode_process
-
-
-def get_encode_hevc_process_use_intel(orig_filename: str, subtitles: list[str], new_filename: str):
-    print("[+]Use Intel QSV Acceleration")
-    encode_process = subprocess.Popen([
-        FFMPEG_EXEC, *_common_ffmpeg_setting(),
-        "-hwaccel", "qsv", "-i", orig_filename, "-vf",
-        ",".join("subtitles=%s" % i for i in subtitles),
-        "-c:v", "hevc_qsv",
-        *_common_ffmpeg_params(),
-        # "-t", "10",
-        new_filename
-    ], **subprocess_args(True))
-    return encode_process
-
-
-def get_encode_hevc_process_use_cpu(orig_filename: str, subtitles: list[str], new_filename: str):
-    print("[+]Use CPU Encode")
-    encode_process = subprocess.Popen([
-        FFMPEG_EXEC, *_common_ffmpeg_setting(),
-        "-i", orig_filename, "-vf",
-        ",".join("subtitles=%s" % i for i in subtitles),
-        "-c:v", "hevc",
-        *_common_ffmpeg_params(),
-        # "-t", "10",
+        HANDBRAKE_EXEC, *_common_handbrake_setting(),
+        "--preset-import-file", HANDBRAKE_PRESET_FILE, "--preset", HANDBRAKE_PRESET,
+        "-i", orig_filename, "-x", HANDBRAKE_ENCOPT,
+        "--ssa-file", ",".join(i for i in subtitles),
+        "--ssa-burn", ",".join("%d" % (i+1) for i in range(len(subtitles))),
+        "-o",
         new_filename
     ], **subprocess_args(True))
     return encode_process
@@ -573,12 +519,23 @@ def is_linux() -> bool:
 
 
 def check_all_prerequisite():
-    if not check_exec(DANMAKU_FACTORY_EXEC):
+    if not check_exec(DANMAKU_EXEC):
         input("弹幕处理工具不存在")
         exit(1)
     if not check_exec(FFMPEG_EXEC):
         input("FFMPEG工具不存在")
         exit(1)
+    if not check_exec(HANDBRAKE_EXEC):
+        input("HANDBRAKE工具不存在")
+        exit(1)
+
+
+def _common_handbrake_setting():
+    return (
+        "--json",
+        "--crop-mode", "none", "--no-comb-detect", "--no-bwdif", "--no-decomb", "--no-detelecine", "--no-hqdn3d",
+        "--no-nlmeans", "--no-chroma-smooth", "--no-unsharp", "--no-lapsharp", "--no-deblock", "--no-optimize"
+    )
 
 
 def _common_ffmpeg_setting():
@@ -587,15 +544,6 @@ def _common_ffmpeg_setting():
     )
 
 
-def _common_ffmpeg_params():
-    return (
-        "-f", "mp4", "-b:v", VIDEO_BITRATE, "-c:a", "aac",
-        "-preset:v", "fast", "-profile:v", "main", "-avoid_negative_ts", "1",
-        "-qmin", "18", "-qmax", "38", "-g:v", str(VIDEO_GOP),
-        "-fflags", "+genpts", "-shortest"
-    )
-
-
 def main():
     check_all_prerequisite()
     app = QApplication(sys.argv)
diff --git a/danmaku_xml_helper.py b/danmaku_xml_helper.py
index a61815b..fe83f8f 100644
--- a/danmaku_xml_helper.py
+++ b/danmaku_xml_helper.py
@@ -5,8 +5,6 @@ from typing import Union
 
 from bs4 import BeautifulSoup
 
-from config import DANMAKU_FACTORY_EXEC, VIDEO_RESOLUTION, DANMAKU_SPEED, DANMAKU_FONT_NAME
-
 
 class NoDanmakuException(Exception):
     ...