chat-export 0.9.2__tar.gz → 0.9.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chat-export
3
- Version: 0.9.2
3
+ Version: 0.9.4
4
4
  Summary: Convert WhatsApp chat exports to HTML format
5
5
  Author-email: matlon GmbH <software@matlon.ch>
6
6
  License: MIT License
@@ -60,7 +60,7 @@ Dynamic: license-file
60
60
  This tool converts a WhatsApp chat export into two HTML formats: one with inline media (such as images, videos, and audio files) and a compact version with media links.
61
61
 
62
62
 
63
- Video Tutorial (still refers to the previously used tool name instead of `chat-export`):
63
+ Video Tutorial (still refers to the previously used tool name instead of `chat-export`, doesn't cover new features like the non-interactive CLI mode yet):
64
64
  [![YouTube](https://img.youtube.com/vi/s1dMO8pjkC8/0.jpg)](https://www.youtube.com/watch?v=s1dMO8pjkC8)"
65
65
 
66
66
  ## Binary Release Downloads
@@ -101,7 +101,28 @@ You can for example save it on Google Drive or Dropbox or send it to yourself wi
101
101
 
102
102
  3. **Install and Run the Tool:**
103
103
 
104
- **Option 1: Install via pip (Recommended)**
104
+ ### Option 1: Download Binary Executables: Easy for non-technical users
105
+
106
+ If you don't know what Python is and just want to use the tool without having to deal with technical details, you can download the binary executables.
107
+
108
+ - For Windows, download [chat-export.exe](https://github.com/mtln/chat-export/releases/latest/download/chat-export.exe).
109
+
110
+ Because the .exe is unsigned, you will see a warning, but after clicking on "More Information" you should have the option to run it anyway. An EV code signing certificate for Windows costs a lot. If I get enough donations, I will buy one, so I can release signed exe files.
111
+
112
+ - For Mac, download [chat-export-installer.pkg](https://github.com/mtln/chat-export/releases/latest/download/chat-export-installer.pkg)
113
+
114
+ Once installed on Mac:
115
+
116
+ 1. Open the **Terminal** app (e.g. by typing "terminal" in Spotlight Search).
117
+ 2. Type: `chat-export`
118
+
119
+ This will start the chat export tool from anywhere in the terminal.
120
+
121
+ To uninstall `chat-export` from Mac, open the Terminal and run: `sudo rm /usr/local/bin/chat-export`
122
+
123
+ The executables have been built with pyinstaller.
124
+
125
+ ### Option 2: Install via pip
105
126
 
106
127
  If you have Python installed, you can install chat-export as a command-line tool:
107
128
  ```
@@ -122,43 +143,41 @@ You can for example save it on Google Drive or Dropbox or send it to yourself wi
122
143
  pip install chat-export[windows]
123
144
  ```
124
145
 
125
-
126
-
146
+
127
147
  *Note: Without these optional dependencies, the tool will fall back to command-line input for file selection or use tkinter (if available) for file dialogs. If you're planning to use the tool in non-interactive mode, you don't need to install these dependencies.*
128
148
 
129
149
  After installation, you can run it from anywhere in your terminal:
130
-
150
+
131
151
  ```
132
152
  chat-export
153
+ ```
154
+
155
+ or
156
+ ```
133
157
  chat-export --help
134
158
  ```
135
-
159
+
136
160
  To uninstall:
137
161
  ```
138
162
  pip uninstall chat-export
139
163
  ```
140
164
 
141
- **Option 2: Download Binary Executables**
142
-
143
- - For Windows, download [chat-export.exe](https://github.com/mtln/chat-export/releases/latest/download/chat-export.exe).
144
-
145
- Because the .exe is unsigned, you will see a warning, but after clicking on "More Information" you should have the option to run it anyway. An EV code signing certificate for Windows costs a lot. If I get enough donations, I will buy one, so I can release signed exe files.
146
-
147
- - For Mac, download [chat-export-installer.pkg](https://github.com/mtln/chat-export/releases/latest/download/chat-export-installer.pkg)
148
-
149
- Once installed on Mac:
150
- 1. Open the **Terminal** app (e.g. by typing "terminal" in Spotlight Search).
151
- 2. Type: `chat-export`
152
-
153
- This will start the chat export tool from anywhere in the terminal.
165
+ #### Using uv tool instead of pip
166
+ If you have uv installed, you can use `uv tool install` instead of `pip install`. This is perfect for tools like chat-export because it will install the dependencies in a virtual environment and not in the global environment - while still making the tool available globally in your shell / path.
154
167
 
155
- To uninstall `chat-export` from Mac, open the Terminal and run: `sudo rm /usr/local/bin/chat-export`
156
-
157
- The executables have been built with pyinstaller.
168
+
169
+ `uv tool install chat-export[windows]`
170
+ or
171
+ `uv tool install chat-export[macos]`
172
+ or just
173
+ `uv tool install chat-export`
174
+ uninstall with:
175
+ `uv tool uninstall chat-export`
176
+
158
177
 
159
- **Option 3: Run Directly (No Installation)**
178
+ ###Option 3: Run Directly (No Installation)
160
179
 
161
- If Python is installed on your Windows, Mac or Linux computer, run the tool directly (no installation required) with the following command:
180
+ If Python is installed on your Windows, Mac or Linux computer, run the tool directly (no pip installation required, no venv required, no additional dependencies required because it's just vanilla standard lib Python in a single scriptfile, always run the latest up to date version directly from GitHub) with the following command:
162
181
  ```
163
182
  python -c "import urllib.request; exec(urllib.request.urlopen('https://raw.githubusercontent.com/mtln/chat-export/refs/heads/main/main.py').read().decode())"
164
183
  ```
@@ -167,11 +186,14 @@ You can for example save it on Google Drive or Dropbox or send it to yourself wi
167
186
  python3 -c "import urllib.request; exec(urllib.request.urlopen('https://raw.githubusercontent.com/mtln/chat-export/refs/heads/main/main.py').read().decode())"
168
187
  ```
169
188
 
170
- **Usage**
189
+ ## Usage
171
190
 
172
191
  The tool supports both interactive and non-interactive modes:
173
192
 
174
193
  ### Interactive Mode (Default)
194
+
195
+ Just run `chat-export` in your terminal.
196
+
175
197
  **CLI Parameters:**
176
198
 
177
199
  `-o, --output-dir`: Base directory where the chat folder will be created (optional, default: current directory)
@@ -186,6 +208,22 @@ The tool supports both interactive and non-interactive modes:
186
208
 
187
209
  For automation, scripting, or when you know all parameters in advance, you can use the non-interactive CLI mode. This mode processes the chat without any prompts and is perfect for batch processing or integration into other tools.
188
210
 
211
+ #### Media Embedding Options
212
+
213
+ The tool offers two approaches for handling media files:
214
+
215
+ **Default Behavior (External Media Files):**
216
+ - Creates a folder structure with HTML files and a separate `media/` folder
217
+ - Media files are extracted and linked to from the HTML
218
+ - Results in multiple files that need to be kept together
219
+ - Smaller HTML files, but requires managing multiple files
220
+
221
+ **Embedded Media (`--embed-media` option):**
222
+ - Creates a single self-contained HTML file with all media embedded as base64
223
+ - No external media files are created or needed
224
+ - Perfect for sharing, archiving, or when you want everything in one file
225
+ - Larger HTML file size, but completely portable
226
+
189
227
  **Basic Usage:**
190
228
  ```
191
229
  chat-export -n -z "path/to/chat.zip" -p "Your Name"
@@ -199,6 +237,7 @@ chat-export -n -z "path/to/chat.zip" -p "Your Name"
199
237
  - `--from-date`: Optional start date for filtering (formats: DD.MM.YYYY, MM/DD/YYYY, DD.MM.YY, MM/DD/YY)
200
238
  - `--until-date`: Optional end date for filtering
201
239
  - `-o, --output-dir`: Base directory where the chat folder will be created (default: current directory)
240
+ - `--embed-media`: Embed media files as base64 in HTML instead of linking to external files (optional)
202
241
 
203
242
 
204
243
  **Examples:**
@@ -227,9 +266,15 @@ Windows paths (important: no trailing backslash):
227
266
  chat-export -n -z "chat.zip" -p "Your Name" -o "C:\temp"
228
267
  ```
229
268
 
269
+ With embedded media (creates a single self-contained HTML file):
270
+ ```
271
+ chat-export -n -z "chat.zip" -p "Your Name" --embed-media
272
+ ```
273
+
230
274
  **Important Notes:**
231
275
  - The participant name must match exactly as it appears in the chat (case-sensitive)
232
276
  - If the participant name is not found, the tool will display all available participants and exit
277
+ - When using `--embed-media`, media files are encoded as base64 and embedded directly in the HTML, creating a single self-contained file that doesn't require external media files
233
278
 
234
279
 
235
280
  * When printing an HTML page, most web browsers are set by default to exclude background colors to save ink or toner. If you want to include them, you need to enable background graphics in your browser settings. See the section below for instructions.
@@ -1,207 +1,252 @@
1
- # chat-export: Convert WhatsApp Chats to HTML
2
-
3
- This tool converts a WhatsApp chat export into two HTML formats: one with inline media (such as images, videos, and audio files) and a compact version with media links.
4
-
5
-
6
- Video Tutorial (still refers to the previously used tool name instead of `chat-export`):
7
- [![YouTube](https://img.youtube.com/vi/s1dMO8pjkC8/0.jpg)](https://www.youtube.com/watch?v=s1dMO8pjkC8)"
8
-
9
- ## Binary Release Downloads
10
- * Windows: [chat-export.exe](https://github.com/mtln/chat-export/releases/latest/download/chat-export.exe)
11
- * Mac: [chat-export-installer.pkg](https://github.com/mtln/chat-export/releases/latest/download/chat-export-installer.pkg)
12
-
13
-
14
- ## Why is this useful?
15
- The HTML export is:
16
-
17
- * **Printable:** You can print the chat or save as a PDF.
18
- * **Searchable:** You can search for specific messages in the browser.
19
- * **Shareable:** You can share the chat with others, but make sure to get consent from all other participants first.
20
- * **Durable:** You can keep the chat as a record for years to come. Apps come and go. Plain HTML is here to stay.
21
- * **Automatable:** With the non-interactive CLI mode, you can script batch processing of multiple chat exports or integrate the tool into other workflows.
22
-
23
- Maybe you want to:
24
-
25
- * keep a record of a conversation with a former loved one, friend, or business partner. You want to delete the chat from your phone, but you don’t want to lose the memories.
26
- * save a chat with important information, such as addresses, phone numbers, or other data.
27
- * keep a chat with a person who has passed away.
28
- * export just an excerpt of a chat from a specific date range.
29
- * archive a chat before freeing up space on your phone by deleting photos, videos, and other documents that were part of the chat.
30
- * ask another chat participant who still has a complete version of the chat (including all pictures) on his or her phone to send you a chat export, so you can convert and archive it.
31
- * batch process multiple chat exports automatically using the CLI mode.
32
- * integrate chat conversion into backup or archival scripts.
33
- * process chat exports on headless servers or in automated workflows.
34
-
35
- Furthermore, the tool is [open-source](https://github.com/mtln/chat-export) and runs offline on your computer, so you can be sure that your data is not being sent to any server.
36
- And by the way, it’s free! If you find it useful, you can [donate](https://donate.stripe.com/3csfZLaIj5JE6dO4gg).
37
-
38
- ## Instructions
39
-
40
- 1. **Export the Chat:** Begin by exporting a chat from the WhatsApp app, preferably with media included. For detailed instructions, refer to [WhatsApp FAQ](https://faq.whatsapp.com/search?helpref=search&query=%20export%20chat).
41
- You can for example save it on Google Drive or Dropbox or send it to yourself with WhatsApp.
42
-
43
- 2. **Transfer the File:** Move the exported ZIP file to your computer. If you have sent it to yourself with WhatsApp, you can download it with WhatsApp Web or with the WhatsApp App on your computer. Delete the WhatsApp message afterwards to save space.
44
-
45
- 3. **Install and Run the Tool:**
46
-
47
- **Option 1: Install via pip (Recommended)**
48
-
49
- If you have Python installed, you can install chat-export as a command-line tool:
50
- ```
51
- pip install chat-export
52
- ```
53
-
54
- **Optional: Install with platform-specific dependencies for native file dialogs**
55
-
56
- For better user experience with native file picker dialogs, you can install platform-specific dependencies:
57
-
58
- **On macOS:**
59
- ```
60
- pip install chat-export[macos]
61
- ```
62
-
63
- **On Windows:**
64
- ```
65
- pip install chat-export[windows]
66
- ```
67
-
68
-
69
-
70
- *Note: Without these optional dependencies, the tool will fall back to command-line input for file selection or use tkinter (if available) for file dialogs. If you're planning to use the tool in non-interactive mode, you don't need to install these dependencies.*
71
-
72
- After installation, you can run it from anywhere in your terminal:
73
-
74
- ```
75
- chat-export
76
- chat-export --help
77
- ```
78
-
79
- To uninstall:
80
- ```
81
- pip uninstall chat-export
82
- ```
83
-
84
- **Option 2: Download Binary Executables**
85
-
86
- - For Windows, download [chat-export.exe](https://github.com/mtln/chat-export/releases/latest/download/chat-export.exe).
87
-
88
- Because the .exe is unsigned, you will see a warning, but after clicking on "More Information" you should have the option to run it anyway. An EV code signing certificate for Windows costs a lot. If I get enough donations, I will buy one, so I can release signed exe files.
89
-
90
- - For Mac, download [chat-export-installer.pkg](https://github.com/mtln/chat-export/releases/latest/download/chat-export-installer.pkg)
91
-
92
- Once installed on Mac:
93
- 1. Open the **Terminal** app (e.g. by typing "terminal" in Spotlight Search).
94
- 2. Type: `chat-export`
95
-
96
- This will start the chat export tool from anywhere in the terminal.
97
-
98
- To uninstall `chat-export` from Mac, open the Terminal and run: `sudo rm /usr/local/bin/chat-export`
99
-
100
- The executables have been built with pyinstaller.
101
-
102
- **Option 3: Run Directly (No Installation)**
103
-
104
- If Python is installed on your Windows, Mac or Linux computer, run the tool directly (no installation required) with the following command:
105
- ```
106
- python -c "import urllib.request; exec(urllib.request.urlopen('https://raw.githubusercontent.com/mtln/chat-export/refs/heads/main/main.py').read().decode())"
107
- ```
108
- or
109
- ```
110
- python3 -c "import urllib.request; exec(urllib.request.urlopen('https://raw.githubusercontent.com/mtln/chat-export/refs/heads/main/main.py').read().decode())"
111
- ```
112
-
113
- **Usage**
114
-
115
- The tool supports both interactive and non-interactive modes:
116
-
117
- ### Interactive Mode (Default)
118
- **CLI Parameters:**
119
-
120
- `-o, --output-dir`: Base directory where the chat folder will be created (optional, default: current directory)
121
-
122
-
123
- * After starting the tool, a file picker dialog will open. Select the ZIP file of the chat export you want to convert. If your installation does not support file dialogs, you will be prompted for the path to the ZIP file.
124
- * You can enter start and end dates to export only a specific date range. If you leave the fields empty, the entire chat will be exported. **If the terminal window doesn't accept your keyboard input, click with your mouse right after the colon in `Enter the number corresponding to your name:`** to set the focus to the terminal window.
125
- * A list of chat participants will appear. Select your name so that your messages are displayed in green chat bubbles, just like on WhatsApp.
126
- * Once the conversion completes, you can choose to open the HTML files immediately in your browser (just hit enter). From there, you can save the chat as a PDF or print it if needed.
127
-
128
- ### Command Line Interface (Non-Interactive Mode)
129
-
130
- For automation, scripting, or when you know all parameters in advance, you can use the non-interactive CLI mode. This mode processes the chat without any prompts and is perfect for batch processing or integration into other tools.
131
-
132
- **Basic Usage:**
133
- ```
134
- chat-export -n -z "path/to/chat.zip" -p "Your Name"
135
- ```
136
-
137
- **CLI Parameters:**
138
-
139
- - `-n, --non-interactive`: Enable non-interactive mode (required)
140
- - `-z, --zip-file`: Path to WhatsApp chat export ZIP file (required)
141
- - `-p, --participant`: Your name exactly as it appears in the chat (required)
142
- - `--from-date`: Optional start date for filtering (formats: DD.MM.YYYY, MM/DD/YYYY, DD.MM.YY, MM/DD/YY)
143
- - `--until-date`: Optional end date for filtering
144
- - `-o, --output-dir`: Base directory where the chat folder will be created (default: current directory)
145
-
146
-
147
- **Examples:**
148
-
149
- Basic conversion:
150
- ```
151
- chat-export -n -z "WhatsApp Chat with John.zip" -p "Your Name"
152
- ```
153
- With absolute path to ZIP file (Windows):
154
- ```
155
- chat-export -n -z "c:\temp\WhatsApp Chat with John.zip" -p "Your Name"
156
- ```
157
-
158
- With date filtering:
159
- ```
160
- chat-export -n -z "chat.zip" -p "Your Name" --from-date "01.01.2024" --until-date "31.12.2024"
161
- ```
162
-
163
- Custom output directory (creates chat folder in /tmp instead of current directory):
164
- ```
165
- chat-export -n -z "chat.zip" -p "Your Name" -o "/tmp"
166
- ```
167
-
168
- Windows paths (important: no trailing backslash):
169
- ```
170
- chat-export -n -z "chat.zip" -p "Your Name" -o "C:\temp"
171
- ```
172
-
173
- **Important Notes:**
174
- - The participant name must match exactly as it appears in the chat (case-sensitive)
175
- - If the participant name is not found, the tool will display all available participants and exit
176
-
177
-
178
- * When printing an HTML page, most web browsers are set by default to exclude background colors to save ink or toner. If you want to include them, you need to enable background graphics in your browser settings. See the section below for instructions.
179
- * **In Google Chrome**: Go to `Print` → `More settings` → Check `Background graphics`.
180
- * **In Mozilla Firefox**: Go to `File` → `Print` → `Page Setup` → Check `Print Background (colors & images)`.
181
- * **In Microsoft Edge**: Go to `Print` → `More settings` → Check `Background graphics`.
182
-
183
-
184
- ## Supported languages
185
- WhatsApp chat exports vary depending on your phone’s system language.
186
-
187
- chat-export has been tested with many languages and regional settings.
188
-
189
- If it doesn't work for your language, please let me know.
190
-
191
- ## Supported Operating Systems
192
-
193
- * Windows
194
- * Mac
195
- * Linux
196
-
197
- ## Limitations
198
- There is information in chats, that is not included in the export.zip created by WhatsApp. As a consequence, this information cannot be rendered by chat-export.
199
-
200
- This includes:
201
-
202
- * Reactions (thumbs up and other emojis that are not a stand-alone message, but created by long tapping a previous message)
203
- * Message relations (in WhatsApp, you can right-swipe a previous message and then write a message in relation to this)
204
- * Chat group metadata (e.g. list of all members, admins, ...)
205
-
206
- ## Disclaimer
207
- chat-export comes with no warranty. Use it responsibly and respect the privacy of other chat participants. The tool is not affiliated with WhatsApp or Meta.
1
+ # chat-export: Convert WhatsApp Chats to HTML
2
+
3
+ This tool converts a WhatsApp chat export into two HTML formats: one with inline media (such as images, videos, and audio files) and a compact version with media links.
4
+
5
+
6
+ Video Tutorial (still refers to the previously used tool name instead of `chat-export`, doesn't cover new features like the non-interactive CLI mode yet):
7
+ [![YouTube](https://img.youtube.com/vi/s1dMO8pjkC8/0.jpg)](https://www.youtube.com/watch?v=s1dMO8pjkC8)"
8
+
9
+ ## Binary Release Downloads
10
+ * Windows: [chat-export.exe](https://github.com/mtln/chat-export/releases/latest/download/chat-export.exe)
11
+ * Mac: [chat-export-installer.pkg](https://github.com/mtln/chat-export/releases/latest/download/chat-export-installer.pkg)
12
+
13
+
14
+ ## Why is this useful?
15
+ The HTML export is:
16
+
17
+ * **Printable:** You can print the chat or save as a PDF.
18
+ * **Searchable:** You can search for specific messages in the browser.
19
+ * **Shareable:** You can share the chat with others, but make sure to get consent from all other participants first.
20
+ * **Durable:** You can keep the chat as a record for years to come. Apps come and go. Plain HTML is here to stay.
21
+ * **Automatable:** With the non-interactive CLI mode, you can script batch processing of multiple chat exports or integrate the tool into other workflows.
22
+
23
+ Maybe you want to:
24
+
25
+ * keep a record of a conversation with a former loved one, friend, or business partner. You want to delete the chat from your phone, but you don’t want to lose the memories.
26
+ * save a chat with important information, such as addresses, phone numbers, or other data.
27
+ * keep a chat with a person who has passed away.
28
+ * export just an excerpt of a chat from a specific date range.
29
+ * archive a chat before freeing up space on your phone by deleting photos, videos, and other documents that were part of the chat.
30
+ * ask another chat participant who still has a complete version of the chat (including all pictures) on his or her phone to send you a chat export, so you can convert and archive it.
31
+ * batch process multiple chat exports automatically using the CLI mode.
32
+ * integrate chat conversion into backup or archival scripts.
33
+ * process chat exports on headless servers or in automated workflows.
34
+
35
+ Furthermore, the tool is [open-source](https://github.com/mtln/chat-export) and runs offline on your computer, so you can be sure that your data is not being sent to any server.
36
+ And by the way, it’s free! If you find it useful, you can [donate](https://donate.stripe.com/3csfZLaIj5JE6dO4gg).
37
+
38
+ ## Instructions
39
+
40
+ 1. **Export the Chat:** Begin by exporting a chat from the WhatsApp app, preferably with media included. For detailed instructions, refer to [WhatsApp FAQ](https://faq.whatsapp.com/search?helpref=search&query=%20export%20chat).
41
+ You can for example save it on Google Drive or Dropbox or send it to yourself with WhatsApp.
42
+
43
+ 2. **Transfer the File:** Move the exported ZIP file to your computer. If you have sent it to yourself with WhatsApp, you can download it with WhatsApp Web or with the WhatsApp App on your computer. Delete the WhatsApp message afterwards to save space.
44
+
45
+ 3. **Install and Run the Tool:**
46
+
47
+ ### Option 1: Download Binary Executables: Easy for non-technical users
48
+
49
+ If you don't know what Python is and just want to use the tool without having to deal with technical details, you can download the binary executables.
50
+
51
+ - For Windows, download [chat-export.exe](https://github.com/mtln/chat-export/releases/latest/download/chat-export.exe).
52
+
53
+ Because the .exe is unsigned, you will see a warning, but after clicking on "More Information" you should have the option to run it anyway. An EV code signing certificate for Windows costs a lot. If I get enough donations, I will buy one, so I can release signed exe files.
54
+
55
+ - For Mac, download [chat-export-installer.pkg](https://github.com/mtln/chat-export/releases/latest/download/chat-export-installer.pkg)
56
+
57
+ Once installed on Mac:
58
+
59
+ 1. Open the **Terminal** app (e.g. by typing "terminal" in Spotlight Search).
60
+ 2. Type: `chat-export`
61
+
62
+ This will start the chat export tool from anywhere in the terminal.
63
+
64
+ To uninstall `chat-export` from Mac, open the Terminal and run: `sudo rm /usr/local/bin/chat-export`
65
+
66
+ The executables have been built with pyinstaller.
67
+
68
+ ### Option 2: Install via pip
69
+
70
+ If you have Python installed, you can install chat-export as a command-line tool:
71
+ ```
72
+ pip install chat-export
73
+ ```
74
+
75
+ **Optional: Install with platform-specific dependencies for native file dialogs**
76
+
77
+ For better user experience with native file picker dialogs, you can install platform-specific dependencies:
78
+
79
+ **On macOS:**
80
+ ```
81
+ pip install chat-export[macos]
82
+ ```
83
+
84
+ **On Windows:**
85
+ ```
86
+ pip install chat-export[windows]
87
+ ```
88
+
89
+
90
+ *Note: Without these optional dependencies, the tool will fall back to command-line input for file selection or use tkinter (if available) for file dialogs. If you're planning to use the tool in non-interactive mode, you don't need to install these dependencies.*
91
+
92
+ After installation, you can run it from anywhere in your terminal:
93
+
94
+ ```
95
+ chat-export
96
+ ```
97
+
98
+ or
99
+ ```
100
+ chat-export --help
101
+ ```
102
+
103
+ To uninstall:
104
+ ```
105
+ pip uninstall chat-export
106
+ ```
107
+
108
+ #### Using uv tool instead of pip
109
+ If you have uv installed, you can use `uv tool install` instead of `pip install`. This is perfect for tools like chat-export because it will install the dependencies in a virtual environment and not in the global environment - while still making the tool available globally in your shell / path.
110
+
111
+
112
+ `uv tool install chat-export[windows]`
113
+ or
114
+ `uv tool install chat-export[macos]`
115
+ or just
116
+ `uv tool install chat-export`
117
+ uninstall with:
118
+ `uv tool uninstall chat-export`
119
+
120
+
121
+ ###Option 3: Run Directly (No Installation)
122
+
123
+ If Python is installed on your Windows, Mac or Linux computer, run the tool directly (no pip installation required, no venv required, no additional dependencies required because it's just vanilla standard lib Python in a single scriptfile, always run the latest up to date version directly from GitHub) with the following command:
124
+ ```
125
+ python -c "import urllib.request; exec(urllib.request.urlopen('https://raw.githubusercontent.com/mtln/chat-export/refs/heads/main/main.py').read().decode())"
126
+ ```
127
+ or
128
+ ```
129
+ python3 -c "import urllib.request; exec(urllib.request.urlopen('https://raw.githubusercontent.com/mtln/chat-export/refs/heads/main/main.py').read().decode())"
130
+ ```
131
+
132
+ ## Usage
133
+
134
+ The tool supports both interactive and non-interactive modes:
135
+
136
+ ### Interactive Mode (Default)
137
+
138
+ Just run `chat-export` in your terminal.
139
+
140
+ **CLI Parameters:**
141
+
142
+ `-o, --output-dir`: Base directory where the chat folder will be created (optional, default: current directory)
143
+
144
+
145
+ * After starting the tool, a file picker dialog will open. Select the ZIP file of the chat export you want to convert. If your installation does not support file dialogs, you will be prompted for the path to the ZIP file.
146
+ * You can enter start and end dates to export only a specific date range. If you leave the fields empty, the entire chat will be exported. **If the terminal window doesn't accept your keyboard input, click with your mouse right after the colon in `Enter the number corresponding to your name:`** to set the focus to the terminal window.
147
+ * A list of chat participants will appear. Select your name so that your messages are displayed in green chat bubbles, just like on WhatsApp.
148
+ * Once the conversion completes, you can choose to open the HTML files immediately in your browser (just hit enter). From there, you can save the chat as a PDF or print it if needed.
149
+
150
+ ### Command Line Interface (Non-Interactive Mode)
151
+
152
+ For automation, scripting, or when you know all parameters in advance, you can use the non-interactive CLI mode. This mode processes the chat without any prompts and is perfect for batch processing or integration into other tools.
153
+
154
+ #### Media Embedding Options
155
+
156
+ The tool offers two approaches for handling media files:
157
+
158
+ **Default Behavior (External Media Files):**
159
+ - Creates a folder structure with HTML files and a separate `media/` folder
160
+ - Media files are extracted and linked to from the HTML
161
+ - Results in multiple files that need to be kept together
162
+ - Smaller HTML files, but requires managing multiple files
163
+
164
+ **Embedded Media (`--embed-media` option):**
165
+ - Creates a single self-contained HTML file with all media embedded as base64
166
+ - No external media files are created or needed
167
+ - Perfect for sharing, archiving, or when you want everything in one file
168
+ - Larger HTML file size, but completely portable
169
+
170
+ **Basic Usage:**
171
+ ```
172
+ chat-export -n -z "path/to/chat.zip" -p "Your Name"
173
+ ```
174
+
175
+ **CLI Parameters:**
176
+
177
+ - `-n, --non-interactive`: Enable non-interactive mode (required)
178
+ - `-z, --zip-file`: Path to WhatsApp chat export ZIP file (required)
179
+ - `-p, --participant`: Your name exactly as it appears in the chat (required)
180
+ - `--from-date`: Optional start date for filtering (formats: DD.MM.YYYY, MM/DD/YYYY, DD.MM.YY, MM/DD/YY)
181
+ - `--until-date`: Optional end date for filtering
182
+ - `-o, --output-dir`: Base directory where the chat folder will be created (default: current directory)
183
+ - `--embed-media`: Embed media files as base64 in HTML instead of linking to external files (optional)
184
+
185
+
186
+ **Examples:**
187
+
188
+ Basic conversion:
189
+ ```
190
+ chat-export -n -z "WhatsApp Chat with John.zip" -p "Your Name"
191
+ ```
192
+ With absolute path to ZIP file (Windows):
193
+ ```
194
+ chat-export -n -z "c:\temp\WhatsApp Chat with John.zip" -p "Your Name"
195
+ ```
196
+
197
+ With date filtering:
198
+ ```
199
+ chat-export -n -z "chat.zip" -p "Your Name" --from-date "01.01.2024" --until-date "31.12.2024"
200
+ ```
201
+
202
+ Custom output directory (creates chat folder in /tmp instead of current directory):
203
+ ```
204
+ chat-export -n -z "chat.zip" -p "Your Name" -o "/tmp"
205
+ ```
206
+
207
+ Windows paths (important: no trailing backslash):
208
+ ```
209
+ chat-export -n -z "chat.zip" -p "Your Name" -o "C:\temp"
210
+ ```
211
+
212
+ With embedded media (creates a single self-contained HTML file):
213
+ ```
214
+ chat-export -n -z "chat.zip" -p "Your Name" --embed-media
215
+ ```
216
+
217
+ **Important Notes:**
218
+ - The participant name must match exactly as it appears in the chat (case-sensitive)
219
+ - If the participant name is not found, the tool will display all available participants and exit
220
+ - When using `--embed-media`, media files are encoded as base64 and embedded directly in the HTML, creating a single self-contained file that doesn't require external media files
221
+
222
+
223
+ * When printing an HTML page, most web browsers are set by default to exclude background colors to save ink or toner. If you want to include them, you need to enable background graphics in your browser settings. See the section below for instructions.
224
+ * **In Google Chrome**: Go to `Print` → `More settings` → Check `Background graphics`.
225
+ * **In Mozilla Firefox**: Go to `File` → `Print` → `Page Setup` → Check `Print Background (colors & images)`.
226
+ * **In Microsoft Edge**: Go to `Print` → `More settings` → Check `Background graphics`.
227
+
228
+
229
+ ## Supported languages
230
+ WhatsApp chat exports vary depending on your phone’s system language.
231
+
232
+ chat-export has been tested with many languages and regional settings.
233
+
234
+ If it doesn't work for your language, please let me know.
235
+
236
+ ## Supported Operating Systems
237
+
238
+ * Windows
239
+ * Mac
240
+ * Linux
241
+
242
+ ## Limitations
243
+ There is information in chats, that is not included in the export.zip created by WhatsApp. As a consequence, this information cannot be rendered by chat-export.
244
+
245
+ This includes:
246
+
247
+ * Reactions (thumbs up and other emojis that are not a stand-alone message, but created by long tapping a previous message)
248
+ * Message relations (in WhatsApp, you can right-swipe a previous message and then write a message in relation to this)
249
+ * Chat group metadata (e.g. list of all members, admins, ...)
250
+
251
+ ## Disclaimer
252
+ chat-export comes with no warranty. Use it responsibly and respect the privacy of other chat participants. The tool is not affiliated with WhatsApp or Meta.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chat-export
3
- Version: 0.9.2
3
+ Version: 0.9.4
4
4
  Summary: Convert WhatsApp chat exports to HTML format
5
5
  Author-email: matlon GmbH <software@matlon.ch>
6
6
  License: MIT License
@@ -60,7 +60,7 @@ Dynamic: license-file
60
60
  This tool converts a WhatsApp chat export into two HTML formats: one with inline media (such as images, videos, and audio files) and a compact version with media links.
61
61
 
62
62
 
63
- Video Tutorial (still refers to the previously used tool name instead of `chat-export`):
63
+ Video Tutorial (still refers to the previously used tool name instead of `chat-export`, doesn't cover new features like the non-interactive CLI mode yet):
64
64
  [![YouTube](https://img.youtube.com/vi/s1dMO8pjkC8/0.jpg)](https://www.youtube.com/watch?v=s1dMO8pjkC8)"
65
65
 
66
66
  ## Binary Release Downloads
@@ -101,7 +101,28 @@ You can for example save it on Google Drive or Dropbox or send it to yourself wi
101
101
 
102
102
  3. **Install and Run the Tool:**
103
103
 
104
- **Option 1: Install via pip (Recommended)**
104
+ ### Option 1: Download Binary Executables: Easy for non-technical users
105
+
106
+ If you don't know what Python is and just want to use the tool without having to deal with technical details, you can download the binary executables.
107
+
108
+ - For Windows, download [chat-export.exe](https://github.com/mtln/chat-export/releases/latest/download/chat-export.exe).
109
+
110
+ Because the .exe is unsigned, you will see a warning, but after clicking on "More Information" you should have the option to run it anyway. An EV code signing certificate for Windows costs a lot. If I get enough donations, I will buy one, so I can release signed exe files.
111
+
112
+ - For Mac, download [chat-export-installer.pkg](https://github.com/mtln/chat-export/releases/latest/download/chat-export-installer.pkg)
113
+
114
+ Once installed on Mac:
115
+
116
+ 1. Open the **Terminal** app (e.g. by typing "terminal" in Spotlight Search).
117
+ 2. Type: `chat-export`
118
+
119
+ This will start the chat export tool from anywhere in the terminal.
120
+
121
+ To uninstall `chat-export` from Mac, open the Terminal and run: `sudo rm /usr/local/bin/chat-export`
122
+
123
+ The executables have been built with pyinstaller.
124
+
125
+ ### Option 2: Install via pip
105
126
 
106
127
  If you have Python installed, you can install chat-export as a command-line tool:
107
128
  ```
@@ -122,43 +143,41 @@ You can for example save it on Google Drive or Dropbox or send it to yourself wi
122
143
  pip install chat-export[windows]
123
144
  ```
124
145
 
125
-
126
-
146
+
127
147
  *Note: Without these optional dependencies, the tool will fall back to command-line input for file selection or use tkinter (if available) for file dialogs. If you're planning to use the tool in non-interactive mode, you don't need to install these dependencies.*
128
148
 
129
149
  After installation, you can run it from anywhere in your terminal:
130
-
150
+
131
151
  ```
132
152
  chat-export
153
+ ```
154
+
155
+ or
156
+ ```
133
157
  chat-export --help
134
158
  ```
135
-
159
+
136
160
  To uninstall:
137
161
  ```
138
162
  pip uninstall chat-export
139
163
  ```
140
164
 
141
- **Option 2: Download Binary Executables**
142
-
143
- - For Windows, download [chat-export.exe](https://github.com/mtln/chat-export/releases/latest/download/chat-export.exe).
144
-
145
- Because the .exe is unsigned, you will see a warning, but after clicking on "More Information" you should have the option to run it anyway. An EV code signing certificate for Windows costs a lot. If I get enough donations, I will buy one, so I can release signed exe files.
146
-
147
- - For Mac, download [chat-export-installer.pkg](https://github.com/mtln/chat-export/releases/latest/download/chat-export-installer.pkg)
148
-
149
- Once installed on Mac:
150
- 1. Open the **Terminal** app (e.g. by typing "terminal" in Spotlight Search).
151
- 2. Type: `chat-export`
152
-
153
- This will start the chat export tool from anywhere in the terminal.
165
+ #### Using uv tool instead of pip
166
+ If you have uv installed, you can use `uv tool install` instead of `pip install`. This is perfect for tools like chat-export because it will install the dependencies in a virtual environment and not in the global environment - while still making the tool available globally in your shell / path.
154
167
 
155
- To uninstall `chat-export` from Mac, open the Terminal and run: `sudo rm /usr/local/bin/chat-export`
156
-
157
- The executables have been built with pyinstaller.
168
+
169
+ `uv tool install chat-export[windows]`
170
+ or
171
+ `uv tool install chat-export[macos]`
172
+ or just
173
+ `uv tool install chat-export`
174
+ uninstall with:
175
+ `uv tool uninstall chat-export`
176
+
158
177
 
159
- **Option 3: Run Directly (No Installation)**
178
+ ###Option 3: Run Directly (No Installation)
160
179
 
161
- If Python is installed on your Windows, Mac or Linux computer, run the tool directly (no installation required) with the following command:
180
+ If Python is installed on your Windows, Mac or Linux computer, run the tool directly (no pip installation required, no venv required, no additional dependencies required because it's just vanilla standard lib Python in a single scriptfile, always run the latest up to date version directly from GitHub) with the following command:
162
181
  ```
163
182
  python -c "import urllib.request; exec(urllib.request.urlopen('https://raw.githubusercontent.com/mtln/chat-export/refs/heads/main/main.py').read().decode())"
164
183
  ```
@@ -167,11 +186,14 @@ You can for example save it on Google Drive or Dropbox or send it to yourself wi
167
186
  python3 -c "import urllib.request; exec(urllib.request.urlopen('https://raw.githubusercontent.com/mtln/chat-export/refs/heads/main/main.py').read().decode())"
168
187
  ```
169
188
 
170
- **Usage**
189
+ ## Usage
171
190
 
172
191
  The tool supports both interactive and non-interactive modes:
173
192
 
174
193
  ### Interactive Mode (Default)
194
+
195
+ Just run `chat-export` in your terminal.
196
+
175
197
  **CLI Parameters:**
176
198
 
177
199
  `-o, --output-dir`: Base directory where the chat folder will be created (optional, default: current directory)
@@ -186,6 +208,22 @@ The tool supports both interactive and non-interactive modes:
186
208
 
187
209
  For automation, scripting, or when you know all parameters in advance, you can use the non-interactive CLI mode. This mode processes the chat without any prompts and is perfect for batch processing or integration into other tools.
188
210
 
211
+ #### Media Embedding Options
212
+
213
+ The tool offers two approaches for handling media files:
214
+
215
+ **Default Behavior (External Media Files):**
216
+ - Creates a folder structure with HTML files and a separate `media/` folder
217
+ - Media files are extracted and linked to from the HTML
218
+ - Results in multiple files that need to be kept together
219
+ - Smaller HTML files, but requires managing multiple files
220
+
221
+ **Embedded Media (`--embed-media` option):**
222
+ - Creates a single self-contained HTML file with all media embedded as base64
223
+ - No external media files are created or needed
224
+ - Perfect for sharing, archiving, or when you want everything in one file
225
+ - Larger HTML file size, but completely portable
226
+
189
227
  **Basic Usage:**
190
228
  ```
191
229
  chat-export -n -z "path/to/chat.zip" -p "Your Name"
@@ -199,6 +237,7 @@ chat-export -n -z "path/to/chat.zip" -p "Your Name"
199
237
  - `--from-date`: Optional start date for filtering (formats: DD.MM.YYYY, MM/DD/YYYY, DD.MM.YY, MM/DD/YY)
200
238
  - `--until-date`: Optional end date for filtering
201
239
  - `-o, --output-dir`: Base directory where the chat folder will be created (default: current directory)
240
+ - `--embed-media`: Embed media files as base64 in HTML instead of linking to external files (optional)
202
241
 
203
242
 
204
243
  **Examples:**
@@ -227,9 +266,15 @@ Windows paths (important: no trailing backslash):
227
266
  chat-export -n -z "chat.zip" -p "Your Name" -o "C:\temp"
228
267
  ```
229
268
 
269
+ With embedded media (creates a single self-contained HTML file):
270
+ ```
271
+ chat-export -n -z "chat.zip" -p "Your Name" --embed-media
272
+ ```
273
+
230
274
  **Important Notes:**
231
275
  - The participant name must match exactly as it appears in the chat (case-sensitive)
232
276
  - If the participant name is not found, the tool will display all available participants and exit
277
+ - When using `--embed-media`, media files are encoded as base64 and embedded directly in the HTML, creating a single self-contained file that doesn't require external media files
233
278
 
234
279
 
235
280
  * When printing an HTML page, most web browsers are set by default to exclude background colors to save ink or toner. If you want to include them, you need to enable background graphics in your browser settings. See the section below for instructions.
@@ -1,4 +1,5 @@
1
1
  import argparse
2
+ import base64
2
3
  import difflib
3
4
  import os
4
5
  import sys
@@ -286,7 +287,7 @@ def windows_file_picker():
286
287
 
287
288
 
288
289
 
289
- version = "0.9.2"
290
+ version = "0.9.4"
290
291
 
291
292
  donate_link = "https://donate.stripe.com/3csfZLaIj5JE6dO4gg"
292
293
 
@@ -355,6 +356,10 @@ Examples:
355
356
  type=str,
356
357
  help='Base directory where the ZIP-derived output folder will be created (default: current working directory)')
357
358
 
359
+ parser.add_argument('--embed-media',
360
+ action='store_true',
361
+ help='Embed media files as base64 in HTML instead of linking to external files')
362
+
358
363
  args = parser.parse_args()
359
364
 
360
365
  # Validate non-interactive mode requirements
@@ -455,6 +460,44 @@ class MessageParser:
455
460
  except (ValueError, AttributeError):
456
461
  return None
457
462
 
463
+ @staticmethod
464
+ def trim_zero_widths(text: str) -> str:
465
+ """Trim harmless zero-width characters from the start and end of a string.
466
+
467
+ Safe to strip: ZWSP, ZWNJ, ZWJ, BOM.
468
+ Does NOT strip directional controls like LRM, RLM, FSI, PDI, etc.
469
+ """
470
+
471
+ SAFE_ZERO_WIDTHS = "\u200B\u200C\u200D\uFEFF" # ZWSP, ZWNJ, ZWJ, BOM
472
+ return text.strip(SAFE_ZERO_WIDTHS).strip()
473
+
474
+
475
+ @staticmethod
476
+ def mark_invisible_chars(text: str) -> str:
477
+ """Replace zero-width and directional Unicode control chars with ASCII markers."""
478
+
479
+ replacements = {
480
+ "\u200B": ":ZWSP:", # Zero Width Space
481
+ "\u200C": ":ZWNJ:", # Zero Width Non-Joiner
482
+ "\u200D": ":ZWJ:", # Zero Width Joiner
483
+ "\uFEFF": ":BOM:", # Zero Width No-Break Space / BOM
484
+
485
+ "\u200E": ":LRM:", # Left-to-Right Mark
486
+ "\u200F": ":RLM:", # Right-to-Left Mark
487
+ "\u2066": ":LRI:", # Left-to-Right Isolate
488
+ "\u2067": ":RLI:", # Right-to-Left Isolate
489
+ "\u2068": ":FSI:", # First Strong Isolate
490
+ "\u2069": ":PDI:", # Pop Directional Isolate
491
+
492
+ "\u202A": ":LRE:", # Left-to-Right Embedding
493
+ "\u202B": ":RLE:", # Right-to-Left Embedding
494
+ "\u202C": ":PDF:", # Pop Directional Formatting
495
+ "\u202D": ":LRO:", # Left-to-Right Override
496
+ "\u202E": ":RLO:", # Right-to-Left Override
497
+ }
498
+
499
+ return "".join(replacements.get(ch, ch) for ch in text)
500
+
458
501
  def get_senders(self, chat_content):
459
502
  """Extract all unique senders from chat content."""
460
503
  senders = set()
@@ -463,6 +506,8 @@ class MessageParser:
463
506
  match = pattern.match(line)
464
507
  if match:
465
508
  sender = match.group(2)
509
+ sender = self.trim_zero_widths(sender)
510
+ sender = self.mark_invisible_chars(sender)
466
511
  senders.add(sender)
467
512
  return sorted(list(senders))
468
513
 
@@ -585,6 +630,8 @@ class MessageParser:
585
630
 
586
631
  if match:
587
632
  timestamp, sender, content = match.groups()
633
+ sender = self.trim_zero_widths(sender)
634
+ sender = self.mark_invisible_chars(sender)
588
635
  messages.append(Message.create_with_context(
589
636
  timestamp=timestamp,
590
637
  sender=sender,
@@ -610,16 +657,25 @@ class MessageParser:
610
657
  class HTMLRenderer(Renderer):
611
658
  """Renders messages to HTML format."""
612
659
 
613
- def __init__(self, output_dir, has_media=False):
660
+ def __init__(self, output_dir, has_media=False, embed_media=False, zip_path=None):
614
661
  super().__init__(output_dir)
615
662
  self.has_media = has_media
663
+ self.embed_media = embed_media
664
+ self.zip_path = zip_path
616
665
  self.html_filename = 'chat.html'
617
666
  self.html_filename_media_linked = 'chat_media_linked.html'
667
+ if embed_media:
668
+ # replace .zip with .html
669
+ self.html_filename = Path(self.zip_path).stem + '.html'
670
+ self.html_filename_media_linked = None
618
671
  self.attachments_to_extract = set()
619
672
 
620
673
  def get_generated_files(self) -> list[Path]:
621
674
  """Get the generated files."""
622
- return [Path(self.output_dir, self.html_filename), Path(self.output_dir, self.html_filename_media_linked)]
675
+ result = [Path(self.output_dir, self.html_filename)]
676
+ if self.html_filename_media_linked:
677
+ result.append(Path(self.output_dir, self.html_filename_media_linked))
678
+ return result
623
679
 
624
680
  def get_css_styles(self):
625
681
  """Return the CSS styles for the HTML output."""
@@ -654,6 +710,30 @@ class HTMLRenderer(Renderer):
654
710
  border-radius: 5px;
655
711
  margin: 5px 0;
656
712
  }
713
+ .text-file {
714
+ border: 1px solid #ddd;
715
+ border-radius: 5px;
716
+ padding: 10px;
717
+ background-color: #f9f9f9;
718
+ margin: 5px 0;
719
+ }
720
+ .text-file h4 {
721
+ margin: 0 0 10px 0;
722
+ color: #333;
723
+ font-size: 0.9em;
724
+ }
725
+ .text-content {
726
+ background-color: #fff;
727
+ border: 1px solid #e0e0e0;
728
+ border-radius: 3px;
729
+ padding: 10px;
730
+ font-family: 'Courier New', monospace;
731
+ font-size: 0.8em;
732
+ max-height: 300px;
733
+ overflow-y: auto;
734
+ white-space: pre-wrap;
735
+ word-wrap: break-word;
736
+ }
657
737
  .timestamp {
658
738
  color: #667781;
659
739
  font-size: 0.75em;
@@ -711,6 +791,83 @@ class HTMLRenderer(Renderer):
711
791
  </body>
712
792
  </html>"""
713
793
 
794
+ def get_mime_type(self, filename):
795
+ """Get MIME type based on file extension."""
796
+ ext = filename.lower()
797
+ if ext.endswith(('.jpg', '.jpeg')):
798
+ return 'image/jpeg'
799
+ elif ext.endswith('.png'):
800
+ return 'image/png'
801
+ elif ext.endswith('.gif'):
802
+ return 'image/gif'
803
+ elif ext.endswith('.webp'):
804
+ return 'image/webp'
805
+ elif ext.endswith('.mp4'):
806
+ return 'video/mp4'
807
+ elif ext.endswith('.opus'):
808
+ return 'audio/ogg'
809
+ elif ext.endswith('.mp3'):
810
+ return 'audio/mpeg'
811
+ elif ext.endswith('.wav'):
812
+ return 'audio/wav'
813
+ elif ext.endswith('.m4a'):
814
+ return 'audio/mp4'
815
+ elif ext.endswith('.pdf'):
816
+ return 'application/pdf'
817
+ elif ext.endswith(('.doc', '.docx')):
818
+ return 'application/msword'
819
+ elif ext.endswith(('.xls', '.xlsx')):
820
+ return 'application/vnd.ms-excel'
821
+ elif ext.endswith(('.ppt', '.pptx')):
822
+ return 'application/vnd.ms-powerpoint'
823
+ elif ext.endswith('.txt'):
824
+ return 'text/plain'
825
+ elif ext.endswith('.rtf'):
826
+ return 'application/rtf'
827
+ elif ext.endswith('.zip'):
828
+ return 'application/zip'
829
+ elif ext.endswith('.rar'):
830
+ return 'application/x-rar-compressed'
831
+ elif ext.endswith('.7z'):
832
+ return 'application/x-7z-compressed'
833
+ elif ext.endswith('.tar'):
834
+ return 'application/x-tar'
835
+ elif ext.endswith('.gz'):
836
+ return 'application/gzip'
837
+ elif ext.endswith('.csv'):
838
+ return 'text/csv'
839
+ elif ext.endswith('.json'):
840
+ return 'application/json'
841
+ elif ext.endswith('.xml'):
842
+ return 'application/xml'
843
+ elif ext.endswith('.html'):
844
+ return 'text/html'
845
+ elif ext.endswith('.css'):
846
+ return 'text/css'
847
+ elif ext.endswith('.js'):
848
+ return 'application/javascript'
849
+ elif ext.endswith('.py'):
850
+ return 'text/x-python'
851
+ elif ext.endswith('.java'):
852
+ return 'text/x-java-source'
853
+ elif ext.endswith(('.cpp', '.c', '.h')):
854
+ return 'text/x-c'
855
+ else:
856
+ return 'application/octet-stream'
857
+
858
+ def encode_media_to_base64(self, attachment_name):
859
+ """Read media file from zip and encode to base64."""
860
+ try:
861
+ with zipfile.ZipFile(self.zip_path, 'r') as zip_ref:
862
+ with zip_ref.open(attachment_name) as media_file:
863
+ media_data = media_file.read()
864
+ base64_data = base64.b64encode(media_data).decode('utf-8')
865
+ mime_type = self.get_mime_type(attachment_name)
866
+ return f"data:{mime_type};base64,{base64_data}"
867
+ except Exception as e:
868
+ print(f"Warning: Could not encode {attachment_name} to base64: {e}")
869
+ return None
870
+
714
871
  def render_media_element(self, attachment_name, is_media_linked=False):
715
872
  """Render a media element based on its file extension."""
716
873
  media_path = f"./media/{attachment_name}"
@@ -721,6 +878,32 @@ class HTMLRenderer(Renderer):
721
878
 
722
879
  # Render media inline in main version
723
880
  ext = attachment_name.lower()
881
+
882
+ # If embed_media is enabled, try to encode as base64
883
+ if self.embed_media and self.zip_path:
884
+ base64_data = self.encode_media_to_base64(attachment_name)
885
+ if base64_data:
886
+ # Images
887
+ if ext.endswith(('.jpg', '.jpeg', '.png', '.webp', '.gif')):
888
+ return f'<img class="media" src="{base64_data}"><br>'
889
+ # Videos
890
+ elif ext.endswith('.mp4'):
891
+ return f'<video class="media" controls><source src="{base64_data}" type="video/mp4"></video><br>'
892
+ # Audio files
893
+ elif ext.endswith('.opus'):
894
+ return f'<audio class="media" controls><source src="{base64_data}" type="audio/ogg"></audio><br>'
895
+ elif ext.endswith('.wav'):
896
+ return f'<audio class="media" controls><source src="{base64_data}" type="audio/wav"></audio><br>'
897
+ elif ext.endswith('.mp3'):
898
+ return f'<audio class="media" controls><source src="{base64_data}" type="audio/mpeg"></audio><br>'
899
+ elif ext.endswith('.m4a'):
900
+ return f'<audio class="media" controls><source src="{base64_data}" type="audio/mp4"></audio><br>'
901
+
902
+ # Other documents - provide download link with base64 data
903
+ else:
904
+ return f'<a href="{base64_data}" download="{attachment_name}">📎 {attachment_name}</a><br>'
905
+
906
+ # Fallback to file references
724
907
  if ext.endswith(('.jpg', '.jpeg', '.png', '.webp', '.gif')):
725
908
  return f'<img class="media" src="{media_path}"><br>'
726
909
  elif ext.endswith('.mp4'):
@@ -796,7 +979,11 @@ class HTMLRenderer(Renderer):
796
979
  self.chat = chat
797
980
  # Prepare file paths
798
981
  main_html_path = os.path.join(self.output_dir, self.html_filename)
799
- media_linked_html_path = os.path.join(self.output_dir, self.html_filename_media_linked)
982
+ if self.html_filename_media_linked:
983
+ media_linked_html_path = os.path.join(self.output_dir, self.html_filename_media_linked)
984
+ else:
985
+ # temp file
986
+ media_linked_html_path = "_temp.tmp"
800
987
 
801
988
  # Open both files for writing
802
989
  with open(main_html_path, 'w', encoding='utf-8') as main_f, \
@@ -833,11 +1020,14 @@ class HTMLRenderer(Renderer):
833
1020
  main_f.write(footer)
834
1021
  media_f.write(footer)
835
1022
 
1023
+ if self.embed_media:
1024
+ os.remove(media_linked_html_path)
1025
+
836
1026
  return self.attachments_to_extract
837
1027
 
838
1028
 
839
1029
  class ChatExport:
840
- def __init__(self, zip_path, from_date=None, until_date=None, participant_name=None, base_output_dir=None):
1030
+ def __init__(self, zip_path, from_date=None, until_date=None, participant_name=None, base_output_dir=None, embed_media=False):
841
1031
  # Validate zip file existence
842
1032
  if not os.path.exists(zip_path):
843
1033
  raise FileNotFoundError(f"Could not find the file: {zip_path}\nPlease check if the file path is correct.")
@@ -851,15 +1041,22 @@ class ChatExport:
851
1041
  self.from_date = from_date
852
1042
  self.until_date = until_date
853
1043
  self.participant_name = participant_name
1044
+ self.embed_media = embed_media
854
1045
 
855
1046
  # Set up output directory: base_output_dir/zip_filename or just zip_filename
856
1047
  zip_stem = Path(zip_path).stem
857
- if base_output_dir:
858
- # Normalize the base output directory path (handle Windows paths, quotes, etc.)
859
- normalized_base_dir = parse_path(base_output_dir)
860
- self.output_dir = os.path.join(normalized_base_dir, zip_stem)
1048
+ if self.embed_media:
1049
+ if base_output_dir:
1050
+ self.output_dir = parse_path(base_output_dir)
1051
+ else:
1052
+ self.output_dir = ""
861
1053
  else:
862
- self.output_dir = zip_stem
1054
+ if base_output_dir:
1055
+ # Normalize the base output directory path (handle Windows paths, quotes, etc.)
1056
+ normalized_base_dir = parse_path(base_output_dir)
1057
+ self.output_dir = os.path.join(normalized_base_dir, zip_stem)
1058
+ else:
1059
+ self.output_dir = zip_stem
863
1060
  self.media_dir = os.path.join(self.output_dir, "media")
864
1061
 
865
1062
  self.own_name = participant_name
@@ -930,7 +1127,9 @@ class ChatExport:
930
1127
  # Setup renderer
931
1128
  self.renderer = HTMLRenderer(
932
1129
  output_dir=self.output_dir,
933
- has_media=self.has_media
1130
+ has_media=self.has_media,
1131
+ embed_media=self.embed_media,
1132
+ zip_path=self.zip_path
934
1133
  )
935
1134
 
936
1135
  @staticmethod
@@ -969,13 +1168,18 @@ class ChatExport:
969
1168
  # Get the base name of the zip file without extension
970
1169
  zip_base_name = Path(self.zip_path).stem
971
1170
 
972
- if os.path.exists(self.output_dir):
973
- print(f"Cleaning existing directory: {self.output_dir}")
974
- shutil.rmtree(self.output_dir)
1171
+ # safety: clean only if it's a subfolder with zip_base_name
1172
+ if os.path.exists(self.output_dir) and str(self.output_dir).endswith(zip_base_name):
1173
+ print(f"Cleaning existing directory: {self.output_dir}")
1174
+ shutil.rmtree(self.output_dir)
1175
+
1176
+ os.makedirs(self.output_dir, exist_ok=True)
975
1177
 
1178
+ # if not embed_media, create media directory
1179
+ if not self.embed_media:
976
1180
  # Create fresh output directories
977
- os.makedirs(self.output_dir)
978
- os.makedirs(self.media_dir)
1181
+ os.makedirs(self.media_dir)
1182
+
979
1183
 
980
1184
 
981
1185
 
@@ -1004,7 +1208,9 @@ class ChatExport:
1004
1208
  print(f"ZIP file is an {'iOS' if self.is_ios else 'Android'} export with media/attachments, '{chat_file}' is the chat text file.")
1005
1209
  else:
1006
1210
  print(f"ZIP file is an {'iOS' if self.is_ios else 'Android'} export without media/attachments, '{chat_file}' is the chat text file.")
1007
- shutil.rmtree(self.media_dir)
1211
+ # delete if exists
1212
+ if os.path.exists(self.media_dir):
1213
+ shutil.rmtree(self.media_dir)
1008
1214
  # Setup modular components now that we know the platform and media status
1009
1215
  self.setup_modular_components()
1010
1216
 
@@ -1047,7 +1253,7 @@ class ChatExport:
1047
1253
  # Render messages using the new HTMLRenderer
1048
1254
  attachments_to_extract = self.renderer.render(chat)
1049
1255
 
1050
- if self.has_media:
1256
+ if self.has_media and not self.embed_media:
1051
1257
  print("Extracting attachments/media...")
1052
1258
  # extract attachments of rendered messages
1053
1259
  with zipfile.ZipFile(self.zip_path, 'r') as zip_ref:
@@ -1055,6 +1261,8 @@ class ChatExport:
1055
1261
  for file in zip_ref.namelist():
1056
1262
  if file in attachments_to_extract:
1057
1263
  zip_ref.extract(file, self.media_dir)
1264
+ elif self.has_media and self.embed_media:
1265
+ print("Media will be embedded as base64 in HTML (no file extraction needed)")
1058
1266
  processing_end_time = time.time()
1059
1267
  print(f"Processing took {processing_end_time - processing_start_time:.3f} seconds")
1060
1268
 
@@ -1081,13 +1289,16 @@ class ChatExport:
1081
1289
  # Get the base name of the zip file without extension
1082
1290
  zip_base_name = Path(self.zip_path).stem
1083
1291
 
1084
- if os.path.exists(self.output_dir):
1292
+ # safety: clean only if it's a subfolder with zip_base_name
1293
+ if os.path.exists(self.output_dir) and str(self.output_dir).endswith(zip_base_name):
1085
1294
  print(f"Cleaning existing directory: {self.output_dir}")
1086
1295
  shutil.rmtree(self.output_dir)
1296
+
1297
+ os.makedirs(self.output_dir, exist_ok=True)
1087
1298
 
1088
- # Create fresh output directories
1089
- os.makedirs(self.output_dir)
1090
- os.makedirs(self.media_dir)
1299
+ if not self.embed_media:
1300
+ # Create fresh output directories
1301
+ os.makedirs(self.media_dir)
1091
1302
 
1092
1303
  # Validate that it's a proper ZIP file
1093
1304
  try:
@@ -1120,7 +1331,10 @@ class ChatExport:
1120
1331
  print(f"ZIP file is an {'iOS' if self.is_ios else 'Android'} export with media/attachments, '{chat_file}' is the chat text file.")
1121
1332
  else:
1122
1333
  print(f"ZIP file is an {'iOS' if self.is_ios else 'Android'} export without media/attachments, '{chat_file}' is the chat text file.")
1123
- shutil.rmtree(self.media_dir)
1334
+ # delete if exists
1335
+ if os.path.exists(self.media_dir):
1336
+ shutil.rmtree(self.media_dir)
1337
+
1124
1338
 
1125
1339
  # Setup modular components now that we know the platform and media status
1126
1340
  self.setup_modular_components()
@@ -1150,7 +1364,7 @@ class ChatExport:
1150
1364
  # Render messages using the new HTMLRenderer
1151
1365
  attachments_to_extract = self.renderer.render(chat)
1152
1366
 
1153
- if self.has_media:
1367
+ if self.has_media and not self.embed_media:
1154
1368
  print("Extracting attachments/media...")
1155
1369
  # extract attachments of rendered messages
1156
1370
  with zipfile.ZipFile(self.zip_path, 'r') as zip_ref:
@@ -1158,6 +1372,8 @@ class ChatExport:
1158
1372
  for file in zip_ref.namelist():
1159
1373
  if file in attachments_to_extract:
1160
1374
  zip_ref.extract(file, self.media_dir)
1375
+ elif self.has_media and self.embed_media:
1376
+ print("Media will be embedded as base64 in HTML (no file extraction needed)")
1161
1377
  processing_end_time = time.time()
1162
1378
  print(f"Processing took {processing_end_time - processing_start_time:.3f} seconds")
1163
1379
 
@@ -1170,7 +1386,7 @@ def check_tkinter_availability():
1170
1386
  root = tk.Tk()
1171
1387
  root.destroy()
1172
1388
  return True
1173
- except Exception as e:
1389
+ except Exception:
1174
1390
  print("Tkinter is not available on your system. Using prompt input instead of file picker dialog.")
1175
1391
  return False
1176
1392
 
@@ -1225,7 +1441,7 @@ def main():
1225
1441
  from_date = args.from_date if args.from_date else None
1226
1442
  until_date = args.until_date if args.until_date else None
1227
1443
 
1228
- chat_export = ChatExport(args.zip_file, from_date, until_date, args.participant, args.output_dir)
1444
+ chat_export = ChatExport(args.zip_file, from_date, until_date, args.participant, args.output_dir, args.embed_media)
1229
1445
  chat_export.process_chat_non_interactive()
1230
1446
  print(f'Written: {", ".join([str(p.absolute()) for p in chat_export.renderer.get_generated_files()])}')
1231
1447
  print("Done.")
@@ -1253,7 +1469,7 @@ def main():
1253
1469
  if not selected_zip_file:
1254
1470
  raise FileNotFoundError("No file selected.")
1255
1471
  print(f"Processing selected file: {selected_zip_file}...")
1256
- chat_export = ChatExport(selected_zip_file, base_output_dir=args.output_dir)
1472
+ chat_export = ChatExport(selected_zip_file, base_output_dir=args.output_dir, embed_media=args.embed_media)
1257
1473
  chat_export.process_chat()
1258
1474
  print(f'Written: {", ".join([str(p.absolute()) for p in chat_export.renderer.get_generated_files()])}')
1259
1475
  print("Done.")
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "chat-export"
7
- version = "0.9.2"
7
+ version = "0.9.4"
8
8
  description = "Convert WhatsApp chat exports to HTML format"
9
9
  readme = "README.md"
10
10
  license = {file = "LICENSE"}
File without changes
File without changes
File without changes