From 364ceb29a13222aedf26fc71bb726336fda3e267 Mon Sep 17 00:00:00 2001
From: Jerry Yan <792602257@qq.com>
Date: Tue, 1 Apr 2025 14:39:01 +0800
Subject: [PATCH] =?UTF-8?q?=E9=9F=B3=E9=A2=91=E6=B7=A1=E5=87=BA?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 biz/ffmpeg.py  |  1 +
 biz/task.py    |  7 +++++--
 util/api.py    |  4 +++-
 util/ffmpeg.py | 37 +++++++++++++++++++++++++++++++++++++
 4 files changed, 46 insertions(+), 3 deletions(-)

diff --git a/biz/ffmpeg.py b/biz/ffmpeg.py
index d7d9e4d..340211e 100644
--- a/biz/ffmpeg.py
+++ b/biz/ffmpeg.py
@@ -8,6 +8,7 @@ from entity.ffmpeg import FfmpegTask
 import logging
 
 from util import ffmpeg, oss
+from util.ffmpeg import fade_out_audio
 from telemetry import get_tracer
 
 logger = logging.getLogger('biz/ffmpeg')
diff --git a/biz/task.py b/biz/task.py
index 2e2cc49..0cacded 100644
--- a/biz/task.py
+++ b/biz/task.py
@@ -2,7 +2,7 @@ import json
 
 from opentelemetry.trace import Status, StatusCode
 
-from biz.ffmpeg import parse_ffmpeg_task, start_ffmpeg_task, clear_task_tmp_file, probe_video_info
+from biz.ffmpeg import parse_ffmpeg_task, start_ffmpeg_task, clear_task_tmp_file, probe_video_info, fade_out_audio
 from telemetry import get_tracer
 from template import get_template_def
 from util import api
@@ -20,12 +20,15 @@ def start_task(task_info):
         if not result:
             span.set_status(Status(StatusCode.ERROR))
             return api.report_task_failed(task_info)
+        width, height, duration = probe_video_info(ffmpeg_task)
+        # 音频淡出
+        new_fn = fade_out_audio(ffmpeg_task.get_output_file(), duration)
+        ffmpeg_task.set_output_file(new_fn)
         oss_result = api.upload_task_file(task_info, ffmpeg_task)
         if not oss_result:
             span.set_status(Status(StatusCode.ERROR))
             return api.report_task_failed(task_info)
         # 获取视频长度宽度和时长
-        width, height, duration = probe_video_info(ffmpeg_task)
         clear_task_tmp_file(ffmpeg_task)
         api.report_task_success(task_info, videoInfo={
             "width": width,
diff --git a/util/api.py b/util/api.py
index b2d31a4..51ffeaa 100644
--- a/util/api.py
+++ b/util/api.py
@@ -189,7 +189,9 @@ def report_task_start(task_info):
 
 def report_task_failed(task_info, reason=''):
     tracer = get_tracer(__name__)
-    with tracer.start_as_current_span("report_task_failed"):
+    with tracer.start_as_current_span("report_task_failed") as span:
+        span.set_attribute("task_id", task_info.get("id"))
+        span.set_attribute("reason", reason)
         with tracer.start_as_current_span("report_task_failed.request") as req_span:
             try:
                 req_span.set_attribute("http.method", "POST")
diff --git a/util/ffmpeg.py b/util/ffmpeg.py
index 4c0efb1..f2068aa 100644
--- a/util/ffmpeg.py
+++ b/util/ffmpeg.py
@@ -154,6 +154,43 @@ def probe_video_audio(video_file, type=None):
             return False
         return True
 
+
+# 音频淡出2秒
+def fade_out_audio(file, duration, fade_out_sec = 2):
+    if type(duration) == str:
+        try:
+            duration = float(duration)
+        except Exception as e:
+            logger.error("duration is not float: %s", e)
+            return file
+    tracer = get_tracer(__name__)
+    with tracer.start_as_current_span("fade_out_audio") as span:
+        span.set_attribute("audio.file", file)
+        if duration <= fade_out_sec:
+            return file
+        else:
+            new_fn = file + "_.mp4"
+            if os.path.exists(new_fn):
+                os.remove(new_fn)
+                logger.info("delete tmp file: " + new_fn)
+            try:
+                process = subprocess.run(["ffmpeg", "-i", file, "-c:v", "copy", "-c:a", "aac", "-af", "afade=t=out:st=" + str(duration - fade_out_sec) + ":d=" + str(fade_out_sec), "-y", new_fn], **subprocess_args(True))
+                span.set_attribute("ffmpeg.args", json.dumps(process.args))
+                logger.info(" ".join(process.args))
+                if process.returncode != 0:
+                    span.set_status(Status(StatusCode.ERROR))
+                    logger.error("FFMPEG ERROR: %s", process.stderr)
+                    return file
+                else:
+                    span.set_status(Status(StatusCode.OK))
+                    return new_fn
+            except Exception as e:
+                span.set_status(Status(StatusCode.ERROR))
+                logger.error("FFMPEG ERROR: %s", e)
+                return file
+
+
+
 # Create a set of arguments which make a ``subprocess.Popen`` (and
 # variants) call work with or without Pyinstaller, ``--noconsole`` or
 # not, on Windows and Linux. Typical use::