Hello,
i'm trying to download a videos from guruflix.biz that uses anti scrape by cloudflare. The page loads a file called drm.js and a wasm component. The manifest of .m3u8 is able to download only if you include the correct headers and cookies by cloudflare. The chunk in the playlist can be downloaded without this but are encripted. The .m3u8 says AES-128 but i think that the videos doesnt exist in .ts format
for every chunck from the playlist the cdn response with something that seems base64 encoded, maybe a token for single fragment or info/key for the wasm.
in drm.js i've found a function that calculate a key from my email and one of x-ray headers token but the key is too long for aes128
i've have tried to inject the attached js code but after tests... i can only get the audio in the right sequence and the correct mpg header not the video
i've run out of ideas and i need your help -_-"
+ Reply to Thread
Results 1 to 7 of 7
-
-
-
I tried one course myself and it's using widevine for me
Bypass HMACs, One-time-tokens and Lic.Wrapping: https://github.com/DevLARLEY/WidevineProxy2 -
Hello, I had a Python script to download a video from an Italian website using Cloudflare. I did it like this:
Code:import re import base64 import requests from Crypto.Cipher import AES from pathlib import Path import subprocess import time import sys # ================================== # SANITIZE INPUTS (fix unicode issues) # ================================== def sanitize(s: str) -> str: return s.replace("…", "").encode("utf-8", "ignore").decode("utf-8") # ================================== # CONFIG # ================================== M3U8_URL = input("Playlist URL or local m3u8 file: ").strip() TIMEOUT = 20 MAX_RETRIES = 15 DELAY_BETWEEN_SEGMENTS = 2.5 SESSION_COOKIE = sanitize(input("PASTE_COOKIE : ").strip()) Fingerprint = sanitize(input("PASTE_X-Fingerprint : ").strip()) Token = sanitize(input("PASTE_X-Turnstile-Token : ").strip()) OUT_DIR = Path("segments") OUT_DIR.mkdir(exist_ok=True) # ================================== # SESSION # ================================== session = requests.Session() session.headers.update({ "Cookie": SESSION_COOKIE, "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)", "Referer": "https://guruflix.io/", "Origin": "https://guruflix.io", "X-Fingerprint": Fingerprint, "X-Turnstile-Token": Token, }) # ================================== # UTILS # ================================== def unpad_pkcs7(data: bytes) -> bytes: pad = data[-1] if 1 <= pad <= 16: return data[:-pad] return data # ================================== # LOAD PLAYLIST # ================================== print("[+] Loading playlist") if M3U8_URL.startswith("http"): r = session.get(M3U8_URL, timeout=TIMEOUT) r.raise_for_status() lines = r.text.splitlines() else: lines = Path(M3U8_URL).read_text(encoding="utf-8").splitlines() # ================================== # PARSE PLAYLIST # ================================== segments = [] next_key = None next_iv = None index = 0 for line in lines: line = line.strip() if "EXT-X-KEY" in line: key_match = re.search(r'URI="key:([^"]+)"', line) iv_match = re.search(r'IV=0x([0-9a-fA-F]+)', line) if not key_match or not iv_match: raise ValueError("Invalid EXT-X-KEY") next_key = base64.b64decode(key_match.group(1)) next_iv = bytes.fromhex(iv_match.group(1).zfill(32)) continue if line.startswith("http"): if next_key is None or next_iv is None: raise ValueError(f"No key for segment {index}") segments.append({ "index": index, "url": line, "key": next_key, "iv": next_iv, }) next_key = None next_iv = None index += 1 print(f"[+] {len(segments)} segments found") # ================================== # DOWNLOAD + DECRYPT # ================================== print("[+] Downloading segments (stable mode)") for seg in segments: idx = seg["index"] out_file = OUT_DIR / f"{idx:05d}.ts" if out_file.exists() and out_file.stat().st_size > 188: print(f"[→] Segment {idx} already exists, skipping") continue for attempt in range(1, MAX_RETRIES + 1): try: r = session.get(seg["url"], timeout=TIMEOUT) if r.status_code != 200: raise RuntimeError(f"HTTP {r.status_code}") cipher = AES.new(seg["key"], AES.MODE_CBC, seg["iv"]) decrypted = unpad_pkcs7(cipher.decrypt(r.content)) out_file.write_bytes(decrypted) print(f"[✓] Segment {idx} OK") break except Exception as e: print(f"[!] Segment {idx} failed ({attempt}/{MAX_RETRIES}) → {e}") time.sleep(4) else: print(f"❌ Segment {idx} abandoned") sys.exit(1) time.sleep(DELAY_BETWEEN_SEGMENTS) print("✅ All segments downloaded") # ================================== # MERGE # ================================== print("[+] Merging TS → MP4") list_file = OUT_DIR / "list.txt" with list_file.open("w", encoding="utf-8") as f: for seg in segments: f.write(f"file '{(OUT_DIR / f'{seg['index']:05d}.ts').as_posix()}'\n") subprocess.run(["ffmpeg", "-y", "-f", "concat", "-safe", "0", "-i", str(list_file), "-c", "copy", "output.mp4"], check=True) print("🎬 output.mp4 created successfully") -
thanks at all !
in the meanwhile the targetsite has made an update and i think is using widevine. As soon as possible i try to check !
sesamap159 thanks for your code i will try it asap, i've old fragment and their playlist -
I use Cloudlflare and other than the typical things you can do server side like preventing hot linking they don't have any specific copy prevention services I'm aware of. Generally if a challenge is enabled the browser needs to execute JS. If cookies are disabled you'll get a challenge for every request. The custom firewall rules can be quite complex site wide or tailored to specific URL, there is numerous conditions that can trigger a block either singular or combination of them.
Similar Threads
-
delete Windows environment variables
By blanc in forum Newbie / General discussionsReplies: 1Last Post: 31st Jan 2025, 12:58 -
Social environment or Internet?
By MicroMagic in forum Off topicReplies: 3Last Post: 2nd Jan 2024, 22:35 -
How to setup an MSYS2 building environment
By El Heggunte in forum User guidesReplies: 3Last Post: 10th Jul 2022, 00:35



Quote
