| @@ -115,13 +115,17 @@ def generate_all_media_gallery(db_path, output_dir): | |||
| end_idx = min(start_idx + items_per_page, total_media) | |||
| page_media_messages = valid_media_messages[start_idx:end_idx] | |||
| # Create media-gallery subdirectory | |||
| media_gallery_dir = os.path.join(output_dir, "media-gallery") | |||
| os.makedirs(media_gallery_dir, exist_ok=True) | |||
| # Determine filename | |||
| if page_num == 1: | |||
| filename = "media-gallery.html" | |||
| else: | |||
| filename = f"media-gallery-page-{page_num}.html" | |||
| media_gallery_path = os.path.join(output_dir, filename) | |||
| media_gallery_path = os.path.join(media_gallery_dir, filename) | |||
| with open(media_gallery_path, 'w', encoding='utf-8') as f: | |||
| f.write(f""" | |||
| @@ -288,8 +292,8 @@ def generate_all_media_gallery(db_path, output_dir): | |||
| <div class="header"> | |||
| <h1>📷 Media Gallery</h1> | |||
| <div class="nav-links"> | |||
| <a href="whatsapp-chats.html">← Back to Chats</a> | |||
| <a href="index.html">🏠 Home</a> | |||
| <a href="../whatsapp-chats.html">← Back to Chats</a> | |||
| <a href="../index.html">🏠 Home</a> | |||
| </div> | |||
| </div> | |||
| <div class="container"> | |||
| @@ -387,10 +391,10 @@ def generate_all_media_gallery(db_path, output_dir): | |||
| <span class="sender-name">• {html.escape(str(sender_name))}</span> | |||
| <span class="timestamp">{convert_whatsapp_timestamp(message_date)}</span> | |||
| </div> | |||
| <a href="chats/{safe_filename}.html" style="text-decoration: none; color: inherit;"> | |||
| <a href="../chats/{safe_filename}.html" style="text-decoration: none; color: inherit;"> | |||
| {media_html} | |||
| </a> | |||
| <a href="../Message/{media_path}" target="_blank" class="raw-file-link">📁 Open File</a> | |||
| <a href="../../Message/{media_path}" target="_blank" class="raw-file-link">📁 Open File</a> | |||
| </div> | |||
| """) | |||
| @@ -613,7 +617,7 @@ def generate_chat_media_gallery(db_path, output_dir, chat_id, chat_name, contact | |||
| <h1>📷 Media from {html.escape(str(chat_name))}</h1> | |||
| <div class="nav-links"> | |||
| <a href="../chats/{safe_filename}.html">← Back to Chat</a> | |||
| <a href="../media-gallery.html">🖼️ All Media</a> | |||
| <a href="../media-gallery/media-gallery.html">🖼️ All Media</a> | |||
| <a href="../whatsapp-chats.html">💬 All Chats</a> | |||
| </div> | |||
| </div> | |||
| @@ -881,7 +885,7 @@ def generate_html_chat(db_path, media_path, output_dir, chat_id, chat_name, is_g | |||
| {html.escape(chat_name)} | |||
| <div class="chat-header-id">{contact_jid}</div> | |||
| <div class="nav-links"> | |||
| <a href="../whatsapp-chats.html">← Back</a> | |||
| <a href="../whatsapp-chats.html">← Back to Chats</a> | |||
| <a href="../media/{safe_filename}.html">📷 Media</a> | |||
| </div> | |||
| </div> | |||
| @@ -980,73 +984,73 @@ def process_iphone_backup(backup_path, output_dir): | |||
| 'Sticker.sqlite' | |||
| ] | |||
| # Prepare to recreate file structure | |||
| for fileID, domain, relativePath in files: | |||
| src_file = os.path.join(backup_path, fileID[:2], fileID) | |||
| dest_file = os.path.join(output_dir, relativePath) | |||
| os.makedirs(os.path.dirname(dest_file), exist_ok=True) | |||
| # # Prepare to recreate file structure | |||
| # for fileID, domain, relativePath in files: | |||
| # src_file = os.path.join(backup_path, fileID[:2], fileID) | |||
| # dest_file = os.path.join(output_dir, relativePath) | |||
| # os.makedirs(os.path.dirname(dest_file), exist_ok=True) | |||
| if not os.path.exists(src_file): | |||
| # print(f"Source file missing: {src_file}") | |||
| skipped_files += 1 | |||
| continue | |||
| # if not os.path.exists(src_file): | |||
| # # print(f"Source file missing: {src_file}") | |||
| # skipped_files += 1 | |||
| # continue | |||
| # Handle SQLite database files specially - merge data instead of overwriting | |||
| file_basename = os.path.basename(dest_file) | |||
| if file_basename in db_files_to_merge and os.path.exists(dest_file): | |||
| special_db_files += 1 | |||
| try: | |||
| # For SQLite databases, we need to merge the data | |||
| if file_basename == 'ChatStorage.sqlite': | |||
| merge_chat_database(src_file, dest_file) | |||
| else: | |||
| # For other SQLite databases, make a backup and then replace | |||
| # Future enhancement: implement proper merging for all database types | |||
| backup_file = f"{dest_file}.backup_{datetime.now().strftime('%Y%m%d%H%M%S')}" | |||
| shutil.copy2(dest_file, backup_file) | |||
| print(f"Created backup of {file_basename} as {os.path.basename(backup_file)}") | |||
| shutil.copy2(src_file, dest_file) | |||
| except Exception as e: | |||
| print(f"Error processing database {dest_file}: {e}") | |||
| continue | |||
| # # Handle SQLite database files specially - merge data instead of overwriting | |||
| # file_basename = os.path.basename(dest_file) | |||
| # if file_basename in db_files_to_merge and os.path.exists(dest_file): | |||
| # special_db_files += 1 | |||
| # try: | |||
| # # For SQLite databases, we need to merge the data | |||
| # if file_basename == 'ChatStorage.sqlite': | |||
| # merge_chat_database(src_file, dest_file) | |||
| # else: | |||
| # # For other SQLite databases, make a backup and then replace | |||
| # # Future enhancement: implement proper merging for all database types | |||
| # backup_file = f"{dest_file}.backup_{datetime.now().strftime('%Y%m%d%H%M%S')}" | |||
| # shutil.copy2(dest_file, backup_file) | |||
| # print(f"Created backup of {file_basename} as {os.path.basename(backup_file)}") | |||
| # shutil.copy2(src_file, dest_file) | |||
| # except Exception as e: | |||
| # print(f"Error processing database {dest_file}: {e}") | |||
| # continue | |||
| # For non-database files | |||
| if os.path.exists(dest_file): | |||
| # If file exists, we want to keep the newer one | |||
| # For media files, we always keep them (accumulate data) | |||
| is_media_file = any(relativePath.startswith(prefix) for prefix in ['Media/', 'Message/', 'ProfilePictures/', 'Avatar/']) | |||
| # # For non-database files | |||
| # if os.path.exists(dest_file): | |||
| # # If file exists, we want to keep the newer one | |||
| # # For media files, we always keep them (accumulate data) | |||
| # is_media_file = any(relativePath.startswith(prefix) for prefix in ['Media/', 'Message/', 'ProfilePictures/', 'Avatar/']) | |||
| if is_media_file: | |||
| # For media files, don't overwrite but create a version with timestamp if different | |||
| if not files_are_identical(src_file, dest_file): | |||
| filename, ext = os.path.splitext(dest_file) | |||
| timestamp = datetime.now().strftime('%Y%m%d%H%M%S') | |||
| new_dest_file = f"{filename}_{timestamp}{ext}" | |||
| try: | |||
| shutil.copy2(src_file, new_dest_file) | |||
| print(f"Saved additional version of media file: {os.path.relpath(new_dest_file, output_dir)}") | |||
| new_files += 1 | |||
| except Exception as e: | |||
| print(f"Error copying alternate version {src_file}: {e}") | |||
| skipped_files += 1 | |||
| else: | |||
| skipped_files += 1 | |||
| else: | |||
| # For non-media files, we'll take the newer one | |||
| try: | |||
| shutil.copy2(src_file, dest_file) | |||
| updated_files += 1 | |||
| except Exception as e: | |||
| print(f"Error updating {dest_file}: {e}") | |||
| skipped_files += 1 | |||
| else: | |||
| # If file doesn't exist, copy it | |||
| try: | |||
| shutil.copy2(src_file, dest_file) | |||
| new_files += 1 | |||
| except Exception as e: | |||
| print(f"Error copying {src_file} to {dest_file}: {e}") | |||
| skipped_files += 1 | |||
| # if is_media_file: | |||
| # # For media files, don't overwrite but create a version with timestamp if different | |||
| # if not files_are_identical(src_file, dest_file): | |||
| # filename, ext = os.path.splitext(dest_file) | |||
| # timestamp = datetime.now().strftime('%Y%m%d%H%M%S') | |||
| # new_dest_file = f"{filename}_{timestamp}{ext}" | |||
| # try: | |||
| # shutil.copy2(src_file, new_dest_file) | |||
| # print(f"Saved additional version of media file: {os.path.relpath(new_dest_file, output_dir)}") | |||
| # new_files += 1 | |||
| # except Exception as e: | |||
| # print(f"Error copying alternate version {src_file}: {e}") | |||
| # skipped_files += 1 | |||
| # else: | |||
| # skipped_files += 1 | |||
| # else: | |||
| # # For non-media files, we'll take the newer one | |||
| # try: | |||
| # shutil.copy2(src_file, dest_file) | |||
| # updated_files += 1 | |||
| # except Exception as e: | |||
| # print(f"Error updating {dest_file}: {e}") | |||
| # skipped_files += 1 | |||
| # else: | |||
| # # If file doesn't exist, copy it | |||
| # try: | |||
| # shutil.copy2(src_file, dest_file) | |||
| # new_files += 1 | |||
| # except Exception as e: | |||
| # print(f"Error copying {src_file} to {dest_file}: {e}") | |||
| # skipped_files += 1 | |||
| print(f"\nBackup import summary:") | |||
| print(f"- Added {new_files} new files") | |||
| @@ -1358,7 +1362,7 @@ def main(): | |||
| <h1>WhatsApp Chat Export</h1> | |||
| <div class="export-info">Exported on {datetime.now().strftime('%Y-%m-%d %H:%M')}</div> | |||
| <div style="margin-top: 10px;"> | |||
| <a href="media-gallery.html" style="color: rgba(255,255,255,0.9); text-decoration: none; padding: 5px 10px; border-radius: 5px; background-color: rgba(255,255,255,0.1);">📷 View All Media</a> | |||
| <a href="media-gallery/media-gallery.html" style="color: rgba(255,255,255,0.9); text-decoration: none; padding: 5px 10px; border-radius: 5px; background-color: rgba(255,255,255,0.1);">📷 View All Media</a> | |||
| </div> | |||
| </div> | |||
| <div class="container"> | |||
| @@ -1464,7 +1468,7 @@ def main(): | |||
| print(f" • {os.path.abspath(index_path)}") | |||
| print(f" • {os.path.abspath(redirect_index)}") | |||
| print(f"\nAdditional features:") | |||
| print(f" • Media Gallery: {os.path.abspath(os.path.join(args.output, 'media-gallery.html'))}") | |||
| print(f" • Media Gallery: {os.path.abspath(os.path.join(args.output, 'media-gallery', 'media-gallery.html'))}") | |||
| print(f" • Individual chat media galleries available in the media/ folder") | |||