Spaces:
Running
Running
import os | |
import tempfile | |
import requests | |
from flask import Flask, request, send_file, redirect, url_for, render_template_string | |
from huggingface_hub import HfApi, upload_file, hf_hub_url | |
REPO_ID = os.getenv("REPO_ID") | |
TOKEN = os.getenv("HF_TOKEN") | |
app = Flask(__name__) | |
api = HfApi() | |
HTML_TEMPLATE = """ | |
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<title>Drive - {{ repo_id }}</title> | |
<style> | |
body { | |
margin: 0; | |
font-family: 'Segoe UI', sans-serif; | |
background-color: #1e1e1e; | |
color: #ffffff; | |
} | |
header { | |
padding: 16px; | |
background-color: #202124; | |
font-size: 20px; | |
font-weight: bold; | |
} | |
.container { | |
padding: 20px; | |
} | |
.folder { | |
color: #8ab4f8; | |
cursor: pointer; | |
margin: 6px 0; | |
} | |
.file { | |
margin-left: 20px; | |
margin: 4px 0; | |
} | |
a { | |
color: #9cdcfe; | |
text-decoration: none; | |
margin-left: 10px; | |
} | |
a:hover { | |
text-decoration: underline; | |
} | |
.upload-box { | |
margin-top: 30px; | |
background: #2c2c2c; | |
padding: 20px; | |
border-radius: 8px; | |
} | |
input[type="file"], input[type="text"], input[type="submit"] { | |
margin-top: 10px; | |
padding: 8px; | |
font-size: 14px; | |
} | |
</style> | |
<script> | |
function toggleFolder(id) { | |
const el = document.getElementById(id); | |
if (el.style.display === "none") { | |
el.style.display = "block"; | |
} else { | |
el.style.display = "none"; | |
} | |
} | |
</script> | |
</head> | |
<body> | |
<header>π Hugging Face Drive β {{ repo_id }}</header> | |
<div class="container"> | |
{% for folder, contents in folder_tree.items() %} | |
<div class="folder" onclick="toggleFolder('{{ loop.index }}')">π {{ folder }}</div> | |
<div id="{{ loop.index }}" style="display:none; padding-left: 20px;"> | |
{% for file in contents %} | |
<div class="file"> | |
{{ file }} | |
<a href="{{ url_for('download_file', filename=folder + '/' + file) }}">Download</a> | |
</div> | |
{% endfor %} | |
</div> | |
{% endfor %} | |
<div class="upload-box"> | |
<h3>Upload File</h3> | |
<form method="POST" action="/upload" enctype="multipart/form-data"> | |
Folder path (optional): <input type="text" name="folder" placeholder="e.g. my/folder"><br> | |
File: <input type="file" name="file"><br> | |
<input type="submit" value="Upload"> | |
</form> | |
</div> | |
</div> | |
</body> | |
</html> | |
""" | |
def build_folder_tree(file_list): | |
tree = {} | |
for path in file_list: | |
if "/" in path: | |
folder, file = path.rsplit("/", 1) | |
else: | |
folder, file = "", path | |
tree.setdefault(folder, []).append(file) | |
return tree | |
def index(): | |
files = api.list_repo_files(repo_id=REPO_ID, repo_type="dataset", token=TOKEN) | |
folder_tree = build_folder_tree(files) | |
return render_template_string(HTML_TEMPLATE, repo_id=REPO_ID, folder_tree=folder_tree) | |
def download_file(filename): | |
try: | |
url = hf_hub_url(REPO_ID, filename, repo_type="dataset") | |
headers = {"Authorization": f"Bearer {TOKEN}"} | |
response = requests.get(url, headers=headers) | |
if response.status_code != 200: | |
return f"Download failed: {response.status_code}", 500 | |
tmp = tempfile.NamedTemporaryFile(delete=False) | |
tmp.write(response.content) | |
tmp.close() | |
return send_file(tmp.name, as_attachment=True, download_name=os.path.basename(filename)) | |
except Exception as e: | |
return str(e), 500 | |
def upload(): | |
file = request.files["file"] | |
folder = request.form.get("folder", "").strip().strip("/") | |
if file: | |
filename = file.filename | |
path_in_repo = f"{folder}/{filename}" if folder else filename | |
temp_path = os.path.join(tempfile.gettempdir(), filename) | |
file.save(temp_path) | |
upload_file( | |
path_or_fileobj=temp_path, | |
path_in_repo=path_in_repo, | |
repo_id=REPO_ID, | |
repo_type="dataset", | |
token=TOKEN | |
) | |
return redirect(url_for("index")) | |
if __name__ == "__main__": | |
app.run(debug=True, host="0.0.0.0", port=7860) | |