Krixter commited on
Commit
3498bb9
Β·
verified Β·
1 Parent(s): eb30235

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +76 -33
app.py CHANGED
@@ -10,7 +10,6 @@ HF_BUCKET = "hf://buckets/Krixter/storage"
10
  def get_fs():
11
  """Initialize the Hugging Face File System. Uses a token if available, otherwise runs anonymously."""
12
  token = os.environ.get("HF_TOKEN")
13
- # If there's no token, HfFileSystem will just run in public/anonymous mode
14
  return HfFileSystem(token=token)
15
 
16
  def format_size(size_bytes):
@@ -19,40 +18,80 @@ def format_size(size_bytes):
19
  if size_bytes < 1024.0:
20
  return f"{size_bytes:.2f} {unit}"
21
  size_bytes /= 1024.0
 
22
 
23
- def list_files():
24
- """Fetch files from the bucket and return a formatted DataFrame."""
25
  try:
26
  fs = get_fs()
27
- # Fetch file list with details (size, modified date)
28
  files = fs.ls(HF_BUCKET, detail=True)
29
 
30
  data = []
31
  for f in files:
32
- if f["type"] == "file":
33
- name = os.path.basename(f["name"])
34
- size_str = format_size(f["size"])
35
- last_modified = f.get("last_modified", "Unknown")
36
 
37
- data.append({
38
- "File Name": name,
39
- "Last Modified": last_modified,
40
- "Size": size_str
41
- })
42
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  df = pd.DataFrame(data)
44
  if not df.empty:
45
- # Safely convert to datetime using errors='coerce' to turn "Unknown" into NaT (Not a Time)
46
- dt_col = pd.to_datetime(df["Last Modified"], errors='coerce')
 
47
 
48
- # Remove timezone info for clean sorting/display
49
- dt_col = dt_col.dt.tz_localize(None)
50
-
51
- # Convert back to a clean string format, replacing NaT back with "Unknown" for the UI
52
  df["Last Modified"] = dt_col.dt.strftime('%Y-%m-%d %H:%M:%S').fillna("Unknown")
53
 
