# -*- coding:utf-8 -*-
import threading
from mttkinter import mtTkinter as tk
from tkinter import filedialog, ttk, messagebox
import os
import ffmpeg
from speech_synthesis import ss_and_export
import ctypes
import inspect

window = tk.Tk()
window.title('无障碍电影辅助工具')  # 标题
window.geometry('600x400')  # 窗口尺寸
window.resizable(0, 0)
# window.iconphoto(False, tk.PhotoImage(file="cropped-eagle.png"))
window.iconbitmap("eagle64.ico")
# window.tk.call("wm", "iconphoto", window._w, tk.PhotoImage(file="cropped-eagle.png"))


def open_video_file():
    """
    打开文件
    :return:
    """
    video_path = filedialog.askopenfilename(title=u'选择文件',
                                            filetype=[("视频文件", ".avi"), ("视频文件", ".mp4"), ("视频文件", ".rmvb"),
                                                      ("视频文件", ".mkv")])
    if len(video_path) != 0:
        print('打开文件：', video_path)
        inputFilePath.set(video_path)
        # 获取视频的时长等信息，初始化开始结束时间
        info = ffmpeg.probe(video_path)
        vs = next(c for c in info['streams'] if c['codec_type'] == 'video')
        print(vs)
        try:
            duration = int(float(vs['duration']))
            hours = int(duration / 3600)
            minutes = int(duration / 60 - 60 * hours)
            seconds = int(duration - 60 * minutes - 3600 * hours)
            endTime.set("%02d:%02d:%02d" % (hours, minutes, seconds))
        except:
            for k in vs['tags'].keys():
                k_l = str.lower(k)
                if 'duration' in k_l:
                    endTime.set(vs['tags'][k])


def find_save_file():
    """
    找到保存表格的地址
    :return:
    """
    video_path = inputFilePath.get()
    defaultName = ""
    if "." in os.path.basename(video_path):
        defaultName = os.path.basename(video_path).split('.')[0]

    book_path = filedialog.asksaveasfilename(title=u'保存文件至',
                                             initialdir=os.path.dirname(video_path),
                                             initialfile=defaultName,
                                             filetype=[('Excel工作簿', ".xlsx"), ('Excel 97工作簿', ".xls")],
                                             defaultextension='.xlsx')
    print('保存文件至：', book_path)
    outputFilePath.set(book_path)


def trans_to_seconds(timepoint):
    time_in_seconds = 0
    timepoint = timepoint.split(':')
    units = 1
    for i in range(len(timepoint) - 1, -1, -1):
        time_in_seconds += units * float(timepoint[i])
        units *= 60
    return time_in_seconds


def start_process(p, p_label, state, intervals=100):
    """
    启动进度条
    :param p: 进度条组件
    :param p_label: 进度条对应百分比文本
    :param state: 进度条与任务用于通信对齐的变量，代表任务的实际进度
    :param intervals: 进度条前进所需时间
    :return:
    """
    print("进度条开始滚动")
    p.start(interval=int(intervals))
    lastState = state[0]
    while True:
        # 当前进度不为None且与上一进度不一样且当前进度比进度条的状态要多时，对进度条状态进行更新
        if state[0] and state[0] != lastState and state[0] * 100 > p['value']:
            p['value'] = round(state[0] * 100, 2)
            lastState = state[0]
        p_label['text'] = format(format(p['value'], '2.2f'), "5s") + "%"
        if p['value'] == 100.0:
            p.stop()
            p['value'] = 100.0
            break
    print("进度条停止")


