Created
April 3, 2026 18:17
-
-
Save zone559/138df139fcc9ec7b7ec16484b987c547 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import requests | |
| import os | |
| import json | |
| import re | |
| import yt_dlp | |
| from concurrent.futures import ThreadPoolExecutor | |
| # ANSI color codes for better console output | |
| COLOR = { | |
| 'GREEN': '\033[92m', | |
| 'BLUE': '\033[94m', | |
| 'YELLOW': '\033[93m', | |
| 'CYAN': '\033[96m', | |
| 'MAGENTA': '\033[95m', | |
| 'RED': '\033[91m', | |
| 'RESET': '\033[0m' | |
| } | |
| class SnapchatDownloader: | |
| def __init__(self, url): | |
| self.url = url | |
| self.username = self.extract_username() | |
| self.headers = { | |
| 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3' | |
| } | |
| self.create_user_folder() | |
| def extract_username(self): | |
| """Extract username from the URL""" | |
| if "/add/" in self.url: | |
| username = self.url.split("/add/")[-1].strip("/") | |
| else: | |
| username = self.url.split('/')[-2] if self.url.endswith('/') else self.url.split('/')[-1] | |
| print(f"{COLOR['BLUE']}Username: {username}{COLOR['RESET']}") | |
| return username | |
| def create_user_folder(self): | |
| """Create a folder for the user if it doesn't exist""" | |
| if not os.path.exists(self.username): | |
| os.makedirs(self.username) | |
| print(f"{COLOR['BLUE']}Created folder: {self.username}{COLOR['RESET']}") | |
| def download_file(self, url, filename): | |
| """Download a file with progress tracking""" | |
| try: | |
| if os.path.exists(filename): | |
| print(f"{COLOR['MAGENTA']}File exists, skipping: {filename}{COLOR['RESET']}") | |
| return False | |
| print(f"{COLOR['GREEN']}Downloading: {filename}{COLOR['RESET']}") | |
| print(f"{COLOR['CYAN']}From URL: {url}{COLOR['RESET']}") | |
| with requests.get(url, stream=True, headers=self.headers) as r: | |
| r.raise_for_status() | |
| total_size = int(r.headers.get('content-length', 0)) | |
| downloaded = 0 | |
| with open(filename, 'wb') as f: | |
| for chunk in r.iter_content(chunk_size=8192): | |
| f.write(chunk) | |
| downloaded += len(chunk) | |
| if total_size > 0: | |
| progress = (downloaded / total_size) * 100 | |
| print(f"{COLOR['GREEN']}Progress: {progress:.1f}% ({downloaded}/{total_size} bytes){COLOR['RESET']}", end='\r') | |
| print(f"{COLOR['GREEN']}\nDownload completed: {filename}{COLOR['RESET']}") | |
| return True | |
| except Exception as e: | |
| print(f"{COLOR['RED']}Error downloading {filename}: {str(e)}{COLOR['RESET']}") | |
| return False | |
| def download_spotlight(self, snap_id): | |
| """Download a spotlight video using yt-dlp""" | |
| spotlight_url = f"https://www.snapchat.com/spotlight/{snap_id}" | |
| print(f"{COLOR['CYAN']}Spotlight URL: {spotlight_url}{COLOR['RESET']}") | |
| try: | |
| ydl_opts = { | |
| 'outtmpl': f'{self.username}/{snap_id}.%(ext)s', | |
| 'quiet': False, | |
| 'no_warnings': False, | |
| } | |
| with yt_dlp.YoutubeDL(ydl_opts) as ydl: | |
| ydl.download([spotlight_url]) | |
| print(f"{COLOR['GREEN']}Successfully downloaded video: {snap_id}{COLOR['RESET']}") | |
| except Exception as e: | |
| print(f"{COLOR['RED']}Error downloading video {snap_id}: {e}{COLOR['RESET']}") | |
| def process_snap(self, snap, index=None, prefix=""): | |
| """Process a single snap (image or video)""" | |
| snap_media_type = snap.get('snapMediaType') | |
| media_url = snap['snapUrls'].get('mediaUrl') if 'snapUrls' in snap else None | |
| preview_url = snap['snapUrls'].get('mediaPreviewUrl', {}).get('value') if 'snapUrls' in snap else None | |
| # Get ID from different possible fields | |
| snap_id = ( | |
| snap.get('snapId', {}).get('value') or | |
| snap.get('storyId', {}).get('value') or | |
| (media_url.split('/')[-1].split('.')[0] if media_url else f"snap_{index}") | |
| ) | |
| if index is not None: | |
| print(f"{COLOR['YELLOW']}\nProcessing snap {index + 1}:{COLOR['RESET']}") | |
| else: | |
| print(f"{COLOR['YELLOW']}\nProcessing snap:{COLOR['RESET']}") | |
| print(f"{COLOR['CYAN']}Media Type: {snap_media_type} (0=Image, 1=Video){COLOR['RESET']}") | |
| print(f"{COLOR['CYAN']}Media URL: {media_url}{COLOR['RESET']}") | |
| print(f"{COLOR['CYAN']}Preview URL: {preview_url}{COLOR['RESET']}") | |
| print(f"{COLOR['CYAN']}Snap ID: {snap_id}{COLOR['RESET']}") | |
| if snap_media_type == 0: # Image | |
| if media_url: | |
| filename = f"{self.username}/{prefix}{snap_id}.jpg" | |
| self.download_file(media_url, filename) | |
| elif snap_media_type == 1: # Video | |
| if media_url: | |
| filename = f"{self.username}/{prefix}{snap_id}.mp4" | |
| self.download_file(media_url, filename) | |
| if preview_url: # Only download thumbnail for videos | |
| filename = f"{self.username}/{prefix}{snap_id}-thumb.jpg" | |
| self.download_file(preview_url, filename) | |
| else: | |
| print(f"{COLOR['RED']}Skipping snap: Unknown media type {snap_media_type}.{COLOR['RESET']}") | |
| def fetch_page_data(self): | |
| """Fetch and parse the Snapchat page data""" | |
| response = requests.get(self.url, headers=self.headers) | |
| if response.status_code != 200: | |
| print(f"{COLOR['RED']}Failed to retrieve data, status code: {response.status_code}{COLOR['RESET']}") | |
| return None | |
| script_pattern = re.compile(r'<script[^>]*type="application/json"[^>]*>(.*?)</script>', re.DOTALL) | |
| match = script_pattern.search(response.text) | |
| if not match: | |
| print(f"{COLOR['RED']}No JSON data found in the HTML.{COLOR['RESET']}") | |
| return None | |
| try: | |
| return json.loads(match.group(1).strip()) | |
| except json.JSONDecodeError as e: | |
| print(f"{COLOR['RED']}Failed to decode JSON: {e}{COLOR['RESET']}") | |
| return None | |
| def download_content(self): | |
| """Main method to download all available content""" | |
| json_data = self.fetch_page_data() | |
| if not json_data: | |
| return | |
| if 'props' not in json_data or 'pageProps' not in json_data['props']: | |
| print(f"{COLOR['RED']}No 'pageProps' found in JSON data.{COLOR['RESET']}") | |
| return | |
| page_props = json_data['props']['pageProps'] | |
| # Download regular story snaps | |
| if 'story' in page_props and page_props['story']: | |
| snap_list = page_props['story'].get('snapList', []) | |
| print(f"{COLOR['BLUE']}Found {len(snap_list)} story snaps.{COLOR['RESET']}") | |
| for index, snap in enumerate(snap_list): | |
| self.process_snap(snap, index) | |
| # Download curated highlights | |
| if 'curatedHighlights' in page_props: | |
| curated_highlights = page_props['curatedHighlights'] | |
| print(f"{COLOR['BLUE']}Found {len(curated_highlights)} curated highlights.{COLOR['RESET']}") | |
| for highlight in curated_highlights: | |
| snap_list = highlight.get('snapList', []) | |
| print(f"{COLOR['BLUE']}Found {len(snap_list)} snaps in this highlight.{COLOR['RESET']}") | |
| for index, snap in enumerate(snap_list): | |
| self.process_snap(snap, index, "highlight_") | |
| # Download spotlights | |
| if 'spotlightHighlights' in page_props: | |
| spotlight_highlights = page_props['spotlightHighlights'] | |
| print(f"{COLOR['BLUE']}Found {len(spotlight_highlights)} spotlight highlights.{COLOR['RESET']}") | |
| # Use ThreadPoolExecutor to download spotlights in parallel | |
| with ThreadPoolExecutor(max_workers=3) as executor: | |
| for highlight in spotlight_highlights: | |
| snap_id = highlight.get('storyId', {}).get('value') | |
| if not snap_id: | |
| continue | |
| # Download video | |
| executor.submit(self.download_spotlight, snap_id) | |
| # Download thumbnail | |
| thumbnail_url = highlight.get('thumbnailUrl', {}).get('value') | |
| if thumbnail_url: | |
| filename = f"{self.username}/{snap_id}_thumb.jpg" | |
| executor.submit(self.download_file, thumbnail_url, filename) | |
| if __name__ == "__main__": | |
| # Example usage | |
| url = "https://www.snapchat.com/add/snackattackshow" # Replace with your target URL | |
| downloader = SnapchatDownloader(url) | |
| downloader.download_content() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment