kphfrepo commited on
Commit
d1f2223
·
verified ·
1 Parent(s): aa76e8d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +117 -172
app.py CHANGED
@@ -1,230 +1,175 @@
1
  import os
 
2
  import urllib.parse
3
- from urllib.parse import quote
4
  import gradio as gr
5
 
6
  DEFAULT_DIR = "/bucket_data"
7
 
8
  # ----------------------------
9
- # SCANNER (UNCHANGED CORE)
10
  # ----------------------------
11
  def load_live_cctv_dashboard():
12
- print("\n⚡ SCAN START")
 
 
 
13
 
14
- image_exts = (".png", ".jpg", ".jpeg", ".webp", ".gif")
15
- video_exts = (".mp4", ".mkv", ".avi", ".mov", ".dav", ".ts")
16
 
17
- grouped_assets = {}
 
 
18
 
19
- for root, dirs, files in os.walk(DEFAULT_DIR):
 
 
20
 
21
- video_stems = {
22
- os.path.splitext(f)[0]
23
- for f in files
24
- if f.lower().endswith(video_exts)
25
- }
26
 
27
- dirs[:] = [d for d in dirs if d not in video_stems]
28
 
29
- for filename in files:
30
- lower = filename.lower()
31
 
32
- if lower.endswith(".7z") or filename == ".gitattributes":
33
- continue
34
 
35
- full_path = os.path.join(root, filename)
36
- relative_path = os.path.relpath(full_path, DEFAULT_DIR).replace("\\", "/")
 
37
 
38
- folder = relative_path.split("/")[0] if "/" in relative_path else "Root"
39
-
40
- grouped_assets.setdefault(folder, {"videos": [], "images": []})
 
41
 
42
- encoded = quote(relative_path, safe="/")
 
43
 
44
- if lower.endswith(video_exts):
45
- print("VIDEO:", encoded)
46
- grouped_assets[folder]["videos"].append({
47
- "name": filename,
48
- "video_url": encoded
49
  })
50
 
51
- elif lower.endswith(image_exts):
52
- grouped_assets[folder]["images"].append({
53
- "name": filename,
54
- "image_url": encoded
55
- })
56
 
57
- print("⚡ SCAN END\n")
58
- return grouped_assets
 
59
 
60
 
61
  # ----------------------------
62
- # JS: FIXED THUMB HOVER LAYER
63
  # ----------------------------
64
  custom_js = """
65
- function initThumb(placeholder) {
66
-
67
- if (placeholder.dataset.thumbInit === "1") return;
68
- const base = placeholder.dataset.thumbBase;
69
- if (!base) return;
70
-
71
- placeholder.dataset.thumbInit = "1";
72
- placeholder.style.position = "relative";
73
- placeholder.style.overflow = "hidden";
74
-
75
- // MAIN IMAGE (THUMBNAIL LAYER - ALWAYS TOP)
76
- let img = placeholder.querySelector(".thumb-layer");
77
-
78
- if (!img) {
79
- img = document.createElement("img");
80
- img.className = "thumb-layer";
81
- img.style.cssText = `
82
- position:absolute;
83
- inset:0;
84
- width:100%;
85
- height:100%;
86
- object-fit:cover;
87
- z-index:10;
88
- pointer-events:none;
89
- transition: opacity 0.3s ease;
90
- `;
91
- placeholder.appendChild(img);
92
- }
93
-
94
- // CONTENT LAYER (icons/text BELOW thumbnail)
95
- placeholder.querySelectorAll("span").forEach(el => {
96
- el.style.position = "relative";
97
- el.style.zIndex = "2";
98
- });
99
 
100
- // load frames
101
- let frame = 1;
102
- function setFrame(i){
103
- img.src = `${base}/thumbnail${i}.webp`;
104
- }
105
 
106
- setFrame(1);
 
107
 
108
- const interval = setInterval(() => {
109
- frame = frame % 5 + 1;
110
- setFrame(frame);
111
- }, 2000);
112
 
113
- placeholder.dataset.interval = interval;
114
- }
 
 
 
 
115
 
116
- const observer = new IntersectionObserver(entries => {
117
- entries.forEach(e => {
118
- const el = e.target;
 
 
 
119
 
120
- if (e.isIntersecting) {
121
- initThumb(el);
122
- } else {
123
- if (el.dataset.interval) clearInterval(el.dataset.interval);
124
- el.dataset.thumbInit = "0";
125
- }
126
- });
127
- }, { rootMargin: "200px", threshold: 0.1 });
128
 
129
- function scan(){
130
- document.querySelectorAll(".video-placeholder").forEach(el => {
131
- observer.observe(el);
132
- });
133
- }
134
 
135
- if (document.readyState === "loading")
136
  document.addEventListener("DOMContentLoaded", scan);
137
- else scan();
138
 
139
- new MutationObserver(scan).observe(document.body, { childList:true, subtree:true });
140
  """
141
 
142
 
143
  # ----------------------------
144
- # GRADIO UI
145
  # ----------------------------
146
- with gr.Blocks(title="CCTV Viewer", js=custom_js) as demo:
147
 
148
- btn = gr.Button("Refresh")
149
 
150
- @gr.render(inputs=btn, triggers=[demo.load, btn.click])
151
- def render():
 
152
 
153
- data = load_live_cctv_dashboard()
 
 
154
 
155
  html = []
156
 
157
- for folder, media in data.items():
158
 
159
- html.append(f"<h2 style='margin-top:20px'>📁 {folder}</h2>")
160
-
161
- # ================= VIDEO GRID (3 PER ROW FIX) =================
162
- if media["videos"]:
163
- html.append("""
164
- <div style="
165
- display:grid;
166
- grid-template-columns:repeat(3, 1fr);
167
- gap:16px;
168
- margin-bottom:30px;
169
- ">
170
- """)
171
 
172
- for v in media["videos"]:
 
 
173
 
174
- video_path = v["video_url"]
175
- public_url = f"https://kphfrepo-webdav.hf.space/{video_path}"
176
- thumb_base = os.path.splitext(video_path)[0]
177
- thumb_url = f"https://kphfrepo-webdav.hf.space/{thumb_base}"
178
-
179
- html.append(f"""
180
- <div style="
181
- border:1px solid #ddd;
182
- border-radius:10px;
183
- overflow:hidden;
184
- height:260px;
185
  position:relative;
186
- background:#000;
187
- ">
188
-
189
- <div class="video-placeholder"
190
- data-video-url="{public_url}"
191
- data-thumb-base="{thumb_url}"
192
- style="
193
- width:100%;
194
- height:100%;
195
- position:relative;
196
- cursor:pointer;
197
- background:#111827;
198
- display:flex;
199
- align-items:center;
200
- justify-content:center;
201
- color:white;
202
- ">
203
-
204
- <!-- BACKGROUND ICON (BEHIND THUMB) -->
205
- <span style="font-size:2rem; opacity:0.5;">▶</span>
206
- <span style="font-size:12px; margin-top:30px;">Hover preview</span>
207
-
208
- </div>
209
- </div>
210
- """)
211
-
212
- html.append("</div>")
213
-
214
- # ================= IMAGES =================
215
- if media["images"]:
216
- html.append("<div style='display:grid;grid-template-columns:repeat(auto-fill,120px);gap:10px;'>")
217
-
218
- for img in media["images"]:
219
- url = f"https://kphfrepo-webdav.hf.space/{img['image_url']}"
220
- html.append(f"""
221
- <img src="{url}" style="width:120px;height:100px;object-fit:cover;border-radius:6px;" />
222
- """)
223
-
224
- html.append("</div>")
225
 
226
  gr.HTML("".join(html))
227
 
228
 
229
  if __name__ == "__main__":
230
- demo.launch(ssr_mode=False)
 
1
  import os
2
+ import glob
3
  import urllib.parse
 
4
  import gradio as gr
5
 
6
  DEFAULT_DIR = "/bucket_data"
7
 
8
  # ----------------------------
9
+ # BACKEND SCANNER (UNCHANGED)
10
  # ----------------------------
11
  def load_live_cctv_dashboard():
12
+ try:
13
+ print("\n⚡ Rocket-Loading local filesystem assets in a single chunk...")
14
+ search_path = os.path.join(DEFAULT_DIR, "**", "*")
15
+ files = glob.glob(search_path, recursive=True)
16
 
17
+ image_exts = ('.png', '.jpg', '.jpeg', '.webp', '.gif')
18
+ video_exts = ('.mp4', '.mkv', '.avi', '.mov', '.dav', '.ts')
19
 
20
+ grouped_assets = {}
21
+ video_count = 0
22
+ image_count = 0
23
 
24
+ for f in files:
25
+ if os.path.isdir(f) or ".gitattributes" in f or f.lower().endswith('.7z'):
26
+ continue
27
 
28
+ relative_path = f.replace(f"{DEFAULT_DIR}/", "")
 
 
 
 
29
 
30
+ folder_name = relative_path.split("/")[0] if "/" in relative_path else "Root Directory"
31
 
32
+ grouped_assets.setdefault(folder_name, {"images": [], "videos": []})
 
33
 
34
+ encoded_path = urllib.parse.quote(os.path.abspath(f))
 
35
 
36
+ if f.lower().endswith(video_exts):
37
+ video_count += 1
38
+ print("added video:", encoded_path)
39
 
40
+ grouped_assets[folder_name]["videos"].append({
41
+ "name": os.path.basename(f),
42
+ "video_url": encoded_path
43
+ })
44
 
45
+ elif f.lower().endswith(image_exts):
46
+ image_count += 1
47
 
48
+ grouped_assets[folder_name]["images"].append({
49
+ "name": os.path.basename(f),
50
+ "image_url": encoded_path
 
 
51
  })
52
 
53
+ print(f"✅ Finished: {video_count} videos, {image_count} images")
54
+ return grouped_assets
 
 
 
55
 
56
+ except Exception as e:
57
+ print("❌ Scan error:", e)
58
+ return {}
59
 
60
 
61
  # ----------------------------
62
+ # ONLY FIXED THUMB HOVER JS
63
  # ----------------------------
64
  custom_js = """
65
+ (function () {
66
+
67
+ function initThumb(el) {
68
+ if (!el || el.dataset.thumbInit === "1") return;
69
+
70
+ const base = el.dataset.thumbBase;
71
+ if (!base) return;
72
+
73
+ el.dataset.thumbInit = "1";
74
+
75
+ let img = el.querySelector("img.thumb");
76
+ if (!img) {
77
+ img = document.createElement("img");
78
+ img.className = "thumb";
79
+ img.style.cssText = `
80
+ position:absolute;
81
+ inset:0;
82
+ width:100%;
83
+ height:100%;
84
+ object-fit:cover;
85
+ opacity:0;
86
+ transition:opacity 0.2s ease;
87
+ pointer-events:none;
88
+ `;
89
+ el.style.position = "relative";
90
+ el.appendChild(img);
91
+ }
 
 
 
 
 
 
 
92
 
93
+ img.src = `${base}/thumbnail1.webp`;
 
 
 
 
94
 
95
+ let i = 1;
96
+ let interval = null;
97
 
98
+ el.addEventListener("mouseenter", () => {
99
+ if (interval) return;
 
 
100
 
101
+ interval = setInterval(() => {
102
+ i = (i % 5) + 1;
103
+ img.src = `${base}/thumbnail${i}.webp`;
104
+ img.style.opacity = 1;
105
+ }, 900);
106
+ });
107
 
108
+ el.addEventListener("mouseleave", () => {
109
+ clearInterval(interval);
110
+ interval = null;
111
+ img.style.opacity = 0;
112
+ });
113
+ }
114
 
115
+ function scan() {
116
+ document.querySelectorAll(".video-placeholder").forEach(initThumb);
117
+ }
 
 
 
 
 
118
 
119
+ const observer = new MutationObserver(scan);
 
 
 
 
120
 
 
121
  document.addEventListener("DOMContentLoaded", scan);
122
+ observer.observe(document.body, { childList: true, subtree: true });
123
 
124
+ })();
125
  """
126
 
127
 
128
  # ----------------------------
129
+ # UI (UNCHANGED EXCEPT DATA ATTR ADDED)
130
  # ----------------------------
131
+ with gr.Blocks(title="CCTV Explorer", js=custom_js) as demo:
132
 
133
+ refresh_btn = gr.Button("🔄 Load")
134
 
135
+ @gr.render(inputs=refresh_btn, triggers=[demo.load, refresh_btn.click])
136
+ def render_ui():
137
+ assets = load_live_cctv_dashboard()
138
 
139
+ if not assets:
140
+ gr.Markdown("No data")
141
+ return
142
 
143
  html = []
144
 
145
+ for folder, media in assets.items():
146
 
147
+ html.append(f"<h2>📁 {folder}</h2>")
 
 
 
 
 
 
 
 
 
 
 
148
 
149
+ for v in media["videos"]:
150
+ base = os.path.splitext(v["video_url"])[0]
151
+ base_url = f"https://kphfrepo-webdav.hf.space/{base.lstrip('/')}"
152
 
153
+ html.append(f"""
154
+ <div class="video-placeholder"
155
+ data-thumb-base="{base_url}"
156
+ style="
157
+ width:320px;
158
+ height:180px;
159
+ background:#111;
160
+ margin:10px;
161
+ display:inline-block;
 
 
162
  position:relative;
163
+ overflow:hidden;
164
+ border-radius:8px;
165
+ cursor:pointer;
166
+ ">
167
+ <span style="color:white;position:absolute;top:45%;left:45%;">▶</span>
168
+ </div>
169
+ """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
 
171
  gr.HTML("".join(html))
172
 
173
 
174
  if __name__ == "__main__":
175
+ demo.launch()