import os
import time
import requests
from datetime import datetime, timedelta
import json
import re
from concurrent.futures import ThreadPoolExecutor, TimeoutError
from urllib.parse import urljoin
from html import unescape
import sqlite3
from uuid import uuid4

# Cấu hình
base_path = r"D:\Webphim\KKphim"
path_db = r"D:\Webphim\KKphim\config\data.db"
path_m3u8 = r"D:\Webphim\KKphim\M3U8"
path_strm = r"D:\Webphim\KKphim\STRM"
servername = "https://s1.dongnq.net"
log_dir = r"D:\Webphim\KKphim\config\logs"
craw_error_file = False  # Chế độ cào lại file lỗi, mặc định False

def get_log_file_path():
    os.makedirs(log_dir, exist_ok=True)
    date_str = datetime.now().strftime("%Y-%m-%d")
    log_file = os.path.join(log_dir, f"crawl_error_{date_str}.log")
    max_size = 5 * 1024 * 1024  # 5MB
    counter = 1
    base_log_file = log_file
    while os.path.exists(log_file) and os.path.getsize(log_file) > max_size:
        log_file = os.path.join(log_dir, f"crawl_error_{date_str}_{counter}.log")
        counter += 1
    return log_file

def log_error(movie_name, slug, error_msg, episode_name=None):
    log_file = get_log_file_path()
    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    log_entry = f"[{timestamp}] Phim: {movie_name} (slug: {slug})"
    if episode_name:
        log_entry += f" - Tập: {episode_name}"
    log_entry += f" - Lỗi: {error_msg}\n"
    with open(log_file, "a", encoding="utf-8") as f:
        f.write(log_entry)

def init_db(db_path=path_db):
    conn = sqlite3.connect(db_path)
    cursor = conn.cursor()
    cursor.execute('''CREATE TABLE IF NOT EXISTS movies (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        slug TEXT NOT NULL UNIQUE,
        name TEXT,
        origin_name TEXT,
        year TEXT,
        content TEXT,
        type TEXT,
        poster_url TEXT,
        thumb_url TEXT,
        time TEXT
    )''')
    cursor.execute('''CREATE TABLE IF NOT EXISTS m3u8_files (
        access_key TEXT PRIMARY KEY,
        movie_id INTEGER,
        m3u8_path TEXT NOT NULL,
        filename TEXT,
        origin_m3u8_link TEXT,
        strm_path TEXT,
        download_status TEXT DEFAULT 'pending',  -- 'pending', 'success', 'failed'
        FOREIGN KEY (movie_id) REFERENCES movies (id),
        UNIQUE (movie_id, filename, m3u8_path)
    )''')
    cursor.execute('''CREATE TABLE IF NOT EXISTS image_files (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        movie_id INTEGER,
        image_path TEXT NOT NULL,
        image_type TEXT,  -- 'poster', 'backdrop'
        download_status TEXT DEFAULT 'pending',  -- 'pending', 'success', 'failed'
        FOREIGN KEY (movie_id) REFERENCES movies (id),
        UNIQUE (movie_id, image_path)
    )''')
    conn.commit()
    conn.close()

def generate_access_key():
    return str(uuid4())

def sanitize_filename(name, max_length=100):
    invalid_chars = r'[<>:"|?*\x00-\x1F]'
    sanitized = re.sub(invalid_chars, '', name)
    sanitized = re.sub(r'\s+', ' ', sanitized).strip()
    sanitized = sanitized.replace('/', '-').replace('\\', '-')
    # Loại bỏ dấu chấm ở cuối chuỗi
    sanitized = sanitized.rstrip('.')
    # Loại bỏ các ký tự không in được
    sanitized = ''.join(c for c in sanitized if c.isprintable())
    if len(sanitized) > max_length:
        sanitized = sanitized[:max_length]
    return sanitized

def clean_html_entities(text):
    return unescape(text)

def try_request(url, max_attempts=3, timeout=10):
    for attempt in range(max_attempts):
        try:
            response = requests.get(url, timeout=timeout)
            response.raise_for_status()
            return response
        except requests.RequestException as e:
            if attempt == max_attempts - 1:
                return None
            time.sleep(2 ** attempt)  # Exponential backoff
    return None