54
- # Default sort by File Name (alphabetical)
55
- df = df.sort_values(by="File Name", ascending=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
 
57
  file_names = df["File Name"].tolist() if not df.empty else []
58
  return df, gr.update(choices=file_names)
@@ -70,11 +109,9 @@ def download_file(filename):
70
  fs = get_fs()
71
  src_path = f"{HF_BUCKET}/{filename}"
72
 
73
- # Create a temporary directory to hold the downloaded file
74
  temp_dir = tempfile.mkdtemp()
75
  dest_path = os.path.join(temp_dir, filename)
76
 
77
- # Pull the file down from the bucket
78
  print(f"πŸ“₯ Downloading {filename}...")
79
  fs.get(src_path, dest_path)
80
  print("βœ… Download complete!")
@@ -85,14 +122,21 @@ def download_file(filename):
85
  return None
86
 
87
  # --- UI LAYOUT ---
88
- # Theme moved from here to the launch command at the bottom
89
  with gr.Blocks() as app:
90
  gr.Markdown("# πŸ—„οΈ AudioNovel Bucket Explorer")
91
- gr.Markdown("View, sort, and download your processed audio files. **Click the column headers below to sort by Name, Date, or Size.**")
92
 
93
  with gr.Row():
94
- refresh_btn = gr.Button("πŸ”„ Refresh Bucket contents", variant="secondary")
95
-
 
 
 
 
 
 
 
 
96
  # Interactive table
97
  file_table = gr.Dataframe(
98
  headers=["File Name", "Last Modified", "Size"],
@@ -113,12 +157,11 @@ with gr.Blocks() as app:
113
 
114
  # --- EVENT TRIGGERS ---
115
  # Trigger refresh
116
- refresh_btn.click(fn=list_files, inputs=[], outputs=[file_table, file_dropdown])
117
- # Trigger download
118
- download_btn.click(fn=download_file, inputs=[file_dropdown], outputs=[file_output])
119
  # Load data automatically on startup
120
- app.load(fn=list_files, inputs=[], outputs=[file_table, file_dropdown])
121
 
122
  if __name__ == "__main__":
123
- # Theme is applied here now to avoid the Gradio 6.0 warning
124
  app.launch(theme=gr.themes.Soft(primary_hue="indigo"))
 
10
  def get_fs():
11
  """Initialize the Hugging Face File System. Uses a token if available, otherwise runs anonymously."""
12
  token = os.environ.get("HF_TOKEN")
 
13
  return HfFileSystem(token=token)
14
 
15
  def format_size(size_bytes):
 
18
  if size_bytes < 1024.0:
19
  return f"{size_bytes:.2f} {unit}"
20
  size_bytes /= 1024.0
21
+ return f"{size_bytes:.2f} PB" # Fallback
22
 
23
+ def list_files(sort_by="Date (Newest)"):
24
+ """Fetch files from the bucket, extract accurate metadata, and sort."""
25
  try:
26
  fs = get_fs()
 
27
  files = fs.ls(HF_BUCKET, detail=True)
28
 
29
  data = []
30
  for f in files:
31
+ # Skip directories
32
+ if f.get("type") == "directory":
33
+ continue
 
34
 
35
+ name = os.path.basename(f["name"])
36
+ raw_size = f.get("size", 0)
37
+ size_str = format_size(raw_size)
38
+
39
+ # --- AGGRESSIVE DATE EXTRACTION ---
40
+ # Different storage backends (like S3/Xet) use different dictionary keys.
41
+ # We check all possible variations to ensure we catch the Upload Date.
42
+ raw_date = None
43
+ date_keys = ["last_modified", "LastModified", "updated", "updatedAt", "client_modified", "created_at"]
44
+
45
+ for k in date_keys:
46
+ if k in f and f[k] is not None:
47
+ raw_date = f[k]
48
+ break
49
+
50
+ # If missing, attempt to ping the file directly for deeper metadata
51
+ if raw_date is None:
52
+ try:
53
+ info = fs.info(f["name"])
54
+ for k in date_keys:
55
+ if k in info and info[k] is not None:
56
+ raw_date = info[k]
57
+ break
58
+ except Exception:
59
+ pass
60
+
61
+ data.append({
62
+ "File Name": name,
63
+ "Size": size_str,
64
+ "_raw_date": raw_date,
65
+ "_raw_size": raw_size
66
+ })
67
+
68
  df = pd.DataFrame(data)
69
  if not df.empty:
70
+ # Safely parse dates, converting valid dates to datetime objects and invalid ones to NaT
71
+ dt_col = pd.to_datetime(df["_raw_date"], errors='coerce').dt.tz_localize(None)
72
+ df["_raw_date"] = dt_col
73
 
74
+ # Format display column, filling NaT back to "Unknown"
 
 
 
75
  df["Last Modified"] = dt_col.dt.strftime('%Y-%m-%d %H:%M:%S').fillna("Unknown")
76
 
77
+ # --- EXPLICIT SORTING ---
78
+ # We sort against the raw hidden values, NOT the formatted strings
79
+ if sort_by == "Name (A-Z)":
80
+ df = df.sort_values(by="File Name", ascending=True)
81
+ elif sort_by == "Name (Z-A)":
82
+ df = df.sort_values(by="File Name", ascending=False)
83
+ elif sort_by == "Date (Newest)":
84
+ df = df.sort_values(by="_raw_date", ascending=False, na_position='last')
85
+ elif sort_by == "Date (Oldest)":
86
+ df = df.sort_values(by="_raw_date", ascending=True, na_position='last')
87
+ elif sort_by == "Size (Largest)":
88
+ df = df.sort_values(by="_raw_size", ascending=False)
89
+ elif sort_by == "Size (Smallest)":
90
+ df = df.sort_values(by="_raw_size", ascending=True)
91
+
92
+ # Clean up raw columns and enforce column order
93
+ df = df.drop(columns=["_raw_date", "_raw_size"])
94
+ df = df[["File Name", "Last Modified", "Size"]]
95
 
96
  file_names = df["File Name"].tolist() if not df.empty else []
97
  return df, gr.update(choices=file_names)
 
109
  fs = get_fs()
110
  src_path = f"{HF_BUCKET}/{filename}"
111
 
 
112
  temp_dir = tempfile.mkdtemp()
113
  dest_path = os.path.join(temp_dir, filename)
114
 
 
115
  print(f"πŸ“₯ Downloading {filename}...")
116
  fs.get(src_path, dest_path)
117
  print("βœ… Download complete!")
 
122
  return None
123
 
124
  # --- UI LAYOUT ---
 
125
  with gr.Blocks() as app:
126
  gr.Markdown("# πŸ—„οΈ AudioNovel Bucket Explorer")
127
+ gr.Markdown("View, sort, and download your processed audio files. Use the dropdown to sort your files perfectly.")
128
 
129
  with gr.Row():
130
+ with gr.Column(scale=2):
131
+ refresh_btn = gr.Button("πŸ”„ Refresh Bucket contents", variant="secondary")
132
+ with gr.Column(scale=1):
133
+ sort_dropdown = gr.Dropdown(
134
+ label="Sort Files By",
135
+ choices=["Name (A-Z)", "Name (Z-A)", "Date (Newest)", "Date (Oldest)", "Size (Largest)", "Size (Smallest)"],
136
+ value="Date (Newest)",
137
+ interactive=True
138
+ )
139
+
140
  # Interactive table
141
  file_table = gr.Dataframe(
142
  headers=["File Name", "Last Modified", "Size"],
 
157
 
158
  # --- EVENT TRIGGERS ---
159
  # Trigger refresh
160
+ refresh_btn.click(fn=list_files, inputs=[sort_dropdown], outputs=[file_table, file_dropdown])
161
+ # Trigger sort instantly when dropdown changes
162
+ sort_dropdown.change(fn=list_files, inputs=[sort_dropdown], outputs=[file_table, file_dropdown])
163
  # Load data automatically on startup
164
+ app.load(fn=list_files, inputs=[sort_dropdown], outputs=[file_table, file_dropdown])
165
 
166
  if __name__ == "__main__":
 
167
  app.launch(theme=gr.themes.Soft(primary_hue="indigo"))