import os
import sys
import shutil
import urllib.request
import urllib.error
import subprocess
from concurrent.futures import ThreadPoolExecutor, as_completed
import time
import tempfile

# --- 配置区域 ---
MAX_WORKERS = 32  # 并发线程数，网络好可以改大到 64

def check_ffmpeg():
    """检查 ffmpeg 是否存在"""
    try:
        subprocess.run(["ffmpeg", "-version"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
        return True
    except FileNotFoundError:
        return False

def get_ts_urls(m3u8_url):
    """从 M3U8 链接解析出所有的 .ts 下载地址"""
    print(f"[1/4] 正在解析 m3u8 文件: {m3u8_url} ...")
    try:
        req = urllib.request.Request(m3u8_url, headers={'User-Agent': 'Mozilla/5.0'})
        with urllib.request.urlopen(req) as response:
            content = response.read().decode('utf-8')
    except Exception as e:
        print(f"[ERROR] 无法获取 m3u8: {e}")
        return [], None

    base_url = m3u8_url.rsplit('/', 1)[0] + '/'
    lines = content.splitlines()
    ts_urls = []
    
    # 简单的解析逻辑
    for line in lines:
        line = line.strip()
        if not line or line.startswith('#'):
            continue
        
        # 处理相对路径和绝对路径
        if line.startswith('http'):
            ts_urls.append(line)
        else:
            ts_urls.append(base_url + line)
            
    print(f"      成功解析，共找到 {len(ts_urls)} 个分片。")
    return ts_urls

def download_segment(idx, url, save_dir, retries=3):
    """下载单个分片，带重试机制"""
    filename = f"{idx:05d}.ts"
    filepath = os.path.join(save_dir, filename)
    
    if os.path.exists(filepath):
        # 如果文件存在且大小正常（>1KB），跳过
        if os.path.getsize(filepath) > 1024:
            return idx, True

    for i in range(retries):
        try:
            req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'})
            with urllib.request.urlopen(req, timeout=10) as response:
                with open(filepath, 'wb') as f:
                    f.write(response.read())
            return idx, True
        except Exception:
            if i == retries - 1:
                return idx, False
            time.sleep(1)

def merge_ts_files(save_dir, output_filename):
    """使用 ffmpeg 合并下载好的 ts 文件"""
    print(f"\n[3/4] 正在合并分片...")
    
    # 生成 filelist.txt
    ts_files = sorted([f for f in os.listdir(save_dir) if f.endswith('.ts')])
    list_path = os.path.join(save_dir, "filelist.txt")
    
    with open(list_path, 'w', encoding='utf-8') as f:
        for ts in ts_files:
            # ffmpeg concat 的格式: file '文件名'
            f.write(f"file '{ts}'\n")

    # 调用 ffmpeg 合并
    if not output_filename.endswith(".mp4"):
        output_filename += ".mp4"
    
    # 获取绝对路径，确保在切换目录后也能找到输出位置
    abs_output = os.path.abspath(output_filename)
    
    command = [
        "ffmpeg", 
        "-loglevel", "error", # 屏蔽非致命的警告信息
        "-stats",             # 依然显示进度条
        "-f", "concat",
        "-safe", "0",
        "-i", "filelist.txt", 
        "-c", "copy",
        "-bsf:a", "aac_adtstoasc",
        "-y", # 覆盖输出
        abs_output
    ]
    
    try:
        # cwd=save_dir 确保 ffmpeg 能找到 filelist 里列出的相对路径文件
        subprocess.run(command, check=True, cwd=save_dir)
        print(f"\n[4/4] 全部完成！文件已保存: {abs_output}")
    except subprocess.CalledProcessError as e:
        print(f"[ERROR] 合并失败: {e}")

def main():
    print("=== LunaTV 极速多线程下载器 (Turbo) ===")
    
    if not check_ffmpeg():
        print("[ERROR] 必须安装 ffmpeg 才能使用此脚本。")
        return

    # 获取输入
    url_input = input("请输入 m3u8 链接: ").strip()
    if not url_input: return
    
    name_input = input("请输入保存文件名 (例如 movie): ").strip()
    if not name_input: name_input = "output_video"

    temp_dir = tempfile.mkdtemp(prefix=f"luna_{name_input}_")
    print(f"[INFO] 创建临时文件夹: {temp_dir}")
    
    try:
        # 1. 解析
        ts_urls = get_ts_urls(url_input)
        if not ts_urls:
            return

        # 2. 多线程并发下载
        print(f"[2/4] 启动 {MAX_WORKERS} 线程极速下载中... (请稍候)")
        
        total = len(ts_urls)
        completed = 0
        
        with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:
            # 提交所有任务
            futures = {executor.submit(download_segment, i, url, temp_dir): i for i, url in enumerate(ts_urls)}
            
            # 监控进度
            for future in as_completed(futures):
                idx, success = future.result()
                if success:
                    completed += 1
                    # 打印进度条
                    percent = (completed / total) * 100
                    bar = '█' * int(percent // 2) + '-' * (50 - int(percent // 2))
                    sys.stdout.write(f"\r|{bar}| {percent:.1f}% ({completed}/{total})")
                    sys.stdout.flush()
                else:
                    print(f"\n[WARN] 分片 {idx} 下载失败，可能会导致视频有轻微卡顿。")

        # 3. 合并
        merge_ts_files(temp_dir, name_input)
    
    finally:
        # 4. 清理临时文件
        if os.path.exists(temp_dir):
            try:
                shutil.rmtree(temp_dir)
                print(f"\n[INFO] 临时文件夹 {temp_dir} 已清理。")
            except Exception as e:
                print(f"\n[WARN] 清理临时文件夹 {temp_dir} 失败: {e}")

if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        print("\n\n用户取消操作。")
