Tu Nombre commited on
Commit
0c4802c
·
1 Parent(s): 3db5e34

fix: IndentationError template externo

Browse files
Files changed (2) hide show
  1. cliente_template.py +111 -0
  2. main.py +2 -121
cliente_template.py ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os, asyncio, time, random, requests, threading
2
+ import gradio as gr
3
+ from groq import Groq
4
+ import edge_tts
5
+ from moviepy.editor import VideoFileClip, AudioFileClip, TextClip, CompositeVideoClip
6
+
7
+ NICHO = "NICHO_PLACEHOLDER"
8
+ GROQ_KEY = os.environ.get("GROQ_KEY","")
9
+ PEXELS_KEY = os.environ.get("PEXELS_KEY","")
10
+ LOGS = []
11
+ STATUS = "Iniciando..."
12
+
13
+ def log(msg):
14
+ t = time.strftime("%H:%M:%S")
15
+ LOGS.append(f"[{t}] {msg}")
16
+ if len(LOGS) > 40: LOGS.pop(0)
17
+ print(msg, flush=True)
18
+
19
+ def generar_guion():
20
+ client = Groq(api_key=GROQ_KEY)
21
+ res = client.chat.completions.create(
22
+ messages=[{"role":"user","content":f"Crea un guion de video de 60 segundos sobre {NICHO}. Directo, sin simbolos ni hashtags."}],
23
+ model="llama-3.3-70b-versatile", temperature=0.82
24
+ )
25
+ return res.choices[0].message.content.strip()
26
+
27
+ async def pipeline():
28
+ global STATUS
29
+ while True:
30
+ try:
31
+ STATUS = "Generando guion..."
32
+ log(f"Nuevo video: {NICHO}")
33
+ guion = generar_guion()
34
+ log("Guion listo")
35
+ STATUS = "Generando voz..."
36
+ await edge_tts.Communicate(guion, "es-ES-AlvaroNeural").save("voz.mp3")
37
+ log("Voz lista")
38
+ STATUS = "Descargando fondo..."
39
+ h = {"Authorization": PEXELS_KEY}
40
+ r = requests.get(f"https://api.pexels.com/videos/search?query={NICHO}&per_page=10", headers=h)
41
+ vids = r.json().get("videos", [])
42
+ if vids:
43
+ url = random.choice(vids)["video_files"][0]["link"]
44
+ with open("fondo.mp4","wb") as f: f.write(requests.get(url).content)
45
+ log("Fondo listo")
46
+ STATUS = "Renderizando video..."
47
+ from moviepy.editor import AudioFileClip as AC, VideoFileClip as VC
48
+ voz = AC("voz.mp3")
49
+ clip = VC("fondo.mp4").resize(width=1920).loop(duration=voz.duration)
50
+ palabras = guion.split()
51
+ frases = [" ".join(palabras[i:i+6]) for i in range(0,len(palabras),6)]
52
+ t_f = voz.duration / max(len(frases),1)
53
+ txts = [TextClip(t.upper(), fontsize=48, color="#D4AF37", font="DejaVu-Sans-Bold",
54
+ stroke_color="black", stroke_width=2, method="caption",
55
+ size=(clip.w*0.8,None)).set_start(i*t_f).set_duration(t_f).set_pos(("center",880))
56
+ for i,t in enumerate(frases)]
57
+ final = CompositeVideoClip([clip]+txts).set_audio(voz)
58
+ final.write_videofile("out.mp4", fps=20, codec="libx264", preset="ultrafast", logger=None)
59
+ log("Video renderizado")
60
+ STATUS = "Subiendo a YouTube..."
61
+ try:
62
+ import json, google.oauth2.credentials, googleapiclient.discovery, googleapiclient.http
63
+ with open("token.json") as f:
64
+ tok = json.load(f)
65
+ creds = google.oauth2.credentials.Credentials(
66
+ token=tok["access_token"],
67
+ refresh_token=tok.get("refresh_token"),
68
+ token_uri="https://oauth2.googleapis.com/token",
69
+ client_id=tok.get("client_id",""),
70
+ client_secret=tok.get("client_secret","")
71
+ )
72
+ yt = googleapiclient.discovery.build("youtube","v3",credentials=creds)
73
+ titulo = guion[:80].split(".")[0]
74
+ req_yt = yt.videos().insert(
75
+ part="snippet,status",
76
+ body={
77
+ "snippet":{"title":titulo,"description":guion,"categoryId":"22"},
78
+ "status":{"privacyStatus":"public"}
79
+ },
80
+ media_body=googleapiclient.http.MediaFileUpload("out.mp4",mimetype="video/mp4",resumable=True)
81
+ )
82
+ resp = None
83
+ while resp is None:
84
+ _, resp = req_yt.next_chunk()
85
+ log(f"Subido: {titulo[:40]}")
86
+ except FileNotFoundError:
87
+ log("Sin token.json, saltando subida")
88
+ except Exception as ey:
89
+ log(f"Error YouTube: {ey}")
90
+ STATUS = "Esperando siguiente ciclo..."
91
+ await asyncio.sleep(14400)
92
+ except Exception as e:
93
+ log(f"Error: {e}")
94
+ STATUS = f"Error: {str(e)[:40]}"
95
+ await asyncio.sleep(600)
96
+
97
+ def start():
98
+ loop = asyncio.new_event_loop()
99
+ asyncio.set_event_loop(loop)
100
+ loop.run_until_complete(pipeline())
101
+
102
+ threading.Thread(target=start, daemon=True).start()
103
+
104
+ with gr.Blocks(css="body{background:#0a0a0a!important;} .gradio-container{background:#0a0a0a!important;color:#D4AF37!important;}") as demo:
105
+ gr.Markdown(f"# TubeBot — {NICHO.upper()}")
106
+ gr.Markdown("Tu canal está generando videos automáticamente 24/7")
107
+ gr.Textbox(label="Logs en tiempo real", lines=18, every=4,
108
+ value=lambda: "\n".join(LOGS[-18:]) if LOGS else "Iniciando sistema...")
109
+ gr.Textbox(label="Estado actual", every=3, value=lambda: STATUS)
110
+
111
+ demo.launch()
main.py CHANGED
@@ -94,127 +94,8 @@ async def crear_space(nicho: str, email: str) -> str:
94
  repo_id = f"{HF_USER}/{space_id}"
