Khatrimazafull 100mb 300mb Download [ Verified ]
In India, Reliance Jio and Airtel now offer 1.5GB/day plans for less than $3/month. As data becomes cheaper, the need for extreme compression drops.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Feature: Robust download of a large file (100‑300 MiB) with resume,
progress bar, optional speed limiting, and size validation.
Usage example:
python downloader.py \
--url "https://example.com/khatrimazafull.mp4" \
--output "khatrimazafull.mp4" \
--min-size 100M \
--max-size 300M \
--speed-limit 2M # optional, limits to 2 MiB/s
Author: ChatGPT (2024‑06)
"""
import argparse
import os
import sys
import time
import urllib.request
from urllib.error import HTTPError, URLError
from pathlib import Path
# tqdm is tiny and optional – if not installed we fall back to a simple printer
try:
from tqdm import tqdm
except ImportError: # pragma: no cover
tqdm = None
# ----------------------------------------------------------------------
# Helper utilities
# ----------------------------------------------------------------------
def parse_size(size_str: str) -> int:
"""
Convert human‑readable size strings (e.g. "100M", "2GiB") to bytes.
Supports suffixes: K, M, G, T (case‑insensitive) and optional 'i' (MiB, GiB).
"""
size_str = size_str.strip().upper()
factor = 1
if size_str.endswith('KI') or size_str.endswith('K'):
factor = 1024
size_str = size_str.rstrip('KI')
elif size_str.endswith('MI') or size_str.endswith('M'):
factor = 1024 ** 2
size_str = size_str.rstrip('MI')
elif size_str.endswith('GI') or size_str.endswith('G'):
factor = 1024 ** 3
size_str = size_str.rstrip('GI')
elif size_str.endswith('TI') or size_str.endswith('T'):
factor = 1024 ** 4
size_str = size_str.rstrip('TI')
try:
return int(float(size_str) * factor)
except ValueError as exc:
raise argparse.ArgumentTypeError(f"Invalid size value: size_str") from exc
# ----------------------------------------------------------------------
# Speed limiter (token‑bucket)
# ----------------------------------------------------------------------
class SpeedLimiter:
"""
Simple token‑bucket limiter. Call `wait(bytes_transferred)` after each
chunk write; it will sleep just enough to keep the average rate <= limit.
"""
def __init__(self, max_bytes_per_sec: int):
self.rate = max_bytes_per_sec
self._allowance = self.rate
self._last_check = time.monotonic()
def wait(self, bytes_sent: int):
self._allowance -= bytes_sent
now = time.monotonic()
elapsed = now - self._last_check
self._last_check = now
# replenish allowance
self._allowance += elapsed * self.rate
if self._allowance > self.rate:
self._allowance = self.rate
if self._allowance < 0:
# need to sleep
sleep_time = -self._allowance / self.rate
time.sleep(sleep_time)
self._allowance = 0.0
# ----------------------------------------------------------------------
# Core downloader
# ----------------------------------------------------------------------
class Downloader:
"""
Handles a resumable HTTP(S) download with optional speed limiting.
"""
CHUNK_SIZE = 8192 # 8 KiB – good compromise between IO and memory
def __init__(self,
url: str,
output_path: Path,
min_size: int,
max_size: int,
speed_limit: int | None = None,
retries: int = 3,
timeout: int = 30):
self.url = url
self.output_path = output_path
self.tmp_path = output_path.with_suffix('.part')
self.min_size = min_size
self.max_size = max_size
self.speed_limiter = SpeedLimiter(speed_limit) if speed_limit else None
self.retries = retries
self.timeout = timeout
# ------------------------------------------------------------------
# Helper: obtain remote file size (HEAD request)
# ------------------------------------------------------------------
def _remote_file_size(self) -> int | None:
try:
req = urllib.request.Request(self.url, method='HEAD')
with urllib.request.urlopen(req, timeout=self.timeout) as resp:
length = resp.getheader('Content-Length')
if length is None:
return None
return int(length)
except (HTTPError, URLError) as exc:
print(f"[WARN] Could not fetch HEAD info: exc", file=sys.stderr)
return None
# ------------------------------------------------------------------
# Main public method
# ------------------------------------------------------------------
def download(self):
# Determine how many bytes we already have (if any)
existing = self.tmp_path.stat().st_size if self.tmp_path.exists() else 0
# Get total size (if server reports it)
total_size = self._remote_file_size()
if total_size is None:
# Fallback: we will just download till EOF – size checks happen at the end.
total_size = 0
# Prepare request with Range header for resume
headers = {}
if existing:
headers['Range'] = f'bytes=existing-'
req = urllib.request.Request(self.url, headers=headers)
attempt = 0
while attempt <= self.retries:
try:
with urllib.request.urlopen(req, timeout=self.timeout) as resp:
# If server ignored Range and sent the whole file, truncate the file first
if resp.status == 200 and existing:
print("[INFO] Server does not support resume – restarting download.")
existing = 0
self.tmp_path.unlink(missing_ok=True)
# Determine final expected size
content_range = resp.getheader('Content-Range')
if content_range:
# Example: "bytes 524288-1048575/1048576"
_, range_part = content_range.split(' ', 1)
byte_range, total_str = range_part.split('/')
total_size = int(total_str)
# Choose progress bar implementation
use_tqdm = tqdm is not None
bar = None
if use_tqdm:
bar = tqdm(
total=total_size,
unit='B',
unit_scale=True,
unit_divisor=1024,
initial=existing,
desc=self.output_path.name,
ascii=True,
)
else:
# Simple fallback progress printer
def simple_printer(transferred, total):
pct = 100 * transferred / total if total else 0
sys.stdout.write(
f"\rDownloading: transferred:, / total:, bytes (pct:.1f%)"
)
sys.stdout.flush()
last_print = time.time()
transferred = existing
with open(self.tmp_path, 'ab') as out_file:
while True:
chunk = resp.read(self.CHUNK_SIZE)
if not chunk:
break
out_file.write(chunk)
if self.speed_limiter:
self.speed_limiter.wait(len(chunk))
if use_tqdm:
bar.update(len(chunk))
else:
transferred += len(chunk)
now = time.time()
# limit prints to ~2 per second
if now - last_print > 0.5:
simple_printer(transferred, total_size)
last_print = now
if not use_tqdm:
print() # final newline after simple printer
if bar:
bar.close()
break # successful download -> exit retry loop
except (HTTPError, URLError, ConnectionError, TimeoutError) as exc:
attempt += 1
if attempt > self.retries:
raise RuntimeError(f"Download failed after self.retries retries") from exc
wait_sec = 2 ** attempt
print(f"[WARN] Download error (exc). Retrying in wait_secs…", file=sys.stderr)
time.sleep(wait_sec)
# ---- Validation -------------------------------------------------
final_size = self.tmp_path.stat().st_size
if not (self.min_size <= final_size <= self.max_size):
raise ValueError(
f"File size final_size:, bytes is outside the allowed range "
f"[self.min_size:,, self.max_size:,]"
)
# Atomically rename to final name
self.tmp_path.replace(self.output_path)
print(f"[DONE] Saved to 'self.output_path' (final_size:, bytes)")
# ----------------------------------------------------------------------
# CLI entry‑point
# ----------------------------------------------------------------------
def build_argparser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(
description="Robust downloader for large files (100‑300 MiB). "
"Features resume, progress bar, optional speed limit, and size validation."
)
parser.add_argument(
"--url",
required=True,
help="Direct URL of the file to download (e.g. https://example.com/khatrimazafull.mp4)"
)
parser.add_argument(
"--output",
required=True,
help="Destination filename (including path) where the file will be saved."
)
parser.add_argument(
"--min-size",
type=parse_size,
default="100M",
help="Minimum allowed size (default: 100M). Accepts K, M, G, T suffixes."
)
parser.add_argument(
"--max-size",
type=parse_size,
default="300M",
help="Maximum allowed size (default: 300M). Accepts K, M, G, T suffixes."
)
parser.add_argument(
"--speed-limit",
type=parse_size,
default=None,
help="Optional max download speed (e.g. 2M for 2 MiB/s). Omit for unlimited."
)
parser.add_argument(
"--retries",
type=int,
default=3,
help="Number of automatic retry attempts on transient errors."
)
parser.add_argument(
"--timeout",
type=int,
default=30,
help="Network timeout (seconds) for each request."
)
return parser
def main() -> None:
args = build_argparser().parse_args()
downloader = Downloader(
url=args.url,
output_path=Path(args.output).expanduser().resolve(),
min_size=args.min_size,
max_size=args.max_size,
speed_limit=args.speed_limit,
retries=args.retries,
timeout=args.timeout,
)
downloader.download()
if __name__ == "__main__":
main()
Absolutely not.
Searching for "khatrimazafull 100mb 300mb download" is a digital minefield. In 2026, the cost of a data breach or a ransomware attack (often $500+ to unlock your files) far outweighs the saving of $2.99 that a legal rental costs. khatrimazafull 100mb 300mb download
