import os
import sys
import cv2
import time
import numpy as np
import subprocess
import sounddevice as sd
from scipy.io import wavfile
from scipy.signal import resample
from PyQt6 import QtCore, QtGui, QtWidgets
from PyQt6.QtCore import pyqtSignal, QThread
from talkingface.audio_model import AudioModel
from talkingface.render_model import RenderModel
import queue
import sys
import requests
from bs4 import BeautifulSoup
from gne import GeneralNewsExtractor
import requests
from requests.adapters import HTTPAdapter
from requests.exceptions import RequestException
from urllib3.util.retry import Retry
import httpx
#################################################33333





#####################################################3333
class VideoWindow(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Video Window")
        self.setGeometry(100, 100, 800, 600)
        
        # 使用布局使界面更具可扩展性
        self.layout = QtWidgets.QVBoxLayout()
        self.setLayout(self.layout)
        
        self.label = QtWidgets.QLabel(self)
        self.label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
        self.label.setScaledContents(True)  # 自动缩放图像以适应 QLabel 大小
        self.layout.addWidget(self.label)
        
        # 尝试加载静态图像 '0.jpg' 作为初始画面
        image_path = "subscribe.png"  # 确保 '0.jpg' 位于当前工作目录或提供完整路径
        test_image = cv2.imread(image_path)
        
        if test_image is not None:
            # 调整图像大小以匹配 QLabel 的尺寸 (800x600)
            test_image = cv2.resize(test_image, (800, 600))
            print(f"Loaded initial image from {image_path}.")
        else:
            # 如果加载失败，使用默认的绿色背景
            print(f"Failed to load {image_path}. Using green screen instead.")
            test_image = np.zeros((600, 800, 3), dtype=np.uint8)
            test_image[:] = (0, 255, 0)  # 绿色背景
        
        self.update_frame(test_image)

    @QtCore.pyqtSlot(np.ndarray)
    def update_frame(self, frame):
        if frame is None:
            print("Received None frame")
            return
        
        if frame.size == 0:
            print("Received empty frame")
            return
        
        try:
            # 确保传入的帧是 BGR 格式，为 OpenCV 默认格式
            if len(frame.shape) == 3 and frame.shape[2] == 3:
                frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            else:
                frame_rgb = frame.copy()
            
            height, width, channel = frame_rgb.shape
            bytes_per_line = 3 * width
            
            # 创建 QImage 对象
            q_image = QtGui.QImage(
                frame_rgb.data,
                width,
                height,
                bytes_per_line,
                QtGui.QImage.Format.Format_RGB888
            )
            
            # 转换为 QPixmap 并设置到 QLabel
            pixmap = QtGui.QPixmap.fromImage(q_image)
            self.label.setPixmap(pixmap)
        except Exception as e:
            os._exit(1) 
            print(f"Error updating frame: {e}")

class VideoAudioWorker(QThread):
    frame_signal = pyqtSignal(np.ndarray)  # 当新的帧准备好时发射信号

    def __init__(self, parent=None):
        super().__init__(parent)
        self._run_flag = True
        
        # 初始化模型
        self.audioModel = AudioModel()
        try:
            self.audioModel.loadModel("checkpoint/audio.pkl")
            print("AudioModel loaded successfully.")
        except Exception as e:
            print(f"Failed to load AudioModel: {e}")
            self._run_flag = False
        
        self.renderModel = RenderModel()
        try:
            self.renderModel.loadModel("checkpoint/render.pth")
            print("RenderModel loaded successfully.")
        except Exception as e:
            print(f"Failed to load RenderModel: {e}")
            self._run_flag = False
        
        # 设置测试视频和关键点
        self.test_video = "test3"
        self.pkl_path = f"video_data/{self.test_video}/keypoint_rotate.pkl"
        self.video_path = f"video_data/{self.test_video}/circle.mp4"
        try:
            self.renderModel.reset_charactor(self.video_path, self.pkl_path)
            print("Character reset successfully.")
        except Exception as e:
            print(f"Failed to reset character: {e}")
            self._run_flag = False
        
        # 音频处理
        self.wavpath = "output_audio.wav"
        self.wavpath1 = "output_audio1.wav"
        try:
            self.change_sample_rate(self.wavpath, self.wavpath1)
        except Exception as e:
            print(f"Failed to change sample rate: {e}")
            self._run_flag = False
        
        try:
            self.rate, self.wav = wavfile.read(self.wavpath1, mmap=False)
            print(f"Loaded WAV file with rate: {self.rate}")
        except Exception as e:
            print(f"Failed to read WAV file: {e}")
            self.wav = None
            self._run_flag = False
        
        if self.wav is not None:
            # 归一化音频数据
            if self.wav.dtype == np.int16:
                self.wav = self.wav.astype(np.float32) / 32768.0
            elif self.wav.dtype == np.int32:
                self.wav = self.wav.astype(np.float32) / 2147483648.0
            elif self.wav.dtype == np.uint8:
                self.wav = (self.wav.astype(np.float32) - 128) / 128.0
            else:
                print(f"Unsupported WAV format: {self.wav.dtype}")
                self._run_flag = False
            
            # 确保采样率为16000
            self.sample_rate = 16000
            if self.rate != self.sample_rate:
                new_length = int(len(self.wav) * self.sample_rate / self.rate)
                if new_length > 0:
                    self.wav = resample(self.wav, new_length)
                    print(f"Resampled WAV to {self.sample_rate} Hz.")
                else:
                    print(f"Invalid resampling length: {new_length}")
                    self._run_flag = False
        else:
            print("WAV data is not loaded properly.")
            self._run_flag = False
        
        if self._run_flag:
            # 设置帧率和每帧样本数
            self.frame_rate = 25
            self.frame_duration = 1.0 / self.frame_rate
            self.samples_per_frame = int(self.sample_rate * self.frame_duration)
            self.index_ = 0
            
            # 初始化音频缓冲区
            self.audio_queue = queue.Queue(maxsize=20)  # 根据需要调整大小

    def change_sample_rate(self, input_file, output_file, target_sample_rate=16000):
        if not os.path.isfile(input_file):
            raise FileNotFoundError(f"Input file {input_file} not found.")
        command = ['ffmpeg', '-y', '-i', input_file, '-ar', str(target_sample_rate), output_file]
        try:
            result = subprocess.run(command, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            print(f"Sample rate changed to {target_sample_rate} Hz and saved as {output_file}.")
        except subprocess.CalledProcessError as e:
            error_msg = e.stderr.decode()
            print(f"Error occurred during sample rate change: {error_msg}")
            raise
     

                       
    def run(self):
        if not self._run_flag:
            print("Initialization failed or models not loaded. Exiting thread.")
            return
        
        try:
            # 定义音频回调函数
            def audio_callback(outdata, frames, time_info, status):
                if status:
                    print(f"Stream status: {status}")
                try:
                    # 从队列中取出音频数据
                    data = self.audio_queue.get_nowait()
                except queue.Empty:
                    # 如果队列为空，则填充静音
                    data = np.zeros(frames, dtype=np.float32)
                    print("Audio queue empty, filling with silence.")
                # 将数据写入输出缓冲区
                outdata[:, 0] = data
            kernel = np.array([[0, -1, 0],[-1, 5,-1],[0, -1, 0]])
            # 开启音频输出流
            with sd.OutputStream(samplerate=self.sample_rate, channels=1, dtype='float32',
                                 callback=audio_callback, blocksize=self.samples_per_frame):
                print("Audio stream started.")
                
                # 记录开始时间
                start_time = time.perf_counter()
                
                while self._run_flag and self.index_ * self.samples_per_frame < len(self.wav):
                    # 计算目标时间
                    target_time = start_time + self.index_ * self.frame_duration
                    current_time = time.perf_counter()
                    remaining_time = target_time - current_time
                    
                    if remaining_time > 0:
                        time.sleep(remaining_time)
                    elif abs(remaining_time) > self.frame_duration:
                        print(f"Lagging by {-remaining_time:.2f} seconds at index {self.index_}")
                    
                    frame_start_time = time.perf_counter()  # 记录帧开始处理的时间
                    
                    start = self.index_ * self.samples_per_frame
                    end = start + self.samples_per_frame
                    if end > len(self.wav):
                        end = len(self.wav)
                    pcm_data = self.wav[start:end]
                    
                    # 处理音频块
                    try:
                        mouth_frame = self.audioModel.interface_frame(pcm_data)
                        mouth_frame = cv2.filter2D(mouth_frame, -1, kernel)
                        print(f"Processed audio chunk at index {self.index_}")
                    except Exception as e:
                        print(f"Error processing audio at index {self.index_}: {e}")
                        os._exit(1) 
                        mouth_frame = None
                    
                    # 渲染视频帧
                    try:
                        frame = self.renderModel.interface(mouth_frame)
                        frame = cv2.bilateralFilter(frame, 9, 75, 75)
                        if frame is not None:
                            print(f"Generated frame at index {self.index_}")
                        else:
                            print(f"RenderModel returned None at index {self.index_}")
                    except Exception as e:
                        print(f"Error generating frame at index {self.index_}: {e}")
                        frame = None
                    
                    # 发射信号到主线程更新视频帧
                    self.frame_signal.emit(frame)
                    print(f"Emitted frame at index {self.index_}")
                    
                    # 将音频数据推入队列
                    try:
                        self.audio_queue.put_nowait(pcm_data)
                        print(f"Enqueued audio chunk at index {self.index_}")
                    except queue.Full:
                        print("Audio queue is full, dropping audio chunk.")
                    
                    # 更新索引
                    self.index_ += 1
                    
                    # 计算处理时间并调整睡眠时间
                    frame_end_time = time.perf_counter()
                    processing_time = frame_end_time - frame_start_time
                    sleep_time = self.frame_duration - processing_time
                    if sleep_time > 0:
                        time.sleep(sleep_time)
                    else:
                        print(f"Warning: Processing time {processing_time:.4f}s exceeds frame duration at index {self.index_ - 1}")
                
                print("Audio stream finished.")
        except Exception as e:
            os._exit(1) 
            print(f"Error in worker thread: {e}")
        finally:
             os._exit(1) 
        print("Worker thread exiting.")
    
    def stop(self):
        print("Stopping worker thread...")
        self._run_flag = False
        self.wait()
        print("Worker thread stopped.")
def fetch_html_content(url):
    """Fetch HTML content using httpx"""
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36"
    }
    
    try:
        response = httpx.get(url, headers=headers, timeout=10, proxies=None)
        if response.status_code == 200:
            return response.text
        else:
            return None
    except httpx.RequestError as e:
        print(f"Request failed: {e}")
        return None
def extract_news_content(html):
    """使用 GeneralNewsExtractor 提取新闻内容"""
    extractor = GeneralNewsExtractor()
    result = extractor.extract(html)
    return result
def getTTS(text):
    wave_path="output_audio.wav"
    main_process(ref_audio, ref_text, text, ema_model, remove_silence,wave_path)
def getTTS1(text):
    url = "http://127.0.0.1:9880/tts"

    params = {
             "text": text,
             "text_lang": "zh",
             "ref_audio_path": "9.wav",
             "prompt_lang": "zh",
             "prompt_text": "我告诉你一个无比真实的事，差不多",
             "text_split_method": "cut5",
             "batch_size": 30,
             "media_type": "wav",
            "streaming_mode": "true"}
    try:
       response = requests.get(url, params=params, stream=True)
       response.raise_for_status()  # Raise an error for bad status codes

       # Save the audio file if the request is successful
       with open('output_audio.wav', 'wb') as f:
         for chunk in response.iter_content(chunk_size=8192):
            f.write(chunk)
    
       print("Audio saved as output_audio.wav")

    except requests.exceptions.RequestException as e:
         print(f"An error occurred: {e}")

def main():
  app = QtWidgets.QApplication(sys.argv)
  try:
    href_value = sys.argv[1]
    #print(f"Fetching HTML content from: {href_value}")
    
    # 获取 HTML 内容
    #html_content = fetch_html_content(href_value)
    #print(html_content)
    if href_value:
        # 使用 BeautifulSoup 解析 HTML
        #soup = BeautifulSoup(html_content, 'html.parser')
        #print(f"Parsed HTML title: {soup.title.string if soup.title else 'No title'}")
        
        # 提取新闻内容
       # news_content = extract_news_content(html_content)

        text=href_value

        print("======================================"+text)
        #getTTS(text)
        
    else:
        print("Failed to fetch HTML content.")
        os._exit(1) 
    # 创建并显示主窗口
    window = VideoWindow()
    window.show()
    
    # 创建并启动后台线程
    worker = VideoAudioWorker()
    worker.frame_signal.connect(window.update_frame)
    worker.start()
    
    # 确保关闭窗口时停止线程
    def on_close():
        print("Closing application...")
        worker.stop()
        app.quit()
    
    window.destroyed.connect(on_close)
    
    # 运行应用程序
    sys.exit(app.exec())
  except Exception as e:
        print(f"Error in application: {str(e)}")
        
        os._exit(1)   # Exit th
  finally:
       os._exit(1) 

if __name__ == "__main__":
    main()
优化，性能调整，BUG