testdeep123 commited on
Commit
86296cd
Β·
verified Β·
1 Parent(s): 2b78d03

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +92 -130
app.py CHANGED
@@ -1,14 +1,13 @@
1
  import os
2
- import io
3
- import shutil
4
  import tempfile
5
  from flask import Flask, render_template_string, request, redirect, send_file, jsonify
6
- from huggingface_hub import HfApi, HfFileSystem, login, upload_file, delete_file, move_file
7
 
8
- # Set env vars
9
  REPO_ID = os.getenv("REPO_ID")
10
  HF_TOKEN = os.getenv("HF_TOKEN")
11
 
 
12
  app = Flask(__name__)
13
  api = HfApi()
14
  fs = HfFileSystem(token=HF_TOKEN)
@@ -18,111 +17,53 @@ TEMPLATE = """
18
  <!DOCTYPE html>
19
  <html>
20
  <head>
21
- <title>HuggingFace Drive</title>
 
22
  <style>
23
- body {
24
- background-color: #121212;
25
- color: #e0e0e0;
26
- font-family: Arial, sans-serif;
27
- padding: 20px;
28
- }
29
- .folder, .file {
30
- padding: 5px 10px;
31
- margin: 5px 0;
32
- background-color: #1e1e1e;
33
- border-radius: 5px;
34
- cursor: pointer;
35
- }
36
- .folder:hover, .file:hover {
37
- background-color: #2a2a2a;
38
- }
39
- .actions {
40
- margin-top: 10px;
41
- }
42
- button {
43
- background: #333;
44
- color: #fff;
45
- border: none;
46
- padding: 8px 12px;
47
- margin-right: 5px;
48
- border-radius: 4px;
49
- cursor: pointer;
50
- }
51
- button:hover {
52
- background: #444;
53
- }
54
- input[type="text"], input[type="file"] {
55
- background: #1e1e1e;
56
- color: #fff;
57
- border: 1px solid #333;
58
- padding: 5px;
59
- margin-right: 5px;
60
- }
61
  </style>
62
  <script>
63
- async function navigate(path) {
64
- const res = await fetch('/?path=' + encodeURIComponent(path));
65
- document.open();
66
- document.write(await res.text());
67
- document.close();
68
- }
69
- async function deleteFile(path) {
70
- if (!confirm("Delete " + path + "?")) return;
71
- await fetch('/delete', {
72
- method: 'POST',
73
- headers: { 'Content-Type': 'application/json' },
74
- body: JSON.stringify({ path })
75
- });
76
- location.reload();
77
- }
78
- async function createFolder(currentPath) {
79
- const name = prompt("Folder name:");
80
- if (!name) return;
81
- await fetch('/create_folder', {
82
- method: 'POST',
83
- headers: { 'Content-Type': 'application/json' },
84
- body: JSON.stringify({ path: currentPath + '/' + name })
85
- });
86
- location.reload();
87
- }
88
- async function renamePath(oldPath) {
89
- const newPath = prompt("Rename to:", oldPath);
90
- if (!newPath || newPath === oldPath) return;
91
- await fetch('/rename', {
92
- method: 'POST',
93
- headers: { 'Content-Type': 'application/json' },
94
- body: JSON.stringify({ old_path: oldPath, new_path: newPath })
95
- });
96
- location.reload();
97
- }
98
  </script>
99
  </head>
100
  <body>
101
- <h2>HuggingFace Drive - {{ path or 'Root' }}</h2>
102
  {% if parent %}
103
- <div><a href="#" onclick="navigate('{{ parent }}')">⬅️ Back</a></div>
104
  {% endif %}
105
  <div class="actions">
106
- <form method="POST" action="/upload" enctype="multipart/form-data">
107
- <input type="file" name="file">
108
  <input type="hidden" name="path" value="{{ path }}">
109
  <button type="submit">Upload</button>
110
  </form>
111
- <button onclick="createFolder('{{ path }}')">Create Folder</button>
112
  </div>
113
  <hr>
114
  {% for item in items %}
115
- {% if item['type'] == 'dir' %}
116
- <div class="folder" onclick="navigate('{{ item['path'] }}')">πŸ“ {{ item['name'] }}
117
- <button onclick="event.stopPropagation(); renamePath('{{ item['path'] }}')">Rename</button>
118
- <button onclick="event.stopPropagation(); deleteFile('{{ item['path'] }}')">Delete</button>
 
119
  </div>
120
  {% else %}
121
  <div class="file">
122
- πŸ“„ {{ item['name'] }}
123
- <a href="/download?path={{ item['path'] }}"><button>Download</button></a>
124
- <button onclick="renamePath('{{ item['path'] }}')">Rename</button>
125
- <button onclick="deleteFile('{{ item['path'] }}')">Delete</button>
126
  </div>
127
  {% endif %}
128
  {% endfor %}
@@ -130,72 +71,93 @@ TEMPLATE = """
130
  </html>
