Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
A
AI Tutor
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Salma Mohammed Hamed
AI Tutor
Commits
8b8c37b8
Commit
8b8c37b8
authored
Dec 02, 2025
by
salma
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
TTS web interface connected to the new RVC pipeline
parent
e2d1bde7
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
250 additions
and
0 deletions
+250
-0
Dockerfile
TTS_web_interface/Dockerfile
+13
-0
captain-definition
TTS_web_interface/captain-definition
+4
-0
main.py
TTS_web_interface/main.py
+229
-0
requirements.txt
TTS_web_interface/requirements.txt
+4
-0
No files found.
TTS_web_interface/Dockerfile
0 → 100644
View file @
8b8c37b8
FROM
python:3.11-slim
WORKDIR
/app
# Install dependencies
COPY
requirements.txt .
RUN
pip
install
--no-cache-dir
-r
requirements.txt
# Copy application
COPY
main.py .
# Run the application
CMD
["python", "main.py"]
\ No newline at end of file
TTS_web_interface/captain-definition
0 → 100644
View file @
8b8c37b8
{
"schemaVersion": 2,
"dockerfilePath": "./Dockerfile"
}
TTS_web_interface/main.py
0 → 100644
View file @
8b8c37b8
from
fastapi
import
FastAPI
,
HTTPException
from
fastapi.responses
import
HTMLResponse
,
StreamingResponse
from
pydantic
import
BaseModel
import
httpx
import
io
import
os
app
=
FastAPI
()
# ==========================================
# 1. HTML FRONTEND
# ==========================================
HTML_CONTENT
=
"""
<!DOCTYPE html>
<html lang="ar" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>تحويل النص إلى صوت</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0
%
, #764ba2 100
%
);
min-height: 100vh;
display: flex; justify-content: center; align-items: center; padding: 20px;
}
.container {
background: white; border-radius: 20px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
padding: 40px; max-width: 600px; width: 100
%
;
}
h1 { color: #667eea; margin-bottom: 30px; text-align: center; font-size: 2em; }
textarea {
width: 100
%
; min-height: 200px; padding: 15px;
border: 2px solid #e0e0e0; border-radius: 10px;
font-size: 16px; font-family: inherit; resize: vertical; margin-bottom: 20px;
}
textarea:focus { outline: none; border-color: #667eea; }
/* NEW: Speed Control Styles */
.control-group { margin-bottom: 20px; display: flex; align-items: center; gap: 10px; }
.control-group label { font-weight: bold; color: #444; }
.control-group input {
padding: 10px; border: 2px solid #e0e0e0; border-radius: 10px; width: 100px; font-size: 16px;
}
.button-group { display: flex; gap: 15px; margin-top: 10px; }
button {
flex: 1; padding: 15px 30px; border: none; border-radius: 10px;
font-size: 16px; font-weight: bold; cursor: pointer; transition: all 0.3s;
}
#generateBtn { background: linear-gradient(135deg, #667eea 0
%
, #764ba2 100
%
); color: white; }
#generateBtn:disabled { background: #ccc; cursor: not-allowed; }
#downloadBtn { background: #10b981; color: white; display: none; }
audio { width: 100
%
; margin-top: 20px; display: none; }
.status { margin-top: 20px; padding: 15px; border-radius: 10px; text-align: center; font-weight: bold; display: none; }
.status.loading { background: #fef3c7; color: #92400e; display: block; }
.status.success { background: #d1fae5; color: #065f46; display: block; }
.status.error { background: #fee2e2; color: #991b1b; display: block; }
.spinner {
display: inline-block; width: 16px; height: 16px;
border: 3px solid #92400e; border-top-color: transparent;
border-radius: 50
%
; animation: spin 1s linear infinite; margin-left: 10px;
}
@keyframes spin { to { transform: rotate(360deg); } }
</style>
</head>
<body>
<div class="container">
<h1>🎙️ تحويل النص إلى صوت</h1>
<textarea id="textInput" placeholder="اكتب النص العربي هنا...">مرحباً بك في خدمة تحويل النص إلى صوت.</textarea>
<!-- NEW: Speed Input -->
<div class="control-group">
<label for="speedInput">سرعة الصوت (1.0 = عادي):</label>
<input type="number" id="speedInput" value="1.0" min="0.25" max="4.0" step="0.05">
</div>
<div class="button-group">
<button id="generateBtn" onclick="generateAudio()">توليد الصوت</button>
<button id="downloadBtn" onclick="downloadAudio()">تحميل الملف</button>
</div>
<audio id="audioPlayer" controls></audio>
<div id="status" class="status"></div>
</div>
<script>
let audioBlob = null;
async function generateAudio() {
const text = document.getElementById('textInput').value.trim();
// NEW: Get speed value
const speed = parseFloat(document.getElementById('speedInput').value);
const generateBtn = document.getElementById('generateBtn');
const downloadBtn = document.getElementById('downloadBtn');
const audioPlayer = document.getElementById('audioPlayer');
if (!text) {
showStatus('error', 'الرجاء إدخال نص');
return;
}
generateBtn.disabled = true;
downloadBtn.style.display = 'none';
audioPlayer.style.display = 'none';
audioPlayer.src = '';
showStatus('loading', 'جاري الاتصال بالسيرفر...');
try {
// NEW: Send text AND speed
const response = await fetch('/synthesize', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
text: text,
speed: speed
})
});
if (!response.ok) {
const err = await response.json();
throw new Error(err.detail || 'فشل في توليد الصوت');
}
audioBlob = await response.blob();
const audioUrl = URL.createObjectURL(audioBlob);
audioPlayer.src = audioUrl;
audioPlayer.style.display = 'block';
audioPlayer.play();
showStatus('success', '✓ تم التوليد بنجاح!');
downloadBtn.style.display = 'block';
} catch (error) {
showStatus('error', '❌ حدث خطأ: ' + error.message);
} finally {
generateBtn.disabled = false;
}
}
function downloadAudio() {
if (!audioBlob) return;
const url = URL.createObjectURL(audioBlob);
const a = document.createElement('a');
a.href = url;
a.download = 'arabic_speech.wav';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
function showStatus(type, message) {
const status = document.getElementById('status');
status.className = 'status ' + type;
if (type === 'loading') status.innerHTML = message + '<span class="spinner"></span>';
else status.textContent = message;
}
</script>
</body>
</html>
"""
# ==========================================
# 2. FASTAPI APP
# ==========================================
class
TextInput
(
BaseModel
):
text
:
str
speed
:
float
=
1.0
# <--- NEW: Added speed parameter
@
app
.
get
(
"/"
,
response_class
=
HTMLResponse
)
async
def
read_root
():
return
HTML_CONTENT
@
app
.
get
(
"/health"
)
async
def
health_check
():
return
{
"status"
:
"healthy"
}
@
app
.
post
(
"/synthesize"
)
async
def
synthesize
(
input_data
:
TextInput
):
"""
Directly forwards text to the external API without modification.
"""
try
:
# Configuration
external_url
=
"http://ec2-18-193-226-85.eu-central-1.compute.amazonaws.com:5000/generate_audio"
# Raw payload
payload
=
{
"text"
:
input_data
.
text
,
"speed"
:
input_data
.
speed
# <--- NEW: Forwarding speed to External API
}
# Call the external API (Timeout set to 60s for stability)
async
with
httpx
.
AsyncClient
(
timeout
=
60.0
)
as
client
:
response
=
await
client
.
post
(
external_url
,
json
=
payload
)
if
response
.
status_code
!=
200
:
print
(
f
"External API Error: {response.text}"
)
raise
HTTPException
(
status_code
=
response
.
status_code
,
detail
=
"External TTS API failed"
)
# Stream result
return
StreamingResponse
(
io
.
BytesIO
(
response
.
content
),
media_type
=
"audio/wav"
,
headers
=
{
"Content-Disposition"
:
"attachment; filename=arabic_speech.wav"
}
)
except
httpx
.
TimeoutException
:
raise
HTTPException
(
status_code
=
504
,
detail
=
"TTS API took too long (Timeout)"
)
except
Exception
as
e
:
print
(
f
"Server Error: {str(e)}"
)
raise
HTTPException
(
status_code
=
500
,
detail
=
str
(
e
))
if
__name__
==
"__main__"
:
import
uvicorn
print
(
"Starting server on http://localhost:80"
)
uvicorn
.
run
(
app
,
host
=
"0.0.0.0"
,
port
=
80
)
\ No newline at end of file
TTS_web_interface/requirements.txt
0 → 100644
View file @
8b8c37b8
fastapi
uvicorn[standard]
httpx
pydantic
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment