windows-iso-downloader/.claude/worktrees/modest-hamilton-53380c/msdls_v3.py
Shekhar Vaidya 1c54c36d03 feat: expiry countdown, refresh links, CLI tabs, recently viewed, file size support
- Backend: ?force=true on /proxy bypasses cache for fresh link fetch
- Frontend: parse `se` param for live expiry countdown on download links
- Frontend: show Refresh button when link expires in <6h, force-fetches fresh URL
- Frontend: CliCommand component replaces Aria2Tip — wget/curl/aria2 tabs, persists selection in localStorage
- Frontend: RecentlyViewed component — localStorage, shows active countdown or expired state
- Frontend: FileSize shown alongside arch if returned by Microsoft CDN

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 21:06:17 +05:30

158 lines
5.4 KiB
Python

import argparse
import json
import logging
import uuid
import time
import requests
# New v3 JSON Endpoints
BASE_URL = "https://www.microsoft.com/software-download-connector/api"
PROFILE = "606624d44113"
LOCALE = "en-US"
UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
def setup_session():
"""Initializes a session with Microsoft tracking servers."""
session_id = str(uuid.uuid4())
return session_id
def get_product(product_id, session_id):
"""Fetches product/SKU info for a given ID."""
url = f"{BASE_URL}/getskuinformationbyproductedition"
params = {
"profile": PROFILE,
"productEditionId": product_id,
"SKU": "undefined",
"friendlyFileName": "undefined",
"Locale": LOCALE,
"sessionID": session_id
}
headers = {
"User-Agent": UA,
"Accept": "application/json",
"Referer": "https://www.microsoft.com/en-us/software-download/windows11"
}
try:
r = requests.get(url, params=params, headers=headers, timeout=10)
if not r.ok: return None
# Handle Microsoft's double-encoded JSON
raw_text = r.text
try:
# First pass: decode the wrapper JSON
data = json.loads(raw_text)
# Second pass: if the value is a string, it's the real JSON
if isinstance(data, str):
data = json.loads(data)
return data
except:
return None
except Exception as e:
logging.error(f"Error fetching ID {product_id}: {e}")
return None
def scan_id(product_id):
"""Checks if a product ID is valid and active."""
session_id = setup_session()
data = get_product(product_id, session_id)
if not data or "Skus" not in data or len(data["Skus"]) == 0:
return None
if "Errors" in data and data["Errors"] and len(data["Errors"]) > 0:
return None
# MS usually puts the release name in the first SKU
skus = data.get("Skus", [])
if skus:
# Check multiple possible name keys
s = skus[0]
# ReleaseName is often "Windows 10 Version 22H2 (Updated Oct 2025)"
name = s.get("EditionName") or s.get("ReleaseName") or s.get("FriendlyName")
if name:
return name
return f"Windows Product {product_id}"
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Scan for new Windows Product IDs (v3 JSON API)")
parser.add_argument("--first", required=True, type=int, help="First ID to check")
parser.add_argument("--last", required=True, type=int, help="Last ID to check")
parser.add_argument("--write", help="Output JSON file")
args = parser.parse_args()
logging.basicConfig(level=logging.INFO, format='%(message)s')
products = {}
logging.info(f"Scanning range {args.first} to {args.last}...")
for i in range(args.first, args.last + 1):
name = scan_id(i)
if name:
logging.info(f"✅ FOUND: [{i}] {name}")
products[str(i)] = name
else:
# Only log skips for very small ranges to keep output clean
if (args.last - args.first) < 20:
logging.info(f"❌ SKIP: [{i}] Not found")
products[str(i)] = False
# Be gentle to the API
time.sleep(0.5)
if args.write:
import os
catalog = {}
if os.path.exists(args.write):
try:
with open(args.write, 'r', encoding='utf-8') as f:
catalog = json.load(f)
except Exception as e:
logging.error(f"Failed to read existing catalog: {e}")
# Merge new products, preserving existing extra metadata if present
for pid, name in products.items():
if pid in catalog:
if name is False:
if isinstance(catalog[pid], dict):
catalog[pid]["active"] = False
else:
catalog[pid] = {
"name": catalog[pid],
"badge": "",
"archs": [],
"related": [],
"active": False
}
continue
# Update name but keep other fields like badge, archs, etc.
if isinstance(catalog[pid], dict):
catalog[pid]["name"] = name
catalog[pid]["active"] = True
else:
# Upgrade from old flat format
catalog[pid] = {
"name": name,
"badge": "",
"archs": [],
"related": [],
"active": True
}
else:
if name is False:
continue # Do not add new dead entries to the catalog
catalog[pid] = {
"name": name,
"badge": "",
"archs": [],
"related": [],
"active": True
}
with open(args.write, 'w', encoding='utf-8') as f:
json.dump(catalog, f, indent=4)
logging.info(f"\nSaved {len(catalog)} products to {args.write} (merged with existing)")
logging.info("Done.")