def create_strm(movie_folder, m3u8_path, access_key, server=servername):
    try:
        strm_filename = os.path.splitext(os.path.basename(m3u8_path))[0] + ".strm"
        strm_path = os.path.join(movie_folder, strm_filename)
        strm_content = f"{server}/{access_key}.m3u8"
        with open(strm_path, "w", encoding="utf-8") as f:
            f.write(strm_content)
        return strm_path
    except Exception as e:
        log_error(os.path.basename(movie_folder), "unknown-slug", f"Lỗi khi tạo file STRM: {str(e)}", episode_name="STRM")
        return None

def determine_movie_path(base_path, movie, for_m3u8=True):
    movie_type = movie.get("type", "single")
    country_slug = movie.get("country", [{}])[0].get("slug", "") if movie.get("country") else ""
    
    country_folder_map = {
        "viet-nam": "Việt Nam",
        "trung-quoc": "Trung Quốc",
        "thai-lan": "Thái Lan",
        "hong-kong": "Hồng Kông",
        "dai-loan": "Đài Loan",
        "an-do": "Ấn Độ",
        "han-quoc": "Hàn Quốc"
    }
    
    is_adult = any(category["slug"].lower() == "phim-18" for category in movie.get("category", []))
    root_path = path_m3u8 if for_m3u8 else path_strm
    
    if is_adult:
        base_folder = "Phim 18"
        return os.path.join(root_path, base_folder)
    elif movie_type == "hoathinh":
        base_folder = "Hoạt hình"
        time_str = movie.get("time", "")
        is_series = "/" in time_str
        sub_folder = "Phim bộ" if is_series else "Phim lẻ"
        return os.path.join(root_path, base_folder, sub_folder)
    elif movie_type == "tvshows":
        return os.path.join(root_path, "TvShows")
    elif movie_type == "single":
        return os.path.join(root_path, "Phim lẻ")
    elif movie_type == "series":
        base_folder = "Phim bộ"
        if country_slug in country_folder_map:
            sub_folder = country_folder_map[country_slug]
        elif country_slug in ["au-my", "anh"]:
            sub_folder = "Âu Mỹ"
        else:
            sub_folder = "Quốc Gia Khác"
        return os.path.join(root_path, base_folder, sub_folder)
    return root_path

def get_movie_kkphim(slug, skipad=True):
    conn = sqlite3.connect(path_db)
    cursor = conn.cursor()
    
    try:
        # Fetch movie data from API
        api_url = f"https://phimapi.com/phim/{slug}"
        response = try_request(api_url)
        if not response:
            conn.close()
            return {"status": False, "msg": "Lỗi khi gọi API sau 3 lần thử", "movie": {}, "errors": []}
        data = response.json()
        if not data.get("status", False):
            conn.close()
            return {"status": False, "msg": "API không trả về dữ liệu phim", "movie": {}, "errors": []}

        movie = data.get("movie", {})
        episodes = data.get("episodes", [])
        movie_name = sanitize_filename(movie.get("name", slug))

        # Check if movie exists in DB
        cursor.execute("SELECT id FROM movies WHERE slug = ?", (slug,))
        movie_result = cursor.fetchone()
        movie_id = movie_result[0] if movie_result else None

        if not movie_id:
            cursor.execute('''INSERT INTO movies (slug, name, origin_name, year, content, type, poster_url, thumb_url, time)
                VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)''', 
                (slug, movie.get("name", ""), movie.get("origin_name", ""), movie.get("year", ""),
                 movie.get("content", ""), movie.get("type", "single"), movie.get("poster_url", ""),
                 movie.get("thumb_url", ""), movie.get("time", "")))
            movie_id = cursor.lastrowid

        movie_type = movie.get("type", "single")
        m3u8_movie_path = determine_movie_path(base_path, movie, for_m3u8=True)
        strm_movie_path = determine_movie_path(base_path, movie, for_m3u8=False)
        movie_folder_m3u8 = os.path.join(m3u8_movie_path, movie_name)
        movie_folder_strm = os.path.join(strm_movie_path, movie_name)
        
        try:
            os.makedirs(movie_folder_m3u8, exist_ok=True)
            os.makedirs(movie_folder_strm, exist_ok=True)
        except Exception as e:
            conn.close()
            log_error(movie_name, slug, f"Lỗi khi tạo thư mục: {str(e)}")
            return {"status": False, "msg": f"Lỗi khi tạo thư mục: {str(e)}", "movie": movie, "errors": []}

        # Handle images (poster and backdrop)
        errors = []
        for img_type, img_url in [("poster", movie.get("poster_url", "")), ("backdrop", movie.get("thumb_url", ""))]:
            if not img_url:
                continue
            img_filename = f"{img_type}.jpg"
            img_path = os.path.join(movie_folder_strm, img_filename)
            cursor.execute("SELECT download_status FROM image_files WHERE movie_id = ? AND image_path = ?", (movie_id, img_path))
            img_result = cursor.fetchone()
            
            if img_result and img_result[0] == "success" and os.path.exists(img_path):
                continue
                
            response = try_request(img_url)
            status = "failed"
            if response:
                try:
                    with open(img_path, "wb") as f:
                        f.write(response.content)
                    status = "success"
                except Exception as e:
                    log_error(movie_name, slug, f"Lỗi khi ghi file {img_type}: {str(e)}", episode_name=img_type.capitalize())
                    errors.append({"episode": img_type, "error": f"Lỗi khi ghi file {img_type}: {str(e)}"})
            else:
                log_error(movie_name, slug, f"Lỗi khi tải {img_type} sau 3 lần thử", episode_name=img_type.capitalize())
                errors.append({"episode": img_type, "error": f"Lỗi khi tải {img_type} sau 3 lần thử"})
            
            cursor.execute('''INSERT OR REPLACE INTO image_files (movie_id, image_path, image_type, download_status)
                VALUES (?, ?, ?, ?)''', (movie_id, img_path, img_type, status))

        season_match = re.search(r"(Season|Phần)\s*(\d+)", movie.get("name", ""), re.IGNORECASE)
        season_number = f"{int(season_match.group(2)):02}" if season_match else "01"

        # Get existing M3U8 entries
        cursor.execute("SELECT filename, m3u8_path, origin_m3u8_link, strm_path, access_key FROM m3u8_files WHERE movie_id = ?", (movie_id,))
        existing_m3u8 = {row[2]: {"filename": row[0], "m3u8_path": row[1], "strm_path": row[3], "access_key": row[4]} for row in cursor.fetchall()}
        current_m3u8_links = set()
        episodes_processed = 0

        for server in episodes:
            for episode in server.get("server_data", []):
                filename = episode.get("filename", "")
                link_m3u8 = episode.get("link_m3u8", "")
                if not link_m3u8:
                    continue
                current_m3u8_links.add(link_m3u8)

                filename = re.sub(r"Tập\s+(\d+)", lambda m: f"S{season_number}E{int(m.group(1)):02}", filename)
                clean_filename = sanitize_filename(filename)
                m3u8_filename = f"{clean_filename}.m3u8"
                m3u8_path = os.path.join(movie_folder_m3u8, m3u8_filename)

                # Check if M3U8 entry exists and matches
                if link_m3u8 in existing_m3u8:
                    if os.path.exists(m3u8_path) and existing_m3u8[link_m3u8]["filename"] == clean_filename:
                        episodes_processed += 1
                        continue

                # Download M3U8
                m3u8_response = try_request(link_m3u8)
                status = "failed"
                if m3u8_response:
                    try:
                        master_content = m3u8_response.text
                        base_url = urljoin(link_m3u8, ".")
                        variant_url = None
                        for line in master_content.splitlines():
                            if line.startswith("#EXT-X-STREAM-INF"):
                                next_line = master_content.splitlines()[master_content.splitlines().index(line) + 1]
                                if not next_line.startswith("#"):
                                    variant_url = urljoin(base_url, next_line)
                                    break

                        ad_pattern = [
                            "#EXTINF:5.8,", "ts",
                            "#EXTINF:2.8,", "ts",
                            "#EXTINF:1.68,", "ts",
                            "#EXTINF:2.44,", "ts",
                            "#EXTINF:4.72,", "ts",
                            "#EXTINF:1.96,", "ts",
                            "#EXTINF:2.76,", "ts",
                            "#EXTINF:1.96,", "ts",
                            "#EXTINF:5.24,", "ts",
                            "#EXTINF:3.36,", "ts",
                            "#EXTINF:2.0,", "ts"
                        ]

                        def is_ad_sequence(lines, start_idx):
                            """Kiểm tra xem từ start_idx có khớp với toàn bộ chuỗi ad_pattern không"""
                            if start_idx + len(ad_pattern) > len(lines):
                                return False
                            for i, pattern in enumerate(ad_pattern):
                                current_line = lines[start_idx + i].rstrip()  # Giữ nguyên định dạng, chỉ xóa khoảng trắng cuối
                                if pattern == "ts":
                                    if not current_line.endswith(".ts"):
                                        return False
                                elif pattern != current_line:
                                    return False
                            return True

                        def clean_ts_url(line, base_url):
                            """Làm sạch liên kết .ts bằng cách xóa /convertv[0-9]+/"""
                            clean_line = re.sub(r'/convertv\d+/', '/', urljoin(base_url, clean_line))
                            return clean_line

                        access_key = generate_access_key()
                        new_m3u8_content = []
                        if variant_url:
                            variant_response = try_request(variant_url)
                            if variant_response:
                                variant_content = variant_response.text
                                base_variant_url = urljoin(variant_url, ".")
                                lines = variant_content.splitlines()
                                i = 0
                                while i < len(lines):
                                    line = lines[i].rstrip()
                                    if skipad and line == "#EXT-X-DISCONTINUITY":
                                        i += 1
                                        continue
                                    if skipad and i + len(ad_pattern) <= len(lines) and is_ad_sequence(lines, i):
                                        i += len(ad_pattern)
                                        continue
                                    if line.endswith(".ts"):
                                        absolute_ts_url = clean_ts_url(line, base_variant_url)
                                        new_m3u8_content.append(absolute_ts_url)
                                    else:
                                        new_m3u8_content.append(lines[i])
                                    i += 1
                            else:
                                raise Exception("Lỗi khi tải variant M3U8 sau 3 lần thử")
                        else:
                            lines = master_content.splitlines()
                            i = 0
                            while i < len(lines):
                                line = lines[i].rstrip()
                                if skipad and line == "#EXT-X-DISCONTINUITY":
                                    i += 1
                                    continue
                                if skipad and i + len(ad_pattern) <= len(lines) and is_ad_sequence(lines, i):
                                    i += len(ad_pattern)
                                    continue
                                if line.endswith(".ts"):
                                    absolute_ts_url = clean_ts_url(line, base_url)
                                    new_m3u8_content.append(absolute_ts_url)
                                else:
                                    new_m3u8_content.append(lines[i])
                                i += 1
                        with open(m3u8_path, "w", encoding="utf-8") as f:
                            f.write("\n".join(new_m3u8_content))
                        status = "success"
                        strm_path = create_strm(movie_folder_strm, m3u8_path, access_key)
                        episodes_processed += 1
                    except Exception as e:
                        log_error(movie_name, slug, f"Lỗi khi xử lý M3U8: {str(e)}", episode_name=clean_filename)
                        errors.append({"episode": clean_filename, "error": f"Lỗi khi xử lý M3U8: {str(e)}"})
                else:
                    log_error(movie_name, slug, f"Lỗi khi tải M3U8 sau 3 lần thử", episode_name=clean_filename)
                    errors.append({"episode": clean_filename, "error": f"Lỗi khi tải M3U8 sau 3 lần thử"})

                cursor.execute('''INSERT OR REPLACE INTO m3u8_files (access_key, movie_id, m3u8_path, filename, origin_m3u8_link, strm_path, download_status)
                    VALUES (?, ?, ?, ?, ?, ?, ?)''', (access_key, movie_id, m3u8_path, clean_filename, link_m3u8, strm_path, status))

        # Remove outdated M3U8 and STRM files
        for link, info in existing_m3u8.items():
            if link not in current_m3u8_links:
                try:
                    if os.path.exists(info["m3u8_path"]):
                        os.remove(info["m3u8_path"])
                    if info["strm_path"] and os.path.exists(info["strm_path"]):
                        os.remove(info["strm_path"])
                    cursor.execute("DELETE FROM m3u8_files WHERE movie_id = ? AND origin_m3u8_link = ?", (movie_id, link))
                except Exception as e:
                    log_error(movie_name, slug, f"Lỗi khi xóa file cũ: {str(e)}", episode_name=info["filename"])

        # Save NFO file
        nfo_path = os.path.join(movie_folder_strm, "movie.nfo" if movie_type == "single" else "tvshow.nfo")
        if not os.path.exists(nfo_path):
            imdb_id = movie.get("imdb", {}).get("id", "") if movie.get("imdb") else ""
            tmdb_id = movie.get("tmdb", {}).get("id", "") if movie.get("tmdb") else ""
            nfo_content = f"""<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>
<{movie_type if movie_type == 'single' else 'tvshow'}>
    <title>{movie.get("name", "")}</title>
    <originaltitle>{movie.get("origin_name", "")}</originaltitle>
    <plot>{clean_html_entities(movie.get("content", ""))}</plot>
    <year>{movie.get("year", "")}</year>
    <thumb>{movie.get("thumb_url", "")}</thumb>
    <fanart>{movie.get("thumb_url", "")}</fanart>
    <poster>poster.jpg</poster>
    <runtime>{movie.get("time", "").replace(" phút", "")}</runtime>
    <imdbid>{imdb_id}</imdbid>
    <tmdbid>{tmdb_id}</tmdbid>
"""
            for actor in movie.get("actor", []):
                nfo_content += f"    <actor><name>{actor}</name></actor>\n"
            for director in movie.get("director", []):
                nfo_content += f"    <director>{director}</director>\n"
            for category in movie.get("category", []):
                nfo_content += f"    <genre>{category.get('name', '')}</genre>\n"
            for country in movie.get("country", []):
                nfo_content += f"    <country>{country.get('name', '')}</country>\n"
            nfo_content += f"</{movie_type if movie_type == 'single' else 'tvshow'}>"
            try:
                with open(nfo_path, "w", encoding="utf-8") as f:
                    f.write(nfo_content)
            except Exception as e:
                log_error(movie_name, slug, f"Lỗi khi ghi file NFO: {str(e)}", episode_name="NFO")
                errors.append({"episode": "NFO", "error": f"Lỗi khi ghi file NFO: {str(e)}"})

        conn.commit()
        conn.close()
        status = episodes_processed > 0
        msg = "Phim, M3U8, NFO, STRM, ảnh đã được xử lý, một số tập có thể gặp lỗi" if errors else "Phim, M3U8, NFO, STRM, ảnh đã được xử lý thành công"
        return {"status": status, "msg": msg, "movie": movie, "errors": errors}
    
    except Exception as e:
        conn.close()
        log_error(movie_name, slug, f"Lỗi không xác định: {str(e)}")
        return {"status": False, "msg": f"Lỗi không xác định: {str(e)}", "movie": {}, "errors": []}

def fetch_movie_list(page, api_url="https://phimapi.com/danh-sach/phim-moi-cap-nhat-v3?page={}"):
    try:
        response = try_request(api_url.format(page))
        if not response:
            print(f"Hết thời gian hoặc lỗi khi gọi API danh sách phim trang {page} sau 3 lần thử")
            return [], 0
        data = response.json()
        if not data.get("status", False):
            print("API không trả về dữ liệu hợp lệ")
            return [], 0
        return data.get("items", []), data.get("pagination", {}).get("totalPages", 1)
    except Exception as e:
        print(f"Lỗi khi gọi API danh sách phim: {str(e)}")
        return [], 0

def process_movie(slug, movie_name, path_db):
    start_time = time.time()
    result = get_movie_kkphim(slug=slug, skipad=True)
    duration = time.time() - start_time
    if result["status"]:
        print(f"Xử lý thành công phim: {movie_name} (thời gian: {duration:.2f} giây)")
        if result["errors"]:
            print(f"Các tập gặp lỗi:")
            for error in result["errors"]:
                print(f"- Tập: {error['episode']} - Lỗi: {error['error']}")
    else:
        print(f"Lỗi khi xử lý phim {movie_name}: {result['msg']} (thời gian: {duration:.2f} giây)")
        log_error(movie_name, slug, result["msg"])
    return result, duration

def retry_failed_files():
    conn = sqlite3.connect(path_db)
    cursor = conn.cursor()
    
    # Retry failed M3U8 files
    cursor.execute("SELECT movie_id, filename, origin_m3u8_link FROM m3u8_files WHERE download_status = 'failed'")
    failed_m3u8 = cursor.fetchall()
    for movie_id, filename, link_m3u8 in failed_m3u8:
        cursor.execute("SELECT slug, name FROM movies WHERE id = ?", (movie_id,))
        movie = cursor.fetchone()
        if not movie:
            continue
        slug, movie_name = movie
        print(f"Đang thử lại M3U8 cho phim {movie_name}, tập {filename}...")
        m3u8_response = try_request(link_m3u8)
        status = "failed"
        if m3u8_response:
            try:
                m3u8_path = os.path.join(determine_movie_path(base_path, {"slug": slug, "name": movie_name}), movie_name, f"{filename}.m3u8")
                master_content = m3u8_response.text
                base_url = urljoin(link_m3u8, ".")
                variant_url = None
                for line in master_content.splitlines():
                    if line.startswith("#EXT-X-STREAM-INF"):
                        next_line = master_content.splitlines()[master_content.splitlines().index(line) + 1]
                        if not next_line.startswith("#"):
                            variant_url = urljoin(base_url, next_line)
                            break

                ad_pattern = [
                    "#EXTINF:5.8,", "ts",
                    "#EXTINF:2.8,", "ts",
                    "#EXTINF:1.68,", "ts",
                    "#EXTINF:2.44,", "ts",
                    "#EXTINF:4.72,", "ts",
                    "#EXTINF:1.96,", "ts",
                    "#EXTINF:2.76,", "ts",
                    "#EXTINF:1.96,", "ts",
                    "#EXTINF:5.24,", "ts",
                    "#EXTINF:3.36,", "ts",
                    "#EXTINF:2.0,", "ts"
                ]

                def is_ad_sequence(lines, start_idx):
                    """Kiểm tra xem từ start_idx có khớp với toàn bộ chuỗi ad_pattern không"""
                    if start_idx + len(ad_pattern) > len(lines):
                        return False
                    for i, pattern in enumerate(ad_pattern):
                        current_line = lines[start_idx + i].rstrip()
                        if pattern == "ts":
                            if not current_line.endswith(".ts"):
                                return False
                        elif pattern != current_line:
                            return False
                    return True

                def clean_ts_url(line, base_url):
                    """Làm sạch liên kết .ts bằng cách xóa /convertv[0-9]+/"""
                    clean_line = re.sub(r'/convertv\d+/', '/', line)
                    return urljoin(base_url, clean_line)

                new_m3u8_content = []
                access_key = generate_access_key()
                if variant_url:
                    variant_response = try_request(variant_url)
                    if variant_response:
                        variant_content = variant_response.text
                        base_variant_url = urljoin(variant_url, ".")
                        lines = variant_content.splitlines()
                        i = 0
                        while i < len(lines):
                            line = lines[i].rstrip()
                            if line == "#EXT-X-DISCONTINUITY":
                                i += 1
                                continue
                            if i + len(ad_pattern) <= len(lines) and is_ad_sequence(lines, i):
                                i += len(ad_pattern)
                                continue
                            if line.endswith(".ts"):
                                absolute_ts_url = clean_ts_url(line, base_variant_url)
                                new_m3u8_content.append(absolute_ts_url)
                            else:
                                new_m3u8_content.append(lines[i])
                            i += 1
                    else:
                        raise Exception("Lỗi khi tải variant M3U8 sau 3 lần thử")
                else:
                    lines = master_content.splitlines()
                    i = 0
                    while i < len(lines):
                        line = lines[i].rstrip()
                        if line == "#EXT-X-DISCONTINUITY":
                            i += 1
                            continue
                        if i + len(ad_pattern) <= len(lines) and is_ad_sequence(lines, i):
                            i += len(ad_pattern)
                            continue
                        if line.endswith(".ts"):
                            absolute_ts_url = clean_ts_url(line, base_url)
                            new_m3u8_content.append(absolute_ts_url)
                        else:
                            new_m3u8_content.append(lines[i])
                        i += 1
                with open(m3u8_path, "w", encoding="utf-8") as f:
                    f.write("\n".join(new_m3u8_content))
                status = "success"
                strm_path = create_strm(os.path.join(determine_movie_path(base_path, {"slug": slug, "name": movie_name}, for_m3u8=False), movie_name), m3u8_path, access_key)
                cursor.execute("UPDATE m3u8_files SET download_status = ?, access_key = ?, strm_path = ? WHERE movie_id = ? AND filename = ? AND origin_m3u8_link = ?",
                              (status, access_key, strm_path, movie_id, filename, link_m3u8))
            except Exception as e:
                log_error(movie_name, slug, f"Lỗi khi thử lại M3U8: {str(e)}", episode_name=filename)
        else:
            log_error(movie_name, slug, f"Lỗi khi tải M3U8 sau 3 lần thử", episode_name=filename)
        cursor.execute("UPDATE m3u8_files SET download_status = ? WHERE movie_id = ? AND filename = ? AND origin_m3u8_link = ?",
                      (status, movie_id, filename, link_m3u8))

    # Retry failed image files
    cursor.execute("SELECT movie_id, image_path, image_type FROM image_files WHERE download_status = 'failed'")
    failed_images = cursor.fetchall()
    for movie_id, image_path, image_type in failed_images:
        cursor.execute("SELECT slug, name, poster_url, thumb_url FROM movies WHERE id = ?", (movie_id,))
        movie = cursor.fetchone()
        if not movie:
            continue
        slug, movie_name, poster_url, thumb_url = movie
        img_url = poster_url if image_type == "poster" else thumb_url
        print(f"Đang thử lại {image_type} cho phim {movie_name}...")
        response = try_request(img_url)
        status = "failed"
        if response:
            try:
                with open(image_path, "wb") as f:
                    f.write(response.content)
                status = "success"
            except Exception as e:
                log_error(movie_name, slug, f"Lỗi khi ghi file {image_type}: {str(e)}", episode_name=image_type.capitalize())
        else:
            log_error(movie_name, slug, f"Lỗi khi tải {image_type} sau 3 lần thử", episode_name=image_type.capitalize())
        cursor.execute("UPDATE image_files SET download_status = ? WHERE movie_id = ? AND image_path = ?",
                      (status, movie_id, image_path))
    
    conn.commit()
    conn.close()

def crawl_movies(base_path, path_db, max_pages=1):
    init_db(path_db)
    try:
        os.makedirs(base_path, exist_ok=True)
        os.makedirs(path_m3u8, exist_ok=True)
        os.makedirs(path_strm, exist_ok=True)
    except Exception as e:
        log_error("Unknown", "no-slug", f"Lỗi khi tạo thư mục chính: {str(e)}")
        return
    
    current_page = 1
    total_pages = 1
    
    while current_page <= max_pages and current_page <= total_pages:
        print(f"Đang cào trang {current_page}/{min(max_pages, total_pages)}...")
        movies, total_pages = fetch_movie_list(current_page)
        
        for idx, movie in enumerate(movies, 1):
            slug = movie.get("slug", "")
            movie_name = movie.get("name", slug)
            if not slug:
                print("Không tìm thấy slug cho phim, bỏ qua...")
                log_error(movie_name, slug, "Không tìm thấy slug")
                continue
                
            try:
                print(f"Đang xử lý phim {idx}/{len(movies)}: {movie_name}...")
                with ThreadPoolExecutor(max_workers=1) as executor:
                    future = executor.submit(process_movie, slug, movie_name, path_db)
                    result, duration = future.result(timeout=1000)
                if result["status"]:
                    print(f"Xử lý thành công phim: {movie_name} (thời gian: {duration:.2f} giây)")
                else:
                    print(f"Lỗi khi xử lý phim {movie_name}: {result['msg']} (thời gian: {duration:.2f} giây)")
            except TimeoutError:
                print(f"Hết thời gian xử lý phim {movie_name} sau 600 giây, bỏ qua...")
                log_error(movie_name, slug, "Hết thời gian xử lý sau 600 giây")
                continue
            except Exception as e:
                print(f"Lỗi bất ngờ khi xử lý phim {movie_name}: {str(e)}")
                log_error(movie_name, slug, f"Lỗi bất ngờ: {str(e)}")
                continue
        
        current_page += 1
    
    print(f"Hoàn tất cào {current_page - 1} trang tại {datetime.now()}")
    if craw_error_file:
        print("Bắt đầu thử lại các file lỗi...")
        retry_failed_files()
        print("Hoàn tất thử lại các file lỗi.")

def main():
    max_pages = 3
    while True:
        print(f"Bắt đầu cào phim lúc {datetime.now()}")
        crawl_movies(base_path, path_db, max_pages)
        print("Đang chờ 12 giờ để cào lại...")
        time.sleep(12 * 60 * 60)

if __name__ == "__main__":
    main()