|
|
@@ -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") |
|
|
|
|
|
|
|
|
|
|
|