Spaces:
Runtime error
Runtime error
Upgrade for Audio and Visual Files
Browse files- .gitignore +2 -1
- README.md +1 -1
- requirements.txt +1 -0
- src/gradio_user_history/__init__.py +2 -2
- src/gradio_user_history/_user_history.py +85 -2
.gitignore
CHANGED
@@ -141,4 +141,5 @@ cspell.json
|
|
141 |
|
142 |
mock
|
143 |
_user_history
|
144 |
-
_user_history_exports
|
|
|
|
141 |
|
142 |
mock
|
143 |
_user_history
|
144 |
+
_user_history_exports
|
145 |
+
/.vs
|
README.md
CHANGED
@@ -21,7 +21,7 @@ space_ci:
|
|
21 |
## Key features:
|
22 |
|
23 |
- 🤗 **Sign in with Hugging Face**
|
24 |
-
- **Save** generated
|
25 |
- **Export** your history as zip.
|
26 |
- **Delete** your history to respect privacy.
|
27 |
- Compatible with **Persistent Storage** for long-term storage.
|
|
|
21 |
## Key features:
|
22 |
|
23 |
- 🤗 **Sign in with Hugging Face**
|
24 |
+
- **Save** generated image, video, audio and document files with their metadata: prompts, timestamp, hyper-parameters, etc.
|
25 |
- **Export** your history as zip.
|
26 |
- **Delete** your history to respect privacy.
|
27 |
- Compatible with **Persistent Storage** for long-term storage.
|
requirements.txt
CHANGED
@@ -1,2 +1,3 @@
|
|
1 |
git+https://huggingface.co/spaces/Wauplin/gradio-user-history
|
2 |
gradio-space-ci @ git+https://huggingface.co/spaces/Wauplin/[email protected]
|
|
|
|
1 |
git+https://huggingface.co/spaces/Wauplin/gradio-user-history
|
2 |
gradio-space-ci @ git+https://huggingface.co/spaces/Wauplin/[email protected]
|
3 |
+
filetype @ git+https://github.com/h2non/filetype.py.git
|
src/gradio_user_history/__init__.py
CHANGED
@@ -3,7 +3,7 @@ User History is a plugin that you can add to your Spaces to cache generated imag
|
|
3 |
|
4 |
Key features:
|
5 |
- 🤗 Sign in with Hugging Face
|
6 |
-
- Save generated
|
7 |
- Export your history as zip.
|
8 |
- Delete your history to respect privacy.
|
9 |
- Compatible with Persistent Storage for long-term storage.
|
@@ -18,4 +18,4 @@ Useful links:
|
|
18 |
from ._user_history import render, save_image, setup # noqa: F401
|
19 |
|
20 |
|
21 |
-
__version__ = "0.
|
|
|
3 |
|
4 |
Key features:
|
5 |
- 🤗 Sign in with Hugging Face
|
6 |
+
- Save generated image, video, audio and document files with their metadata: prompts, timestamp, hyper-parameters, etc.
|
7 |
- Export your history as zip.
|
8 |
- Delete your history to respect privacy.
|
9 |
- Compatible with Persistent Storage for long-term storage.
|
|
|
18 |
from ._user_history import render, save_image, setup # noqa: F401
|
19 |
|
20 |
|
21 |
+
__version__ = "0.2.0"
|
src/gradio_user_history/_user_history.py
CHANGED
@@ -1,3 +1,4 @@
|
|
|
|
1 |
import json
|
2 |
import os
|
3 |
import shutil
|
@@ -13,6 +14,7 @@ import numpy as np
|
|
13 |
import requests
|
14 |
from filelock import FileLock
|
15 |
from PIL.Image import Image
|
|
|
16 |
|
17 |
|
18 |
def setup(folder_path: str | Path | None = None) -> None:
|
@@ -143,6 +145,56 @@ def save_image(
|
|
143 |
with user_history._user_lock(username):
|
144 |
with user_history._user_jsonl_path(username).open("a") as f:
|
145 |
f.write(json.dumps(data) + "\n")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
146 |
|
147 |
|
148 |
#############
|
@@ -178,7 +230,13 @@ class _UserHistory(object):
|
|
178 |
path = self._user_path(username) / "images"
|
179 |
path.mkdir(parents=True, exist_ok=True)
|
180 |
return path
|
181 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
182 |
|
183 |
def _fetch_user_history(profile: gr.OAuthProfile | None) -> List[Tuple[str, str]]:
|
184 |
"""Return saved history for that user, if it exists."""
|
@@ -262,12 +320,37 @@ def _copy_image(image: Image | np.ndarray | str | Path, dst_folder: Path) -> Pat
|
|
262 |
if isinstance(image, np.ndarray):
|
263 |
image = Image.fromarray(image)
|
264 |
if isinstance(image, Image):
|
265 |
-
dst = dst_folder / f"{uuid4().hex}.png"
|
266 |
image.save(dst)
|
267 |
return dst
|
268 |
|
269 |
raise ValueError(f"Unsupported image type: {type(image)}")
|
270 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
271 |
|
272 |
def _resolve_folder_path(folder_path: str | Path | None) -> Path:
|
273 |
if folder_path is not None:
|
|
|
1 |
+
|
2 |
import json
|
3 |
import os
|
4 |
import shutil
|
|
|
14 |
import requests
|
15 |
from filelock import FileLock
|
16 |
from PIL.Image import Image
|
17 |
+
import filetype
|
18 |
|
19 |
|
20 |
def setup(folder_path: str | Path | None = None) -> None:
|
|
|
145 |
with user_history._user_lock(username):
|
146 |
with user_history._user_jsonl_path(username).open("a") as f:
|
147 |
f.write(json.dumps(data) + "\n")
|
148 |
+
|
149 |
+
def save_av(
|
150 |
+
profile: gr.OAuthProfile | None,
|
151 |
+
image: Image | np.ndarray | str | Path | None = None,
|
152 |
+
video: str | Path | None = None,
|
153 |
+
audio: str | Path | None = None,
|
154 |
+
document: str | Path | None = None,
|
155 |
+
label: str | None = None,
|
156 |
+
metadata: Dict | None = None,
|
157 |
+
):
|
158 |
+
# Ignore files from logged out users
|
159 |
+
if profile is None:
|
160 |
+
return
|
161 |
+
username = profile["preferred_username"]
|
162 |
+
|
163 |
+
# Ignore files if user history not used
|
164 |
+
user_history = _UserHistory()
|
165 |
+
if not user_history.initialized:
|
166 |
+
warnings.warn(
|
167 |
+
"User history is not set in Gradio demo. Saving files is ignored. You must use `user_history.render(...)`"
|
168 |
+
" first."
|
169 |
+
)
|
170 |
+
return
|
171 |
+
|
172 |
+
# Copy image to storage
|
173 |
+
image_path = None
|
174 |
+
if image is not None:
|
175 |
+
image_path = _copy_image(image, dst_folder=user_history._user_images_path(username))
|
176 |
+
|
177 |
+
# Copy video to storage
|
178 |
+
if video is not None:
|
179 |
+
video_path = _copy_file(video, dst_folder=user_history._user_file_path(username, "videos"))
|
180 |
+
|
181 |
+
# Copy audio to storage
|
182 |
+
if audio is not None:
|
183 |
+
audio_path = _copy_file(audio, dst_folder=user_history._user_file_path(username, "audios"))
|
184 |
+
|
185 |
+
# Copy document to storage
|
186 |
+
if document is not None:
|
187 |
+
document_path = _copy_file(document, dst_folder=user_history._user_file_path(username, "documents"))
|
188 |
+
|
189 |
+
# Save new files + metadata
|
190 |
+
if metadata is None:
|
191 |
+
metadata = {}
|
192 |
+
if "datetime" not in metadata:
|
193 |
+
metadata["datetime"] = str(datetime.now())
|
194 |
+
data = {"image_path": str(image_path), "video_path": str(video_path), "audio_path": str(audio_path), "document_path": str(document_path), "label": label, "metadata": metadata}
|
195 |
+
with user_history._user_lock(username):
|
196 |
+
with user_history._user_jsonl_path(username).open("a") as f:
|
197 |
+
f.write(json.dumps(data) + "\n")
|
198 |
|
199 |
|
200 |
#############
|
|
|
230 |
path = self._user_path(username) / "images"
|
231 |
path.mkdir(parents=True, exist_ok=True)
|
232 |
return path
|
233 |
+
|
234 |
+
def _user_file_path(self, username: str, filetype: str = "images") -> Path:
|
235 |
+
path = self._user_path(username) / filetype
|
236 |
+
path.mkdir(parents=True, exist_ok=True)
|
237 |
+
return path
|
238 |
+
|
239 |
+
|
240 |
|
241 |
def _fetch_user_history(profile: gr.OAuthProfile | None) -> List[Tuple[str, str]]:
|
242 |
"""Return saved history for that user, if it exists."""
|
|
|
320 |
if isinstance(image, np.ndarray):
|
321 |
image = Image.fromarray(image)
|
322 |
if isinstance(image, Image):
|
323 |
+
dst = dst_folder / f"Path(file).name}_{uuid4().hex}.png"
|
324 |
image.save(dst)
|
325 |
return dst
|
326 |
|
327 |
raise ValueError(f"Unsupported image type: {type(image)}")
|
328 |
|
329 |
+
def _copy_file(file: any | np.ndarray | str | Path, dst_folder: Path) -> Path:
|
330 |
+
"""Copy file to the appropriate folder."""
|
331 |
+
# Already a path => copy it
|
332 |
+
if isinstance(file, str):
|
333 |
+
file = Path(file)
|
334 |
+
if isinstance(file, Path):
|
335 |
+
dst = dst_folder / f"{file.stem}_{uuid4().hex}{file.suffix}" # keep file ext
|
336 |
+
shutil.copyfile(file, dst)
|
337 |
+
return dst
|
338 |
+
|
339 |
+
# Still a Python object => serialize it
|
340 |
+
if isinstance(file, np.ndarray):
|
341 |
+
file = Image.fromarray(file)
|
342 |
+
dst = dst_folder / f"{file.filename}_{uuid4().hex}{file.suffix}"
|
343 |
+
file.save(dst)
|
344 |
+
return dst
|
345 |
+
|
346 |
+
# try other file types
|
347 |
+
kind = filetype.guess(file)
|
348 |
+
if kind is not None:
|
349 |
+
dst = dst_folder / f"{Path(file).stem}_{uuid4().hex}.{kind.extension}"
|
350 |
+
shutil.copyfile(file, dst)
|
351 |
+
return dst
|
352 |
+
raise ValueError(f"Unsupported file type: {type(file)}")
|
353 |
+
|
354 |
|
355 |
def _resolve_folder_path(folder_path: str | Path | None) -> Path:
|
356 |
if folder_path is not None:
|