EllenBeta commited on
Commit
567695d
·
verified ·
1 Parent(s): 3e5df80

Update helper.py

Browse files
Files changed (1) hide show
  1. helper.py +78 -36
helper.py CHANGED
@@ -12,16 +12,17 @@ from moviepy.editor import VideoFileClip
12
  import wave
13
 
14
  # -------------------------------
15
- # Logging
16
  logger = logging.getLogger(__name__)
17
- logging.basicConfig(level=logging.INFO)
18
 
19
  # Hugging Face credentials
20
  HF_TOKEN = os.getenv("HF_TOKEN")
21
  DATASET_REPO = "EllenBeta/Voxai-data"
22
 
23
  # -------------------------------
24
-
 
25
  def save_to_dataset_repo(file_path, hf_path, file_name):
26
  """Upload audio file to Hugging Face dataset."""
27
  try:
@@ -43,7 +44,8 @@ def save_to_dataset_repo(file_path, hf_path, file_name):
43
  raise Exception(f"Upload failed: {str(e)}")
44
 
45
  # -------------------------------
46
-
 
47
  def save_audio(user_id, audio_url, title, text, duration):
48
  """Save audio metadata to PostgreSQL."""
49
  conn = create_connection()
@@ -66,82 +68,122 @@ def save_audio(user_id, audio_url, title, text, duration):
66
  conn.close()
67
 
68
  # -------------------------------
69
-
 
70
  def generate_random_filename(extension="mp3", length=12):
71
  letters = string.ascii_letters + string.digits
72
  random_str = ''.join(random.choice(letters) for _ in range(length))
73
  return f"{random_str}.{extension}"
74
 
75
  # -------------------------------
76
-
 
77
  def cut_video(video_path, max_duration=2):
78
  """Cut video to a maximum duration using MoviePy."""
79
- clip = VideoFileClip(video_path)
80
- if clip.duration > max_duration:
81
- clip = clip.subclip(0, max_duration)
82
- return clip
 
 
 
83
 
84
  # -------------------------------
85
-
 
86
  def video_to_audio(video_input, output_path="/tmp/temp_reference.wav", max_duration=2):
87
- """Convert video (or base64 video) to WAV audio using MoviePy."""
 
 
 
88
  temp_video_path = None
 
 
89
  try:
90
- # Decode base64 input if needed
91
- if video_input.startswith("data:video/"):
92
- header, encoded = video_input.split(",", 1)
93
- temp_video = tempfile.NamedTemporaryFile(delete=False, suffix=".mp4")
94
- temp_video_path = temp_video.name
95
- with open(temp_video_path, "wb") as f:
96
- f.write(base64.b64decode(encoded))
97
- video_path = temp_video_path
 
 
 
 
 
 
 
98
  else:
99
- video_path = video_input
100
 
101
- # Cut video if needed
102
- clip = cut_video(video_path, max_duration)
103
-
104
- # Extract audio
105
- clip.audio.write_audiofile(output_path, fps=22050, nbytes=2, codec="pcm_s16le")
 
 
 
 
 
 
 
 
 
 
 
 
106
  clip.close()
107
 
 
108
  if not os.path.exists(output_path) or os.path.getsize(output_path) == 0:
109
  raise Exception("Output audio file not created or empty")
110
 
111
- logger.info(f"✅ Video converted to audio: {output_path}")
112
  return output_path
113
 
114
  except Exception as e:
115
- logger.error(f"Video→Audio conversion failed: {e}")
116
  raise
 
117
  finally:
118
- if temp_video_path and os.path.exists(temp_video_path):
119
  try:
120
- os.remove(temp_video_path)
121
  except:
122
  pass
 
 
 
 
 
 
123
 
124
  # -------------------------------
125
-
 
126
  def ensure_wav_format(input_path):
127
- """Convert to WAV if not already valid WAV."""
128
  try:
129
  with wave.open(input_path, 'rb'):
130
- return input_path
131
  except:
132
  try:
133
  audio = AudioSegment.from_file(input_path)
134
  wav_path = os.path.splitext(input_path)[0] + "_converted.wav"
135
  audio.export(wav_path, format="wav")
136
- logger.info(f"Converted {input_path} → WAV ({wav_path})")
137
  return wav_path
138
  except Exception as e:
139
  raise Exception(f"Failed to convert to WAV: {e}")
140
 
141
  # -------------------------------
142
-
 
143
  def validate_audio_file(file_path, max_size_mb=10):
144
- """Validate audio file: size, format, duration."""
145
  try:
146
  if not os.path.exists(file_path):
147
  return False, "File not found"
 
12
  import wave
13
 
14
  # -------------------------------
15
+ # Logging setup
16
  logger = logging.getLogger(__name__)
17
+ logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
18
 
19
  # Hugging Face credentials
20
  HF_TOKEN = os.getenv("HF_TOKEN")
21
  DATASET_REPO = "EllenBeta/Voxai-data"
22
 
23
  # -------------------------------
24
+ # Upload to Hugging Face Dataset
25
+ # -------------------------------
26
  def save_to_dataset_repo(file_path, hf_path, file_name):
27
  """Upload audio file to Hugging Face dataset."""
28
  try:
 
44
  raise Exception(f"Upload failed: {str(e)}")
45
 
46
  # -------------------------------
47
+ # Save metadata to PostgreSQL
48
+ # -------------------------------
49
  def save_audio(user_id, audio_url, title, text, duration):
50
  """Save audio metadata to PostgreSQL."""
51
  conn = create_connection()
 
68
  conn.close()
69
 
70
  # -------------------------------
71
+ # Utility: Generate random filename
72
+ # -------------------------------
73
  def generate_random_filename(extension="mp3", length=12):
74
  letters = string.ascii_letters + string.digits
75
  random_str = ''.join(random.choice(letters) for _ in range(length))
76
  return f"{random_str}.{extension}"
77
 
78
  # -------------------------------
79
+ # Cut video safely
80
+ # -------------------------------
81
  def cut_video(video_path, max_duration=2):
82
  """Cut video to a maximum duration using MoviePy."""
83
+ try:
84
+ clip = VideoFileClip(video_path)
85
+ if clip.duration > max_duration:
86
+ clip = clip.subclip(0, max_duration)
87
+ return clip
88
+ except Exception as e:
89
+ raise Exception(f"Failed to open or trim video: {e}")
90
 
91
  # -------------------------------
92
+ # Convert video to audio safely
93
+ # -------------------------------
94
  def video_to_audio(video_input, output_path="/tmp/temp_reference.wav", max_duration=2):
95
+ """
96
+ Convert video (file path, data URL, or base64) to WAV audio using MoviePy.
97
+ Handles all input formats safely and avoids oversized subprocess arguments.
98
+ """
99
  temp_video_path = None
100
+ clip = None
101
+
102
  try:
103
+ # Detect and decode base64 or data URLs
104
+ if isinstance(video_input, str):
105
+ if video_input.startswith("data:video/"): # full data URL
106
+ logger.info("📹 Detected data URL video input")
107
+ header, encoded = video_input.split(",", 1)
108
+ decoded_bytes = base64.b64decode(encoded)
109
+ elif len(video_input) > 500 and all(c.isalnum() or c in "+/=\n" for c in video_input.strip()):
110
+ logger.info("📹 Detected raw base64 video input")
111
+ decoded_bytes = base64.b64decode(video_input)
112
+ elif os.path.exists(video_input): # already a valid path
113
+ logger.info(f"📁 Detected video file path: {video_input}")
114
+ video_path = video_input
115
+ clip = cut_video(video_path, max_duration)
116
+ else:
117
+ raise Exception("Invalid video input format (not file path or base64)")
118
  else:
119
+ raise Exception("Invalid video input type")
120
 
121
+ # If we got decoded bytes, write to a temp file
122
+ if clip is None:
123
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".mp4") as tmp:
124
+ tmp.write(decoded_bytes)
125
+ temp_video_path = tmp.name
126
+ video_path = temp_video_path
127
+ clip = cut_video(video_path, max_duration)
128
+
129
+ # Extract audio safely
130
+ clip.audio.write_audiofile(
131
+ output_path,
132
+ fps=22050,
133
+ nbytes=2,
134
+ codec="pcm_s16le",
135
+ verbose=False,
136
+ logger=None
137
+ )
138
  clip.close()
139
 
140
+ # Validate output
141
  if not os.path.exists(output_path) or os.path.getsize(output_path) == 0:
142
  raise Exception("Output audio file not created or empty")
143
 
144
+ logger.info(f"✅ Video converted successfully {output_path}")
145
  return output_path
146
 
147
  except Exception as e:
148
+ logger.error(f"Video→Audio conversion failed: {e}")
149
  raise
150
+
151
  finally:
152
+ if clip:
153
  try:
154
+ clip.close()
155
  except:
156
  pass
157
+ if temp_video_path and os.path.exists(temp_video_path):
158
+ try:
159
+ os.remove(temp_video_path)
160
+ logger.debug(f"🧹 Deleted temp file {temp_video_path}")
161
+ except Exception as e:
162
+ logger.warning(f"⚠️ Failed to delete temp file: {e}")
163
 
164
  # -------------------------------
165
+ # Ensure WAV format
166
+ # -------------------------------
167
  def ensure_wav_format(input_path):
168
+ """Convert audio to WAV if not already valid WAV."""
169
  try:
170
  with wave.open(input_path, 'rb'):
171
+ return input_path # valid WAV
172
  except:
173
  try:
174
  audio = AudioSegment.from_file(input_path)
175
  wav_path = os.path.splitext(input_path)[0] + "_converted.wav"
176
  audio.export(wav_path, format="wav")
177
+ logger.info(f"🎵 Converted {input_path} → WAV ({wav_path})")
178
  return wav_path
179
  except Exception as e:
180
  raise Exception(f"Failed to convert to WAV: {e}")
181
 
182
  # -------------------------------
183
+ # Validate audio file
184
+ # -------------------------------
185
  def validate_audio_file(file_path, max_size_mb=10):
186
+ """Validate audio file: size, format, and duration."""
187
  try:
188
  if not os.path.exists(file_path):
189
  return False, "File not found"