lexicalspace commited on
Commit
b64c9e1
Β·
verified Β·
1 Parent(s): b8b8db7

add seo tool

Browse files
Files changed (1) hide show
  1. app.py +79 -246
app.py CHANGED
@@ -2,15 +2,13 @@ import yt_dlp
2
  import os
3
  import gradio as gr
4
  import random
5
- import time
6
  import threading
7
- import datetime
8
- import re
9
- import requests # Added for NoEmbed API
10
  from gradio_client import Client
 
11
 
12
  # =====================================================
13
- # CONFIG
14
  # =====================================================
15
  PROXY_SPACE_URL = "lexicalspace/Proxy-Server"
16
  OUTPUT_DIR = "downloads"
@@ -19,6 +17,10 @@ os.makedirs(OUTPUT_DIR, exist_ok=True)
19
  MAX_CONCURRENCY = 6
20
  MAX_QUEUE = 20
21
 
 
 
 
 
22
  # =====================================================
23
  # GLOBAL STATE
24
  # =====================================================
@@ -26,7 +28,7 @@ active_jobs = {}
26
  job_lock = threading.Lock()
27
 
28
  # =====================================================
29
- # HELPERS
30
  # =====================================================
31
  def get_proxy_batch():
32
  try:
@@ -52,7 +54,6 @@ def get_best_proxy():
52
  return None
53
 
54
  def parse_time(time_str):
55
- """Converts MM:SS or HH:MM:SS to seconds"""
56
  if not time_str: return None
57
  try:
58
  parts = list(map(int, time_str.split(':')))
@@ -64,292 +65,124 @@ def parse_time(time_str):
64
  return None
65
 
66
  def build_format_selector(quality_mode):
67
- """Generates yt-dlp format strings based on user selection"""
68
  if quality_mode == "Audio Only (Best)":
69
- return {
70
- 'format': 'bestaudio/best',
71
- 'postprocessors': [{'key': 'FFmpegExtractAudio','preferredcodec': 'mp3'}],
72
- 'ext': 'mp3'
73
- }
74
  elif quality_mode == "Video (4K / Best)":
75
- return {
76
- 'format': 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best',
77
- 'ext': 'mp4'
78
- }
79
  elif quality_mode == "Video (1080p)":
80
- return {
81
- 'format': 'bestvideo[height<=1080][ext=mp4]+bestaudio[ext=m4a]/best[height<=1080][ext=mp4]',
82
- 'ext': 'mp4'
83
- }
84
  elif quality_mode == "Video (720p)":
85
- return {
86
- 'format': 'bestvideo[height<=720][ext=mp4]+bestaudio[ext=m4a]/best[height<=720][ext=mp4]',
87
- 'ext': 'mp4'
88
- }
89
  elif quality_mode == "Video (360p / Data Saver)":
90
- return {
91
- 'format': 'bestvideo[height<=360][ext=mp4]+bestaudio[ext=m4a]/best[height<=360][ext=mp4]',
92
- 'ext': 'mp4'
93
- }
94
  return {}
95
 
96
- # =====================================================
97
- # METADATA FETCHER (UPDATED: NoEmbed)
98
- # =====================================================
99
  def fetch_metadata(url):
100
  if not url:
101
  return None, "⚠️ Please enter a URL first.", None, None, None
102
-
103
- # Use NoEmbed API to avoid Error 5 / 403 blocks
104
  api_url = f"https://noembed.com/embed?url={url}"
105
-
106
  try:
107
  response = requests.get(api_url, timeout=10)
108
  data = response.json()
109
-
110
  if "error" in data:
111
  return None, f"❌ API Error: {data.get('error')}", gr.update(interactive=False), None, None
112
-
113
- # Extract available data from NoEmbed
114
  title = data.get('title', 'Unknown Title')
115
  uploader = data.get('author_name', 'Unknown Channel')
116
  thumb = data.get('thumbnail_url', None)
