Used by thousands of students worldwide - help us reach 150 members
Audio
/audioText-to-speech audio for book lines. Audio is generated per line field (arabic, english, commentary) with an OpenAI TTS voice, stored as MP3 in S3 under the key bookId/lineId/field/voice_contentHash.mp3, and handed to clients either as a time-limited S3 presigned URL or as an MP3 stream proxied by the API. The content hash covers all of the line text plus the voice, so editing a line invalidates its cached audio. Generating audio is restricted to editors and admins; anonymous callers and members can only fetch audio that has already been generated for the current line content, and the listing and URL endpoints apply the same read-access rules as GET /books/:bookId (private and subscriber-only books require a token).
/audio/voicesList available TTS voicesGET/audio/fieldsList line fields that support audioPOST/audio/:bookId/generateStart a whole-book audio generation jobGET/audio/jobs/:jobIdGet an audio generation jobGET/audio/:bookId/lines-with-audioList every line of a book with audio availability for one voiceGET/audio/jobsList all audio generation jobsPOST/audio/:bookId/lines/:lineId/batchGenerate or fetch audio for several fields of a linePOST/audio/:bookId/lines/:lineId/:fieldGenerate or fetch audio for one field of a lineGET/audio/:bookId/lines/:lineId/:field/streamStream the MP3 audio for one field of a lineGET/audio/:bookId/lines/:lineId/:field/urlGet a presigned URL for the audio of one field of a lineGET/audio/:bookId/lines/:lineIdList stored audio files for a lineDELETE/audio/:bookId/lines/:lineId/:fieldDelete stored audio for one field of a lineDELETE/audio/:bookId/lines/:lineIdDelete all stored audio for a lineDELETE/audio/:bookIdDelete all stored audio for a bookGET/audio/:bookId/allList all stored audio for a book, grouped by lineList available TTS voices
Returns the static list of voices that can be passed as the voice parameter on the other audio endpoints. The default voice everywhere is alloy.
curl -X GET "https://app.ummahspot.com/audio/voices"{
"voices": [
{ "id": "alloy", "name": "Alloy", "description": "Neutral, balanced voice" },
{ "id": "echo", "name": "Echo", "description": "Clear, articulate voice" },
{ "id": "fable", "name": "Fable", "description": "Warm, storytelling voice" },
{ "id": "onyx", "name": "Onyx", "description": "Deep, authoritative voice" },
{ "id": "nova", "name": "Nova", "description": "Bright, energetic voice" },
{ "id": "shimmer", "name": "Shimmer", "description": "Gentle, soothing voice" }
]
}List line fields that support audio
Returns the static list of line fields audio can be generated for. These map to the Arabic, English and commentary text of a book line.
curl -X GET "https://app.ummahspot.com/audio/fields"{
"fields": ["arabic", "english", "commentary"]
}Start a whole-book audio generation job
Creates an AudioJob with status "pending" and responds immediately with its id. Generation then runs in the background inside the API process: the job moves to "in_progress", and for every line of the book and every field (arabic, english, commentary) audio is fetched from the S3 cache or generated with OpenAI TTS and uploaded. progress (0-100) is saved after each line/field, and the job ends as "completed", or "failed" with an error message if the job itself crashes. Failures for an individual line/field (for example an empty field) are only logged, still count towards progress, and do not fail the job. The voice is not validated here. Poll GET /audio/jobs/:jobId to follow the job.
Path parameters
bookIdstring | Book ObjectId |
Body parameters
voicestring | TTS voice id: alloy, echo, fable, onyx, nova or shimmer (see GET /audio/voices)Default: alloy |
Errors
401No bearer token provided (plain-text body)403Invalid or expired token, or the user has neither the editor nor the admin role (plain-text body)404Book not found (or, as plain text, the user behind the token no longer exists)500Failed to start audio generation (including a malformed bookId)
curl -X POST "https://app.ummahspot.com/audio/:bookId/generate" \
-H "Authorization: Bearer $SHARH_TOKEN" \
-H "Content-Type: application/json" \
-d '{"voice":"string"}'{
"success": true,
"jobId": "66f1c2a9e4b0a1d2c3f4a5b6"
}Get an audio generation job
Returns the raw AudioJob document so a client can poll status and progress. status is one of pending, in_progress, completed or failed; progress is a percentage from 0 to 100; error is only present on failed jobs. Any editor or admin can read any job.
Path parameters
jobIdstring | AudioJob ObjectId returned by POST /audio/:bookId/generate |
Errors
401No bearer token provided (plain-text body)403Invalid or expired token, or the user has neither the editor nor the admin role (plain-text body)404Job not found (or, as plain text, the user behind the token no longer exists)500Failed to fetch job (including a malformed jobId)
curl -X GET "https://app.ummahspot.com/audio/jobs/:jobId" \
-H "Authorization: Bearer $SHARH_TOKEN"{
"_id": "66f1c2a9e4b0a1d2c3f4a5b6",
"bookId": "665f1b2c3d4e5f6a7b8c9d0e",
"voice": "alloy",
"status": "in_progress",
"progress": 42,
"createdAt": "2025-01-15T10:30:00.000Z",
"updatedAt": "2025-01-15T10:34:12.000Z",
"__v": 0
}List every line of a book with audio availability for one voice
Lists all audio objects stored in S3 for the book, keeps those matching the requested voice (case-insensitive), and returns every line of the book with, per field (arabic, english, commentary), the field text, whether audio exists, and a presigned S3 URL valid for 1 hour (null when there is no audio). This endpoint never generates audio. Read access to the book is enforced exactly like GET /books/:bookId: audio of public books is readable by anyone, while a bearer token is needed to read audio of private books (owner, contributors and admins only — everyone else gets a 404) and of subscriber-only books (accessModel "creator_subscription": callers who cannot manage the book need an active subscription to its creator at the required tier, otherwise a 402 is returned). An invalid token is silently treated as anonymous.
Path parameters
bookIdstring | Book ObjectId |
Query parameters
voicestring | Voice id to check audio for (alloy, echo, fable, onyx, nova, shimmer). Echoed back in the response.Default: alloy |
Errors
402Creator subscription required to read this book. JSON body: { requiresSubscription: true, message, contentType: "book", contentId, requiredSubscriptionTier, creator }404Book not found: it does not exist, is deleted, or is not public and the caller is not its owner, a contributor or an admin500Failed to get line audio list (S3 error or malformed bookId); body includes details
curl -X GET "https://app.ummahspot.com/audio/:bookId/lines-with-audio" \
-H "Authorization: Bearer $SHARH_TOKEN"{
"success": true,
"bookId": "665f1b2c3d4e5f6a7b8c9d0e",
"bookTitle": "Al-Ajrumiyyah",
"voice": "alloy",
"lines": [
{
"lineId": "665f1b2c3d4e5f6a7b8c9d11",
"fields": {
"arabic": {
"text": "الكلام هو اللفظ المركب المفيد بالوضع",
"hasAudio": true,
"audioUrl": "https://sharh-app-audio.s3.amazonaws.com/665f1b2c3d4e5f6a7b8c9d0e/665f1b2c3d4e5f6a7b8c9d11/arabic/alloy_9f2c4e1a7b3d5c60.mp3?X-Amz-Expires=3600&X-Amz-Signature=..."
},
"english": {
"text": "Speech is a composed utterance that conveys meaning by convention.",
"hasAudio": true,
"audioUrl": "https://sharh-app-audio.s3.amazonaws.com/665f1b2c3d4e5f6a7b8c9d0e/665f1b2c3d4e5f6a7b8c9d11/english/alloy_9f2c4e1a7b3d5c60.mp3?X-Amz-Expires=3600&X-Amz-Signature=..."
},
"commentary": {
"text": "",
"hasAudio": false,
"audioUrl": null
}
}
}
]
}List all audio generation jobs
Returns every AudioJob, newest first, with bookId populated with the book title, author, category, progress and difficulty. Jobs are not filtered by user: any editor or admin sees all jobs. There is no pagination.
Errors
401No bearer token provided (plain-text body)403Invalid or expired token, or the user has neither the editor nor the admin role (plain-text body)404The user behind the token no longer exists (plain-text body)500Failed to fetch audio jobs
curl -X GET "https://app.ummahspot.com/audio/jobs" \
-H "Authorization: Bearer $SHARH_TOKEN"{
"success": true,
"jobs": [
{
"_id": "66f1c2a9e4b0a1d2c3f4a5b6",
"bookId": {
"_id": "665f1b2c3d4e5f6a7b8c9d0e",
"title": "Al-Ajrumiyyah",
"author": "Ibn Ajurrum",
"category": "Grammar",
"progress": "Complete",
"difficulty": "Beginner"
},
"voice": "alloy",
"status": "completed",
"progress": 100,
"createdAt": "2025-01-15T10:30:00.000Z",
"updatedAt": "2025-01-15T10:52:41.000Z",
"__v": 0
}
]
}Generate or fetch audio for several fields of a line
For each requested field, returns the cached MP3 from S3 if one exists for the current line content and voice, otherwise generates it synchronously with OpenAI TTS, uploads it to S3 and removes stale audio for that field. Each result contains the S3 key, a presigned URL valid for 1 hour, and whether it came from the cache. A failure for a single field (for example the field has no text, or the voice is rejected by the TTS provider) does not fail the request: it is reported under errors keyed by field name, and errors is omitted when every field succeeded. Field names are validated against arabic, english and commentary; the voice is not validated up front. Because generation costs money, only editors and admins may call this endpoint; only the role is checked, there is no book ownership check.
Path parameters
bookIdstring | Book ObjectId |
lineIdstring | ObjectId of the line inside the book |
Body parameters
fieldsstring[] | Fields to generate audio for. Allowed values: arabic, english, commentary.Default: ["arabic", "english"] |
voicestring | TTS voice id: alloy, echo, fable, onyx, nova or shimmerDefault: alloy |
Errors
400Invalid fields; body lists invalidFields and availableFields401No bearer token provided (plain-text body)403Invalid or expired token, or the user has neither the editor nor the admin role (plain-text body)404Book not found, or Line not found in that book (or, as plain text, the user behind the token no longer exists)500Failed to generate batch audio (for example fields is not an array of strings, or a malformed id); body includes details
curl -X POST "https://app.ummahspot.com/audio/:bookId/lines/:lineId/batch" \
-H "Authorization: Bearer $SHARH_TOKEN" \
-H "Content-Type: application/json" \
-d '{"fields":[]}'{
"success": true,
"bookId": "665f1b2c3d4e5f6a7b8c9d0e",
"lineId": "665f1b2c3d4e5f6a7b8c9d11",
"voice": "alloy",
"results": {
"arabic": {
"s3Key": "665f1b2c3d4e5f6a7b8c9d0e/665f1b2c3d4e5f6a7b8c9d11/arabic/alloy_9f2c4e1a7b3d5c60.mp3",
"audioUrl": "https://sharh-app-audio.s3.amazonaws.com/665f1b2c3d4e5f6a7b8c9d0e/665f1b2c3d4e5f6a7b8c9d11/arabic/alloy_9f2c4e1a7b3d5c60.mp3?X-Amz-Expires=3600&X-Amz-Signature=...",
"cached": true
}
},
"errors": {
"english": "No content found for field: english"
}
}Generate or fetch audio for one field of a line
Returns the cached MP3 for this line field and voice if one exists for the current line content, otherwise generates it synchronously with OpenAI TTS (long text is split into chunks of up to 4000 characters and concatenated), uploads it to S3 and deletes stale audio for that field. The response carries the S3 key and a presigned URL valid for 1 hour; request GET .../url again for a fresh URL once it expires. Both field and voice are validated. An empty field results in a 500. Because generation costs money, only editors and admins may call this endpoint; only the role is checked, there is no book ownership check.
Path parameters
bookIdstring | Book ObjectId |
lineIdstring | ObjectId of the line inside the book |
fieldstring | Line field: arabic, english or commentary (case-insensitive) |
Body parameters
voicestring | TTS voice id. Must be one of alloy, echo, fable, onyx, nova, shimmer.Default: alloy |
Errors
400Invalid field (body lists availableFields) or Invalid voice (body lists availableVoices)401No bearer token provided (plain-text body)403Invalid or expired token, or the user has neither the editor nor the admin role (plain-text body)404Book not found, or Line not found in that book (or, as plain text, the user behind the token no longer exists)500Failed to generate audio (field has no text, TTS or S3 failure, malformed id); body includes details
curl -X POST "https://app.ummahspot.com/audio/:bookId/lines/:lineId/:field" \
-H "Authorization: Bearer $SHARH_TOKEN" \
-H "Content-Type: application/json" \
-d '{"voice":"string"}'{
"success": true,
"bookId": "665f1b2c3d4e5f6a7b8c9d0e",
"lineId": "665f1b2c3d4e5f6a7b8c9d11",
"field": "english",
"voice": "alloy",
"s3Key": "665f1b2c3d4e5f6a7b8c9d0e/665f1b2c3d4e5f6a7b8c9d11/english/alloy_9f2c4e1a7b3d5c60.mp3",
"audioUrl": "https://sharh-app-audio.s3.amazonaws.com/665f1b2c3d4e5f6a7b8c9d0e/665f1b2c3d4e5f6a7b8c9d11/english/alloy_9f2c4e1a7b3d5c60.mp3?X-Amz-Expires=3600&X-Amz-Signature=...",
"cached": false,
"message": "New audio generated"
}Stream the MP3 audio for one field of a line
Looks up the MP3 for the line field and voice in the S3 cache and pipes the S3 object through the API as the response body. For anonymous callers and members this endpoint only serves audio that has ALREADY been generated for the line's current content and voice; if there is none (never generated, or the line was edited since) it answers 404 and nothing is generated. Only when the bearer token belongs to an editor or admin is missing audio generated and uploaded on demand. field must be arabic, english or commentary and voice must be one of the ids from GET /audio/voices, otherwise a 400 is returned. The response is sent with Content-Type: audio/mpeg, Accept-Ranges: bytes and Cache-Control: max-age=31536000. Although Accept-Ranges is advertised, the handler always sends the complete file with status 200 and does not honour Range headers. Book read access is NOT checked on this route, because media players (an audio element src) cannot send an Authorization header; use GET .../url when access control matters. An invalid token is silently treated as anonymous.
Path parameters
bookIdstring | Book ObjectId |
lineIdstring | ObjectId of the line inside the book |
fieldstring | Line field: arabic, english or commentary (case-insensitive) |
Query parameters
voicestring | TTS voice id. Must be one of alloy, echo, fable, onyx, nova, shimmer.Default: alloy |
Errors
400Invalid field or voice ({ "error": "Invalid field or voice" })404Book not found, Line not found in that book, or — for callers who are not editors or admins — { "error": "Audio has not been generated for this line" }500Failed to stream audio (S3 failure, malformed id, or for editors/admins an empty field or TTS failure during generation)
curl -X GET "https://app.ummahspot.com/audio/:bookId/lines/:lineId/:field/stream" \
-H "Authorization: Bearer $SHARH_TOKEN"Binary MP3 stream (Content-Type: audio/mpeg), not JSON. Suitable as the src of an audio element.Get a presigned URL for the audio of one field of a line
Looks up the MP3 for the line field and voice in the S3 cache and returns a fresh S3 presigned GET URL with a caller-chosen lifetime. The URL points directly at S3 (served as audio/mpeg with inline disposition) and stops working after expiresIn seconds. As with the stream endpoint, anonymous callers and members only get audio that has ALREADY been generated for the line's current content and voice (cached is then always true) and receive a 404 otherwise; only editors and admins trigger generation on demand. field must be arabic, english or commentary and voice must be one of the ids from GET /audio/voices, otherwise a 400 is returned. Read access to the book is enforced exactly like GET /books/:bookId: audio of public books is readable by anyone, while a bearer token is needed to read audio of private books (owner, contributors and admins only — everyone else gets a 404) and of subscriber-only books (accessModel "creator_subscription": callers who cannot manage the book need an active subscription to its creator at the required tier, otherwise a 402 is returned). An invalid token is silently treated as anonymous.
Path parameters
bookIdstring | Book ObjectId |
lineIdstring | ObjectId of the line inside the book |
fieldstring | Line field: arabic, english or commentary (case-insensitive) |
Query parameters
voicestring | TTS voice id. Must be one of alloy, echo, fable, onyx, nova, shimmer.Default: alloy |
expiresnumber | Lifetime of the presigned URL in seconds (parsed as an integer)Default: 3600 |
Errors
400Invalid field or voice ({ "error": "Invalid field or voice" })402Creator subscription required to read this book. JSON body: { requiresSubscription: true, message, contentType: "book", contentId, requiredSubscriptionTier, creator }404Book not found (it does not exist, is deleted, or is not public and the caller is not its owner, a contributor or an admin), Line not found in that book, or — for callers who are not editors or admins — { "error": "Audio has not been generated for this line" }500Failed to get audio URL (S3 failure, malformed id, or for editors/admins an empty field or TTS failure during generation); body includes details
curl -X GET "https://app.ummahspot.com/audio/:bookId/lines/:lineId/:field/url" \
-H "Authorization: Bearer $SHARH_TOKEN"{
"success": true,
"bookId": "665f1b2c3d4e5f6a7b8c9d0e",
"lineId": "665f1b2c3d4e5f6a7b8c9d11",
"field": "english",
"voice": "alloy",
"audioUrl": "https://sharh-app-audio.s3.amazonaws.com/665f1b2c3d4e5f6a7b8c9d0e/665f1b2c3d4e5f6a7b8c9d11/english/alloy_9f2c4e1a7b3d5c60.mp3?X-Amz-Expires=600&X-Amz-Signature=...",
"expiresIn": 600,
"cached": true
}List stored audio files for a line
Lists every audio object stored in S3 for the line, across all fields and voices, each with a presigned URL valid for 1 hour, together with the text content of the line. This endpoint never generates audio. Read access to the book is enforced exactly like GET /books/:bookId: audio of public books is readable by anyone, while a bearer token is needed to read audio of private books (owner, contributors and admins only — everyone else gets a 404) and of subscriber-only books (accessModel "creator_subscription": callers who cannot manage the book need an active subscription to its creator at the required tier, otherwise a 402 is returned). An invalid token is silently treated as anonymous.
Path parameters
bookIdstring | Book ObjectId |
lineIdstring | ObjectId of the line inside the book |
Errors
402Creator subscription required to read this book. JSON body: { requiresSubscription: true, message, contentType: "book", contentId, requiredSubscriptionTier, creator }404Book not found (it does not exist, is deleted, or is not public and the caller is not its owner, a contributor or an admin), or Line not found in that book500Failed to list audio files (S3 error or malformed id); body includes details
curl -X GET "https://app.ummahspot.com/audio/:bookId/lines/:lineId" \
-H "Authorization: Bearer $SHARH_TOKEN"{
"success": true,
"bookId": "665f1b2c3d4e5f6a7b8c9d0e",
"lineId": "665f1b2c3d4e5f6a7b8c9d11",
"audioFiles": [
{
"s3Key": "665f1b2c3d4e5f6a7b8c9d0e/665f1b2c3d4e5f6a7b8c9d11/english/alloy_9f2c4e1a7b3d5c60.mp3",
"field": "english",
"voice": "alloy",
"contentHash": "9f2c4e1a7b3d5c60",
"size": 48213,
"lastModified": "2025-01-15T10:31:07.000Z",
"url": "https://sharh-app-audio.s3.amazonaws.com/665f1b2c3d4e5f6a7b8c9d0e/665f1b2c3d4e5f6a7b8c9d11/english/alloy_9f2c4e1a7b3d5c60.mp3?X-Amz-Expires=3600&X-Amz-Signature=..."
}
],
"lineContent": {
"arabic": "الكلام هو اللفظ المركب المفيد بالوضع",
"english": "Speech is a composed utterance that conveys meaning by convention.",
"commentary": "",
"rootwords": ""
}
}Delete stored audio for one field of a line
With a voice query parameter, deletes the single S3 object for that voice and the current line content (deletedFiles is always reported as 1, whether or not the object existed). Without voice, deletes every stored audio file for the field, across all voices (deletedFiles is the string "all"). Only the role is checked: there is no book ownership check, so any editor or admin can delete audio of any book. The field value is not validated.
Path parameters
bookIdstring | Book ObjectId |
lineIdstring | ObjectId of the line inside the book |
fieldstring | Line field whose audio should be deleted: arabic, english or commentary |
Query parameters
voicestring | Voice id to delete. When omitted, audio for all voices of the field is deleted. |
Errors
401No bearer token provided (plain-text body)403Invalid or expired token, or the user has neither the editor nor the admin role (plain-text body)404Book not found, or Line not found in that book (or, as plain text, the user behind the token no longer exists)500Failed to delete audio (S3 error or malformed id); body includes details
curl -X DELETE "https://app.ummahspot.com/audio/:bookId/lines/:lineId/:field" \
-H "Authorization: Bearer $SHARH_TOKEN"{
"success": true,
"message": "Deleted audio for english with voice alloy",
"deletedFiles": 1
}Delete all stored audio for a line
Deletes every audio object stored in S3 for the line, across all fields and voices, and returns how many files were removed. Only the role is checked; there is no book ownership check.
Path parameters
bookIdstring | Book ObjectId |
lineIdstring | ObjectId of the line inside the book |
Errors
401No bearer token provided (plain-text body)403Invalid or expired token, or the user has neither the editor nor the admin role (plain-text body)404Book not found, or Line not found in that book (or, as plain text, the user behind the token no longer exists)500Failed to delete line audio (S3 error or malformed id); body includes details
curl -X DELETE "https://app.ummahspot.com/audio/:bookId/lines/:lineId" \
-H "Authorization: Bearer $SHARH_TOKEN"{
"success": true,
"message": "Deleted all audio for line 665f1b2c3d4e5f6a7b8c9d11",
"deletedFiles": 3
}Delete all stored audio for a book
Deletes every audio object stored in S3 under the book, for all lines, fields and voices, and returns how many files were removed. The book itself is not modified. Only the role is checked; there is no book ownership check.
Path parameters
bookIdstring | Book ObjectId |
Errors
401No bearer token provided (plain-text body)403Invalid or expired token, or the user has neither the editor nor the admin role (plain-text body)404Book not found (or, as plain text, the user behind the token no longer exists)500Failed to delete book audio (S3 error or malformed bookId); body includes details
curl -X DELETE "https://app.ummahspot.com/audio/:bookId" \
-H "Authorization: Bearer $SHARH_TOKEN"{
"success": true,
"message": "Deleted all audio for book 665f1b2c3d4e5f6a7b8c9d0e",
"deletedFiles": 126
}List all stored audio for a book, grouped by line
Lists every audio object stored in S3 for the book, optionally filtered by field and voice, and groups them by line and field. Only lines that have at least one matching audio file are returned, in book order, each with its text content and, per field, an array of audio files with presigned URLs valid for 2 hours. totalFiles is the number of matching files. This endpoint never generates audio. Read access to the book is enforced exactly like GET /books/:bookId: audio of public books is readable by anyone, while a bearer token is needed to read audio of private books (owner, contributors and admins only — everyone else gets a 404) and of subscriber-only books (accessModel "creator_subscription": callers who cannot manage the book need an active subscription to its creator at the required tier, otherwise a 402 is returned). An invalid token is silently treated as anonymous.
Path parameters
bookIdstring | Book ObjectId |
Query parameters
fieldsstring | Comma-separated list of fields to include. Allowed values: arabic, english, commentary (case-insensitive).Default: arabic,english,commentary |
voicestring | Only include audio for this voice id (case-insensitive). When omitted, all voices are returned. |
Errors
400Invalid fields; body lists invalidFields and availableFields402Creator subscription required to read this book. JSON body: { requiresSubscription: true, message, contentType: "book", contentId, requiredSubscriptionTier, creator }404Book not found: it does not exist, is deleted, or is not public and the caller is not its owner, a contributor or an admin500Failed to get book audio list (S3 error or malformed bookId); body includes details
curl -X GET "https://app.ummahspot.com/audio/:bookId/all" \
-H "Authorization: Bearer $SHARH_TOKEN"{
"success": true,
"bookId": "665f1b2c3d4e5f6a7b8c9d0e",
"bookTitle": "Al-Ajrumiyyah",
"requestedFields": ["arabic", "english", "commentary"],
"totalFiles": 84,
"lines": [
{
"lineId": "665f1b2c3d4e5f6a7b8c9d11",
"content": {
"arabic": "الكلام هو اللفظ المركب المفيد بالوضع",
"english": "Speech is a composed utterance that conveys meaning by convention.",
"commentary": "",
"rootwords": ""
},
"audio": {
"english": [
{
"s3Key": "665f1b2c3d4e5f6a7b8c9d0e/665f1b2c3d4e5f6a7b8c9d11/english/alloy_9f2c4e1a7b3d5c60.mp3",
"voice": "alloy",
"size": 48213,
"lastModified": "2025-01-15T10:31:07.000Z",
"url": "https://sharh-app-audio.s3.amazonaws.com/665f1b2c3d4e5f6a7b8c9d0e/665f1b2c3d4e5f6a7b8c9d11/english/alloy_9f2c4e1a7b3d5c60.mp3?X-Amz-Expires=7200&X-Amz-Signature=..."
}
]
}
}
]
}