131
  """
132
 
133
- def list_dataset_files(path=""):
134
- files = fs.ls(f"datasets/{REPO_ID}/{path}", detail=True)
135
- folders = []
 
 
136
  items = []
137
- seen = set()
138
 
139
- for file in files:
140
- relative_path = file["name"].replace(f"datasets/{REPO_ID}/", "")
141
- parts = relative_path.split("/")
142
- if len(parts) > 1:
143
- folder_path = "/".join(parts[:1])
144
- full_folder = f"{path}/{folder_path}".strip("/")
145
- if full_folder not in seen:
146
- folders.append({"type": "dir", "name": folder_path, "path": full_folder})
147
- seen.add(full_folder)
148
- elif len(parts) == 1:
149
- items.append({"type": "file", "name": parts[0], "path": f"{path}/{parts[0]}".strip("/")})
150
 
151
- return sorted(folders + items, key=lambda x: x["name"].lower())
 
 
152
 
153
  @app.route("/", methods=["GET"])
154
  def index():
155
- path = request.args.get("path", "").strip("/")
156
  parent = "/".join(path.split("/")[:-1]) if path else None
157
- items = list_dataset_files(path)
158
  return render_template_string(TEMPLATE, items=items, path=path, parent=parent)
159
 
160
- @app.route("/download")
161
  def download():
162
- path = request.args.get("path")
163
- with tempfile.NamedTemporaryFile(delete=False) as tmp:
164
- fs.download(f"datasets/{REPO_ID}/{path}", tmp.name)
165
- return send_file(tmp.name, as_attachment=True, download_name=os.path.basename(path))
166
 
167
  @app.route("/upload", methods=["POST"])
168
  def upload():
169
  file = request.files["file"]
170
- path = request.form.get("path", "").strip("/")
171
  dest = f"{path}/{file.filename}".strip("/")
172
- with tempfile.NamedTemporaryFile() as tmp:
173
- file.save(tmp.name)
174
- upload_file(path_or_fileobj=tmp.name, path_in_repo=dest, repo_id=REPO_ID, repo_type="dataset", token=HF_TOKEN)
175
  return redirect(f"/?path={path}")
176
 
177
  @app.route("/delete", methods=["POST"])
178
  def delete():
179
  data = request.get_json()
180
- delete_file(repo_id=REPO_ID, path_in_repo=data["path"], repo_type="dataset", token=HF_TOKEN)
181
- return jsonify({"status": "deleted"})
 
 
 
 
 
182
 
183
  @app.route("/create_folder", methods=["POST"])
184
  def create_folder():
185
  data = request.get_json()
186
- path = data["path"].strip("/")
187
- dummy_path = f"{path}/.keep"
188
- with tempfile.NamedTemporaryFile() as tmp:
189
- tmp.write(b"")
190
- tmp.flush()
191
- upload_file(path_or_fileobj=tmp.name, path_in_repo=dummy_path, repo_id=REPO_ID, repo_type="dataset", token=HF_TOKEN)
192
- return jsonify({"status": "folder_created"})
193
 
194
  @app.route("/rename", methods=["POST"])
195
  def rename():
196
  data = request.get_json()
197
- move_file(repo_id=REPO_ID, src_path=data["old_path"], dst_path=data["new_path"], repo_type="dataset", token=HF_TOKEN)
198
- return jsonify({"status": "renamed"})
 
 
 
 
 
 
 
 
 
 
 
 
 
199
 
200
  if __name__ == "__main__":
201
- app.run(debug=True, port=7860)
 
1
  import os
 
 
2
  import tempfile
3
  from flask import Flask, render_template_string, request, redirect, send_file, jsonify
4
+ from huggingface_hub import HfApi, HfFileSystem, login, upload_file, delete_file
5
 
6
+ # Environment variables
7
  REPO_ID = os.getenv("REPO_ID")
8
  HF_TOKEN = os.getenv("HF_TOKEN")
9
 
10
+ # Initialize
11
  app = Flask(__name__)
12
  api = HfApi()
13
  fs = HfFileSystem(token=HF_TOKEN)
 
17
  <!DOCTYPE html>
18
  <html>
19
  <head>
20
+ <meta charset="UTF-8">
21
+ <title>HuggingFace Drive - {{ path or 'Root' }}</title>
22
  <style>
23
+ body { background:#121212; color:#e0e0e0; font-family:Arial,sans-serif; padding:20px; }
24
+ h2 { margin-top:0; }
25
+ .folder, .file { padding:6px 12px; margin:6px 0; background:#1e1e1e; border-radius:4px; cursor:pointer; }
26
+ .folder:hover, .file:hover { background:#2a2a2a; }
27
+ .actions { margin-bottom:16px; }
28
+ button { background:#333; color:#fff; border:none; padding:6px 10px; margin-right:6px; border-radius:4px; cursor:pointer; }
29
+ button:hover { background:#444; }
30
+ input[type="file"], input[type="text"] { background:#1e1e1e; color:#fff; border:1px solid #333; padding:6px; margin-right:6px; }
31
+ a { color:#8ab4f8; text-decoration:none; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  </style>
33
  <script>
34
+ function navigate(p){ location.href = '/?path='+encodeURIComponent(p); }
35
+ async function deleteItem(p){ if(!confirm('Delete '+p+'?'))return; await fetch('/delete',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({path:p})}); location.reload(); }
36
+ async function createFolder(cp){ let name=prompt('Folder name:'); if(!name)return; await fetch('/create_folder',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({path:cp+'/'+name})}); location.reload(); }
37
+ async function renameItem(old){ let neu=prompt('Rename to:',old); if(!neu||neu===old)return; await fetch('/rename',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({old_path:old,new_path:neu})}); location.reload(); }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  </script>
39
  </head>
40
  <body>
41
+ <h2>πŸ“ HuggingFace Drive β€” {{ path or 'Root' }}</h2>
42
  {% if parent %}
43
+ <div><a href="#" onclick="navigate('{{ parent }}')">⬅️ Back to {{ parent or 'Root' }}</a></div>
44
  {% endif %}
45
  <div class="actions">
46
+ <form style="display:inline" method="POST" action="/upload" enctype="multipart/form-data">
47
+ <input type="file" name="file" required>
48
  <input type="hidden" name="path" value="{{ path }}">
49
  <button type="submit">Upload</button>
50
  </form>
51
+ <button onclick="createFolder('{{ path }}')">New Folder</button>
52
  </div>
53
  <hr>
54
  {% for item in items %}
55
+ {% if item.type=='dir' %}
56
+ <div class="folder" onclick="navigate('{{ item.path }}')">
57
+ πŸ“‚ {{ item.name }}
58
+ <button style="float:right" onclick="event.stopPropagation(); renameItem('{{ item.path }}')">Rename</button>
59
+ <button style="float:right;margin-right:4px" onclick="event.stopPropagation(); deleteItem('{{ item.path }}')">Delete</button>
60
  </div>
61
  {% else %}
62
  <div class="file">
63
+ πŸ“„ {{ item.name }}
64
+ <a href="/download?path={{ item.path }}"><button>Download</button></a>
65
+ <button onclick="renameItem('{{ item.path }}')">Rename</button>
66
+ <button onclick="deleteItem('{{ item.path }}')">Delete</button>
67
  </div>
68
  {% endif %}
69
  {% endfor %}
 
71
  </html>
72
  """