95
  headers = {"Authorization": f"Bearer {HF_TOKEN}", "Content-Type": "application/json"}
96
 
97
- import textwrap
98
- main_code = textwrap.dedent(f'''
99
- import os, asyncio, time, random, requests, threading
100
- import gradio as gr
101
- from groq import Groq
102
- import edge_tts
103
- from moviepy.editor import VideoFileClip, AudioFileClip, TextClip, CompositeVideoClip
104
-
105
- NICHO = "{nicho}"
106
- GROQ_KEY = os.environ.get("GROQ_KEY","")
107
- PEXELS_KEY = os.environ.get("PEXELS_KEY","")
108
- LOGS = []
109
- STATUS = "Iniciando..."
110
-
111
- def log(msg):
112
- t = time.strftime("%H:%M:%S")
113
- LOGS.append(f"[{{t}}] {{msg}}")
114
- if len(LOGS) > 40: LOGS.pop(0)
115
- print(msg, flush=True)
116
-
117
- def generar_guion():
118
- client = Groq(api_key=GROQ_KEY)
119
- res = client.chat.completions.create(
120
- messages=[{{"role":"user","content":f"Crea un guion de video de 60 segundos sobre {{NICHO}}. Directo, sin simbolos ni hashtags."}}],
121
- model="llama-3.3-70b-versatile", temperature=0.82
122
- )
123
- return res.choices[0].message.content.strip()
124
-
125
- async def pipeline():
126
- global STATUS
127
- while True:
128
- try:
129
- STATUS = "Generando guion..."
130
- log(f"🎬 Nuevo video: {{NICHO}}")
131
- guion = generar_guion()
132
- log("✅ Guion listo")
133
-
134
- STATUS = "Generando voz..."
135
- await edge_tts.Communicate(guion, "es-ES-AlvaroNeural").save("voz.mp3")
136
- log("✅ Voz lista")
137
-
138
- STATUS = "Descargando fondo..."
139
- h = {{"Authorization": PEXELS_KEY}}
140
- r = requests.get(f"https://api.pexels.com/videos/search?query={{NICHO}}&per_page=10", headers=h)
141
- vids = r.json().get("videos", [])
142
- if vids:
143
- url = random.choice(vids)["video_files"][0]["link"]
144
- with open("fondo.mp4","wb") as f: f.write(requests.get(url).content)
145
- log("✅ Fondo listo")
146
-
147
- STATUS = "Renderizando video..."
148
- from moviepy.editor import AudioFileClip as AC, VideoFileClip as VC
149
- voz = AC("voz.mp3")
150
- clip = VC("fondo.mp4").resize(width=1920).loop(duration=voz.duration)
151
- palabras = guion.split()
152
- frases = [" ".join(palabras[i:i+6]) for i in range(0,len(palabras),6)]
153
- t_f = voz.duration / max(len(frases),1)
154
- txts = [TextClip(t.upper(), fontsize=48, color="#D4AF37", font="DejaVu-Sans-Bold",
155
- stroke_color="black", stroke_width=2, method="caption",
156
- size=(clip.w*0.8,None)).set_start(i*t_f).set_duration(t_f).set_pos(("center",880))
157
- for i,t in enumerate(frases)]
158
- final = CompositeVideoClip([clip]+txts).set_audio(voz)
159
- final.write_videofile("out.mp4", fps=20, codec="libx264", preset="ultrafast", logger=None)
160
- log("✅ Video renderizado")
161
-
162
- STATUS = "Subiendo a YouTube..."
163
- try:
164
- import json, google.oauth2.credentials, googleapiclient.discovery, googleapiclient.http
165
- with open("token.json") as f:
166
- tok = json.load(f)
167
- creds = google.oauth2.credentials.Credentials(
168
- token=tok["access_token"],
169
- refresh_token=tok.get("refresh_token"),
170
- token_uri="https://oauth2.googleapis.com/token",
171
- client_id=tok.get("client_id",""),
172
- client_secret=tok.get("client_secret","")
173
- )
174
- yt = googleapiclient.discovery.build("youtube","v3",credentials=creds)
175
- titulo = guion[:80].split(".")[0]
176
- req_yt = yt.videos().insert(
177
- part="snippet,status",
178
- body={{
179
- "snippet":{{"title":titulo,"description":guion,"categoryId":"22"}},
180
- "status":{{"privacyStatus":"public"}}
181
- }},
182
- media_body=googleapiclient.http.MediaFileUpload("out.mp4",mimetype="video/mp4",resumable=True)
183
- )
184
- resp = None
185
- while resp is None:
186
- _, resp = req_yt.next_chunk()
187
- log(f"✅ Subido: {{titulo[:40]}}")
188
- except FileNotFoundError:
189
- log("⚠️ Sin token.json, saltando subida")
190
- except Exception as ey:
191
- log(f"❌ Error YouTube: {{ey}}")
192
-
193
- STATUS = "Esperando siguiente ciclo..."
194
- log("⏳ Esperando 4 horas...")
195
- await asyncio.sleep(14400)
196
-
197
- except Exception as e:
198
- log(f"❌ Error: {{e}}")
199
- STATUS = f"Error: {{str(e)[:40]}}"
200
- await asyncio.sleep(600)
201
-
202
- def start():
203
- loop = asyncio.new_event_loop()
204
- asyncio.set_event_loop(loop)
205
- loop.run_until_complete(pipeline())
206
-
207
- threading.Thread(target=start, daemon=True).start()
208
-
209
- with gr.Blocks(css="body{{background:#0a0a0a!important;}} .gradio-container{{background:#0a0a0a!important;color:#D4AF37!important;}}") as demo:
210
- gr.Markdown(f"# 🤖 TubeBot — {{NICHO.upper()}}")
211
- gr.Markdown("Tu canal está generando videos automáticamente 24/7")
212
- gr.Textbox(label="📜 Logs en tiempo real", lines=18, every=4,
213
- value=lambda: "\\n".join(LOGS[-18:]) if LOGS else "Iniciando sistema...")
214
- gr.Textbox(label="⚡ Estado actual", every=3, value=lambda: STATUS)
215
-
216
- demo.launch()
217
- ''').strip()
218
 
219
  req = "gradio\ngroq\nedge-tts\nmoviepy\nrequests\nhttpx\ngoogle-api-python-client\ngoogle-auth\ngoogle-auth-oauthlib"
220
 
 
94
  repo_id = f"{HF_USER}/{space_id}"
95
  headers = {"Authorization": f"Bearer {HF_TOKEN}", "Content-Type": "application/json"}
96
 
97
+ with open("cliente_template.py", "r") as _f:
98
+ main_code = _f.read().replace("NICHO_PLACEHOLDER", nicho)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
 
100
  req = "gradio\ngroq\nedge-tts\nmoviepy\nrequests\nhttpx\ngoogle-api-python-client\ngoogle-auth\ngoogle-auth-oauthlib"
101