zhangyong 2 months ago
parent
commit
868dd67e41

+ 3 - 1
carry_data_redis.py

@@ -17,7 +17,9 @@ def bot_carry_data():
         logger.info(f"[+] 开始获取{NAME},时区为{dt}")
         count = insert_carry_data(dt, REDIS_NAME,FS_SHEET, NAME)
         logger.info(f"[+] {NAME},时区为{dt}共获取{count}条")
-
+        time.sleep(3)
+        nrfx_count = insert_carry_data(dt, "task:carry_redis_nrfx",FS_SHEET, NAME)
+        logger.info(f"[+] 内容分析时区为{dt}共获取{nrfx_count}条")
     except Exception as e:
         logger.error(f"[+] 获取{NAME},时区为{dt}失败,失败信息{e}")
 

+ 60 - 0
carry_nrfx_data_handle.py

@@ -0,0 +1,60 @@
+import json
+import os
+import time
+import uuid
+
+import schedule
+from loguru import logger
+
+from carry_video.nrfx_carry_video import NrfxCarryViode
+from common import AliyunLogger
+from common.redis import get_carry_data, in_carry_video_data
+
+ENV = os.getenv('ENV', 'dev')
+NAME = os.getenv('NAME')
+REDIS_NAME = os.getenv('REDIS_NAME')
+CACHE_DIR = '/app/cache/' if ENV == 'prod' else os.path.expanduser('~/Downloads/')
+
+
+def nrfx_video_task_start():
+    logger.info(f"[+] {REDIS_NAME}任务开始redis获取")
+    data = get_carry_data("task:carry_redis_nrfx")
+    if not data:
+        return
+    try:
+        logger.info(f"[+] {NAME}任务开始,数据为{data}")
+        uid = str(uuid.uuid4())
+        file_path = os.path.join(CACHE_DIR, uid)
+        nrfx_carry_video = NrfxCarryViode()
+        mark = nrfx_carry_video.main(json.loads(data), file_path, "AIzaSyB2kjF2-S2B5cJiosx_LpApd227w33CVvs")
+        print(f"返回用户名: {mark}")
+        logger.info(f"[+] {NAME}处理一条成功")
+        for filename in os.listdir(CACHE_DIR):
+            # 检查文件名是否包含关键字
+            if uid in filename:
+                file_path = os.path.join(CACHE_DIR, filename)
+                try:
+                    # 删除文件
+                    os.remove(file_path)
+                    logger.info(f"已删除文件: {file_path}")
+                except Exception as e:
+                    logger.error(f"删除文件时出错: {file_path}, 错误: {e}")
+        return
+    except Exception as e:
+        data = json.loads(data)
+        in_carry_video_data("task:carry_redis_nrfx", json.dumps(data, ensure_ascii=False, indent=4))
+        AliyunLogger.logging(data["name"], "内容分析", data["tag_transport_channel"], data["video_url"],
+                             f"视频处理失败,失败信息{e}", "3003", str(data))
+        logger.error(f"[+] {data}处理失败,失败信息{e}")
+        return
+
+def schedule_tasks():
+    schedule.every(7).minutes.do(nrfx_video_task_start)
+
+
+if __name__ == '__main__':
+    schedule_tasks()  # 调用任务调度函数
+    while True:
+        schedule.run_pending()
+        time.sleep(1)  # 每秒钟检查一次
+    # nrfx_video_task_start()

+ 366 - 0
carry_video/nrfx_carry_video.py