117
-
118
- # Note: NoEmbed does not typically provide Duration or Views
119
- duration_str = "N/A (API Limit)"
120
- view_count = "N/A (API Limit)"
121
-
122
- desc = (
123
- f"### 🎬 {title}\n"
124
- f"**πŸ‘€ Channel:** {uploader}\n"
125
- f"**⏱️ Duration:** {duration_str}\n"
126
- f"**πŸ‘οΈ Views:** {view_count}"
127
- )
128
-
129
- # Auto-fill the custom filename box with the title (sanitized)
130
  safe_title = "".join([c for c in title if c.isalnum() or c in " ._-"])
131
-
132
- return thumb, desc, gr.update(interactive=True), gr.update(value=safe_title), "βœ… Metadata fetched (NoEmbed Mode)"
133
-
134
  except Exception as e:
135
- return None, f"❌ Error fetching metadata: {str(e)}", gr.update(interactive=False), None, None
136
 
137
- # =====================================================
138
- # ENGINE CORE (Unified Logic)
139
- # =====================================================
140
  def run_downloader(engine_name, url, quality, start_time, end_time, custom_name, cookies, progress):
141
-
142
- # 1. Setup Logging & State
143
- log = [f"πŸš€ Engine: {engine_name} | Quality: {quality}"]
144
-
145
  with job_lock:
146
- if active_jobs.get(url) == "running":
147
- return None, "β›” Already running", "Duplicate request blocked"
148
  active_jobs[url] = "running"
149
-
150
- # 2. Configure Options
151
- format_opts = build_format_selector(quality)
152
 
153
- # Filename handling
154
  filename_tmpl = f"{custom_name}.%(ext)s" if custom_name else '%(title)s.%(ext)s'
 
 
155
 
156
- # Proxy Handling
157
- if "V1" in engine_name:
158
- proxies = get_proxy_batch() or [None]
159
- else:
160
- p = get_best_proxy()
161
- proxies = [p] if p else [None]
162
-
163
- # Strategies (Clients)
164
- strategies = ["android", "ios", "web"] if "V1" in engine_name else ["android", "ios", "web", "tv"]
165
-
166
- # Trimming Logic (Range Download)
167
- download_ranges = None
168
- s_sec = parse_time(start_time)
169
- e_sec = parse_time(end_time)
170
-
171
- if s_sec is not None or e_sec is not None:
172
- log.append(f"βœ‚οΈ Cutting video: {start_time or 'Start'} to {end_time or 'End'}")
173
-
174
- def range_func(info_dict, ydl):
175
- return [{
176
- 'start_time': s_sec if s_sec else 0,
177
- 'end_time': e_sec if e_sec else float('inf')
178
- }]
179
- download_ranges = range_func
180
-
181
- # 3. Execution Loop
182
- success = False
183
- final_file = None
184
-
185
- # Cookie File Creation (Temporary)
186
- cookie_path = None
187
- if cookies:
188
- cookie_path = f"cookies_{random.randint(1000,9999)}.txt"
189
- with open(cookie_path, "w") as f:
190
- f.write(cookies)
191
- log.append("πŸͺ Using provided cookies")
192
 
 
193
  for proxy in proxies:
194
  if success: break
195
-
196
  for strat in strategies:
197
- if success: break
198
-
199
- log.append(f"πŸ”„ Trying: Client={strat} | Proxy={proxy or 'Direct'}")
200
-
201
  ydl_opts = {
202
  'outtmpl': os.path.join(OUTPUT_DIR, filename_tmpl),
203
- 'noplaylist': True,
204
- 'quiet': True,
205
- 'force_ipv4': True,
206
- 'retries': 3,
207
- 'socket_timeout': 15,
208
- 'extractor_args': {'youtube': {'player_client': [strat]}},
209
- **format_opts
210
  }
211
-
212
  if proxy: ydl_opts['proxy'] = proxy
213
  if download_ranges: ydl_opts['download_ranges'] = download_ranges