def start_detect():
    """
    开始检测旁白
    :return:
    """
    # 检测各种输入的合理性
    video_path = inputFilePath.get()
    book_path = outputFilePath.get()

    if len(video_path) == 0:
        messagebox.showwarning('警告', "请输入视频文件路径")
        return
    if len(book_path) == 0:
        messagebox.showwarning("警告", "请输入表格存放路径")
        return

    # 开始检测后，将“开始检测”按钮设置为不可点击状态，“停止检测”按钮设置为可点击状态
    startDetection.config(state=tk.DISABLED)
    stopDetection.config(state=tk.ACTIVE)

    processState.set("正在启动中……")
    from narratage_detection import detect
    # 显示进度条及开始检测
    progressbar_1.grid(column=2, row=1, sticky="W")
    progress_1.grid(column=3, row=1, sticky="W")
    processState.set("开始检测")
    # 多线程同步进行检测和进度条更新
    state = [None]
    threads = [
        threading.Thread(target=start_process, args=(progressbar_1, progress_1, state, 100000), name="startProgress1"),
        threading.Thread(target=detect,
                         args=(video_path, startTime.get(), endTime.get(), book_path, state, hasSubtitle.get()),
                         name="detect")]
    for t in threads:
        t.start()
    # 线程完成任务后结束线程
    for t in threads:
        t.join()
    # 将进度条的进度拉满到100%，并给出“任务已完成”的提示
    progressbar_1['value'] = 100
    progress_1['text'] = '100.0%'
    processState.set("任务已完成")
    # 检测完成后，将“停止检测”按钮设置为不可点击状态，”开始检测“按钮设置为可点击状态
    stopDetection.config(state=tk.DISABLED)
    startDetection.config(state=tk.ACTIVE)


def stop_detect():
    for x in threading.enumerate():
        if x.getName() in ["startDetect", "startProgress1", "detect"]:
            _async_raise(x.ident, SystemExit)
    # 设置检测状态为”已停止“，”停止检测“按钮为不可点击状态，”开始检测“按钮为可点击状态，检测进度条初始化为0，并隐藏
    processState.set("已停止")
    stopDetection.config(state=tk.DISABLED)
    startDetection.config(state=tk.ACTIVE)
    progressbar_1.stop()
    progressbar_1['value'] = 0
    progress_1['text'] = "00.00%"
    progressbar_1.grid_forget()
    progress_1.grid_forget()


def open_sheet_file():
    """
    选择导入的旁白解说脚本表格所在位置
    :return:
    """
    sheet_path = filedialog.askopenfilename(title=u'选择文件',
                                            filetype=[('Excel工作簿', ".xlsx"), ('Excel 97工作簿', ".xls")], )
    if len(sheet_path) != 0:
        print("打开表格", sheet_path)
        narratagePath.set(sheet_path)


def find_save_dir():
    """
    寻找存储音频的文件夹
    :return:
    """
    audio_dir = filedialog.askdirectory(title=u'保存文件至')
    print('保存音频于：', audio_dir)
    audioDir.set(audio_dir)


def set_caption_file():
    """
    设置字幕文件存储路径（使用存放音频的文件夹作为默认文件夹、旁白表格名作为默认字幕名）
    :return:
    """
    defaultName = os.path.basename(videoPath.get()).split('.')[0] + ".srt"
    defaultDir = audioDir.get()
    caption_path = filedialog.asksaveasfilename(title=u'保存字幕文件至',
                                                initialdir=defaultDir,
                                                initialfile=defaultName,
                                                filetype=[('字幕文件', ".srt")])
    captionPath.set(caption_path)


def confirm_video_path():
    # 仅能打开mp4\rmvb\avi\mkv格式的文件
    video_path = filedialog.askopenfilename(title=u'选择文件',
                                            filetype=[("视频文件", ".avi"), ("视频文件", ".mp4"), ("视频文件", ".rmvb"),
                                                      ("视频文件", ".mkv")])
    videoPath.set(video_path)


