CoolFace
Apppublic

kinostream4/asil-tv

sourceHugging Faceupdated 11d agoView on Hugging Face
0likes
feed.py246 linesDownload Raw Back to root
1import subprocess, requests, time, os, threading, re, tempfile2from datetime import datetime, timedelta, timezone3 4HLS_DIR_1 = "/tmp/hls1"5HLS_DIR_2 = "/tmp/hls2"6 7SOURCE_M3U8_1 = os.environ.get("SOURCE1_M3U8_URL", "")8SOURCE_M3U8_2 = os.environ.get("SOURCE2_M3U8_URL", "")9 10current_titles = {11    "stream1": {"title": "Yuklanmoqda...", "index": 0, "total": 0},12    "stream2": {"title": "Yuklanmoqda...", "index": 0, "total": 0}13}14 15schedule_lock_1 = threading.Lock()16schedule_lock_2 = threading.Lock()17program_schedule_1 = []18program_schedule_2 = []19 20DEFAULT_DURATION = 90 * 6021_real_durations_1 = {}22_real_durations_2 = {}23_dur_lock = threading.Lock()24 25 26def build_schedule_fast(items, stream_num, current_index, current_start_time):27    global program_schedule_1, program_schedule_228    now = datetime.now(timezone.utc)29 30    idx = current_index31    start_of_current = current_start_time if current_start_time else now32 33    durations = []34    for item in items:35        with _dur_lock:36            dur = _real_durations_1.get(item["url"], DEFAULT_DURATION) if stream_num == 1 else _real_durations_2.get(item["url"], DEFAULT_DURATION)37        durations.append(dur)38 39    cursor = start_of_current40    starts = [None] * len(items)41    starts[idx] = cursor42 43    for i in range(idx, len(items)):44        starts[i] = cursor45        cursor += timedelta(seconds=durations[i])46 47    cursor = start_of_current48    for i in range(idx - 1, -1, -1):49        cursor -= timedelta(seconds=durations[i])50        starts[i] = cursor51 52    sched = []53    for i, item in enumerate(items):54        start = starts[i]55        stop  = start + timedelta(seconds=durations[i])56        sched.append({"title": item["title"], "start": start, "stop": stop, "logo": item.get("logo", ""), "desc": item.get("desc", ""), "trailer": item.get("trailer", "")})57 58    if stream_num == 1:59        with schedule_lock_1:60            program_schedule_1 = sched61    else:62        with schedule_lock_2:63            program_schedule_2 = sched64 65 66def get_schedule_1():67    with schedule_lock_1:68        return list(program_schedule_1)69 70def get_schedule_2():71    with schedule_lock_2:72        return list(program_schedule_2)73 74 75def parse_playlist(url):76    try:77        resp = requests.get(url, timeout=10)78        resp.raise_for_status()79        items, title, pending_dur, pending_logo, pending_desc, pending_trailer = [], "", None, None, "", ""80        for line in resp.text.splitlines():81            line = line.strip()82            if line.startswith("#EXTINF"):83                m = re.match(r'#EXTINF:([\d.-]+)', line)84                if m:85                    try:86                        d = float(m.group(1))87                        if d > 0:88                            pending_dur = d89                    except Exception:90                        pass91                logo_m = re.search(r'tvg-logo="([^"]*)"', line)92                pending_logo = logo_m.group(1) if logo_m else None93                desc_m = re.search(r'tvg-description="([^"]*)"', line)94                pending_desc = desc_m.group(1) if desc_m else ""95                trailer_m = re.search(r'tvg-trailer="([^"]*)"', line)96                pending_trailer = trailer_m.group(1) if trailer_m else ""97                clean = re.sub(r'#EXTINF:[^\s,]+', '', line)98                clean = re.sub(r'\w[\w-]*="[^"]*"', '', clean)99                if ',' in clean:100                    title = clean.split(',', 1)[1].strip()101                else:102                    title = clean.strip()103            elif line and not line.startswith("#"):104                item = {"url": line, "title": title or line.split("/")[-1], "logo": pending_logo or "", "desc": pending_desc or "", "trailer": pending_trailer or ""}105                if pending_dur:106                    with _dur_lock:107                        _real_durations_1[line] = pending_dur108                    pending_dur = None109                items.append(item)110                title = ""111                pending_logo = None112                pending_desc = ""113                pending_trailer = ""114        return items115    except Exception as e:116        print(f"❌ Playlist xato ({url}): {e}")117        return []118 119 120def run_worker(stream_url, hls_dir, stream_name, stream_num):121    global current_titles122    os.makedirs(hls_dir, exist_ok=True)123 124    if not stream_url:125        print(f"❌ {stream_name} uchun URL topilmadi!")126        while True:127            time.sleep(60)128 129    seg_counter = 0130 131    def _next_seg_start():132        nonlocal seg_counter133        try:134            segs = [f for f in os.listdir(hls_dir) if f.startswith("seg") and f.endswith(".ts")]135            if segs:136                nums = [int(re.search(r'\d+', s).group()) for s in segs if re.search(r'\d+', s)]137                if nums:138                    seg_counter = max(nums) + 1139        except Exception:140            pass141        return seg_counter142 143    def _update_seg_counter():144        nonlocal seg_counter145        try:146            segs = [f for f in os.listdir(hls_dir) if f.startswith("seg") and f.endswith(".ts")]147            if segs:148                nums = [int(re.search(r'\d+', s).group()) for s in segs if re.search(r'\d+', s)]149                if nums:150                    seg_counter = max(nums) + 1151        except Exception:152            pass153 154    while True:155        try:156            items = parse_playlist(stream_url)157            if not items:158                print(f"⚠️ {stream_name} playlist bo'sh, 30s kutilmoqda...")159                time.sleep(30)160                continue161 162            for index, item in enumerate(items, 1):163                current_index = index - 1164                current_start_time = datetime.now(timezone.utc)165 166                current_titles[stream_name] = {"title": item["title"], "index": index, "total": len(items)}167                print(f"🎬 [{stream_name}] [{index}/{len(items)}] Efir: {item['title']}")168 169                build_schedule_fast(items, stream_num, current_index, current_start_time)170 171                start_num = _next_seg_start()172                cmd = [173                    "ffmpeg", "-y",174                    "-re",175                    "-i", item["url"],176                    "-c:v", "copy",177                    "-c:a", "aac", "-b:a", "128k",178                    "-f", "hls",179                    "-hls_time", "4",180                    "-hls_list_size", "10",181                    "-hls_flags", "append_list+delete_segments",182                    "-hls_delete_threshold", "3",183                    "-start_number", str(start_num),184                    "-hls_segment_filename", f"{hls_dir}/seg%d.ts",185                    f"{hls_dir}/stream.m3u8"186                ]187 188                with _dur_lock:189                    known_dur = _real_durations_1.get(item["url"], DEFAULT_DURATION) if stream_num == 1 else _real_durations_2.get(item["url"], DEFAULT_DURATION)190                max_allowed = known_dur * 1.5 + 300191 192                t_start = time.time()193                proc = None194                errfile = tempfile.NamedTemporaryFile(mode="w+", dir="/tmp", prefix=f"ffmpeg_err_{stream_name}_", suffix=".log", delete=False)195                try:196                    proc = subprocess.Popen(cmd, stdout=subprocess.DEVNULL, stderr=errfile)197                    while True:198                        ret = proc.poll()199                        if ret is not None:200                            break201                        elapsed = time.time() - t_start202                        if elapsed > max_allowed:203                            proc.kill()204                            proc.wait(timeout=10)205                            break206                        time.sleep(5)207                except Exception as e:208                    if proc is not None:209                        try:210                            proc.kill()211                            proc.wait(timeout=10)212                        except Exception:213                            pass214                finally:215                    try:216                        errfile.close()217                        os.remove(errfile.name)218                    except Exception:219                        pass220 221                _update_seg_counter()222                real_dur = time.time() - t_start223                if real_dur > 10:224                    with _dur_lock:225                        if stream_num == 1:226                            _real_durations_1[item["url"]] = real_dur227                        else:228                            _real_durations_2[item["url"]] = real_dur229 230                time.sleep(1)231 232            print(f"🔄 [{stream_name}] Barcha kinolar tugadi, qayta boshlanmoqda...")233        except Exception as e:234            print(f"❌ [{stream_name}] Sikl xatosi: {e}")235            time.sleep(10)236 237 238def stream_all():239    t1 = threading.Thread(target=run_worker, args=(SOURCE_M3U8_1, HLS_DIR_1, "stream1", 1), daemon=True)240    t2 = threading.Thread(target=run_worker, args=(SOURCE_M3U8_2, HLS_DIR_2, "stream2", 2), daemon=True)241 242    t1.start()243    t2.start()244 245    t1.join()246    t2.join()