214
- if cookie_path: ydl_opts['cookiefile'] = cookie_path
215
-
216
- # Progress Hook
217
- def hook(d):
218
- if d['status'] == 'downloading':
219
- total = d.get('total_bytes') or d.get('total_bytes_estimate')
220
- if total:
221
- progress(d.get('downloaded_bytes', 0) / total)
222
- ydl_opts['progress_hooks'] = [hook]
223
-
224
  try:
225
  with yt_dlp.YoutubeDL(ydl_opts) as ydl:
226
  info = ydl.extract_info(url, download=True)
227
  name = ydl.prepare_filename(info)
228
-
229
- # Extension fix
230
  base, _ = os.path.splitext(name)
231
- chk_ext = format_opts.get('ext', 'mp4')
232
- final_file = f"{base}.{chk_ext}"
233
-
234
- # Fallback check
235
- if not os.path.exists(final_file):
236
- # Try finding any file with that base name
237
- for f in os.listdir(OUTPUT_DIR):
238
- if f.startswith(os.path.basename(base)):
239
- final_file = os.path.join(OUTPUT_DIR, f)
240
- break
241
-
242
- if os.path.exists(final_file):
243
- success = True
244
- except Exception as e:
245
- log.append(f"⚠️ Error: {str(e)[:50]}")
246
-
247
- # Cleanup
248
- if cookie_path and os.path.exists(cookie_path):
249
- os.remove(cookie_path)
250
-
251
- with job_lock:
252
- active_jobs.pop(url, None)
253
-
254
- if success:
255
- return final_file, f"βœ… {engine_name}: Complete", "\n".join(log)
256
- return None, f"❌ {engine_name}: Failed", "\n".join(log)
257
 
258
  # =====================================================
259
- # ROUTER
260
  # =====================================================
261
- def smart_engine_logic(url):
262
- if not url: return "V1 β€’ Stable Engine"
263
- if len(url) > 70 or "list=" in url: return "V2 β€’ Fast Engine"
264
- return "V1 β€’ Stable Engine"
265
-
266
- def download_router(mode, quality, start, end, custom_name, cookies, url, progress=gr.Progress()):
267
- if "Auto" in mode:
268
- engine = smart_engine_logic(url)
269
- elif "V1" in mode:
270
- engine = "V1 β€’ Stable Engine"
271
- else:
272
- engine = "V2 β€’ Fast Engine"
273
-
274
- return run_downloader(engine, url, quality, start, end, custom_name, cookies, progress)
275
 
276
  # =====================================================
277
- # UI LAYOUT
278
  # =====================================================
279
- with gr.Blocks(title="UltraMax Ultimate", theme=gr.themes.Soft()) as app:
 
280
 
281
- gr.Markdown("# πŸš€ UltraMax: The Ultimate Downloader")
282
-
283
- # --- SECTION 1: INPUT ---
284
- with gr.Row(variant="panel"):
285
- with gr.Column(scale=4):
286
- url_input = gr.Textbox(
287
- label="Media URL",
288
- placeholder="Paste YouTube, Instagram, or TikTok link...",
289
- show_label=False
290
- )
291
- with gr.Column(scale=1):
292
- fetch_btn = gr.Button("πŸ” Fetch Info", variant="primary")
293
-
294
- # --- SECTION 2: METADATA ---
295
- with gr.Row():
296
- with gr.Column(scale=1):
297
- thumb_preview = gr.Image(label="Thumbnail", interactive=False, height=180)
298
- with gr.Column(scale=3):
299
- info_display = gr.Markdown("### ⏳ Waiting for link...")
300
- fetch_status = gr.Label(value="Idle", show_label=False)
301
-
302
- # --- SECTION 3: ADVANCED CONFIG ---
303
- with gr.Row(variant="panel"):
304
- with gr.Column(scale=1):
305
- quality_select = gr.Dropdown(
306
- ["Audio Only (Best)", "Video (4K / Best)", "Video (1080p)", "Video (720p)", "Video (360p / Data Saver)"],
307
- value="Audio Only (Best)",
308
- label="πŸ’Ž Quality / Format"
309
- )
310
- engine_mode = gr.Radio(
311
- ["Auto (Smart)", "V1 (Stable)", "V2 (Fast)"],
312
- value="Auto (Smart)",
313
- label="βš™οΈ Engine"
314
- )
315
 