@@ -0,0 +1,366 @@
+import html
+import json
+import os
+import random
+import re
+import time
+import uuid
+import requests
+from datetime import datetime
+from urllib.parse import urlparse, parse_qs
+from loguru import logger
+from common import Oss, Feishu, AliyunLogger, Material
+from common.download_video import DownLoad
+from common.ffmpeg import FFmpeg
+from common.google_ai_studio import GoogleAI
+from common.gpt4o_mini_help import GPT4oMini
+from common.redis import in_carry_video_data
+from common.sql_help import sqlCollect
+from common.tag_video import Tag
+from common.tts_help import TTS
+from data_channel.piaoquan import PQ
+
+
+class NrfxCarryViode:
+
+    def get_text_dy_video(self,url):
+        max_retries = 3
+        retry_count = 0
+        while retry_count < max_retries:
+            try:
+                if "&vid=" in url:
+                    parsed_url = urlparse(url)
+                    params = parse_qs(parsed_url.query)
+                    video_id = params.get('vid', [None])[0]
+                elif "?modal_id=" in url:
+                    parsed_url = urlparse(url)
+                    params = parse_qs(parsed_url.query)
+                    video_id = params.get('modal_id', [None])[0]
+                else:
+                    headers = {
+                        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;'
+                                  'q=0.8,application/signed-exchange;v=b3;q=0.7',
+                        'Accept-Language': 'zh-CN,zh;q=0.9',
+                        'Cache-Control': 'no-cache',
+                        'Pragma': 'no-cache',
+                        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) '
+                                      'Chrome/127.0.0.0 Safari/537.36',
+                    }
+                    response = requests.request(url=url, method='GET', headers=headers, allow_redirects=False, timeout=30)
+                    location = response.headers.get('Location', None)
+                    video_id = re.search(r'/video/(\d+)/?', location.split('?')[0] if location else url).group(1)
+                url = "http://8.217.192.46:8889/crawler/dou_yin/detail"
+                if not video_id or not video_id.strip():
+                    return None, None, None
+                payload = json.dumps({
+                    "content_id": str(video_id)
+                })
+                headers = {
+                    'Content-Type': 'application/json'
+                }
+
+                response = requests.request("POST", url, headers=headers, data=payload, timeout= 60)
+                response = response.json()
+                code = response["code"]
+                if code == 0:
+                    data = response["data"]["data"]
+                    video_url = data["video_url_list"][0]["video_url"]
+                    original_title = data["title"]
+                    return video_url, original_title, video_id
+                if code == 22002:
+                    if '抖音内容已被删除或无法访问' in response['msg']:
+                        return "作品不存在", None, None
+            except Exception as e:
+                retry_count += 1
+                logger.error(f"[+] 抖音{url}获取视频链接失败,失败信息{e}")
+                time.sleep(1)
+        return None, None, None
+
+    def get_text_ks_video(self,url):
+        try:
+            headers = {
+                'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;'
+                          'q=0.8,application/signed-exchange;v=b3;q=0.7',
+                'Accept-Language': 'zh-CN,zh;q=0.9',
+                'Cache-Control': 'no-cache',
+                'Pragma': 'no-cache',
+                'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) '
+                              'Chrome/127.0.0.0 Safari/537.36',
+            }
+            response = requests.request(url=url, method='GET', headers=headers, allow_redirects=False, timeout= 30)
+            location = response.headers.get('Location', None)
+            video_id = re.search(r'/(f|photo|short-video|long-video)/(.*)/?',
+                                 location.split('?')[0] if location else url).group(2)
+            url = "http://8.217.192.46:8889/crawler/kuai_shou/detail"
+            if not video_id or not video_id.strip():
+                return None, None, None
+            payload = json.dumps({
+                "content_id": str(video_id)
+            })
+            headers = {
+                'Content-Type': 'application/json'
+            }
+            time.sleep(random.uniform(10, 30))
+            response = requests.request("POST", url, headers=headers, data=payload, timeout= 30)
+            response = response.json()
+            code = response["code"]
+            if code == 0:
+                data = response["data"]["data"]
+                content_type = data['content_type']
+                if content_type == 'note':
+                    return "note","note"
+                video_url = data["video_url_list"][0]["video_url"]
+                original_title = data["title"]
+                return video_url, original_title, video_id
+            elif code == 27006:
+                if "作品不存在" in response['msg'] or "不存在" in response['msg'] or "私密作品" in response['msg']:
+                    return "作品不存在", None, None
+            time.sleep(3)
+        except Exception as e:
+            logger.error(f"[+] 快手{url}获取视频链接失败,失败信息{e}")
+            return None, None,None
+
+
+    def insert_pq(self, REDIS_NAME, data, oss_object_key, title, tags, tag_transport_channel, channel_mark, n_ids, type):
+        logger.info(f"[+] {REDIS_NAME}的{data}开始写入票圈")
+        if ',' in n_ids:
+            n_id_list = n_ids.split(',')
+        else:
+            n_id_list = [n_ids]
+        pq_list = []
+        for n_id in n_id_list:
+            code = PQ.insert_piaoquantv(oss_object_key, title, n_id)
+            if not code:
+                logger.error(f"[+] {REDIS_NAME}的{data}写入票圈后台失败")
+                AliyunLogger.logging(data["name"], type, tag_transport_channel, data["video_url"],
+                                     "改造失败,写入票圈后台失败", "3003", str(data))
+                continue
+
+            pq_list.append(code)
+            logger.info(f"[+] {REDIS_NAME}的{data}写入票圈成功,返回视频id{code}")
+            tag_status = Tag.video_tag(code, str(tags))
+            if tag_status == 0:
+                logger.info(f"[+] {REDIS_NAME}的{data}写入标签成功,后台视频ID为{code}")
+            try:
+                current_time = datetime.now()
+                formatted_time = current_time.strftime("%Y-%m-%d %H:%M:%S")
+                sqlCollect.insert_machine_making_data(data["name"], type, tag_transport_channel,
+                                                      data["video_url"], data["video_url"], data["pq_ids"],
+                                                      data["title_category"],
+                                                      code,
+                                                      formatted_time, data["title_category"], oss_object_key)
+                pq_url = f'https://admin.piaoquantv.com/cms/post-detail/{code}/detail'  # 站内视频链接
+                values = [
+                    [
+                        str(code),
+                        str(n_id),
+                        formatted_time,
+                        channel_mark,
+                        data["name"],
+                        data["pq_ids"],
+                        data["pq_label"],
+                        data["activate_data"],
+                        data["video_url"],
+                        data["title_category"],
+                        tag_transport_channel,
+                        data["tag_transport_scene"],
+                        data["tag_transport_keyword"],
+                        data["tag"],
+                        data["transform_rule"],
+                        data["video_share"],
+                        data["trailer_share"],
+                        data["trailer_share_audio"],
+                        data["video_clipping"],
+                        data["video_clipping_time"],
+                        data["title_transform"],
+                        pq_url
+                    ]
+                ]
+                Feishu.insert_columns("R4dLsce8Jhz9oCtDMr9ccpFHnbI", 'Um1nWA', "ROWS", 1, 2)
+                time.sleep(0.5)
+                Feishu.update_values("R4dLsce8Jhz9oCtDMr9ccpFHnbI", 'Um1nWA', "A2:Z2", values)
+                logger.info(f"[+] {REDIS_NAME}的{data}写入飞书成功")
+            except Exception as e:
+                logger.error(f"[+] {REDIS_NAME}的{data}写入飞书失败{e}")
+                pass
+        AliyunLogger.logging(data["name"], "内容分析", tag_transport_channel, data["video_url"],
+                             "改造成功", "1000", str(data), str(pq_list))
+        return
+
+
+
+    def main(self, data, file_path, GEMINI_API_KEY):
+        REDIS_NAME = 'task:carry_redis_nrfx'
+        try:
+            if data["transform_rule"] == '否':
+                return
+            url = data['video_url']
+            if "&vid=" in url or "?modal_id=" in url:
+                host = urlparse(url).netloc
+            else:
+                msg = html.unescape(url).split('?')[0]
+                pattern = re.search(r'https?://[^\s<>"\'\u4e00-\u9fff]+', msg)
+                if not pattern:
+                    in_carry_video_data(REDIS_NAME, json.dumps(data, ensure_ascii=False, indent=4))
+                    return
+                url = pattern.group()
+                host = urlparse(url).netloc
+            if host in ['v.douyin.com', 'www.douyin.com', 'www.iesdouyin.com']:
+                tag_transport_channel = "抖音"
+                logger.info(f"[+] {url}开始获取抖音视频链接")
+                url, original_title, video_id =  self.get_text_dy_video(url=url)
+            elif host in ['v.kuaishou.com', 'www.kuaishou.com', 'v.m.chenzhongtech.com', 'creater.eozatvmq.com']:
+                tag_transport_channel = "快手"
+                logger.info(f"[+] {url}开始获取快手视频链接")
+                url, original_title, video_id = self.get_text_ks_video(url=url)
+            else:
+                AliyunLogger.logging(data["name"], "内容分析", "", data["video_url"], "扫描到一条视频",
+                                     "2001", str(data))
+                logger.error(f"[+] {url}该链接不是抖/快 不做处理")
+                AliyunLogger.logging(data["name"], "内容分析","", data["video_url"],
+                                     "不是抖/快不做处理", "1001", str(data))
+                return
+            if url == "作品不存在":
+                return
+        except Exception as e:
+            logger.info(f"[+] 获取视频链接异常{e}")
+            in_carry_video_data(REDIS_NAME, json.dumps(data, ensure_ascii=False, indent=4))
+            return
+        AliyunLogger.logging(data["name"],"内容分析", tag_transport_channel, data["video_url"], "扫描到一条视频", "2001", str(data))
+        AliyunLogger.logging(data["name"], "内容分析",tag_transport_channel, data["video_url"],  "符合规则等待改造", "2004", str(data))
+        if not url:
+            in_carry_video_data(REDIS_NAME, json.dumps(data, ensure_ascii=False, indent=4))
+            logger.info(f"[+] {url}没有获取到视频链接,等待重新处理")
+            AliyunLogger.logging(data["name"], "内容分析",tag_transport_channel, data["video_url"],
+                                 "没有获取到视频链接,等待重新处理", "1002", str(data))
+            return
+        if url == "note":
+            logger.info(f"[+] {url}是图文不做处理")
+            AliyunLogger.logging(data["name"], "内容分析", tag_transport_channel, data["video_url"],
+                                 "是图文不做处理", "1002", str(data))
+            return
+
+        logger.info(f"[+] {url}开始下载视频")
+        video_path = DownLoad.download_video(url, file_path, tag_transport_channel, video_id)
+        if not os.path.exists(video_path) or os.path.getsize(video_path) == 0:
+            in_carry_video_data(REDIS_NAME, json.dumps(data, ensure_ascii=False, indent=4))
+            logger.error(f"[+] {url}下载失败")
+            AliyunLogger.logging(data["name"],"内容分析", tag_transport_channel, data["video_url"],
+                                 "视频下载失败等待重新处理", "3002", str(data))
+            return
+        logger.info(f"[+] {url}开始视频下载成功")
+        logger.info(f"[+] {url}开始处理标题")
+        if data["title_category"] == "AI标题" or data["trailer_share"] == "AI标题":
+            title = GPT4oMini.get_ai_mini_title(
+                original_title if data["title_category"] == "AI标题" else data["title_category"])
+        else:
+            title = original_title if data["title_category"] == "原标题" else data["title_category"]
+        if tag_transport_channel == "抖音":
+            if "复制打开抖音" in data['video_url']:
+                channel_mark = "APP"
+            else:
+                channel_mark = "PC"
+        else:
+            if "https://www.kuaishou.com/f" in data['video_url']:
+                channel_mark = "PC"
+            else:
+                channel_mark = "APP"
+        if data["transform_rule"] == "仅改造" or data["transform_rule"] == "是":
+            width, height = FFmpeg.get_w_h_size(video_path)
+            if width < height:  # 判断是否需要修改为竖屏
+                video_path = FFmpeg.update_video_h_w(video_path, file_path)
+            logger.info(f"[+] {REDIS_NAME}的{data}视频更改分辨率处理")
+            video_path = FFmpeg.video_640(video_path, file_path)
+            if not os.path.exists(video_path) or os.path.getsize(video_path) == 0:
+                in_carry_video_data(REDIS_NAME, json.dumps(data, ensure_ascii=False, indent=4))
+                logger.error(f"[+] {REDIS_NAME}的{data}视频更改分辨率失败")
+                AliyunLogger.logging(data["name"], "内容分析", tag_transport_channel, data["video_url"],
+                                     "改造失败,片尾拼接失败", "3001", str(data))
+                return
+            logger.info(f"[+] {REDIS_NAME}的{data}视频更改分辨率处理成功")
+            if data["video_clipping"]:  # 判断是否需要裁剪
+                video_path = FFmpeg.video_crop(video_path, file_path)
+            if data["video_clipping_time"]:  # 判断是否需要指定视频时长
+                video_path = FFmpeg.video_ggduration(video_path, file_path, data["video_clipping_time"])
+            logger.info(f"[+] 内容分析-开始获取视频口播内容")
+            video_text = GoogleAI.run(GEMINI_API_KEY, video_path)
+            if not video_text:
+                AliyunLogger.logging(data["name"], "内容分析", "", data["video_url"],
+                                     "内容分析,获取口播文案失败", "3003", str(data))
+                return
+            logger.info(f"[+] 内容分析-开始获取AI片尾")
+            pw_srt_text = GPT4oMini.get_content_understanding_pw(video_text)
+            voice = data['trailer_share_audio']
+            if voice:
+                if ',' in voice:
+                    voices = voice.split(',')
+                else:
+                    voices = [voice]
+                voice = random.choice(voices)
+            else:
+                voice = "zhifeng_emo"
+            pw_url = TTS.get_pw_zm(pw_srt_text, voice)
+            if not pw_url:
+                logger.error(f"[+] 内容分析-片尾获取失败")
+                data["transform_rule"] = "仅改造"
+                AliyunLogger.logging(data["name"], "内容分析", "", data["video_url"],
+                                     "内容分析,片尾获取失败", "3003", str(data))
+
+                return
+            logger.info(f"[+] 内容分析-片尾获取成功")
+            pw_srt = TTS.getSrt(pw_url)
+            if not pw_srt:
+                AliyunLogger.logging(data["name"], "内容分析", "", data["video_url"],
+                                     "内容分析,片尾音频下载失败", "3003", str(data))
+                return
+            pw_mp3_path = TTS.download_mp3(pw_url, file_path)
+            if not pw_mp3_path:
+                AliyunLogger.logging(data["name"], "内容分析", "", data["video_url"],
+                                     "内容分析,片尾音频下载失败", "3003", str(data))
+                return
+            logger.info(f"[+] 内容分析-片尾音频下载成功")
+            logger.info(f"[+] 内容分析-片尾获取最后一帧成功")
+            jpg_path = FFmpeg.video_png(video_path, file_path)  # 生成视频最后一帧jpg
+            pw_path = FFmpeg.pw_video(jpg_path, file_path, pw_mp3_path, pw_srt)  # 生成片尾视频
+            if not os.path.exists(pw_path) or os.path.getsize(pw_path) == 0:
+                logger.error(f"[+] 内容分析-片尾拼接失败")
+                AliyunLogger.logging(data["name"], "内容分析", "", data["video_url"],
+                                     "内容分析,片尾拼接失败", "3003", str(data))
+                return
+            logger.info(f"[+] 内容分析-合并开始拼接")
+            video_path = FFmpeg.h_b_video(video_path, pw_path, file_path)
+            single_video_path = FFmpeg.single_video(video_path, file_path, data["video_share"])
+            if not os.path.exists(single_video_path) or os.path.getsize(single_video_path) == 0:
+                data["transform_rule"] = "仅改造"
+                in_carry_video_data(REDIS_NAME, json.dumps(data, ensure_ascii=False, indent=4))
+                logger.error(f"[+] 内容分析-添加片中字幕失败")
+                AliyunLogger.logging(data["name"], "内容分析", tag_transport_channel, data["video_url"],
+                                     "内容分析,添加片中字幕失败", "3003", str(data))
+                return
+            logger.info(f"[+] 内容分析-添加片中字幕成功")
+            logger.info(f"[+] 内容分析-开始发送oss")
+            oss_object_key = Oss.stitching_sync_upload_oss(single_video_path, str(uuid.uuid4()))  # 视频发送OSS
+            status = oss_object_key.get("status")
+            if status != 200:
+                logger.error(f"[+] 内容分析-发送oss失败")
+                AliyunLogger.logging(data["name"], "内容分析", tag_transport_channel, data["video_url"],
+                                     "内容分析,发送oss失败", "3003", str(data))
+                return
+            logger.info(f"[+] 内容分析-发送oss成功")
+            oss_object_key = oss_object_key.get("oss_object_key")
+            tags = 'lev-供给,rol-机器,#str-搬运改造内容理解引导语实验_60'
+            self.insert_pq(REDIS_NAME, data, oss_object_key, title, tags, tag_transport_channel, channel_mark, "50322062", "内容分析")
+            return
+
+
+        return
+
+
+
+
+
+
+
+
+
+