def start_synthesis():
    """
    开始合成语音
    :return:
    """
    video_path = videoPath.get()
    audio_dir = audioDir.get()
    sheet_path = narratagePath.get()
    speed = float(audio_speed.get().split('(')[0])
    caption_path = captionPath.get()

    # 判断各个变量的合理性
    if len(video_path) == 0:
        messagebox.showwarning("警告", "请给出原视频存放路径")
        return
    elif not os.path.exists(video_path):
        messagebox.showwarning("警告", "当前给出的视频路径有误，请检查一遍")
        return
    if len(sheet_path) == 0:
        messagebox.showwarning("警告", "请选择你要处理的表格")
        return
    elif not os.path.exists(sheet_path):
        messagebox.showwarning("警告", "当前输入的表格不存在，请检查一遍。")
        return
    if len(audio_dir) == 0:
        messagebox.showwarning("警告", "请选择音频存放路径")
        return
    elif not os.path.exists(audio_dir):
        messagebox.showwarning("警告", "当前音频存放路径有误，请检查一遍。")
        return
    if len(caption_path) == 0:
        messagebox.showwarning("警告", "请选择字幕文件存放路径")
        return

    # 显示进度条、进度条百分比及任务状态提示文本
    startSynthesis.config(state=tk.DISABLED)
    stopSynthesis.config(state=tk.ACTIVE)
    progressbar_2.grid(column=2, row=2)
    progress_2.grid(column=3, row=2)
    processState_2.set("开始生成音频及字幕")

    # 多线程同时实现语音合成+字幕导出、进度条
    state = [None]
    threads = [
        threading.Thread(target=start_process, args=(progressbar_2, progress_2, state, 100000), name="startProgress2"),
        threading.Thread(target=ss_and_export,
                         args=(video_path, sheet_path, audio_dir, speed, caption_path, state), name="ssAndExport")]
    for t in threads:
        t.start()
    for t in threads:
        t.join()
    processState_2.set("语音和字幕已导出完毕")
    startSynthesis.config(state=tk.ACTIVE)
    stopSynthesis.config(state=tk.DISABLED)


def stop_synthesis():
    print(threading.enumerate())
    for x in threading.enumerate():
        if x.getName() in ["startSynthesis", "startProgress2", "ssAndExport"]:
            _async_raise(x.ident, SystemExit)
    # 设置检测状态为”已停止“，”停止检测“按钮为不可点击状态，”开始检测“按钮为可点击状态，检测进度条初始化为0，并隐藏
    processState_2.set("已停止")
    stopSynthesis.config(state=tk.DISABLED)
    startSynthesis.config(state=tk.ACTIVE)
    progressbar_2.stop()
    progressbar_2['value'] = 0
    progress_2['text'] = "00.00%"
    progressbar_2.grid_forget()
    progress_2.grid_forget()


def thread_it(func, *args, name):
    # 创建线程
    t = threading.Thread(target=func, args=args, name=name)
    # 守护
    t.setDaemon(True)
    # 启动
    t.start()


def _async_raise(tid, exctype):
    """
    终结线程
    :param tid: 线程id
    :param exctype: 关闭方式
    :return:
    """
    tid = ctypes.c_long(tid)
    if not inspect.isclass(exctype):
        exctype = type(exctype)
    res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype))
    if res == 0:
        raise ValueError("invalid thread id")
    elif res != 1:
        ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, None)
        raise SystemError("PyThreadState_SetAsyncExc failed")


def _quit():
    window.quit()
    window.destroy()
    exit()


# 创建tab栏
tabControl = ttk.Notebook(window)

tab1 = ttk.Frame(tabControl)
tabControl.add(tab1, text="旁白位置推荐")
tab2 = ttk.Frame(tabControl)
tabControl.add(tab2, text="旁白语音合成")
tabControl.pack(expand=1, fill="both")

"""
    为”旁白位置推荐“添加按钮、进度条等部件
"""
"""
    视频信息相关内容，包含以下内容：
    - 视频文件|视频文件路径文本框|上传文件按钮
    - 视频实际开始时间|文本框
    - 视频实际结束时间|文本框
"""
video_info = ttk.LabelFrame(tab1, text=" 视频信息操作 ")
video_info.place(relx=0.05, rely=0.05, relwidth=0.9, relheight=0.4)

# rows = 4
# for i in range(rows):
#     video_info.grid_rowconfigure(i, weight=1)
#     video_info.grid_columnconfigure(i, weight=1)