316
- with gr.Column(scale=1):
317
- with gr.Accordion("βœ‚οΈ Cutter & Renamer", open=False):
318
- with gr.Row():
319
- start_time = gr.Textbox(label="Start (MM:SS)", placeholder="00:00")
320
- end_time = gr.Textbox(label="End (MM:SS)", placeholder="01:30")
321
- custom_filename = gr.Textbox(label="Rename File (Optional)", placeholder="MySong")
322
-
323
- with gr.Accordion("πŸͺ Cookies / Auth", open=False):
324
- cookies_input = gr.Textbox(
325
- label="Netscape Cookies (For Age-Restricted)",
326
- placeholder="Paste cookie content here...",
327
- lines=2
328
- )
329
-
330
- # --- SECTION 4: ACTION ---
331
- dl_btn = gr.Button("⬇️ START DOWNLOAD", variant="stop", interactive=False, size="lg")
332
-
333
- # --- SECTION 5: OUTPUT ---
334
- with gr.Accordion("πŸ“‚ Output & Logs", open=True):
335
- with gr.Row():
336
- file_out = gr.File(label="Ready to Download")
337
- log_out = gr.Textbox(label="System Logs", lines=5)
338
-
339
- # --- EVENTS ---
340
-
341
- fetch_btn.click(
342
- fn=fetch_metadata,
343
- inputs=[url_input],
344
- outputs=[thumb_preview, info_display, dl_btn, custom_filename, fetch_status]
345
- )
346
-
347
- dl_btn.click(
348
- fn=download_router,
349
- inputs=[engine_mode, quality_select, start_time, end_time, custom_filename, cookies_input, url_input],
350
- outputs=[file_out, fetch_status, log_out],
351
- concurrency_limit=MAX_CONCURRENCY
352
- )
353
-
354
- app.queue(max_size=MAX_QUEUE)
355
- app.launch()
 
2
  import os
3
  import gradio as gr
4
  import random
 
5
  import threading
6
+ import requests
 
 
7
  from gradio_client import Client
8
+ from huggingface_hub import InferenceClient
9
 
10
  # =====================================================
11
+ # CONFIG & CLIENTS
12
  # =====================================================
13
  PROXY_SPACE_URL = "lexicalspace/Proxy-Server"
14
  OUTPUT_DIR = "downloads"
 
17
  MAX_CONCURRENCY = 6
18
  MAX_QUEUE = 20
19
 
20
+ # SEO Tool Configuration
21
+ REPO_ID = "Qwen/Qwen2.5-Coder-32B-Instruct"
22
+ seo_client = InferenceClient(model=REPO_ID)
23
+
24
  # =====================================================
25
  # GLOBAL STATE
26
  # =====================================================
 
28
  job_lock = threading.Lock()
29
 
30
  # =====================================================
31
+ # DOWNLOADER HELPERS & LOGIC
32
  # =====================================================
33
  def get_proxy_batch():
34
  try:
 
54
  return None
55
 
56
  def parse_time(time_str):
 
57
  if not time_str: return None
58
  try:
59
  parts = list(map(int, time_str.split(':')))
 
65
  return None
66
 
67
  def build_format_selector(quality_mode):
 
68
  if quality_mode == "Audio Only (Best)":
69
+ return {'format': 'bestaudio/best', 'postprocessors': [{'key': 'FFmpegExtractAudio','preferredcodec': 'mp3'}], 'ext': 'mp3'}
 
 
 
 
70
  elif quality_mode == "Video (4K / Best)":