+ 76 - 0
common/google_ai_studio.py

@@ -0,0 +1,76 @@
+import os
+import time
+import uuid
+from typing import  Optional
+
+import google.generativeai as genai
+import orjson
+import requests
+from google.generativeai.types import (HarmBlockThreshold, HarmCategory)
+from loguru import logger
+
+
+CACHE_DIR = '/app/cache/'
+# CACHE_DIR = '/Users/z/Downloads/'
+# PROXY_ADDR = 'http://localhost:1081'
+# os.environ['http_proxy'] = PROXY_ADDR
+# os.environ['https_proxy'] = PROXY_ADDR
+
+class GoogleAI(object):
+
+    @classmethod
+    def download_video(cls, video_link: str) -> Optional[str]:
+        file_path = os.path.join(CACHE_DIR, f'{str(uuid.uuid4())}.mp4')
+        for _ in range(3):
+            try:
+                response = requests.get(url=video_link, timeout=60)
+                if response.status_code == 200:
+                    with open(file_path, 'wb') as f:
+                        f.write(response.content)
+                    logger.info(f'[内容分析] 视频链接: {video_link}, 存储地址: {file_path}')
+                    return file_path
+            except Exception:
+                time.sleep(1)
+                continue
+        return
+
+    @classmethod
+    def run(cls, api_key, video_path):
+        try:
+            genai.configure(api_key=api_key)
+            video = genai.upload_file(path=video_path, mime_type='video/mp4')
+            while video.state.name == 'PROCESSING':
+                time.sleep(1)
+                video = genai.get_file(name=video.name)
+            if video.state.name != 'ACTIVE':
+                genai.delete_file(name=video.name)
+                return
+            model = genai.GenerativeModel(
+                model_name='gemini-1.5-flash',
+                generation_config=genai.GenerationConfig(response_mime_type='application/json'),
+                safety_settings={
+                    HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT: HarmBlockThreshold.BLOCK_NONE,
+                },
+            )
+            response = model.generate_content(
+                contents=[
+                    video,
+                    "你是一名专业的短视频分析师,请你输出这个视频的完整口播,只输出文字即可。使用一下JSON格式输出:{'text': string}",
+                ],
+                stream=False,
+                request_options={
+                    'timeout': 600,
+                },
+            )
+            text = orjson.loads(response.text.strip())['text']
+            genai.delete_file(name=video.name)
+            return text
+        except Exception as e:
+            logger.error(f"[内容分析] 处理异常,异常信息{e}")
+            return
+
+
+if __name__ == '__main__':
+    GoogleAI.run("AIzaSyAwGqthDADh5NPVe3BMcOJBQkJaf0HWBuQ",
+                 "http://rescdn.yishihui.com/jq_oss/video/2025012215472528213")
+

