Update shortener logic and add documentation
Browse files- app.py +109 -104
- modules/storage.md +154 -0
- modules/storage.py +36 -22
- modules/version_info.py +11 -0
app.py
CHANGED
@@ -320,120 +320,125 @@ def get_open_graph_meta_tags(query_params):
|
|
320 |
<meta name="twitter:image" content="{og_image}">
|
321 |
'''
|
322 |
return meta_tags
|
|
|
|
|
|
|
|
|
323 |
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
gr.set_static_paths(paths=["images/", "models/", "assets/"])
|
329 |
-
with gr.Blocks(css_paths="style_20250503.css", title="3D viewer", theme='Surn/beeuty',delete_cache=(21600,86400), fill_width=True, head=initial_og_tags) as viewer3d:
|
330 |
-
gr.Markdown("# 3D Model Viewer")
|
331 |
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
|
340 |
-
)
|
341 |
-
image_slider = gr.ImageSlider(
|
342 |
-
label="2D Images",
|
343 |
-
value=None,
|
344 |
-
height="100%",
|
345 |
-
elem_id="image_slider", key="image_slider",
|
346 |
-
type="filepath"
|
347 |
-
)
|
348 |
-
|
349 |
-
with gr.Row():
|
350 |
-
gr.Markdown("## Upload your own files")
|
351 |
-
gr.Markdown("### Supported formats: " + ", ".join([f"`{ext}`" for ext in constants.upload_file_types]))
|
352 |
-
with gr.Row():
|
353 |
-
upload_btn = gr.UploadButton(
|
354 |
-
"Upload 3D Files", elem_id="upload_btn", key="upload_btn",
|
355 |
-
file_count="multiple",
|
356 |
-
file_types=constants.upload_file_types
|
357 |
)
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
key="folder_name",
|
365 |
-
placeholder="Enter folder name...",
|
366 |
-
elem_classes="solid centered"
|
367 |
)
|
368 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
369 |
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
|
379 |
-
|
380 |
-
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
|
389 |
-
|
390 |
-
|
391 |
-
|
392 |
|
393 |
-
|
394 |
-
|
395 |
-
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
|
402 |
-
|
403 |
-
|
404 |
-
|
405 |
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
|
421 |
-
|
422 |
-
|
423 |
-
|
424 |
-
|
425 |
-
|
426 |
-
|
427 |
-
|
428 |
-
|
429 |
-
|
430 |
-
|
431 |
-
|
432 |
-
|
433 |
-
|
|
|
|
|
|
|
|
|
434 |
|
435 |
if __name__ == "__main__":
|
436 |
-
|
|
|
437 |
allowed_paths=["assets", "assets/", "./assets", "images/", "./images", 'e:/TMP', 'models/', '3d_model_viewer/'],
|
438 |
favicon_path="./assets/favicon.ico", show_api=True, strict_cors=False
|
439 |
)
|
|
|
320 |
<meta name="twitter:image" content="{og_image}">
|
321 |
'''
|
322 |
return meta_tags
|
323 |
+
def build_gradio_interface() -> gr.Blocks:
|
324 |
+
placeholder_initial_query_params = {}
|
325 |
+
processed_placeholder_params = _resolve_short_id_to_query_params(placeholder_initial_query_params)
|
326 |
+
initial_og_tags = get_open_graph_meta_tags(processed_placeholder_params)
|
327 |
|
328 |
+
gr.set_static_paths(paths=["images/", "models/", "assets/"])
|
329 |
+
with gr.Blocks(css_paths="style_20250503.css", title="3D viewer", theme='Surn/beeuty',delete_cache=(21600,86400), fill_width=True, head=initial_og_tags) as viewer3d:
|
330 |
+
gr.Markdown("# 3D Model Viewer")
|
|
|
|
|
|
|
|
|
331 |
|
332 |
+
with gr.Row():
|
333 |
+
with gr.Column():
|
334 |
+
model_3d = gr.Model3D(
|
335 |
+
label="3D Model",
|
336 |
+
value=None,
|
337 |
+
elem_id="model_3d", key="model_3d", clear_color=[1.0, 1.0, 1.0, 0.1],
|
338 |
+
elem_classes="centered solid imgcontainer", interactive=True
|
339 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
340 |
)
|
341 |
+
image_slider = gr.ImageSlider(
|
342 |
+
label="2D Images",
|
343 |
+
value=None,
|
344 |
+
height="100%",
|
345 |
+
elem_id="image_slider", key="image_slider",
|
346 |
+
type="filepath"
|
|
|
|
|
|
|
347 |
)
|
348 |
+
|
349 |
+
with gr.Row():
|
350 |
+
gr.Markdown("## Upload your own files")
|
351 |
+
gr.Markdown("### Supported formats: " + ", ".join([f"`{ext}`" for ext in constants.upload_file_types]))
|
352 |
+
with gr.Row():
|
353 |
+
upload_btn = gr.UploadButton(
|
354 |
+
"Upload 3D Files", elem_id="upload_btn", key="upload_btn",
|
355 |
+
file_count="multiple",
|
356 |
+
file_types=constants.upload_file_types
|
357 |
+
)
|
358 |
+
|
359 |
+
with gr.Row():
|
360 |
+
folder_name_box = gr.Textbox(
|
361 |
+
label="Upload Folder Name",
|
362 |
+
value=default_folder,
|
363 |
+
elem_id="folder_name",
|
364 |
+
key="folder_name",
|
365 |
+
placeholder="Enter folder name...",
|
366 |
+
elem_classes="solid centered"
|
367 |
+
)
|
368 |
+
permalink_button = gr.Button("Generate Permalink", elem_id="permalink_button", key="permalink_button", elem_classes="solid small centered")
|
369 |
|
370 |
+
with gr.Row(visible=False, elem_id="permalink_row") as permalink_row:
|
371 |
+
permalink = gr.Textbox(
|
372 |
+
show_copy_button=True,
|
373 |
+
label="Permalink",
|
374 |
+
elem_id="permalink",
|
375 |
+
key="permalink",
|
376 |
+
elem_classes="solid centered",
|
377 |
+
max_lines=5,
|
378 |
+
lines=4
|
379 |
+
)
|
380 |
+
gr.Markdown("### Copy the permalink to share your model and images.", elem_classes="solid centered",)
|
381 |
+
permalink_short = gr.Textbox(
|
382 |
+
show_copy_button=True,
|
383 |
+
label="Shortened Permalink",
|
384 |
+
elem_id="short_permalink",
|
385 |
+
key="permalink",
|
386 |
+
elem_classes="solid centered",
|
387 |
+
max_lines=5,
|
388 |
+
lines=3
|
389 |
+
)
|
390 |
+
with gr.Row():
|
391 |
+
gr.HTML(value=getVersions(), visible=True, elem_id="versions")
|
392 |
|
393 |
+
viewer3d.load(
|
394 |
+
load_data,
|
395 |
+
inputs=[model_3d, image_slider],
|
396 |
+
outputs=[model_3d, image_slider, permalink, permalink_short],
|
397 |
+
scroll_to_output=True
|
398 |
+
).then(
|
399 |
+
lambda link: (gr.update(visible=True), gr.update(interactive=False))
|
400 |
+
if link and len(link) > 0
|
401 |
+
else (gr.update(visible=False), gr.update(interactive=True)),
|
402 |
+
inputs=[permalink],
|
403 |
+
outputs=[permalink_row, permalink_button]
|
404 |
+
)
|
405 |
|
406 |
+
upload_btn.upload(
|
407 |
+
process_upload,
|
408 |
+
inputs=[upload_btn, model_3d, image_slider],
|
409 |
+
outputs=[model_3d, image_slider],
|
410 |
+
scroll_to_output=True,
|
411 |
+
api_name="process_upload",
|
412 |
+
show_progress=True
|
413 |
+
).then(
|
414 |
+
lambda m, i: gr.update(interactive=True),
|
415 |
+
inputs=[model_3d, image_slider],
|
416 |
+
outputs=[permalink_button]
|
417 |
+
)
|
418 |
+
permalink_button.click(
|
419 |
+
lambda model, images, folder: (
|
420 |
+
lambda res: (res.get("permalink", ""), res.get("short_permalink", ""))
|
421 |
+
)(storage.upload_files_to_repo(
|
422 |
+
files=[model] + list(images if images else []),
|
423 |
+
repo_id=constants.HF_REPO_ID,
|
424 |
+
folder_name=folder,
|
425 |
+
create_permalink=True,
|
426 |
+
repo_type="dataset"
|
427 |
+
)),
|
428 |
+
inputs=[model_3d, image_slider, folder_name_box],
|
429 |
+
outputs=[permalink, permalink_short],
|
430 |
+
scroll_to_output=True
|
431 |
+
).then(
|
432 |
+
lambda link: gr.update(visible=True) if link and len(link) > 0 else gr.update(visible=False),
|
433 |
+
inputs=[permalink],
|
434 |
+
outputs=[permalink_row]
|
435 |
+
)
|
436 |
+
|
437 |
+
return viewer3d
|
438 |
|
439 |
if __name__ == "__main__":
|
440 |
+
v3d = build_gradio_interface()
|
441 |
+
v3d.launch(
|
442 |
allowed_paths=["assets", "assets/", "./assets", "images/", "./images", 'e:/TMP', 'models/', '3d_model_viewer/'],
|
443 |
favicon_path="./assets/favicon.ico", show_api=True, strict_cors=False
|
444 |
)
|
modules/storage.md
ADDED
@@ -0,0 +1,154 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Storage Module (`modules/storage.py`) Usage Guide
|
2 |
+
|
3 |
+
The `storage.py` module provides helper functions for:
|
4 |
+
- Generating permalinks for 3D viewer projects.
|
5 |
+
- Uploading files in batches to a Hugging Face repository.
|
6 |
+
- Managing URL shortening by storing (short URL, full URL) pairs in a JSON file on the repository.
|
7 |
+
|
8 |
+
## Key Functions
|
9 |
+
|
10 |
+
### 1. `generate_permalink(valid_files, base_url_external, permalink_viewer_url="surn-3d-viewer.hf.space")`
|
11 |
+
- **Purpose:**
|
12 |
+
Given a list of file paths, it looks for exactly one model file (with an extension defined in `model_extensions`) and exactly two image files (extensions defined in `image_extensions`). If the criteria are met, it returns a permalink URL built from the base URL and query parameters.
|
13 |
+
- **Usage Example:**from modules.storage import generate_permalink
|
14 |
+
|
15 |
+
valid_files = [
|
16 |
+
"models/3d_model.glb",
|
17 |
+
"images/model_texture.png",
|
18 |
+
"images/model_depth.png"
|
19 |
+
]
|
20 |
+
base_url_external = "https://huggingface.co/datasets/Surn/Storage/resolve/main/saved_models/my_model"
|
21 |
+
permalink = generate_permalink(valid_files, base_url_external)
|
22 |
+
if permalink:
|
23 |
+
print("Permalink:", permalink)
|
24 |
+
### 2. `generate_permalink_from_urls(model_url, hm_url, img_url, permalink_viewer_url="surn-3d-viewer.hf.space")`
|
25 |
+
- **Purpose:**
|
26 |
+
Constructs a permalink URL by combining individual URLs for a 3D model (`model_url`), height map (`hm_url`), and image (`img_url`) into a single URL with corresponding query parameters.
|
27 |
+
- **Usage Example:**from modules.storage import generate_permalink_from_urls
|
28 |
+
|
29 |
+
model_url = "https://example.com/model.glb"
|
30 |
+
hm_url = "https://example.com/heightmap.png"
|
31 |
+
img_url = "https://example.com/source.png"
|
32 |
+
|
33 |
+
permalink = generate_permalink_from_urls(model_url, hm_url, img_url)
|
34 |
+
print("Generated Permalink:", permalink)
|
35 |
+
### 3. `upload_files_to_repo(files, repo_id, folder_name, create_permalink=False, repo_type="dataset", permalink_viewer_url="surn-3d-viewer.hf.space")`
|
36 |
+
- **Purpose:**
|
37 |
+
Uploads a batch of files (each file represented as a path string) to a specified Hugging Face repository (e.g. `"Surn/Storage"`) under a given folder.
|
38 |
+
The function's return type is `Union[Dict[str, Any], List[Tuple[Any, str]]]`.
|
39 |
+
- When `create_permalink` is `True` and exactly three valid files (one model and two images) are provided, the function returns a dictionary:```
|
40 |
+
{
|
41 |
+
"response": <upload_folder_response>,
|
42 |
+
"permalink": "<full_permalink_url>",
|
43 |
+
"short_permalink": "<shortened_permalink_url_with_sid>"
|
44 |
+
}
|
45 |
+
``` - Otherwise (or if `create_permalink` is `False` or conditions for permalink creation are not met), it returns a list of tuples, where each tuple is `(upload_folder_response, individual_file_link)`.
|
46 |
+
- If no valid files are provided, it returns an empty list `[]` (this case should ideally also return the dictionary with empty/None values for consistency, but currently returns `[]` as per the code).
|
47 |
+
- **Usage Example:**
|
48 |
+
|
49 |
+
**a. Uploading with permalink creation:**from modules.storage import upload_files_to_repo
|
50 |
+
|
51 |
+
files_for_permalink = [
|
52 |
+
"local/path/to/model.glb",
|
53 |
+
"local/path/to/heightmap.png",
|
54 |
+
"local/path/to/image.png"
|
55 |
+
]
|
56 |
+
repo_id = "Surn/Storage" # Make sure this is defined, e.g., from constants
|
57 |
+
folder_name = "my_new_model_with_permalink"
|
58 |
+
|
59 |
+
upload_result = upload_files_to_repo(
|
60 |
+
files_for_permalink,
|
61 |
+
repo_id,
|
62 |
+
folder_name,
|
63 |
+
create_permalink=True
|
64 |
+
)
|
65 |
+
|
66 |
+
if isinstance(upload_result, dict):
|
67 |
+
print("Upload Response:", upload_result.get("response"))
|
68 |
+
print("Full Permalink:", upload_result.get("permalink"))
|
69 |
+
print("Short Permalink:", upload_result.get("short_permalink"))
|
70 |
+
elif upload_result: # Check if list is not empty
|
71 |
+
print("Upload Response for individual files:")
|
72 |
+
for res, link in upload_result:
|
73 |
+
print(f" Response: {res}, Link: {link}")
|
74 |
+
else:
|
75 |
+
print("No files uploaded or error occurred.")
|
76 |
+
**b. Uploading without permalink creation (or if conditions for permalink are not met):**from modules.storage import upload_files_to_repo
|
77 |
+
|
78 |
+
files_individual = [
|
79 |
+
"local/path/to/another_model.obj",
|
80 |
+
"local/path/to/texture.jpg"
|
81 |
+
]
|
82 |
+
repo_id = "Surn/Storage"
|
83 |
+
folder_name = "my_other_uploads"
|
84 |
+
|
85 |
+
upload_results_list = upload_files_to_repo(
|
86 |
+
files_individual,
|
87 |
+
repo_id,
|
88 |
+
folder_name,
|
89 |
+
create_permalink=False # Or if create_permalink=True but not 1 model & 2 images
|
90 |
+
)
|
91 |
+
|
92 |
+
if upload_results_list: # Will be a list of tuples
|
93 |
+
print("Upload results for individual files:")
|
94 |
+
for res, link in upload_results_list:
|
95 |
+
print(f" Upload Response: {res}, File Link: {link}")
|
96 |
+
else:
|
97 |
+
print("No files uploaded or error occurred.")
|
98 |
+
### 4. URL Shortening Functions: `gen_full_url(...)` and Helpers
|
99 |
+
The module also enables URL shortening by managing a JSON file (e.g. `shortener.json`) in a Hugging Face repository. It supports CRUD-like operations:
|
100 |
+
- **Read:** Look up the full URL using a provided short URL ID.
|
101 |
+
- **Create:** Generate a new short URL ID for a full URL if no existing mapping exists.
|
102 |
+
- **Update/Conflict Handling:**
|
103 |
+
If both short URL ID and full URL are provided, it checks consistency and either confirms or reports a conflict.
|
104 |
+
|
105 |
+
#### `gen_full_url(short_url=None, full_url=None, repo_id=None, repo_type="dataset", permalink_viewer_url="surn-3d-viewer.hf.space", json_file="shortener.json")`
|
106 |
+
- **Purpose:**
|
107 |
+
Based on which parameter is provided, it retrieves or creates a mapping between a short URL ID and a full URL.
|
108 |
+
- If only `short_url` (the ID) is given, it returns the corresponding `full_url`.
|
109 |
+
- If only `full_url` is given, it looks up an existing `short_url` ID or generates and stores a new one.
|
110 |
+
- If both are given, it validates and returns the mapping or an error status.
|
111 |
+
- **Returns:** A tuple `(status_message, result_url)`, where `status_message` indicates the outcome (e.g., `"success_retrieved_full"`, `"created_short"`) and `result_url` is the relevant URL (full or short ID).
|
112 |
+
- **Usage Examples:**
|
113 |
+
|
114 |
+
**a. Convert a full URL into a short URL ID:**from modules.storage import gen_full_url
|
115 |
+
from modules.constants import HF_REPO_ID, SHORTENER_JSON_FILE # Assuming these are defined
|
116 |
+
|
117 |
+
full_permalink = "https://surn-3d-viewer.hf.space/?3d=https%3A%2F%2Fexample.com%2Fmodel.glb&hm=https%3A%2F%2Fexample.com%2Fheightmap.png&image=https%3A%2F%2Fexample.com%2Fsource.png"
|
118 |
+
|
119 |
+
status, short_id = gen_full_url(
|
120 |
+
full_url=full_permalink,
|
121 |
+
repo_id=HF_REPO_ID,
|
122 |
+
json_file=SHORTENER_JSON_FILE
|
123 |
+
)
|
124 |
+
print("Status:", status)
|
125 |
+
if status == "created_short" or status == "success_retrieved_short":
|
126 |
+
print("Shortened URL ID:", short_id)
|
127 |
+
# Construct the full short URL for sharing:
|
128 |
+
# permalink_viewer_url = "surn-3d-viewer.hf.space" # Or from constants
|
129 |
+
# shareable_short_url = f"https://{permalink_viewer_url}/?sid={short_id}"
|
130 |
+
# print("Shareable Short URL:", shareable_short_url)
|
131 |
+
**b. Retrieve the full URL from a short URL ID:**from modules.storage import gen_full_url
|
132 |
+
from modules.constants import HF_REPO_ID, SHORTENER_JSON_FILE # Assuming these are defined
|
133 |
+
|
134 |
+
short_id_to_lookup = "aBcDeFg1" # Example short URL ID
|
135 |
+
|
136 |
+
status, retrieved_full_url = gen_full_url(
|
137 |
+
short_url=short_id_to_lookup,
|
138 |
+
repo_id=HF_REPO_ID,
|
139 |
+
json_file=SHORTENER_JSON_FILE
|
140 |
+
)
|
141 |
+
print("Status:", status)
|
142 |
+
if status == "success_retrieved_full":
|
143 |
+
print("Retrieved Full URL:", retrieved_full_url)
|
144 |
+
## Notes
|
145 |
+
- **Authentication:** All functions that interact with Hugging Face Hub use the HF API token defined as `HF_API_TOKEN` in `modules/constants.py`. Ensure this environment variable is correctly set.
|
146 |
+
- **Constants:** Functions like `gen_full_url` and `upload_files_to_repo` (when creating short links) rely on `HF_REPO_ID` and `SHORTENER_JSON_FILE` from `modules/constants.py` for the URL shortening feature.
|
147 |
+
- **File Types:** Only files with extensions included in `upload_file_types` (a combination of `model_extensions` and `image_extensions` from `modules/constants.py`) are processed by `upload_files_to_repo`.
|
148 |
+
- **Repository Configuration:** When using URL shortening and file uploads, ensure that the specified Hugging Face repository (e.g., defined by `HF_REPO_ID`) exists and that you have write permissions.
|
149 |
+
- **Temporary Directory:** `upload_files_to_repo` temporarily copies files to a local directory (configured by `TMPDIR` in `modules/constants.py`) before uploading.
|
150 |
+
- **Error Handling:** Functions include basic error handling (e.g., catching `RepositoryNotFoundError`, `EntryNotFoundError`, JSON decoding errors, or upload issues) and print messages to the console for debugging. Review function return values to handle these cases appropriately in your application.
|
151 |
+
|
152 |
+
---
|
153 |
+
|
154 |
+
This guide provides the essential usage examples for interacting with the storage and URL-shortening functionality. You can integrate these examples into your application or use them as a reference when extending functionality.
|
modules/storage.py
CHANGED
@@ -7,7 +7,10 @@ import json
|
|
7 |
import base64
|
8 |
from huggingface_hub import login, upload_folder, hf_hub_download, HfApi
|
9 |
from huggingface_hub.utils import RepositoryNotFoundError, EntryNotFoundError
|
10 |
-
from modules.constants import HF_API_TOKEN, upload_file_types, model_extensions, image_extensions
|
|
|
|
|
|
|
11 |
|
12 |
def generate_permalink(valid_files, base_url_external, permalink_viewer_url="surn-3d-viewer.hf.space"):
|
13 |
"""
|
@@ -52,7 +55,14 @@ def generate_permalink_from_urls(model_url, hm_url, img_url, permalink_viewer_ur
|
|
52 |
query_str = urllib.parse.urlencode(params)
|
53 |
return f"https://{permalink_viewer_url}/?{query_str}"
|
54 |
|
55 |
-
def upload_files_to_repo(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
56 |
"""
|
57 |
Uploads multiple files to a Hugging Face repository using a batch upload approach via upload_folder.
|
58 |
|
@@ -64,18 +74,24 @@ def upload_files_to_repo(files, repo_id, folder_name, create_permalink=False, re
|
|
64 |
returns a single permalink to the project with query parameters.
|
65 |
Otherwise, returns individual permalinks for each file.
|
66 |
repo_type (str): Repository type ("space", "dataset", etc.). Default is "dataset".
|
|
|
67 |
|
68 |
Returns:
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
|
|
|
|
|
|
|
|
74 |
"""
|
75 |
# Log in using the HF API token.
|
76 |
login(token=HF_API_TOKEN)
|
77 |
|
78 |
valid_files = []
|
|
|
79 |
|
80 |
# Ensure folder_name does not have a trailing slash.
|
81 |
folder_name = folder_name.rstrip("/")
|
@@ -110,8 +126,6 @@ def upload_files_to_repo(files, repo_id, folder_name, create_permalink=False, re
|
|
110 |
)
|
111 |
|
112 |
# Construct external URLs for each uploaded file.
|
113 |
-
# For datasets, files are served at:
|
114 |
-
# https://huggingface.co/datasets/<repo_id>/resolve/main/<folder_name>/<filename>
|
115 |
base_url_external = f"https://huggingface.co/datasets/{repo_id}/resolve/main/{folder_name}"
|
116 |
individual_links = []
|
117 |
for file_path in valid_files:
|
@@ -124,8 +138,19 @@ def upload_files_to_repo(files, repo_id, folder_name, create_permalink=False, re
|
|
124 |
if create_permalink and len(valid_files) == 3:
|
125 |
permalink = generate_permalink(valid_files, base_url_external, permalink_viewer_url)
|
126 |
if permalink:
|
127 |
-
|
128 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
129 |
# Otherwise, return individual tuples for each file.
|
130 |
return [(response, link) for link in individual_links]
|
131 |
|
@@ -249,20 +274,9 @@ def gen_full_url(short_url=None, full_url=None, repo_id=None, repo_type="dataset
|
|
249 |
new_short_id = _generate_short_id()
|
250 |
# Construct the short URL using the permalink_viewer_url and the new_short_id as a query parameter or path
|
251 |
# For this example, let's assume short URLs are like: https://permalink_viewer_url/?id=new_short_id
|
252 |
-
# This part might need adjustment based on how you want to structure your short URLs.
|
253 |
-
# A common pattern is permalink_viewer_url/new_short_id if the viewer can handle path-based routing.
|
254 |
-
# Or, if the viewer expects a query param like `?short=new_short_id`
|
255 |
-
# For now, let's assume the short_url itself is just the ID, and the full viewer URL is prepended elsewhere if needed.
|
256 |
-
# Or, more directly, the `short_url` parameter to this function *is* the ID.
|
257 |
-
# The request implies `short_url` is the *key* in the JSON.
|
258 |
-
|
259 |
-
# Let's refine: the `short_url` stored in JSON is the ID. The "shortened URL" returned to user might be different.
|
260 |
-
# The function is `gen_full_url`, implying it can also *generate* a short URL if one doesn't exist for a full_url.
|
261 |
|
262 |
url_data = _add_url_to_json(url_data, new_short_id, full_url)
|
263 |
if _upload_json_to_repo(url_data, repo_id, json_file, repo_type):
|
264 |
-
# The value returned as "shortened_url" should be the ID itself, or a URL constructed with it.
|
265 |
-
# Let's return the ID for now, as the prompt asks for "shortened_url" as output.
|
266 |
return "created_short", new_short_id
|
267 |
else:
|
268 |
return "error_upload", None
|
|
|
7 |
import base64
|
8 |
from huggingface_hub import login, upload_folder, hf_hub_download, HfApi
|
9 |
from huggingface_hub.utils import RepositoryNotFoundError, EntryNotFoundError
|
10 |
+
from modules.constants import HF_API_TOKEN, upload_file_types, model_extensions, image_extensions, HF_REPO_ID, SHORTENER_JSON_FILE
|
11 |
+
from typing import Any, Dict, List, Tuple, Union
|
12 |
+
|
13 |
+
# see storage.md for detailed information about the storage module and its functions.
|
14 |
|
15 |
def generate_permalink(valid_files, base_url_external, permalink_viewer_url="surn-3d-viewer.hf.space"):
|
16 |
"""
|
|
|
55 |
query_str = urllib.parse.urlencode(params)
|
56 |
return f"https://{permalink_viewer_url}/?{query_str}"
|
57 |
|
58 |
+
def upload_files_to_repo(
|
59 |
+
files: List[Any],
|
60 |
+
repo_id: str,
|
61 |
+
folder_name: str,
|
62 |
+
create_permalink: bool = False,
|
63 |
+
repo_type: str = "dataset",
|
64 |
+
permalink_viewer_url: str = "surn-3d-viewer.hf.space"
|
65 |
+
) -> Union[Dict[str, Any], List[Tuple[Any, str]]]:
|
66 |
"""
|
67 |
Uploads multiple files to a Hugging Face repository using a batch upload approach via upload_folder.
|
68 |
|
|
|
74 |
returns a single permalink to the project with query parameters.
|
75 |
Otherwise, returns individual permalinks for each file.
|
76 |
repo_type (str): Repository type ("space", "dataset", etc.). Default is "dataset".
|
77 |
+
permalink_viewer_url (str): The base viewer URL.
|
78 |
|
79 |
Returns:
|
80 |
+
Union[Dict[str, Any], List[Tuple[Any, str]]]:
|
81 |
+
If create_permalink is True and files match the criteria:
|
82 |
+
dict: {
|
83 |
+
"response": <upload response>,
|
84 |
+
"permalink": <full_permalink URL>,
|
85 |
+
"short_permalink": <shortened permalink URL>
|
86 |
+
}
|
87 |
+
Otherwise:
|
88 |
+
list: A list of tuples (response, permalink) for each file.
|
89 |
"""
|
90 |
# Log in using the HF API token.
|
91 |
login(token=HF_API_TOKEN)
|
92 |
|
93 |
valid_files = []
|
94 |
+
permalink_short = None
|
95 |
|
96 |
# Ensure folder_name does not have a trailing slash.
|
97 |
folder_name = folder_name.rstrip("/")
|
|
|
126 |
)
|
127 |
|
128 |
# Construct external URLs for each uploaded file.
|
|
|
|
|
129 |
base_url_external = f"https://huggingface.co/datasets/{repo_id}/resolve/main/{folder_name}"
|
130 |
individual_links = []
|
131 |
for file_path in valid_files:
|
|
|
138 |
if create_permalink and len(valid_files) == 3:
|
139 |
permalink = generate_permalink(valid_files, base_url_external, permalink_viewer_url)
|
140 |
if permalink:
|
141 |
+
result, short_id = gen_full_url(
|
142 |
+
full_url=permalink,
|
143 |
+
repo_id=HF_REPO_ID,
|
144 |
+
json_file=SHORTENER_JSON_FILE
|
145 |
+
)
|
146 |
+
permalink_short = f"https://{permalink_viewer_url}/?sid={short_id}"
|
147 |
+
print(f"Creating shortened URL: {result} - {short_id}")
|
148 |
+
return {
|
149 |
+
"response": response,
|
150 |
+
"permalink": permalink,
|
151 |
+
"short_permalink": permalink_short
|
152 |
+
}
|
153 |
+
|
154 |
# Otherwise, return individual tuples for each file.
|
155 |
return [(response, link) for link in individual_links]
|
156 |
|
|
|
274 |
new_short_id = _generate_short_id()
|
275 |
# Construct the short URL using the permalink_viewer_url and the new_short_id as a query parameter or path
|
276 |
# For this example, let's assume short URLs are like: https://permalink_viewer_url/?id=new_short_id
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
277 |
|
278 |
url_data = _add_url_to_json(url_data, new_short_id, full_url)
|
279 |
if _upload_json_to_repo(url_data, repo_id, json_file, repo_type):
|
|
|
|
|
280 |
return "created_short", new_short_id
|
281 |
else:
|
282 |
return "error_upload", None
|
modules/version_info.py
CHANGED
@@ -102,6 +102,15 @@ def versions_html():
|
|
102 |
</a>
|
103 |
'''
|
104 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
105 |
v_html = f"""
|
106 |
version: <a href="https://huggingface.co/spaces/Surn/3D-Viewer/commit/{"huggingface" if commit == "<none>" else commit}" target="_blank">{"huggingface" if commit == "<none>" else commit}</a>
|
107 |
 • 
|
@@ -114,6 +123,8 @@ def versions_html():
|
|
114 |
gradio: {gr.__version__}
|
115 |
 • 
|
116 |
{toggle_dark_link}
|
|
|
|
|
117 |
<br>
|
118 |
Full GPU Info:
|
119 |
"""
|
|
|
102 |
</a>
|
103 |
'''
|
104 |
|
105 |
+
# Add a link to the shortener JSON file in the Hugging Face repo
|
106 |
+
from modules.constants import HF_REPO_ID, SHORTENER_JSON_FILE # Import constants
|
107 |
+
shortener_url = f"https://huggingface.co/datasets/{HF_REPO_ID}/resolve/main/{SHORTENER_JSON_FILE}"
|
108 |
+
shortener_link = f'''
|
109 |
+
<a href="{shortener_url}" target="_blank" style="cursor: pointer; text-decoration: underline;">
|
110 |
+
View Shortener JSON
|
111 |
+
</a>
|
112 |
+
'''
|
113 |
+
|
114 |
v_html = f"""
|
115 |
version: <a href="https://huggingface.co/spaces/Surn/3D-Viewer/commit/{"huggingface" if commit == "<none>" else commit}" target="_blank">{"huggingface" if commit == "<none>" else commit}</a>
|
116 |
 • 
|
|
|
123 |
gradio: {gr.__version__}
|
124 |
 • 
|
125 |
{toggle_dark_link}
|
126 |
+
 • 
|
127 |
+
{shortener_link}
|
128 |
<br>
|
129 |
Full GPU Info:
|
130 |
"""
|