input_label = ttk.Label(video_info, text="视频文件")
input_label.grid(column=0, row=0)
inputFilePath = tk.StringVar()
inputFile = ttk.Entry(video_info, width=30, textvariable=inputFilePath)
inputFile.grid(column=1, row=0, sticky="W")
upload_button = ttk.Button(video_info, text="上传文件", command=open_video_file)
upload_button.grid(column=2, row=0, sticky="W")

startTime_label = ttk.Label(video_info, text="视频实际开始时间")
startTime_label.grid(column=0, row=1)
startTime = tk.StringVar(value="00:00:00")
startTime_entered = ttk.Entry(video_info, width=11, textvariable=startTime)
startTime_entered.grid(column=1, row=1, sticky="W")

endTime_label = ttk.Label(video_info, text="视频实际结束时间")
endTime_label.grid(column=0, row=2)
endTime = tk.StringVar(value="23:59:59")
endTime_entered = ttk.Entry(video_info, width=11, textvariable=endTime)
endTime_entered.grid(column=1, row=2, sticky="W")

subtitle_label = ttk.Label(video_info, text="是否有字幕")
subtitle_label.grid(column=0, row=3)
hasSubtitle = tk.IntVar()
choice_1 = tk.Radiobutton(video_info, text="未知", variable=hasSubtitle, value=0)
choice_1.grid(column=1, row=3, sticky="W")
choice_2 = tk.Radiobutton(video_info, text="是", variable=hasSubtitle, value=1)
choice_2.grid(column=2, row=3, sticky="W")
choice_3 = tk.Radiobutton(video_info, text="否", variable=hasSubtitle, value=2)
choice_3.grid(column=3, row=3, sticky="W")

"""
    检测步骤相关内容，包含以下内容：
    - 输出表格路径|输出表格路径文本框|打开文件夹
    - 开始检测按钮|当前检测状态提示文本|任务进度条|进度条百分比
    - 停止检测按钮
"""
detect_command = ttk.LabelFrame(tab1, text=" 检测步骤 ")
detect_command.place(relx=0.05, rely=0.6, relwidth=0.9, relheight=0.3)

# rows = 4
# for i in range(rows):
#     detect_command.grid_rowconfigure(i, weight=1)
#     detect_command.grid_columnconfigure(i, weight=1)

output_label = ttk.Label(detect_command, text="输出表格")
output_label.grid(column=0, row=0)
outputFilePath = tk.StringVar()
outputFile = ttk.Entry(detect_command, width=30, textvariable=outputFilePath)
outputFile.grid(column=1, row=0, sticky="W")
save_button = ttk.Button(detect_command, text="打开文件夹", command=find_save_file)
save_button.grid(column=2, row=0, sticky="W")

startDetection = ttk.Button(detect_command, text="开始检测", command=lambda: thread_it(start_detect, name="startDetect"))
startDetection.grid(column=0, row=1)
processState = tk.StringVar()
stateLabel = tk.Label(detect_command, textvariable=processState, fg="green")
stateLabel.grid(column=1, row=1, sticky="W")
progressbar_1 = ttk.Progressbar(detect_command, length=80, mode="determinate")
progress_1 = tk.Label(detect_command, text="0%")

stopDetection = ttk.Button(detect_command, text="停止检测", command=lambda: thread_it(stop_detect, name="stopDetect"))
stopDetection.grid(column=0, row=2)
stopDetection.config(state=tk.DISABLED)

"""
    为旁白语音合成添加部件
"""
"""
    语音相关设置，包含以下内容：
    - 原视频|视频路径|上传文件按钮
    - 旁白脚本表格|表格路径|上传文件按钮
    - 旁白语速选择
"""
audio_info = ttk.LabelFrame(tab2, text=" 语音相关设置 ")
audio_info.place(relx=0.05, rely=0.05, relwidth=0.9, relheight=0.3)

# rows = 3
# for i in range(rows):
#     audio_info.grid_rowconfigure(i, weight=1)
#     audio_info.grid_columnconfigure(i, weight=1)