+ 79 - 0
common/gpt4o_mini_help.py

@@ -157,5 +157,84 @@ class GPT4oMini:
                 time.sleep(1)  # 延迟1秒后重试
         return "这个视频,分享给我的老友,祝愿您能幸福安康"
 
+    @classmethod
+    def get_content_understanding_pw(cls, pw):
+        """AI标题"""
+        url = "http://aigc-api.cybertogether.net//aigc/dev/test/gpt"
+        payload = json.dumps({
+            "imageList": [],
+            "model": "gpt-4o-mini-2024-07-18",
+            "prompt": (
+                "你是一名专业的短视频分析师,工作是帮助短视频平台的视频撰写视频结尾用于引导用户分享视频的文案。视频的主要用户为60岁以上中国老年人,请你理解视频内容,根据规则选择选择模板,并结合视频内容输出用于引导用户分享视频的文案。"
+
+                "请注意:"
+                "1、总的内容输出在100~150字之间,绝对不能低于100字,绝对不能超过150字,如果超过150字,可以针对模板进行适当删减。"
+                "2、针对视频核心内容的提炼,内容需要适配老年人的阅读习惯和理解能力,禁止出现太燃了、佛系、躺平、内卷、炸裂等形容词,以及阶级固化等专业名词。"
+                "3、模板中[]内的内容填充,不要超过15个字。"
+                
+                "规则:"
+                "1、视频内容是否围绕着健康科普/安全防范/政策解读/生活技巧/情感激励等知识科普/信息通知的主题?若是,则根据视频内容在“科普/信息分享模板”中,选择合适的模板进行输出,无需处理其他规则。若不是,则继续处理规则2。"
+                "2、视频内容是否围绕着对感动/温情/趣味/祝福、罕见画面、社会正能量等正面主题?若是,则根据视频内容在“正面情绪模板”中,选择合适的模板进行输出,无需处理其他规则。若不是,则继续处理规则3。"
+                "3、频内容是否围绕着对社会风气/现象的不满/批判、对生活的不满等主题?若是,则根据视频内容在“负面情绪模板”中,选择合适的模板进行输出,无需处理其他规则。若不是,则继续处理规则4。"
+                "4、若视频同时不符合规则1、2、3,则根据视频内容,在所有模板中选择适配的模板进行输出。"
+                
+                "输出内容包含:"
+                "1、视频内容符合规则1/2/3/4中的哪一条?"
+                "2、你选择的是哪一类模板,具体哪一条模板?"
+                "3、引导语文案"
+                
+                "模板List:"
+                "“科普/信息分享模板”"
+                "模板1:健康科普类 ""这个视频讲解的[健康知识]真是太实用了,很多人都不知道原来[具体内容]还有这么多讲究。看完后我才明白,平时我们[具体行为]都做错了。群里的朋友们都说,这些知识太及时了,简直是生活中的智慧宝典。老朋友们,你们[相关问题]做对了吗?如果觉得这个视频对你有帮助,请马上转发到3个群里,让更多人了解正确的[健康知识]。记住,健康是最重要的,您的转发可能会帮助一位需要的朋友,让我们一起关爱健康,守护幸福。多多分享,感恩有你。"
+                "模板2:安全防范类 ""各位老友们要注意了,这个视频提醒的[安全隐患]太重要了,现在的[具体威胁]真是防不胜防。看完后我心惊肉跳,原来我们身边竟然有这么多危险。为了家人的安全,请立即把这个视频转发到至少3个群里,让大家都提高警惕。特别是家里有老人和孩子的,一定要多加注意。记住,一个不小心就可能造成无法挽回的损失。您的一次转发,可能就会挽救一个家庭。让我们一起守护平安,转发传递,功德无量。"
+                "模板3:政策解读类 ""重要通知!这个关于[最新政策]的视频太及时了,从[具体日期]开始实施的新规定,将给我们的生活带来重大变化。很多老年朋友还不知道这些新政策,可能会影响到我们的切身利益。为了不让身边的朋友吃亏,请大家一定要把这个视频转发到至少3个群里。让更多人了解这些新变化,提前做好准备。您的分享就是对他人的关爱,让我们互帮互助,共同进步。转发分享,福气多多。"
+                "模板4:生活技巧类 ""看完这个视频,我才知道原来[日常行为]还可以这样做,太神奇了!视频里教的这些[具体技巧],简直是生活小妙招。群里的朋友们都说学到了,这些方法太实用了。特别是对我们这些[特定人群]来说,掌握这些技巧后,生活会变得更加便利。如果你也觉得有用,请马上转发到3个群,分享给身边的朋友,让大家一起学习这些生活智慧。记住,分享就是快乐,您的转发一定会帮助到需要的人。多多转发,感恩有你。"
+                "模板5:情感激励类 ""这个视频的内容太有意义了,让我深受触动。在这个快节奏的社会里,我们常常忽视了[具体内容]的重要性。视频里说的每一句话都说到心坎里,相信看完的朋友们都会有同感。为了让更多人受益,请把这个视频转发到至少3个群里。特别是那些正在经历[具体困境]的朋友,看了一定会很有启发。记住,您的一次分享,可能就会改变一个人的生活。让我们用爱心传递温暖,共同创造更美好的明天。转发分享,福气满满。"
+                
+                "“正面情绪模板”"
+                "模板1:感动温情类 ""亲爱的老朋友们,这个视频真是太[感动/温馨/暖心]了,看到[具体内容]的画面,让人心里暖暖的,相信您和我一样被深深打动。这份[幸福/感动/温情]太珍贵了,值得与更多人分享。请您一定要把这个视频转发到您所在的3个微信群,让更多的家人朋友感受这份美好。您的分享就是一份关爱,群友们看到后一定会感谢您的。多一次转发,多一份温暖,让我们一起传递这份感动,还在等什么,赶紧行动起来吧!"
+                "模板2:趣味互动类 ""老朋友们,这个[有趣/精彩]的视频里,您觉得[具体内容]哪一个最棒呢?快来说说您的看法吧!看完后千万别私藏,一定要把这么精彩的内容转发分享到您所在的3个微信群,让群友们也一起来欣赏评论。这么有意思的视频,大家看到后一定会眼前一亮,纷纷感谢您的分享。记住,您的每一次转发都能带来好运哦!多多转发,福气多多,快乐多多。动动手指,传递欢乐,一起分享精彩时刻!"
+                "模板3:祝福传递类 ""亲爱的朋友,这个充满[祝福/好运/福气]的视频,我第一时间就想到要分享给您。据说看到这个视频的人,只要转发给八个好友或三个群,就能收获满满的[福气/好运/健康]。我已经把这份祝福传递给了最重要的朋友,其中就包括您。请您也一定要把这份美好传递下去,转发到您所在的微信群,让更多人感受这份祝福。记住,千万不要让祝福在您手中终止,多多转发,幸福多多!"
+                "模板4:文化传承类 ""老友们,这段展现[中华文化/传统技艺/民族精神]的视频太珍贵了!看到[具体内容],让人深感自豪。作为炎黄子孙,我们要把这份文化瑰宝传承下去。请您一定要把视频转发到3个群,让更多人了解我们的传统文化。您的每一次转发,都是对中华文化的传播,都是对传统的守护。让我们共同努力,让中华文化发扬光大。转发视频,传承文明,让更多人感受中华文化的魅力!"
+                "模板5:稀奇见闻类 ""亲爱的朋友,这个展现[自然奇观/稀有现象/独特事物]的视频实在太难得了!这么[神奇/罕见/精彩]的画面,错过太可惜了。我第一时间就想分享给身边的好友,也包括您。请您看完后也别忘了转发分享到您所在的3个微信群,让群友们也大开眼界。这么稀奇的视频,绝对不能在您这里终止啊!据说转发的人都能沾上好运气呢。多多转发,惊喜多多,让我们一起分享这份难得的奇观,传递这份惊喜!"
+                
+                "“负面情绪模板”"
+                "模板1:真实共鸣型 ""这个视频说出了我们的心里话,[视频核心内容提炼]真是太真实了。相信很多朋友看了都会有同感,这样难得的真话不常见。如果您也觉得说得对,请一定要转发分享给3个群的群友们看看。这样的真实声音,明天可能就听不到了。您的每一次转发,都是在发声,都是在传递真相。让我们一起行动起来,点点转发,让更多人听到这样的心声。"
+                "模板2:善意提醒型 ""老朋友们,这个视频太重要了,[视频核心内容提炼]对我们每个人都很有帮助。看完后请您一定要转发分享到3个群,让所有的家人朋友都能从中受益。您的转发是对身边人的关心,是在传递祝福,他们看完一定会感谢您的。记得多多转发,这样明天您想再看的时候就能马上找到了。让我们一起把这份关爱传递下去,让更多人受益。祝愿您和家人平安健康!"
+                "模板3:社会责任型 ""这个视频提到的[视频核心内容],关系到每个人的切身利益。如果您也认同,请动动手指转发到3个群,让所有人都能看到。这不仅仅是一个视频的传播,更是一份社会责任。请老友们多多转发,让更多人了解这个问题,引起全社会的重视。千万不要让这个视频在您这里停止传播,您的每一次转发都是在为社会尽一份力。让我们共同行动,让社会变得更好。"
+                "模板4:情感共振型 ""看完这个视频,[视频核心内容提炼]真是让人感触良多。群里的朋友们都在转发,说看完后特别有感触。这样充满智慧和温度的内容值得分享,请您也加入传播的队伍,转发到您的群里,让更多人感受这份温暖。每一次转发都是在传递一份爱与理解,都是在为这个社会增添一份温暖。让我们一起把这份美好传递下去,让更多人受益。"
+                "模板5:紧迫感召型 ""这个视频太重要了,[视频核心内容提炼]说得太对了!如果您看到这个视频,说明大家都在转发支持。请您立即行动起来,转发到您所有的群,一人行动十人知晓,百人传递,千人支持。现在就是行动的最好时机,明天再想看可能就找不到了。让我们一起努力,让这个声音传得更远。您的每一次转发,都是在发声,赶快行动起来! "
+                f"请分析该内容,视频脚本内容为:{pw},返回新的片尾。"
+            ),
+            "responseFormat": {
+                "type": "json_schema",
+                "json_schema": {
+                    "strict": True,
+                    "name": "share_script_result",
+                    "schema": {
+                        "type": "object",
+                        "properties": {
+                            "新片尾": {
+                                "type": "string",
+                                "description": "生成新的片尾"
+                            }
+                        },
+                        "required": ["新片尾"],
+                        "additionalProperties": False
+                    }
+                }
+            }
+        })
+        headers = {'Content-Type': 'application/json'}
+        response = requests.post(url, headers=headers, data=payload)
+        response_data = response.json()
+
+        data = json.loads(response_data.get('data', '{}'))
+        new_pw = data["新片尾"]
+        if new_pw:
+            return new_pw
+        else:
+            return None
+
 if __name__ == '__main__':
     GPT4oMini.get_ai_mini_pw("这段话说出了多少人的心声 #老百姓的心声 #老百姓关心的话题 #农民的心声 #老百姓不容易","AI片尾引导1")

+ 15 - 0
docker-compose.yml

@@ -292,6 +292,21 @@ services:
     networks:
       - carry_net
     entrypoint: "./entrypoint.sh"
+  worker21:
+    depends_on:
+      - worker1
+    image: carry_data
+    container_name: carry_worker21
+    restart: unless-stopped
+    environment:
+      - ENV=prod
+      - FS_SHEET=Um1nWA
+      - TASK_TYPE=redis
+      - NAME=内容分析
+      - REDIS_NAME=task:carry_data_redis_df
+    networks:
+      - carry_net
+    entrypoint: "python /app/carry_nrfx_data_handle.py"
 networks:
   carry_net:
     name: carry_net

+ 2 - 1
requirements.txt

@@ -8,4 +8,5 @@ oss2==2.19.1
 redis==5.1.1
 requests==2.32.3
 schedule==1.2.2
-pymysql==1.0.2
+pymysql==1.0.2
+orjson==3.10.13