Recover Lost File, Delete 3d_model folder
Browse files- .gitattributes +1 -1
- app.py +53 -6
- modules/constants.py +7 -1
- modules/storage.py +100 -0
- style_20250503.css +2 -2
.gitattributes
CHANGED
|
@@ -40,4 +40,4 @@ images/beeuty_545jlbh1_v12_alpha96_300dpi_depth.png filter=lfs diff=lfs merge=lf
|
|
| 40 |
*.gltf filter=lfs diff=lfs merge=lfs -text
|
| 41 |
*.ply filter=lfs diff=lfs merge=lfs -text
|
| 42 |
*.stl filter=lfs diff=lfs merge=lfs -text
|
| 43 |
-
*.obj filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 40 |
*.gltf filter=lfs diff=lfs merge=lfs -text
|
| 41 |
*.ply filter=lfs diff=lfs merge=lfs -text
|
| 42 |
*.stl filter=lfs diff=lfs merge=lfs -text
|
| 43 |
+
*.obj filter=lfs diff=lfs merge=lfs -text
|
app.py
CHANGED
|
@@ -1,10 +1,13 @@
|
|
| 1 |
import gradio as gr
|
| 2 |
import os
|
|
|
|
| 3 |
import modules.constants as constants
|
| 4 |
import modules.version_info as version_info
|
|
|
|
| 5 |
|
| 6 |
|
| 7 |
user_dir = constants.TMPDIR
|
|
|
|
| 8 |
|
| 9 |
def getVersions():
|
| 10 |
#return html_versions
|
|
@@ -53,10 +56,10 @@ def process_upload(files, current_model, current_images):
|
|
| 53 |
file_name = f.name if hasattr(f, "name") else f
|
| 54 |
ext = os.path.splitext(file_name)[1].lower()
|
| 55 |
|
| 56 |
-
if ext in
|
| 57 |
if extracted_model is None:
|
| 58 |
extracted_model = file_name
|
| 59 |
-
elif ext in
|
| 60 |
if len(extracted_images) < 2:
|
| 61 |
extracted_images.append(file_name)
|
| 62 |
|
|
@@ -94,7 +97,8 @@ with gr.Blocks(css_paths="style_20250503.css", title="3D viewer", theme='Surn/be
|
|
| 94 |
label="3D Model",
|
| 95 |
value=None,
|
| 96 |
height=480,
|
| 97 |
-
elem_id="model_3d", key="model_3d"
|
|
|
|
| 98 |
|
| 99 |
)
|
| 100 |
image_slider = gr.ImageSlider(
|
|
@@ -105,12 +109,38 @@ with gr.Blocks(css_paths="style_20250503.css", title="3D viewer", theme='Surn/be
|
|
| 105 |
type="filepath"
|
| 106 |
)
|
| 107 |
|
|
|
|
|
|
|
|
|
|
| 108 |
with gr.Row():
|
| 109 |
upload_btn = gr.UploadButton(
|
| 110 |
-
"Upload", elem_id="upload_btn", key="upload_btn",
|
| 111 |
file_count="multiple",
|
| 112 |
-
file_types=
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 113 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 114 |
with gr.Row():
|
| 115 |
gr.HTML(value=getVersions(), visible=True, elem_id="versions")
|
| 116 |
|
|
@@ -136,9 +166,26 @@ with gr.Blocks(css_paths="style_20250503.css", title="3D viewer", theme='Surn/be
|
|
| 136 |
show_progress=True
|
| 137 |
|
| 138 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 139 |
|
| 140 |
if __name__ == "__main__":
|
| 141 |
viewer3d.launch(
|
| 142 |
-
allowed_paths=["assets", "assets/", "./assets", "images/", "./images", 'e:/TMP', 'models/'],
|
| 143 |
favicon_path="./assets/favicon.ico", show_api=True, strict_cors=False
|
| 144 |
)
|
|
|
|
| 1 |
import gradio as gr
|
| 2 |
import os
|
| 3 |
+
import random
|
| 4 |
import modules.constants as constants
|
| 5 |
import modules.version_info as version_info
|
| 6 |
+
import modules.storage as storage
|
| 7 |
|
| 8 |
|
| 9 |
user_dir = constants.TMPDIR
|
| 10 |
+
default_folder = "saved_models/3d_model_" + format(random.randint(1, 999999), "06d")
|
| 11 |
|
| 12 |
def getVersions():
|
| 13 |
#return html_versions
|
|
|
|
| 56 |
file_name = f.name if hasattr(f, "name") else f
|
| 57 |
ext = os.path.splitext(file_name)[1].lower()
|
| 58 |
|
| 59 |
+
if ext in constants.model_extensions:
|
| 60 |
if extracted_model is None:
|
| 61 |
extracted_model = file_name
|
| 62 |
+
elif ext in constants.image_extensions:
|
| 63 |
if len(extracted_images) < 2:
|
| 64 |
extracted_images.append(file_name)
|
| 65 |
|
|
|
|
| 97 |
label="3D Model",
|
| 98 |
value=None,
|
| 99 |
height=480,
|
| 100 |
+
elem_id="model_3d", key="model_3d", clear_color=[1.0, 1.0, 1.0, 0.1],
|
| 101 |
+
elem_classes="centered solid imgcontainer", interactive=True
|
| 102 |
|
| 103 |
)
|
| 104 |
image_slider = gr.ImageSlider(
|
|
|
|
| 109 |
type="filepath"
|
| 110 |
)
|
| 111 |
|
| 112 |
+
with gr.Row():
|
| 113 |
+
gr.Markdown("## Upload your own files")
|
| 114 |
+
gr.Markdown("### Supported formats: " + ", ".join([f"`{ext}`" for ext in constants.upload_file_types]))
|
| 115 |
with gr.Row():
|
| 116 |
upload_btn = gr.UploadButton(
|
| 117 |
+
"Upload 3D Files", elem_id="upload_btn", key="upload_btn",
|
| 118 |
file_count="multiple",
|
| 119 |
+
file_types=constants.upload_file_types
|
| 120 |
+
)
|
| 121 |
+
|
| 122 |
+
with gr.Row():
|
| 123 |
+
# New textbox for folder name.
|
| 124 |
+
folder_name_box = gr.Textbox(
|
| 125 |
+
label="Folder Name",
|
| 126 |
+
value=default_folder,
|
| 127 |
+
elem_id="folder_name",
|
| 128 |
+
key="folder_name",
|
| 129 |
+
placeholder="Enter folder name..."
|
| 130 |
)
|
| 131 |
+
permalink_button = gr.Button("Generate Permalink", elem_id="permalink_button", key="permalink_button", elem_classes="solid small centered")
|
| 132 |
+
|
| 133 |
+
with gr.Row(visible=False, elem_id="permalink_row") as permalink_row:
|
| 134 |
+
permalink = gr.Textbox(
|
| 135 |
+
show_copy_button=True,
|
| 136 |
+
label="Permalink",
|
| 137 |
+
elem_id="permalink",
|
| 138 |
+
key="permalink",
|
| 139 |
+
elem_classes="solid small centered",
|
| 140 |
+
max_lines=5,
|
| 141 |
+
lines=3
|
| 142 |
+
)
|
| 143 |
+
gr.Markdown("### Copy the link above to share your model and images.")
|
| 144 |
with gr.Row():
|
| 145 |
gr.HTML(value=getVersions(), visible=True, elem_id="versions")
|
| 146 |
|
|
|
|
| 166 |
show_progress=True
|
| 167 |
|
| 168 |
)
|
| 169 |
+
# Generate a permalink based on the current model, images, and folder name.
|
| 170 |
+
permalink_button.click(
|
| 171 |
+
lambda model, images, folder: storage.upload_files_to_repo(
|
| 172 |
+
files=[model] + list(images),
|
| 173 |
+
repo_id="Surn/Storage",
|
| 174 |
+
folder_name=folder,
|
| 175 |
+
create_permalink=True,
|
| 176 |
+
repo_type="dataset"
|
| 177 |
+
)[1], # Extract the permalink from the returned tuple if criteria met.
|
| 178 |
+
inputs=[model_3d, image_slider, folder_name_box],
|
| 179 |
+
outputs=[permalink],
|
| 180 |
+
scroll_to_output=True
|
| 181 |
+
).then(
|
| 182 |
+
lambda link: gr.update(visible=True) if link and len(link) > 0 else gr.update(visible=False),
|
| 183 |
+
inputs=[permalink],
|
| 184 |
+
outputs=[permalink_row]
|
| 185 |
+
)
|
| 186 |
|
| 187 |
if __name__ == "__main__":
|
| 188 |
viewer3d.launch(
|
| 189 |
+
allowed_paths=["assets", "assets/", "./assets", "images/", "./images", 'e:/TMP', 'models/', '3d_model_viewer/'],
|
| 190 |
favicon_path="./assets/favicon.ico", show_api=True, strict_cors=False
|
| 191 |
)
|
modules/constants.py
CHANGED
|
@@ -21,4 +21,10 @@ try:
|
|
| 21 |
except:
|
| 22 |
TMPDIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'tmp')
|
| 23 |
|
| 24 |
-
os.makedirs(TMPDIR, exist_ok=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
except:
|
| 22 |
TMPDIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'tmp')
|
| 23 |
|
| 24 |
+
os.makedirs(TMPDIR, exist_ok=True)
|
| 25 |
+
|
| 26 |
+
model_extensions = {".glb", ".gltf", ".obj", ".ply"}
|
| 27 |
+
model_extensions_list = list(model_extensions)
|
| 28 |
+
image_extensions = {".png", ".jpg", ".jpeg", ".webp"}
|
| 29 |
+
image_extensions_list = list(image_extensions)
|
| 30 |
+
upload_file_types = model_extensions_list + image_extensions_list
|
modules/storage.py
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# modules/storage.py
|
| 2 |
+
import os
|
| 3 |
+
import urllib.parse
|
| 4 |
+
import tempfile
|
| 5 |
+
import shutil
|
| 6 |
+
from huggingface_hub import login, upload_folder
|
| 7 |
+
from modules.constants import HF_API_TOKEN, upload_file_types, model_extensions, image_extensions
|
| 8 |
+
|
| 9 |
+
def upload_files_to_repo(files, repo_id, folder_name, create_permalink=False, repo_type="dataset"):
|
| 10 |
+
"""
|
| 11 |
+
Uploads multiple files to a Hugging Face repository using a batch upload approach via upload_folder.
|
| 12 |
+
|
| 13 |
+
Parameters:
|
| 14 |
+
files (list): A list of file paths (str) to upload.
|
| 15 |
+
repo_id (str): The repository ID on Hugging Face for storage, e.g. "Surn/Storage".
|
| 16 |
+
folder_name (str): The subfolder within the repository where files will be saved.
|
| 17 |
+
create_permalink (bool): If True and if exactly three files are uploaded (1 model and 2 images),
|
| 18 |
+
returns a single permalink to the project with query parameters.
|
| 19 |
+
Otherwise, returns individual permalinks for each file.
|
| 20 |
+
repo_type (str): Repository type ("space", "dataset", etc.). Default is "dataset".
|
| 21 |
+
|
| 22 |
+
Returns:
|
| 23 |
+
If create_permalink is True and files match the criteria:
|
| 24 |
+
tuple: (response, permalink) where response is the output of the batch upload
|
| 25 |
+
and permalink is the URL string (with fully qualified file paths) for the project.
|
| 26 |
+
Otherwise:
|
| 27 |
+
list: A list of tuples (response, permalink) for each file.
|
| 28 |
+
"""
|
| 29 |
+
# Log in using the HF API token.
|
| 30 |
+
login(token=HF_API_TOKEN)
|
| 31 |
+
|
| 32 |
+
valid_files = []
|
| 33 |
+
|
| 34 |
+
# Ensure folder_name does not have a trailing slash.
|
| 35 |
+
folder_name = folder_name.rstrip("/")
|
| 36 |
+
|
| 37 |
+
# Filter for valid files based on allowed extensions.
|
| 38 |
+
for f in files:
|
| 39 |
+
file_name = f if isinstance(f, str) else f.name if hasattr(f, "name") else None
|
| 40 |
+
if file_name is None:
|
| 41 |
+
continue
|
| 42 |
+
ext = os.path.splitext(file_name)[1].lower()
|
| 43 |
+
if ext in upload_file_types:
|
| 44 |
+
valid_files.append(f)
|
| 45 |
+
|
| 46 |
+
if not valid_files:
|
| 47 |
+
return [] # or raise an exception
|
| 48 |
+
|
| 49 |
+
# Create a temporary directory and a sub-directory using folder_name; copy valid files into it.
|
| 50 |
+
with tempfile.TemporaryDirectory() as temp_dir:
|
| 51 |
+
target_dir = os.path.join(temp_dir, folder_name)
|
| 52 |
+
os.makedirs(target_dir, exist_ok=True)
|
| 53 |
+
for file_path in valid_files:
|
| 54 |
+
filename = os.path.basename(file_path)
|
| 55 |
+
dest_path = os.path.join(target_dir, filename)
|
| 56 |
+
shutil.copy(file_path, dest_path)
|
| 57 |
+
|
| 58 |
+
# Batch upload all files in the temporary folder.
|
| 59 |
+
# Files will be uploaded under the folder (path_in_repo) given by folder_name.
|
| 60 |
+
response = upload_folder(
|
| 61 |
+
folder_path=temp_dir,
|
| 62 |
+
repo_id=repo_id,
|
| 63 |
+
repo_type=repo_type,
|
| 64 |
+
path_in_repo=folder_name,
|
| 65 |
+
commit_message="Batch upload files"
|
| 66 |
+
)
|
| 67 |
+
|
| 68 |
+
# Construct external URLs for each uploaded file.
|
| 69 |
+
# For datasets, files are served at:
|
| 70 |
+
# https://huggingface.co/datasets/<repo_id>/resolve/main/<folder_name>/<filename>
|
| 71 |
+
base_url_external = f"https://huggingface.co/datasets/{repo_id}/resolve/main/{folder_name}"
|
| 72 |
+
individual_links = []
|
| 73 |
+
for file_path in valid_files:
|
| 74 |
+
filename = os.path.basename(file_path)
|
| 75 |
+
link = f"{base_url_external}/{filename}"
|
| 76 |
+
individual_links.append(link)
|
| 77 |
+
|
| 78 |
+
# If exactly 3 files are provided and create_permalink is True,
|
| 79 |
+
# check if they contain 1 model file and 2 image files.
|
| 80 |
+
if create_permalink and len(valid_files) == 3:
|
| 81 |
+
model_link = None
|
| 82 |
+
images_links = []
|
| 83 |
+
for f in valid_files:
|
| 84 |
+
filename = os.path.basename(f)
|
| 85 |
+
ext = os.path.splitext(filename)[1].lower()
|
| 86 |
+
if ext in model_extensions:
|
| 87 |
+
if model_link is None:
|
| 88 |
+
model_link = f"{base_url_external}/{filename}"
|
| 89 |
+
elif ext in image_extensions:
|
| 90 |
+
images_links.append(f"{base_url_external}/{filename}")
|
| 91 |
+
if model_link and len(images_links) == 2:
|
| 92 |
+
# Construct a permalink to the viewer project with querystring parameters.
|
| 93 |
+
base_viewer_url = "https://huggingface.co/spaces/Surn/3D-Viewer"
|
| 94 |
+
params = {"3d": model_link, "hm": images_links[0], "image": images_links[1]}
|
| 95 |
+
query_str = urllib.parse.urlencode(params)
|
| 96 |
+
permalink = f"{base_viewer_url}?{query_str}"
|
| 97 |
+
return response, permalink
|
| 98 |
+
|
| 99 |
+
# Otherwise, return individual tuples for each file.
|
| 100 |
+
return [(response, link) for link in individual_links]
|
style_20250503.css
CHANGED
|
@@ -102,7 +102,7 @@ a {
|
|
| 102 |
position: relative !important;
|
| 103 |
}
|
| 104 |
|
| 105 |
-
.gradio-container .image-container
|
| 106 |
object-fit: cover;
|
| 107 |
}
|
| 108 |
.gradio-container::before {
|
|
@@ -115,7 +115,7 @@ a {
|
|
| 115 |
height: 100%;
|
| 116 |
opacity: 0.25;
|
| 117 |
background-image: url('gradio_api/file=./images/logo.png');
|
| 118 |
-
background-repeat:repeat
|
| 119 |
background-position: 50% 0;
|
| 120 |
background-size: contain;
|
| 121 |
background-color: rgba(0,0,0,0.9);
|
|
|
|
| 102 |
position: relative !important;
|
| 103 |
}
|
| 104 |
|
| 105 |
+
.gradio-container .image-container .preview.svelte-k63p1v {
|
| 106 |
object-fit: cover;
|
| 107 |
}
|
| 108 |
.gradio-container::before {
|
|
|
|
| 115 |
height: 100%;
|
| 116 |
opacity: 0.25;
|
| 117 |
background-image: url('gradio_api/file=./images/logo.png');
|
| 118 |
+
background-repeat:repeat;
|
| 119 |
background-position: 50% 0;
|
| 120 |
background-size: contain;
|
| 121 |
background-color: rgba(0,0,0,0.9);
|