| @@ -115,13 +115,17 @@ def generate_all_media_gallery(db_path, output_dir): | |||||
| end_idx = min(start_idx + items_per_page, total_media) | end_idx = min(start_idx + items_per_page, total_media) | ||||
| page_media_messages = valid_media_messages[start_idx:end_idx] | 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 | # Determine filename | ||||
| if page_num == 1: | if page_num == 1: | ||||
| filename = "media-gallery.html" | filename = "media-gallery.html" | ||||
| else: | else: | ||||
| filename = f"media-gallery-page-{page_num}.html" | 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: | with open(media_gallery_path, 'w', encoding='utf-8') as f: | ||||
| f.write(f""" | f.write(f""" | ||||
| @@ -288,8 +292,8 @@ def generate_all_media_gallery(db_path, output_dir): | |||||
| <div class="header"> | <div class="header"> | ||||
| <h1>📷 Media Gallery</h1> | <h1>📷 Media Gallery</h1> | ||||
| <div class="nav-links"> | <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> | </div> | ||||
| <div class="container"> | <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="sender-name">• {html.escape(str(sender_name))}</span> | ||||
| <span class="timestamp">{convert_whatsapp_timestamp(message_date)}</span> | <span class="timestamp">{convert_whatsapp_timestamp(message_date)}</span> | ||||
| </div> | </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} | {media_html} | ||||
| </a> | </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> | </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> | <h1>📷 Media from {html.escape(str(chat_name))}</h1> | ||||
| <div class="nav-links"> | <div class="nav-links"> | ||||
| <a href="../chats/{safe_filename}.html">← Back to Chat</a> | <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> | <a href="../whatsapp-chats.html">💬 All Chats</a> | ||||
| </div> | </div> | ||||
| </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)} | {html.escape(chat_name)} | ||||
| <div class="chat-header-id">{contact_jid}</div> | <div class="chat-header-id">{contact_jid}</div> | ||||
| <div class="nav-links"> | <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> | <a href="../media/{safe_filename}.html">📷 Media</a> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| @@ -980,73 +984,73 @@ def process_iphone_backup(backup_path, output_dir): | |||||
| 'Sticker.sqlite' | '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"\nBackup import summary:") | ||||
| print(f"- Added {new_files} new files") | print(f"- Added {new_files} new files") | ||||
| @@ -1358,7 +1362,7 @@ def main(): | |||||
| <h1>WhatsApp Chat Export</h1> | <h1>WhatsApp Chat Export</h1> | ||||
| <div class="export-info">Exported on {datetime.now().strftime('%Y-%m-%d %H:%M')}</div> | <div class="export-info">Exported on {datetime.now().strftime('%Y-%m-%d %H:%M')}</div> | ||||
| <div style="margin-top: 10px;"> | <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> | </div> | ||||
| <div class="container"> | <div class="container"> | ||||
| @@ -1464,7 +1468,7 @@ def main(): | |||||
| print(f" • {os.path.abspath(index_path)}") | print(f" • {os.path.abspath(index_path)}") | ||||
| print(f" • {os.path.abspath(redirect_index)}") | print(f" • {os.path.abspath(redirect_index)}") | ||||
| print(f"\nAdditional features:") | 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") | print(f" • Individual chat media galleries available in the media/ folder") | ||||