71
+ return {'format': 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best', 'ext': 'mp4'}
 
 
 
72
  elif quality_mode == "Video (1080p)":
73
+ return {'format': 'bestvideo[height<=1080][ext=mp4]+bestaudio[ext=m4a]/best[height<=1080][ext=mp4]', 'ext': 'mp4'}
 
 
 
74
  elif quality_mode == "Video (720p)":
75
+ return {'format': 'bestvideo[height<=720][ext=mp4]+bestaudio[ext=m4a]/best[height<=720][ext=mp4]', 'ext': 'mp4'}
 
 
 
76
  elif quality_mode == "Video (360p / Data Saver)":
77
+ return {'format': 'bestvideo[height<=360][ext=mp4]+bestaudio[ext=m4a]/best[height<=360][ext=mp4]', 'ext': 'mp4'}
 
 
 
78
  return {}
79
 
 
 
 
80
  def fetch_metadata(url):
81
  if not url:
82
  return None, "⚠️ Please enter a URL first.", None, None, None
 
 
83
  api_url = f"https://noembed.com/embed?url={url}"
 
84
  try:
85
  response = requests.get(api_url, timeout=10)
86
  data = response.json()
 
87
  if "error" in data:
88
  return None, f"❌ API Error: {data.get('error')}", gr.update(interactive=False), None, None
 
 
89
  title = data.get('title', 'Unknown Title')
90
  uploader = data.get('author_name', 'Unknown Channel')
91
  thumb = data.get('thumbnail_url', None)
92
+ desc = f"### 🎬 {title}\n**πŸ‘€ Channel:** {uploader}\n**⏱️ Duration:** N/A\n**πŸ‘οΈ Views:** N/A"
 
 
 
 
 
 
 
 
 
 
 
 
93
  safe_title = "".join([c for c in title if c.isalnum() or c in " ._-"])
94
+ return thumb, desc, gr.update(interactive=True), gr.update(value=safe_title), "βœ… Metadata fetched"
 
 
95
  except Exception as e:
96
+ return None, f"❌ Error: {str(e)}", gr.update(interactive=False), None, None
97
 
 
 
 
98
  def run_downloader(engine_name, url, quality, start_time, end_time, custom_name, cookies, progress):
99
+ log = [f"πŸš€ Engine: {engine_name}"]
 
 
 
100
  with job_lock:
101
+ if active_jobs.get(url) == "running": return None, "β›” Already running", "Blocked"
 
102
  active_jobs[url] = "running"
 
 
 
103
 
104
+ format_opts = build_format_selector(quality)
105
  filename_tmpl = f"{custom_name}.%(ext)s" if custom_name else '%(title)s.%(ext)s'
106
+ proxies = get_proxy_batch() if "V1" in engine_name else ([get_best_proxy()] if get_best_proxy() else [None])
107
+ strategies = ["android", "ios", "web"]
108
 
109
+ s_sec, e_sec = parse_time(start_time), parse_time(end_time)
110
+ download_ranges = (lambda info, ydl: [{'start_time': s_sec or 0, 'end_time': e_sec or float('inf')}]) if (s_sec or e_sec) else None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
 
112
+ success, final_file = False, None
113
  for proxy in proxies:
114
  if success: break
 
115
  for strat in strategies:
 
 
 
 
116
  ydl_opts = {
117
  'outtmpl': os.path.join(OUTPUT_DIR, filename_tmpl),
118
+ 'noplaylist': True, 'quiet': True, 'force_ipv4': True,
119
+ 'extractor_args': {'youtube': {'player_client': [strat]}}, **format_opts
 
 
 
 
 
120
  }
 
121
  if proxy: ydl_opts['proxy'] = proxy
122
  if download_ranges: ydl_opts['download_ranges'] = download_ranges
 
 
 
 
 
 
 
 
 
 
123
  try:
124
  with yt_dlp.YoutubeDL(ydl_opts) as ydl:
125
  info = ydl.extract_info(url, download=True)
126
  name = ydl.prepare_filename(info)
 
 
127
  base, _ = os.path.splitext(name)
128
+ final_file = f"{base}.{format_opts.get('ext', 'mp4')}"
129
+ if os.path.exists(final_file): success = True; break
130
+ except: continue
131
+
132
+ with job_lock: active_jobs.pop(url, None)
133
+ return (final_file, "βœ… Complete", "\n".join(log)) if success else (None, "❌ Failed", "\n".join(log))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
134
 
135
  # =====================================================
136
+ # SEO TOOL LOGIC
137
  # =====================================================
138
+ def generate_seo(code_snippet, file_type):
139
+ if not code_snippet.strip(): return "Please paste some code first."
140
+ prompt = f"Expert SEO analysis for {file_type} code:\n{code_snippet}\nGenerate Title, Meta Description, Keywords, and JSON-LD."
141
+ try:
142
+ return seo_client.text_generation(prompt, max_new_tokens=1024, temperature=0.3)
143
+ except Exception as e:
144
+ return f"Error: {str(e)}"
 
 
 
 
 
 
 
145
 
146
  # =====================================================
147
+ # COMBINED GRADIO UI
148
  # =====================================================
149
+ with gr.Blocks(title="Lexical Space Tools", theme=gr.themes.Soft()) as demo:
150
+ gr.Markdown("# πŸ› οΈ Lexical Space Multi-Tool")
151
 
152
+ with gr.Tabs():
153
+ # --- TAB 1: DOWNLOADER ---
154
+ with gr.TabItem("πŸš€ UltraMax Downloader"):
155
+ with gr.Row(variant="panel"):
156
+ url_input = gr.Textbox(label="Media URL", placeholder="Paste link here...")
157
+ fetch_btn = gr.Button("πŸ” Fetch Info", variant="primary")
158
+ with gr.Row():
159
+ thumb_preview = gr.Image(label="Thumbnail", interactive=False, height=180)
160
+ info_display = gr.Markdown("### ⏳ Waiting for link...")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
161
 
162
+ with gr.Row(variant="panel"):
163
+ quality_select = gr.Dropdown(["Audio Only (Best)", "Video (4K / Best)", "Video (1080p)", "Video (720p)"], value="Audio Only (Best)", label="Quality")
164
+ engine_mode = gr.Radio(["Auto (Smart)", "V1 (Stable)", "V2 (Fast)"], value="Auto (Smart)", label="Engine")
165
+ custom_filename = gr.Textbox(label="Rename File", placeholder="MyFile")
166
+
167
+ dl_btn = gr.Button("⬇️ START DOWNLOAD", variant="stop", interactive=False)
168
+ file_out = gr.File(label="Output")
169
+ log_out = gr.Textbox(label="Logs", lines=3)
170
+
171
+ fetch_btn.click(fetch_metadata, [url_input], [thumb_preview, info_display, dl_btn, custom_filename])
172
+ dl_btn.click(run_downloader, [engine_mode, url_input, quality_select, gr.State(""), gr.State(""), custom_filename, gr.State("")], [file_out, gr.State(), log_out])
173
+
174
+ # --- TAB 2: SEO GENERATOR ---
175
+ with gr.TabItem("πŸ” AI Code SEO"):
176
+ gr.Markdown("### πŸš€ AI Code-to-SEO Generator")
177
+ with gr.Row():
178
+ with gr.Column():
179
+ input_type = gr.Radio(["python", "html"], label="File Type", value="python")
180
+ code_input = gr.Code(language="python", label="Paste Code", lines=10)
181
+ submit_btn = gr.Button("Generate SEO Strategy", variant="primary")
182
+ with gr.Column():
183
+ output_markdown = gr.Markdown(label="Generated Result")
184
+
185
+ input_type.change(lambda x: gr.Code(language=x), inputs=input_type, outputs=code_input)
186
+ submit_btn.click(generate_seo, [code_input, input_type], [output_markdown])
187
+
188
+ demo.queue(max_size=MAX_QUEUE).launch()