Spaces:
Sleeping
Sleeping
Commit
·
d31b48e
1
Parent(s):
2908edd
thread dict
Browse files- app/config.py +0 -2
- app/services/encoder_service.py +10 -8
app/config.py
CHANGED
|
@@ -11,8 +11,6 @@ SECRET_KEY = os.getenv('SECRET_KEY', 'your-secret-key-here')
|
|
| 11 |
# File upload configuration
|
| 12 |
UPLOAD_FOLDER = os.getenv('UPLOAD_FOLDER', BASE_DIR / 'uploads')
|
| 13 |
ENCODED_FOLDER = os.getenv('ENCODED_FOLDER', BASE_DIR / 'encoded')
|
| 14 |
-
ALLOWED_EXTENSIONS = {'mp4', 'mov', 'avi', 'mkv', 'wmv'}
|
| 15 |
-
|
| 16 |
|
| 17 |
# Create required directories
|
| 18 |
Path(UPLOAD_FOLDER).mkdir(parents=True, exist_ok=True)
|
|
|
|
| 11 |
# File upload configuration
|
| 12 |
UPLOAD_FOLDER = os.getenv('UPLOAD_FOLDER', BASE_DIR / 'uploads')
|
| 13 |
ENCODED_FOLDER = os.getenv('ENCODED_FOLDER', BASE_DIR / 'encoded')
|
|
|
|
|
|
|
| 14 |
|
| 15 |
# Create required directories
|
| 16 |
Path(UPLOAD_FOLDER).mkdir(parents=True, exist_ok=True)
|
app/services/encoder_service.py
CHANGED
|
@@ -17,6 +17,7 @@ logger = logging.getLogger(__name__)
|
|
| 17 |
class EncoderService:
|
| 18 |
def __init__(self):
|
| 19 |
self.jobs = {}
|
|
|
|
| 20 |
# Optimized settings for web streaming
|
| 21 |
self.default_qualities = {
|
| 22 |
'480p': {
|
|
@@ -27,7 +28,7 @@ class EncoderService:
|
|
| 27 |
'bufsize': '2000k',
|
| 28 |
'audio_bitrate': '128k',
|
| 29 |
'keyframe': '48', # Keyframe every 2 seconds at 24fps
|
| 30 |
-
'preset': 'ultrafast', #
|
| 31 |
'profile': 'main',
|
| 32 |
'level': '3.1',
|
| 33 |
'tune': 'fastdecode'
|
|
@@ -86,12 +87,12 @@ class EncoderService:
|
|
| 86 |
'settings': qualities
|
| 87 |
}
|
| 88 |
|
| 89 |
-
# Start encoding in a separate thread
|
| 90 |
-
# Note: Removing the daemon flag to ensure the thread completes.
|
| 91 |
thread = threading.Thread(
|
| 92 |
target=self._encode_video,
|
| 93 |
args=(filename, job_id)
|
| 94 |
)
|
|
|
|
| 95 |
thread.start()
|
| 96 |
logger.info(f"Started encoding thread for job {job_id}")
|
| 97 |
|
|
@@ -175,17 +176,14 @@ class EncoderService:
|
|
| 175 |
# Monitor FFmpeg progress with a timeout if no output in 30 seconds
|
| 176 |
last_output_time = time.time()
|
| 177 |
while True:
|
| 178 |
-
if process.stdout.readable()
|
| 179 |
-
line = process.stdout.readline()
|
| 180 |
-
else:
|
| 181 |
-
line = ''
|
| 182 |
if line:
|
| 183 |
last_output_time = time.time()
|
| 184 |
progress = self._parse_ffmpeg_progress(line, duration)
|
| 185 |
if progress is not None:
|
| 186 |
quality_progress = ((completed_steps + progress / 100) / total_steps) * 100
|
| 187 |
self.jobs[job_id]['progress'] = quality_progress
|
| 188 |
-
#
|
| 189 |
if time.time() - last_output_time > 30:
|
| 190 |
logger.warning(f"No ffmpeg output for 30 seconds on quality {quality}, attempt {attempts+1} for job {job_id}")
|
| 191 |
break
|
|
@@ -236,6 +234,10 @@ class EncoderService:
|
|
| 236 |
'status': 'failed',
|
| 237 |
'error': str(e)
|
| 238 |
})
|
|
|
|
|
|
|
|
|
|
|
|
|
| 239 |
|
| 240 |
def _get_video_duration(self, input_file):
|
| 241 |
"""Get video duration using FFprobe"""
|
|
|
|
| 17 |
class EncoderService:
|
| 18 |
def __init__(self):
|
| 19 |
self.jobs = {}
|
| 20 |
+
self.threads = {} # Store encoding thread references
|
| 21 |
# Optimized settings for web streaming
|
| 22 |
self.default_qualities = {
|
| 23 |
'480p': {
|
|
|
|
| 28 |
'bufsize': '2000k',
|
| 29 |
'audio_bitrate': '128k',
|
| 30 |
'keyframe': '48', # Keyframe every 2 seconds at 24fps
|
| 31 |
+
'preset': 'ultrafast', # Faster encoding speed
|
| 32 |
'profile': 'main',
|
| 33 |
'level': '3.1',
|
| 34 |
'tune': 'fastdecode'
|
|
|
|
| 87 |
'settings': qualities
|
| 88 |
}
|
| 89 |
|
| 90 |
+
# Start encoding in a separate thread and store the thread reference
|
|
|
|
| 91 |
thread = threading.Thread(
|
| 92 |
target=self._encode_video,
|
| 93 |
args=(filename, job_id)
|
| 94 |
)
|
| 95 |
+
self.threads[job_id] = thread
|
| 96 |
thread.start()
|
| 97 |
logger.info(f"Started encoding thread for job {job_id}")
|
| 98 |
|
|
|
|
| 176 |
# Monitor FFmpeg progress with a timeout if no output in 30 seconds
|
| 177 |
last_output_time = time.time()
|
| 178 |
while True:
|
| 179 |
+
line = process.stdout.readline() if process.stdout.readable() else ''
|
|
|
|
|
|
|
|
|
|
| 180 |
if line:
|
| 181 |
last_output_time = time.time()
|
| 182 |
progress = self._parse_ffmpeg_progress(line, duration)
|
| 183 |
if progress is not None:
|
| 184 |
quality_progress = ((completed_steps + progress / 100) / total_steps) * 100
|
| 185 |
self.jobs[job_id]['progress'] = quality_progress
|
| 186 |
+
# Break if no output for 30 seconds
|
| 187 |
if time.time() - last_output_time > 30:
|
| 188 |
logger.warning(f"No ffmpeg output for 30 seconds on quality {quality}, attempt {attempts+1} for job {job_id}")
|
| 189 |
break
|
|
|
|
| 234 |
'status': 'failed',
|
| 235 |
'error': str(e)
|
| 236 |
})
|
| 237 |
+
finally:
|
| 238 |
+
# Remove thread reference once job completes/fails
|
| 239 |
+
if job_id in self.threads:
|
| 240 |
+
del self.threads[job_id]
|
| 241 |
|
| 242 |
def _get_video_duration(self, input_file):
|
| 243 |
"""Get video duration using FFprobe"""
|