gyrus2 commited on
Commit
37a8d5e
·
verified ·
1 Parent(s): ce6c0c1

Fix Gradio file reading (handle NamedString and file-like objects)

Browse files
Files changed (1) hide show
  1. app.py +62 -10
app.py CHANGED
@@ -117,28 +117,80 @@ def generate_video(avatar_file, audio_file):
117
  """
118
  Gradio callback to generate a lip‑synced video.
119
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
  Parameters
121
  ----------
122
- avatar_file : file-like
123
- Uploaded image or video containing the face.
124
- audio_file : file-like
125
- Uploaded audio file.
 
 
126
 
127
  Returns
128
  -------
129
- str
130
- Path to the generated MP4 file (relative to Gradio working directory).
 
131
  """
132
  if avatar_file is None or audio_file is None:
133
  return None
134
 
135
- # Save uploaded files to temporary directory
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
  with tempfile.TemporaryDirectory() as tmpdir:
137
  avatar_path = Path(tmpdir) / "avatar"
138
  audio_path = Path(tmpdir) / "audio"
139
- # Write binary contents
140
- avatar_path.write_bytes(avatar_file.read())
141
- audio_path.write_bytes(audio_file.read())
142
  # Validate audio length
143
  try:
144
  validate_audio_length(str(audio_path))
 
117
  """
118
  Gradio callback to generate a lip‑synced video.
119
 
120
+ This function receives the uploaded avatar and audio files from Gradio's
121
+ ``gr.File`` inputs. Depending on the ``type`` parameter of the file
122
+ component and the version of Gradio, the objects passed into this
123
+ function can take on different forms. They may be file-like objects
124
+ supporting ``read()``, simple strings containing a path on disk, or
125
+ ``NamedString`` instances with a ``name`` attribute pointing to a
126
+ temporary file location. To robustly handle all of these cases, we
127
+ normalise the inputs by copying their contents into a temporary
128
+ directory, ensuring that subsequent processing always operates on
129
+ filesystem paths. This avoids ``AttributeError`` issues such as
130
+ ``'NamedString' object has no attribute 'read'`` seen with newer
131
+ versions of Gradio.
132
+
133
  Parameters
134
  ----------
135
+ avatar_file : Any
136
+ Uploaded image or video containing the face. Can be a file-like
137
+ object, a path string, or a NamedString/UploadFile depending on
138
+ Gradio version.
139
+ audio_file : Any
140
+ Uploaded audio file. Same possible types as ``avatar_file``.
141
 
142
  Returns
143
  -------
144
+ str | None
145
+ Path to the generated MP4 file (relative to Gradio working directory),
146
+ or ``None`` if either input is missing.
147
  """
148
  if avatar_file is None or audio_file is None:
149
  return None
150
 
151
+ def _copy_input_to_path(file_obj, dest_path: Path) -> None:
152
+ """Copy the uploaded file into a destination path.
153
+
154
+ Parameters
155
+ ----------
156
+ file_obj : Any
157
+ The object returned by Gradio's file component.
158
+ dest_path : Path
159
+ Destination path where the file should be written.
160
+ """
161
+ # Case 1: file-like object (has .read attribute)
162
+ if hasattr(file_obj, "read"):
163
+ dest_path.write_bytes(file_obj.read())
164
+ return
165
+ # Case 2: file object implements .getvalue (e.g. io.BytesIO)
166
+ if hasattr(file_obj, "getvalue"):
167
+ dest_path.write_bytes(file_obj.getvalue())
168
+ return
169
+ # Case 3: NamedString or similar with a .name attribute (points to a temp file)
170
+ filename = None
171
+ if hasattr(file_obj, "name") and isinstance(getattr(file_obj, "name"), (str, bytes)):
172
+ filename = file_obj.name
173
+ elif hasattr(file_obj, "path") and isinstance(getattr(file_obj, "path"), (str, bytes)):
174
+ filename = file_obj.path
175
+ # Case 4: the input itself is a string/Path representing a path on disk
176
+ if filename is None and isinstance(file_obj, (str, os.PathLike)):
177
+ filename = str(file_obj)
178
+ if filename is not None:
179
+ # Copy the file from its existing location
180
+ shutil.copy(filename, dest_path)
181
+ else:
182
+ # Last resort: try to convert to bytes directly
183
+ try:
184
+ dest_path.write_bytes(bytes(file_obj))
185
+ except Exception:
186
+ raise gr.Error(f"Unsupported input type: {type(file_obj)}")
187
+
188
+ # Save uploaded files to a temporary directory
189
  with tempfile.TemporaryDirectory() as tmpdir:
190
  avatar_path = Path(tmpdir) / "avatar"
191
  audio_path = Path(tmpdir) / "audio"
192
+ _copy_input_to_path(avatar_file, avatar_path)
193
+ _copy_input_to_path(audio_file, audio_path)
 
194
  # Validate audio length
195
  try:
196
  validate_audio_length(str(audio_path))