# @title HAnime Multi-Quality Downloader & Link Extractor # @markdown Run this cell and enter the video URL below. video_url = "" # @param {type:"string"} # Automatically install yt-dlp if not present !pip install yt-dlp -q import requests import subprocess import re import sys import tempfile import os import hashlib import time import yt_dlp BASE = "https://hanime.tv/api/v8" UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36" BASE_HEADERS = { "User-Agent": UA, "Referer": "https://hanime.tv/", "Origin": "https://hanime.tv", "Accept": "application/json", "Content-Type": "application/json", "X-Signature-Version": "web2", } JS_PREAMBLE = """ delete globalThis.process; var window = new Proxy({ top: { location: { origin: "https://hanime.tv" } }, addEventListener: (e, cb) => {} }, { set(o, k, v) { if (k == "ssignature" || k == "stime") console.log(k, v); o[k] = v; return true; } }); globalThis.window = window; """ _vendor_script_cache = None def make_headers(path: str) -> dict: t = int(time.time()) sig = hashlib.sha1(f"{path}{t}".encode()).hexdigest() return { **BASE_HEADERS, "X-Time": str(t), "X-Signature": sig, } def get_hv_id(slug: str) -> str | None: r = requests.get( f"{BASE}/video", params={"id": slug}, headers=make_headers("/api/v8/video"), ) if r.status_code == 200: data = r.json() hv = data.get("hentai_video", data) vid = hv.get("id") or hv.get("hv_id") if vid: return str(vid) else: print("Error: Could not fetch video ID (Bot Protection Active).") return None def get_vendor_script() -> str | None: global _vendor_script_cache if _vendor_script_cache: return _vendor_script_cache r = requests.get("https://hanime.tv/", headers={"User-Agent": UA, "Accept": "text/html"}) match = re.search(r'src="(https://hanime-cdn\.com/js/vendor\.[^"]+)"', r.text) if not match: print("Could not find vendor JS.") return None vendor_url = match.group(1) r2 = requests.get(vendor_url, headers={"User-Agent": UA, "Referer": "https://hanime.tv/"}) _vendor_script_cache = r2.text return _vendor_script_cache def generate_credentials() -> tuple[str, str] | tuple[None, None]: vendor_js = get_vendor_script() if not vendor_js: return None, None script = JS_PREAMBLE + "\n" + vendor_js tmp_path = None try: with tempfile.NamedTemporaryFile(mode='w', suffix='.js', delete=False) as f: f.write(script) tmp_path = f.name result = subprocess.run( ["node", tmp_path], capture_output=True, text=True, timeout=15 ) output = result.stdout.strip() creds = {} for line in output.split("\n"): parts = line.split(" ", 1) if len(parts) == 2: creds[parts[0]] = parts[1].strip() sig = creds.get("ssignature") t = creds.get("stime") if sig and t: return sig, t return None, None except Exception as e: print(f"JS Bypass Failed: {e}") return None, None finally: if tmp_path and os.path.exists(tmp_path): os.unlink(tmp_path) def get_streams(hv_id: str, sig: str, t: str) -> list[dict]: r = requests.get( f"https://hanime.tv/api/v8/guest/videos/{hv_id}/manifest", headers={ **BASE_HEADERS, "X-Signature": sig, "X-Time": t, } ) if r.status_code != 200: return [] data = r.json() results = [] try: for server in data.get("videos_manifest", {}).get("servers", []): for stream in server.get("streams", []): url = stream.get("url", "") if url: results.append({ "url": url, "resolution": f"{stream.get('width', 0)}x{stream.get('height', 0)}", "height": stream.get("height", 0), }) except Exception as e: pass results.sort(key=lambda x: x["height"], reverse=True) return results def download_video(m3u8_url: str, filename: str): print(f"\nšŸ“„ Preparing to download: {filename}.mp4") print("--------------------------------------------------") # Configure yt-dlp with the necessary bypass headers ydl_opts = { 'outtmpl': f'{filename}.%(ext)s', 'http_headers': { 'Referer': 'https://hanime.tv/', 'Origin': 'https://hanime.tv' }, 'format': 'best', 'merge_output_format': 'mp4', } try: with yt_dlp.YoutubeDL(ydl_opts) as ydl: ydl.download([m3u8_url]) print(f"\nāœ… Download Complete! Check the Colab files for '{filename}.mp4'") except Exception as e: print(f"\nāŒ Error during download: {e}") def process_and_download(url: str): slug = url.rstrip("/").split("/")[-1] print(f"šŸ” Analyzing Link: {slug}...") hv_id = get_hv_id(slug) if not hv_id: print("āŒ Failed to get Video ID.") return print("šŸ”“ Bypassing Security...") sig, t = generate_credentials() if not sig or not t: print("āŒ Failed to bypass security.") return print("🌐 Fetching streams...") streams = get_streams(hv_id, sig, t) if not streams: print("āŒ No streams found or blocked by Cloudflare.") return print("\nšŸ“ŗ Available Qualities and Links:") print("--------------------------------------------------") for i, s in enumerate(streams, 1): print(f" {i}. [{s['resolution']}]\n Link: {s['url']}\n") print(" 0. Skip Download (Only show links)") print("--------------------------------------------------") try: choice = int(input("\nšŸ‘‰ Enter the number of the quality you want to download (e.g., 1) or 0 to skip: ")) if choice == 0: print("\nšŸ›‘ Download Skipped. You can copy the links above.") return if 1 <= choice <= len(streams): selected = streams[choice-1] print(f"\n✨ You selected: {selected['resolution']}") # Filename mein resolution add kar diya taaki identify karna aasan ho file_name = f"{slug}_{selected['resolution']}" download_video(selected["url"], file_name) else: print("\nāŒ Invalid choice. Please run the cell again.") except ValueError: print("\nāŒ Please enter a valid number.") if __name__ == "__main__": url = video_url.strip() if video_url else input("Enter URL: ").strip() if not url.startswith("http"): print("Invalid URL. Please enter a valid Hanime video link.") else: process_and_download(url)