73
 
74
+ def list_dataset(path=""):
75
+ """Return list of dicts {'type':'dir'|'file','name':..., 'path':...} for this folder."""
76
+ prefix = path.strip("/")+("/" if path else "")
77
+ all_files = api.list_repo_files(repo_id=REPO_ID, repo_type="dataset", token=HF_TOKEN)
78
+ seen_dirs = set()
79
  items = []
 
80
 
81
+ for f in all_files:
82
+ if not f.startswith(prefix): continue
83
+ rest = f[len(prefix):]
84
+ if "/" in rest:
85
+ dir_name = rest.split("/")[0]
86
+ dir_path = (prefix+dir_name).strip("/")
87
+ if dir_path not in seen_dirs:
88
+ seen_dirs.add(dir_path)
89
+ items.append({"type":"dir","name":dir_name,"path":dir_path})
90
+ else:
91
+ items.append({"type":"file","name":rest,"path":(prefix+rest).strip("/")})
92
 
93
+ # sort folders first then files, both alphabetically
94
+ items.sort(key=lambda x: (x["type"]!="dir", x["name"].lower()))
95
+ return items
96
 
97
  @app.route("/", methods=["GET"])
98
  def index():
99
+ path = request.args.get("path","").strip("/")
100
  parent = "/".join(path.split("/")[:-1]) if path else None
101
+ items = list_dataset(path)
102
  return render_template_string(TEMPLATE, items=items, path=path, parent=parent)
103
 
104
+ @app.route("/download", methods=["GET"])
105
  def download():
106
+ path = request.args.get("path","")
107
+ tmp = tempfile.NamedTemporaryFile(delete=False)
108
+ fs.download(f"datasets/{REPO_ID}/{path}", tmp.name)
109
+ return send_file(tmp.name, as_attachment=True, download_name=os.path.basename(path))
110
 
111
  @app.route("/upload", methods=["POST"])
112
  def upload():
113
  file = request.files["file"]
114
+ path = request.form.get("path","").strip("/")
115
  dest = f"{path}/{file.filename}".strip("/")
116
+ tmp = tempfile.NamedTemporaryFile(delete=False)
117
+ file.save(tmp.name)
118
+ upload_file(path_or_fileobj=tmp.name, path_in_repo=dest, repo_id=REPO_ID, repo_type="dataset", token=HF_TOKEN)
119
  return redirect(f"/?path={path}")
120
 
121
  @app.route("/delete", methods=["POST"])
122
  def delete():
123
  data = request.get_json()
124
+ target = data["path"]
125
+ # If folder: delete each file under it
126
+ all_files = api.list_repo_files(repo_id=REPO_ID, repo_type="dataset", token=HF_TOKEN)
127
+ for f in all_files:
128
+ if f == target or f.startswith(target.rstrip("/") + "/"):
129
+ delete_file(repo_id=REPO_ID, path_in_repo=f, repo_type="dataset", token=HF_TOKEN)
130
+ return jsonify(status="deleted")
131
 
132
  @app.route("/create_folder", methods=["POST"])
133
  def create_folder():
134
  data = request.get_json()
135
+ folder = data["path"].strip("/")
136
+ keep = f"{folder}/.keep"
137
+ tmp = tempfile.NamedTemporaryFile(delete=False)
138
+ tmp.write(b"")
139
+ tmp.flush()
140
+ upload_file(path_or_fileobj=tmp.name, path_in_repo=keep, repo_id=REPO_ID, repo_type="dataset", token=HF_TOKEN)
141
+ return jsonify(status="folder_created")
142
 
143
  @app.route("/rename", methods=["POST"])
144
  def rename():
145
  data = request.get_json()
146
+ old = data["old_path"].strip("/")
147
+ new = data["new_path"].strip("/")
148
+ all_files = api.list_repo_files(repo_id=REPO_ID, repo_type="dataset", token=HF_TOKEN)
149
+ for f in all_files:
150
+ if f == old or f.startswith(old + "/"):
151
+ rel = f[len(old):].lstrip("/")
152
+ new_path = (new + "/" + rel).strip("/")
153
+ # download
154
+ tmp = tempfile.NamedTemporaryFile(delete=False)
155
+ fs.download(f"datasets/{REPO_ID}/{f}", tmp.name)
156
+ # upload under new
157
+ upload_file(path_or_fileobj=tmp.name, path_in_repo=new_path, repo_id=REPO_ID, repo_type="dataset", token=HF_TOKEN)
158
+ # delete old
159
+ delete_file(repo_id=REPO_ID, path_in_repo=f, repo_type="dataset", token=HF_TOKEN)
160
+ return jsonify(status="renamed")
161
 
162
  if __name__ == "__main__":
163
+ app.run(debug=True, host="0.0.0.0", port=7860)