video_label = ttk.Label(audio_info, text="原视频")
video_label.grid(column=0, row=0)
videoPath = tk.StringVar()
videoPath_input = ttk.Entry(audio_info, width=30, textvariable=videoPath)
videoPath_input.grid(column=1, row=0, sticky="W")
upload_button_3 = ttk.Button(audio_info, text="上传文件", command=confirm_video_path)
upload_button_3.grid(column=2, row=0)

narratage_label = ttk.Label(audio_info, text="旁白脚本表格")
narratage_label.grid(column=0, row=1)
narratagePath = tk.StringVar()
narratagePath_input = ttk.Entry(audio_info, width=30, textvariable=narratagePath)
narratagePath_input.grid(column=1, row=1, sticky="W")
upload_button_2 = ttk.Button(audio_info, text="上传文件", command=open_sheet_file)
upload_button_2.grid(column=2, row=1)

speed_label = ttk.Label(audio_info, text="旁白语速")
speed_label.grid(column=0, row=2)
audio_speed = tk.StringVar()
speedChosen = ttk.Combobox(audio_info, width=12, textvariable=audio_speed)
speedChosen['values'] = (
    "1.00(4字/秒)", "1.10(4.5字/秒)", "1.25(5字/秒)", "1.50(6字/秒)", "1.75(7字/秒)", "2.00(8字/秒)", "2.50(10字/秒)")
speedChosen.current(0)
speedChosen.grid(column=1, row=2, sticky="W")

"""
    语音合成步骤，包含以下内容：
    - 输出音频存放于|路径文本框|打开文件夹
    - 输出字幕文件|路径文本框|打开文件夹
    - 开始合成按钮|当前检测状态提示文本|任务进度条|进度条百分比
    - 停止合成按钮
"""
synthesis_command = ttk.LabelFrame(tab2, text=" 语音合成步骤 ")
synthesis_command.place(relx=0.05, rely=0.45, relwidth=0.9, relheight=0.4)

# rows = 4
# for i in range(rows):
#     synthesis_command.grid_rowconfigure(i, weight=1)
#     synthesis_command.grid_columnconfigure(i, weight=1)

audioDir_label = ttk.Label(synthesis_command, text="输出音频存放于")
audioDir_label.grid(column=0, row=0, sticky="")
audioDir = tk.StringVar()
audioDir_input = ttk.Entry(synthesis_command, width=30, textvariable=audioDir)
audioDir_input.grid(column=1, row=0)
save_button_2 = ttk.Button(synthesis_command, text="打开文件夹", command=find_save_dir)
save_button_2.grid(column=2, row=0)

caption_label = ttk.Label(synthesis_command, text="输出字幕文件于")
caption_label.grid(column=0, row=1)
captionPath = tk.StringVar()
captionPath_input = ttk.Entry(synthesis_command, width=30, textvariable=captionPath)
captionPath_input.grid(column=1, row=1)
save_button_2 = ttk.Button(synthesis_command, text="打开文件夹", command=set_caption_file)
save_button_2.grid(column=2, row=1)

startSynthesis = ttk.Button(synthesis_command, text="开始合成",
                            command=lambda: thread_it(start_synthesis, name="startSynthesis"))
startSynthesis.grid(column=0, row=2)
processState_2 = tk.StringVar()
stateLabel_2 = tk.Label(synthesis_command, textvariable=processState_2, fg="green")
stateLabel_2.grid(column=1, row=2, sticky="W")
progressbar_2 = ttk.Progressbar(synthesis_command, length=100, mode="determinate")
progress_2 = tk.Label(synthesis_command, text="00.00%")
stopSynthesis = ttk.Button(synthesis_command, text="停止合成",
                           command=lambda: thread_it(stop_synthesis, name="stopSynthesis"))
stopSynthesis.grid(column=0, row=3)
stopSynthesis.config(state=tk.DISABLED)

# 刷新显示
window.mainloop()
