universal-mcp-applications 0.1.21__py3-none-any.whl → 0.1.22__py3-none-any.whl
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.
Potentially problematic release.
This version of universal-mcp-applications might be problematic. Click here for more details.
- universal_mcp/applications/BEST_PRACTICES.md +166 -0
- universal_mcp/applications/airtable/app.py +0 -1
- universal_mcp/applications/apollo/app.py +0 -1
- universal_mcp/applications/aws_s3/app.py +40 -39
- universal_mcp/applications/browser_use/README.md +1 -0
- universal_mcp/applications/browser_use/__init__.py +0 -0
- universal_mcp/applications/browser_use/app.py +76 -0
- universal_mcp/applications/calendly/app.py +125 -125
- universal_mcp/applications/canva/app.py +95 -99
- universal_mcp/applications/confluence/app.py +0 -1
- universal_mcp/applications/contentful/app.py +4 -5
- universal_mcp/applications/domain_checker/app.py +11 -15
- universal_mcp/applications/e2b/app.py +4 -4
- universal_mcp/applications/elevenlabs/app.py +18 -15
- universal_mcp/applications/exa/app.py +17 -17
- universal_mcp/applications/falai/app.py +28 -29
- universal_mcp/applications/file_system/app.py +9 -9
- universal_mcp/applications/firecrawl/app.py +36 -36
- universal_mcp/applications/fireflies/app.py +55 -56
- universal_mcp/applications/fpl/app.py +49 -50
- universal_mcp/applications/ghost_content/app.py +0 -1
- universal_mcp/applications/github/app.py +41 -43
- universal_mcp/applications/google_calendar/app.py +40 -39
- universal_mcp/applications/google_docs/app.py +56 -56
- universal_mcp/applications/google_drive/app.py +212 -215
- universal_mcp/applications/google_gemini/app.py +1 -5
- universal_mcp/applications/google_mail/app.py +91 -90
- universal_mcp/applications/google_searchconsole/app.py +29 -29
- universal_mcp/applications/google_sheet/app.py +115 -115
- universal_mcp/applications/hashnode/README.md +6 -3
- universal_mcp/applications/hashnode/app.py +174 -25
- universal_mcp/applications/http_tools/app.py +10 -11
- universal_mcp/applications/hubspot/__init__.py +1 -1
- universal_mcp/applications/hubspot/api_segments/api_segment_base.py +36 -7
- universal_mcp/applications/hubspot/api_segments/crm_api.py +368 -368
- universal_mcp/applications/hubspot/api_segments/marketing_api.py +115 -115
- universal_mcp/applications/hubspot/app.py +131 -72
- universal_mcp/applications/jira/app.py +0 -1
- universal_mcp/applications/linkedin/app.py +20 -20
- universal_mcp/applications/markitdown/app.py +10 -5
- universal_mcp/applications/ms_teams/app.py +123 -123
- universal_mcp/applications/openai/app.py +40 -39
- universal_mcp/applications/outlook/app.py +32 -32
- universal_mcp/applications/perplexity/app.py +4 -4
- universal_mcp/applications/reddit/app.py +69 -70
- universal_mcp/applications/resend/app.py +116 -117
- universal_mcp/applications/rocketlane/app.py +0 -1
- universal_mcp/applications/scraper/__init__.py +1 -1
- universal_mcp/applications/scraper/app.py +80 -81
- universal_mcp/applications/serpapi/app.py +14 -14
- universal_mcp/applications/sharepoint/app.py +19 -20
- universal_mcp/applications/shopify/app.py +0 -1
- universal_mcp/applications/slack/app.py +48 -48
- universal_mcp/applications/tavily/app.py +4 -4
- universal_mcp/applications/twitter/api_segments/compliance_api.py +13 -15
- universal_mcp/applications/twitter/api_segments/dm_conversations_api.py +20 -20
- universal_mcp/applications/twitter/api_segments/dm_events_api.py +12 -12
- universal_mcp/applications/twitter/api_segments/likes_api.py +12 -12
- universal_mcp/applications/twitter/api_segments/lists_api.py +37 -39
- universal_mcp/applications/twitter/api_segments/spaces_api.py +24 -24
- universal_mcp/applications/twitter/api_segments/trends_api.py +4 -4
- universal_mcp/applications/twitter/api_segments/tweets_api.py +105 -105
- universal_mcp/applications/twitter/api_segments/usage_api.py +4 -4
- universal_mcp/applications/twitter/api_segments/users_api.py +136 -136
- universal_mcp/applications/twitter/app.py +6 -2
- universal_mcp/applications/unipile/app.py +90 -97
- universal_mcp/applications/whatsapp/app.py +53 -54
- universal_mcp/applications/whatsapp/audio.py +39 -35
- universal_mcp/applications/whatsapp/whatsapp.py +176 -154
- universal_mcp/applications/whatsapp_business/app.py +92 -92
- universal_mcp/applications/yahoo_finance/app.py +105 -63
- universal_mcp/applications/youtube/app.py +193 -196
- universal_mcp/applications/zenquotes/__init__.py +2 -0
- universal_mcp/applications/zenquotes/app.py +3 -3
- {universal_mcp_applications-0.1.21.dist-info → universal_mcp_applications-0.1.22.dist-info}/METADATA +2 -1
- {universal_mcp_applications-0.1.21.dist-info → universal_mcp_applications-0.1.22.dist-info}/RECORD +78 -74
- {universal_mcp_applications-0.1.21.dist-info → universal_mcp_applications-0.1.22.dist-info}/WHEEL +0 -0
- {universal_mcp_applications-0.1.21.dist-info → universal_mcp_applications-0.1.22.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
from typing import Any
|
|
2
2
|
|
|
3
3
|
import requests
|
|
4
|
+
from universal_mcp.agentr.integration import AgentrIntegration
|
|
5
|
+
from universal_mcp.applications.application import BaseApplication
|
|
6
|
+
from universal_mcp.exceptions import NotAuthorizedError
|
|
7
|
+
|
|
4
8
|
from universal_mcp.applications.whatsapp.whatsapp import (
|
|
5
9
|
WHATSAPP_API_BASE_URL,
|
|
6
10
|
)
|
|
@@ -41,10 +45,6 @@ from universal_mcp.applications.whatsapp.whatsapp import (
|
|
|
41
45
|
send_message as whatsapp_send_message,
|
|
42
46
|
)
|
|
43
47
|
|
|
44
|
-
from universal_mcp.applications.application import BaseApplication
|
|
45
|
-
from universal_mcp.exceptions import NotAuthorizedError
|
|
46
|
-
from universal_mcp.agentr.integration import AgentrIntegration
|
|
47
|
-
|
|
48
48
|
|
|
49
49
|
class WhatsappApp(BaseApplication):
|
|
50
50
|
"""
|
|
@@ -63,11 +63,10 @@ class WhatsappApp(BaseApplication):
|
|
|
63
63
|
"""
|
|
64
64
|
if not self.integration:
|
|
65
65
|
raise ValueError("No integration available to get API key from")
|
|
66
|
-
|
|
67
66
|
|
|
68
67
|
try:
|
|
69
68
|
headers = self.integration.client.client.headers
|
|
70
|
-
api_key = headers.get(
|
|
69
|
+
api_key = headers.get("X-API-KEY")
|
|
71
70
|
if api_key:
|
|
72
71
|
return api_key
|
|
73
72
|
else:
|
|
@@ -160,16 +159,16 @@ class WhatsappApp(BaseApplication):
|
|
|
160
159
|
) -> list[dict[str, Any]]:
|
|
161
160
|
"""
|
|
162
161
|
Searches for WhatsApp contacts by name or phone number. This function takes a query string, handles user authentication, and calls the underlying API to find and return a list of matching contacts. It serves as the primary method to look up contact information within the application.
|
|
163
|
-
|
|
162
|
+
|
|
164
163
|
Args:
|
|
165
164
|
query (string): Search term to match against contact names or phone numbers
|
|
166
|
-
|
|
165
|
+
|
|
167
166
|
Returns:
|
|
168
167
|
List[Dict[str, Any]]: Retrieved collection
|
|
169
|
-
|
|
168
|
+
|
|
170
169
|
Raises:
|
|
171
170
|
ValueError: Raised when required parameters are missing.
|
|
172
|
-
|
|
171
|
+
|
|
173
172
|
Tags:
|
|
174
173
|
whatsapp.contacts, important
|
|
175
174
|
"""
|
|
@@ -198,7 +197,7 @@ class WhatsappApp(BaseApplication):
|
|
|
198
197
|
) -> list[dict[str, Any]]:
|
|
199
198
|
"""
|
|
200
199
|
Searches for and retrieves a paginated list of WhatsApp messages using various filters like date, sender, chat, or content query. It can optionally include surrounding contextual messages for each result, unlike `get_message_context` which targets a single message ID.
|
|
201
|
-
|
|
200
|
+
|
|
202
201
|
Args:
|
|
203
202
|
after (string): Optional ISO-8601 formatted string to only return messages after this date
|
|
204
203
|
before (string): Optional ISO-8601 formatted string to only return messages before this date
|
|
@@ -210,13 +209,13 @@ class WhatsappApp(BaseApplication):
|
|
|
210
209
|
include_context (boolean): Whether to include messages before and after matches (default True)
|
|
211
210
|
context_before (integer): Number of messages to include before each match (default 1)
|
|
212
211
|
context_after (integer): Number of messages to include after each match (default 1)
|
|
213
|
-
|
|
212
|
+
|
|
214
213
|
Returns:
|
|
215
214
|
List[Dict[str, Any]]: Retrieved collection
|
|
216
|
-
|
|
215
|
+
|
|
217
216
|
Raises:
|
|
218
217
|
ValueError: Raised when required parameters are missing.
|
|
219
|
-
|
|
218
|
+
|
|
220
219
|
Tags:
|
|
221
220
|
whatsapp.messages, important
|
|
222
221
|
"""
|
|
@@ -249,20 +248,20 @@ class WhatsappApp(BaseApplication):
|
|
|
249
248
|
) -> list[dict[str, Any]]:
|
|
250
249
|
"""
|
|
251
250
|
Retrieves a paginated list of WhatsApp chats, allowing filtering by a search query and sorting by activity or name. Unlike `get_chat`, which fetches a single known chat, this function provides broad search and discovery capabilities across multiple user conversations.
|
|
252
|
-
|
|
251
|
+
|
|
253
252
|
Args:
|
|
254
253
|
query (string): Optional search term to filter chats by name or JID
|
|
255
254
|
limit (integer): Maximum number of chats to return (default 20)
|
|
256
255
|
page (integer): Page number for pagination (default 0)
|
|
257
256
|
include_last_message (boolean): Whether to include the last message in each chat (default True)
|
|
258
257
|
sort_by (string): Field to sort results by, either "last_active" or "name" (default "last_active")
|
|
259
|
-
|
|
258
|
+
|
|
260
259
|
Returns:
|
|
261
260
|
List[Dict[str, Any]]: Retrieved collection
|
|
262
|
-
|
|
261
|
+
|
|
263
262
|
Raises:
|
|
264
263
|
ValueError: Raised when required parameters are missing.
|
|
265
|
-
|
|
264
|
+
|
|
266
265
|
Tags:
|
|
267
266
|
whatsapp.chats, important
|
|
268
267
|
"""
|
|
@@ -287,17 +286,17 @@ class WhatsappApp(BaseApplication):
|
|
|
287
286
|
) -> dict[str, Any]:
|
|
288
287
|
"""
|
|
289
288
|
Retrieves metadata for a specific WhatsApp chat (direct or group) using its unique JID. It can optionally include the most recent message. This precise JID-based lookup distinguishes it from `get_direct_chat_by_contact`, which uses a phone number, and `list_chats`, which performs a broader search.
|
|
290
|
-
|
|
289
|
+
|
|
291
290
|
Args:
|
|
292
291
|
chat_jid (string): The JID of the chat to retrieve
|
|
293
292
|
include_last_message (boolean): Whether to include the last message (default True)
|
|
294
|
-
|
|
293
|
+
|
|
295
294
|
Returns:
|
|
296
295
|
Dict[str, Any]: Retrieved chat metadata
|
|
297
|
-
|
|
296
|
+
|
|
298
297
|
Raises:
|
|
299
298
|
ValueError: Raised when required parameters are missing.
|
|
300
|
-
|
|
299
|
+
|
|
301
300
|
Tags:
|
|
302
301
|
whatsapp.chat, important
|
|
303
302
|
"""
|
|
@@ -317,16 +316,16 @@ class WhatsappApp(BaseApplication):
|
|
|
317
316
|
) -> dict[str, Any]:
|
|
318
317
|
"""
|
|
319
318
|
Retrieves metadata for a direct (one-on-one) WhatsApp chat using a contact's phone number. Unlike `get_chat` which requires a JID, this provides a simpler way to find direct conversations. Returns a dictionary containing the chat's details, such as its JID and name.
|
|
320
|
-
|
|
319
|
+
|
|
321
320
|
Args:
|
|
322
321
|
sender_phone_number (string): The phone number to search for
|
|
323
|
-
|
|
322
|
+
|
|
324
323
|
Returns:
|
|
325
324
|
Dict[str, Any]: Retrieved chat metadata
|
|
326
|
-
|
|
325
|
+
|
|
327
326
|
Raises:
|
|
328
327
|
ValueError: Raised when required parameters are missing.
|
|
329
|
-
|
|
328
|
+
|
|
330
329
|
Tags:
|
|
331
330
|
whatsapp.chat, important
|
|
332
331
|
"""
|
|
@@ -348,18 +347,18 @@ class WhatsappApp(BaseApplication):
|
|
|
348
347
|
) -> list[dict[str, Any]]:
|
|
349
348
|
"""
|
|
350
349
|
Retrieves a paginated list of all WhatsApp chats, including direct messages and groups, that a specific contact participates in. The contact is identified by their unique JID. This differs from `get_direct_chat_by_contact` which only finds one-on-one chats.
|
|
351
|
-
|
|
350
|
+
|
|
352
351
|
Args:
|
|
353
352
|
jid (string): The contact's JID to search for
|
|
354
353
|
limit (integer): Maximum number of chats to return (default 20)
|
|
355
354
|
page (integer): Page number for pagination (default 0)
|
|
356
|
-
|
|
355
|
+
|
|
357
356
|
Returns:
|
|
358
357
|
List[Dict[str, Any]]: Retrieved collection
|
|
359
|
-
|
|
358
|
+
|
|
360
359
|
Raises:
|
|
361
360
|
ValueError: Raised when required parameters are missing.
|
|
362
|
-
|
|
361
|
+
|
|
363
362
|
Tags:
|
|
364
363
|
whatsapp.contact_chats, important
|
|
365
364
|
"""
|
|
@@ -379,16 +378,16 @@ class WhatsappApp(BaseApplication):
|
|
|
379
378
|
) -> str:
|
|
380
379
|
"""
|
|
381
380
|
Retrieves the content of the most recent message involving a specific contact, identified by their JID. It authenticates the user and returns the message directly as a string, offering a quick way to view the last communication without fetching full message objects or chat histories.
|
|
382
|
-
|
|
381
|
+
|
|
383
382
|
Args:
|
|
384
383
|
jid (string): The JID of the contact to search for
|
|
385
|
-
|
|
384
|
+
|
|
386
385
|
Returns:
|
|
387
386
|
string: Retrieved message
|
|
388
|
-
|
|
387
|
+
|
|
389
388
|
Raises:
|
|
390
389
|
ValueError: Raised when required parameters are missing.
|
|
391
|
-
|
|
390
|
+
|
|
392
391
|
Tags:
|
|
393
392
|
whatsapp.interaction, important
|
|
394
393
|
"""
|
|
@@ -410,18 +409,18 @@ class WhatsappApp(BaseApplication):
|
|
|
410
409
|
) -> dict[str, Any]:
|
|
411
410
|
"""
|
|
412
411
|
Fetches the conversational context surrounding a specific WhatsApp message ID. It retrieves a configurable number of messages immediately preceding and following the target message. This provides a focused view of a dialogue, unlike `list_messages` which performs broader, filter-based searches.
|
|
413
|
-
|
|
412
|
+
|
|
414
413
|
Args:
|
|
415
414
|
message_id (string): The ID of the message to get context for
|
|
416
415
|
before (integer): Number of messages to include before the target message (default 5)
|
|
417
416
|
after (integer): Number of messages to include after the target message (default 5)
|
|
418
|
-
|
|
417
|
+
|
|
419
418
|
Returns:
|
|
420
419
|
Dict[str, Any]: Retrieved message context
|
|
421
|
-
|
|
420
|
+
|
|
422
421
|
Raises:
|
|
423
422
|
ValueError: Raised when required parameters are missing.
|
|
424
|
-
|
|
423
|
+
|
|
425
424
|
Tags:
|
|
426
425
|
whatsapp.message_context, important
|
|
427
426
|
"""
|
|
@@ -442,18 +441,18 @@ class WhatsappApp(BaseApplication):
|
|
|
442
441
|
) -> dict[str, Any]:
|
|
443
442
|
"""
|
|
444
443
|
Authenticates the user and sends a text message to a specified WhatsApp recipient. The recipient can be an individual (via phone number) or a group (via JID). It returns a dictionary indicating the operation's success status and a corresponding message.
|
|
445
|
-
|
|
444
|
+
|
|
446
445
|
Args:
|
|
447
446
|
recipient (string): The recipient - either a phone number with country code but no + or other symbols,
|
|
448
447
|
or a JID (e.g., "123456789@s.whatsapp.net" or a group JID like "123456789@g.us")
|
|
449
448
|
message (string): The message text to send
|
|
450
|
-
|
|
449
|
+
|
|
451
450
|
Returns:
|
|
452
451
|
Dict[str, Any]: A dictionary containing success status and a status message
|
|
453
|
-
|
|
452
|
+
|
|
454
453
|
Raises:
|
|
455
454
|
ValueError: Raised when required parameters are missing.
|
|
456
|
-
|
|
455
|
+
|
|
457
456
|
Tags:
|
|
458
457
|
whatsapp.send_message, important
|
|
459
458
|
"""
|
|
@@ -480,18 +479,18 @@ class WhatsappApp(BaseApplication):
|
|
|
480
479
|
) -> dict[str, Any]:
|
|
481
480
|
"""
|
|
482
481
|
Sends a media file (image, video, document, raw audio) as a standard attachment to a WhatsApp contact or group using their phone number or JID. Unlike `send_audio_message`, which creates a playable voice note, this function handles general file transfers. Returns a success status dictionary.
|
|
483
|
-
|
|
482
|
+
|
|
484
483
|
Args:
|
|
485
484
|
recipient (string): The recipient - either a phone number with country code but no + or other symbols,
|
|
486
485
|
or a JID (e.g., "123456789@s.whatsapp.net" or a group JID like "123456789@g.us")
|
|
487
486
|
media_path (string): The absolute path to the media file to send (image, video, document)
|
|
488
|
-
|
|
487
|
+
|
|
489
488
|
Returns:
|
|
490
489
|
Dict[str, Any]: A dictionary containing success status and a status message
|
|
491
|
-
|
|
490
|
+
|
|
492
491
|
Raises:
|
|
493
492
|
ValueError: Raised when required parameters are missing.
|
|
494
|
-
|
|
493
|
+
|
|
495
494
|
Tags:
|
|
496
495
|
whatsapp.send_file, important
|
|
497
496
|
"""
|
|
@@ -518,18 +517,18 @@ class WhatsappApp(BaseApplication):
|
|
|
518
517
|
) -> dict[str, Any]:
|
|
519
518
|
"""
|
|
520
519
|
Sends a local audio file as a playable WhatsApp voice message, converting it to the required format. Unlike `send_file` which sends audio as a document attachment, this function formats the audio as a voice note. It can be sent to an individual contact or a group chat.
|
|
521
|
-
|
|
520
|
+
|
|
522
521
|
Args:
|
|
523
522
|
recipient (string): The recipient - either a phone number with country code but no + or other symbols,
|
|
524
523
|
or a JID (e.g., "123456789@s.whatsapp.net" or a group JID like "123456789@g.us")
|
|
525
524
|
media_path (string): The absolute path to the audio file to send (will be converted to Opus .ogg if it's not a .ogg file)
|
|
526
|
-
|
|
525
|
+
|
|
527
526
|
Returns:
|
|
528
527
|
Dict[str, Any]: A dictionary containing success status and a status message
|
|
529
|
-
|
|
528
|
+
|
|
530
529
|
Raises:
|
|
531
530
|
ValueError: Raised when required parameters are missing.
|
|
532
|
-
|
|
531
|
+
|
|
533
532
|
Tags:
|
|
534
533
|
whatsapp.send_audio_message, important
|
|
535
534
|
"""
|
|
@@ -557,17 +556,17 @@ class WhatsappApp(BaseApplication):
|
|
|
557
556
|
) -> dict[str, Any]:
|
|
558
557
|
"""
|
|
559
558
|
Downloads media from a specific WhatsApp message, identified by its ID and chat JID. It saves the content to a local file and returns the file's path upon success. The function automatically handles user authentication before initiating the download.
|
|
560
|
-
|
|
559
|
+
|
|
561
560
|
Args:
|
|
562
561
|
message_id (string): The ID of the message containing the media
|
|
563
562
|
chat_jid (string): The JID of the chat containing the message
|
|
564
|
-
|
|
563
|
+
|
|
565
564
|
Returns:
|
|
566
565
|
Dict[str, Any]: A dictionary containing success status, a status message, and the file path if successful
|
|
567
|
-
|
|
566
|
+
|
|
568
567
|
Raises:
|
|
569
568
|
ValueError: Raised when required parameters are missing.
|
|
570
|
-
|
|
569
|
+
|
|
571
570
|
Tags:
|
|
572
571
|
whatsapp.download_media, important
|
|
573
572
|
"""
|
|
@@ -2,77 +2,84 @@ import os
|
|
|
2
2
|
import subprocess
|
|
3
3
|
import tempfile
|
|
4
4
|
|
|
5
|
+
|
|
5
6
|
def convert_to_opus_ogg(input_file, output_file=None, bitrate="32k", sample_rate=24000):
|
|
6
7
|
"""
|
|
7
8
|
Convert an audio file to Opus format in an Ogg container.
|
|
8
|
-
|
|
9
|
+
|
|
9
10
|
Args:
|
|
10
11
|
input_file (str): Path to the input audio file
|
|
11
12
|
output_file (str, optional): Path to save the output file. If None, replaces the
|
|
12
13
|
extension of input_file with .ogg
|
|
13
14
|
bitrate (str, optional): Target bitrate for Opus encoding (default: "32k")
|
|
14
15
|
sample_rate (int, optional): Sample rate for output (default: 24000)
|
|
15
|
-
|
|
16
|
+
|
|
16
17
|
Returns:
|
|
17
18
|
str: Path to the converted file
|
|
18
|
-
|
|
19
|
+
|
|
19
20
|
Raises:
|
|
20
21
|
FileNotFoundError: If the input file doesn't exist
|
|
21
22
|
RuntimeError: If the ffmpeg conversion fails
|
|
22
23
|
"""
|
|
23
24
|
if not os.path.isfile(input_file):
|
|
24
25
|
raise FileNotFoundError(f"Input file not found: {input_file}")
|
|
25
|
-
|
|
26
|
+
|
|
26
27
|
# If no output file is specified, replace the extension with .ogg
|
|
27
28
|
if output_file is None:
|
|
28
29
|
output_file = os.path.splitext(input_file)[0] + ".ogg"
|
|
29
|
-
|
|
30
|
+
|
|
30
31
|
# Ensure the output directory exists
|
|
31
32
|
output_dir = os.path.dirname(output_file)
|
|
32
33
|
if output_dir and not os.path.exists(output_dir):
|
|
33
34
|
os.makedirs(output_dir)
|
|
34
|
-
|
|
35
|
+
|
|
35
36
|
# Build the ffmpeg command
|
|
36
37
|
cmd = [
|
|
37
38
|
"ffmpeg",
|
|
38
|
-
"-i",
|
|
39
|
-
|
|
40
|
-
"-
|
|
41
|
-
"
|
|
42
|
-
"-
|
|
43
|
-
|
|
44
|
-
"-
|
|
45
|
-
|
|
46
|
-
"-
|
|
47
|
-
|
|
39
|
+
"-i",
|
|
40
|
+
input_file,
|
|
41
|
+
"-c:a",
|
|
42
|
+
"libopus",
|
|
43
|
+
"-b:a",
|
|
44
|
+
bitrate,
|
|
45
|
+
"-ar",
|
|
46
|
+
str(sample_rate),
|
|
47
|
+
"-application",
|
|
48
|
+
"voip", # Optimize for voice
|
|
49
|
+
"-vbr",
|
|
50
|
+
"on", # Variable bitrate
|
|
51
|
+
"-compression_level",
|
|
52
|
+
"10", # Maximum compression
|
|
53
|
+
"-frame_duration",
|
|
54
|
+
"60", # 60ms frames (good for voice)
|
|
55
|
+
"-y", # Overwrite output file if it exists
|
|
56
|
+
output_file,
|
|
48
57
|
]
|
|
49
|
-
|
|
58
|
+
|
|
50
59
|
try:
|
|
51
60
|
# Run the ffmpeg command and capture output
|
|
52
|
-
|
|
53
|
-
cmd,
|
|
54
|
-
stdout=subprocess.PIPE,
|
|
55
|
-
stderr=subprocess.PIPE,
|
|
56
|
-
text=True,
|
|
57
|
-
check=True
|
|
61
|
+
subprocess.run(
|
|
62
|
+
cmd, capture_output=True, text=True, check=True
|
|
58
63
|
)
|
|
59
64
|
return output_file
|
|
60
65
|
except subprocess.CalledProcessError as e:
|
|
61
|
-
raise RuntimeError(
|
|
66
|
+
raise RuntimeError(
|
|
67
|
+
f"Failed to convert audio. You likely need to install ffmpeg {e.stderr}"
|
|
68
|
+
)
|
|
62
69
|
|
|
63
70
|
|
|
64
71
|
def convert_to_opus_ogg_temp(input_file, bitrate="32k", sample_rate=24000):
|
|
65
72
|
"""
|
|
66
73
|
Convert an audio file to Opus format in an Ogg container and store in a temporary file.
|
|
67
|
-
|
|
74
|
+
|
|
68
75
|
Args:
|
|
69
76
|
input_file (str): Path to the input audio file
|
|
70
77
|
bitrate (str, optional): Target bitrate for Opus encoding (default: "32k")
|
|
71
78
|
sample_rate (int, optional): Sample rate for output (default: 24000)
|
|
72
|
-
|
|
79
|
+
|
|
73
80
|
Returns:
|
|
74
81
|
str: Path to the temporary file with the converted audio
|
|
75
|
-
|
|
82
|
+
|
|
76
83
|
Raises:
|
|
77
84
|
FileNotFoundError: If the input file doesn't exist
|
|
78
85
|
RuntimeError: If the ffmpeg conversion fails
|
|
@@ -80,7 +87,7 @@ def convert_to_opus_ogg_temp(input_file, bitrate="32k", sample_rate=24000):
|
|
|
80
87
|
# Create a temporary file with .ogg extension
|
|
81
88
|
temp_file = tempfile.NamedTemporaryFile(suffix=".ogg", delete=False)
|
|
82
89
|
temp_file.close()
|
|
83
|
-
|
|
90
|
+
|
|
84
91
|
try:
|
|
85
92
|
# Convert the audio
|
|
86
93
|
convert_to_opus_ogg(input_file, temp_file.name, bitrate, sample_rate)
|
|
@@ -95,16 +102,13 @@ def convert_to_opus_ogg_temp(input_file, bitrate="32k", sample_rate=24000):
|
|
|
95
102
|
if __name__ == "__main__":
|
|
96
103
|
# Example usage
|
|
97
104
|
import sys
|
|
98
|
-
|
|
105
|
+
|
|
99
106
|
if len(sys.argv) < 2:
|
|
100
|
-
print("Usage: python audio.py input_file [output_file]")
|
|
101
107
|
sys.exit(1)
|
|
102
|
-
|
|
108
|
+
|
|
103
109
|
input_file = sys.argv[1]
|
|
104
|
-
|
|
110
|
+
|
|
105
111
|
try:
|
|
106
112
|
result = convert_to_opus_ogg_temp(input_file)
|
|
107
|
-
|
|
108
|
-
except Exception as e:
|
|
109
|
-
print(f"Error: {e}")
|
|
113
|
+
except Exception:
|
|
110
114
|
sys.exit(1)
|