Reooiq commited on
Commit
56bcdb4
·
verified ·
1 Parent(s): 44fbcc3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +155 -419
app.py CHANGED
@@ -1,8 +1,3 @@
1
- """
2
- 📹 TikTok Live Recorder - Hugging Face Spaces
3
- Record TikTok live streams and upload to Telegram (unlimited free storage)
4
- """
5
-
6
  import streamlit as st
7
  import subprocess
8
  import time
@@ -11,514 +6,255 @@ from datetime import datetime
11
  from pathlib import Path
12
  import requests
13
 
14
- # Page configuration
15
  st.set_page_config(
16
  page_title="TikTok Live Recorder",
17
  page_icon="📹",
18
- layout="wide",
19
- initial_sidebar_state="collapsed"
20
  )
21
 
22
- # Custom CSS
23
  st.markdown("""
24
  <style>
25
- .main-header {
26
- font-size: 2.5rem;
27
- font-weight: bold;
28
- background: linear-gradient(90deg, #ff0050, #00f2ea);
29
- -webkit-background-clip: text;
30
- -webkit-text-fill-color: transparent;
31
- margin-bottom: 0.5rem;
32
- }
33
- .sub-header {
34
- color: #888;
35
- margin-bottom: 2rem;
36
- }
37
- .status-box {
38
- padding: 1rem;
39
- border-radius: 0.5rem;
40
- margin: 1rem 0;
41
- text-align: center;
42
- font-weight: bold;
43
- }
44
- .status-live {
45
- background: linear-gradient(90deg, #ff4444, #ff0000);
46
- color: white;
47
- animation: pulse 2s infinite;
48
- }
49
- .status-offline {
50
- background: linear-gradient(90deg, #444444, #222222);
51
- color: #aaa;
52
- }
53
- .status-recording {
54
- background: linear-gradient(90deg, #ff6b6b, #ee5a5a);
55
- color: white;
56
- animation: pulse 1s infinite;
57
- }
58
- .status-ready {
59
- background: linear-gradient(90deg, #00ff88, #00cc66);
60
- color: black;
61
- }
62
- @keyframes pulse {
63
- 0%, 100% { opacity: 1; }
64
- 50% { opacity: 0.7; }
65
- }
66
- .stat-card {
67
- background: rgba(255, 255, 255, 0.05);
68
- padding: 1.5rem;
69
- border-radius: 1rem;
70
- text-align: center;
71
- border: 1px solid rgba(255, 255, 255, 0.1);
72
- height: 100%;
73
- }
74
- .stat-value {
75
- font-size: 2.5rem;
76
- font-weight: bold;
77
- background: linear-gradient(90deg, #00f2ea, #ff0050);
78
- -webkit-background-clip: text;
79
- -webkit-text-fill-color: transparent;
80
- }
81
- .stat-label {
82
- font-size: 0.9rem;
83
- color: #888;
84
- margin-top: 0.5rem;
85
- }
86
  </style>
87
  """, unsafe_allow_html=True)
88
 
89
- # Session state initialization
90
  if 'recording' not in st.session_state:
91
  st.session_state.recording = False
92
  if 'process' not in st.session_state:
93
  st.session_state.process = None
94
- if 'stats' not in st.session_state:
95
- st.session_state.stats = {'viewers': 0, 'likes': 0, 'comments': 0, 'gifts': 0}
96
  if 'stream_url' not in st.session_state:
97
  st.session_state.stream_url = None
98
- if 'room_id' not in st.session_state:
99
- st.session_state.room_id = None
100
- if 'start_time' not in st.session_state:
101
- st.session_state.start_time = None
102
  if 'username' not in st.session_state:
103
  st.session_state.username = None
104
  if 'video_path' not in st.session_state:
105
  st.session_state.video_path = None
 
 
106
 
107
- # Create output directory
108
  OUTPUT_DIR = "/tmp/tiktok_recordings"
109
  Path(OUTPUT_DIR).mkdir(parents=True, exist_ok=True)
110
 
111
  def check_tiktok_live(username):
112
- """Check if TikTok user is live and get stream info"""
113
  try:
114
  from TikTokLive import TikTokLiveClient
115
-
116
  client = TikTokLiveClient(unique_id=f"@{username}")
117
  is_live = client.fetch_is_live()
118
 
119
  if not is_live:
120
- return {'is_live': False, 'error': 'المستخدم ليس بث مباشر حالياً'}
121
 
122
  room_info = client.fetch_room_info()
123
-
124
  stream_url = None
 
125
  if hasattr(room_info, 'stream_url') and room_info.stream_url:
126
- if hasattr(room_info.stream_url, 'hls_pull_url') and room_info.stream_url.hls_pull_url:
127
  stream_url = room_info.stream_url.hls_pull_url
128
- elif hasattr(room_info.stream_url, 'flv_pull_url') and room_info.stream_url.flv_pull_url:
129
  stream_url = room_info.stream_url.flv_pull_url
130
 
131
  return {
132
  'is_live': True,
133
  'stream_url': stream_url,
134
  'room_id': getattr(room_info, 'webcast_room_id', None),
135
- 'title': getattr(room_info, 'title', f'بث @{username}'),
136
  'viewer_count': getattr(room_info, 'user_count', 0)
137
  }
138
  except Exception as e:
139
  return {'is_live': False, 'error': str(e)}
140
 
141
- def start_ffmpeg_recording(stream_url, output_path):
142
- """Start FFmpeg recording of the stream"""
143
  try:
144
- cmd = [
145
- 'ffmpeg',
146
- '-y',
147
- '-re',
148
- '-i', stream_url,
149
- '-c:v', 'copy',
150
- '-c:a', 'copy',
151
- '-f', 'mp4',
152
- output_path
153
- ]
154
-
155
- process = subprocess.Popen(
156
- cmd,
157
- stdout=subprocess.PIPE,
158
- stderr=subprocess.PIPE
159
- )
160
-
161
  return process
162
  except Exception as e:
163
- st.error(f"فشل بدء التسجيل: {e}")
164
  return None
165
 
166
- def stop_ffmpeg_recording():
167
- """Stop the FFmpeg recording process"""
168
  if st.session_state.process:
169
  try:
170
  st.session_state.process.terminate()
171
  st.session_state.process.wait(timeout=5)
172
  except:
173
  st.session_state.process.kill()
174
- finally:
175
- st.session_state.process = None
176
- st.session_state.recording = False
177
 
178
- def format_duration(seconds):
179
- """Format seconds to HH:MM:SS"""
180
- hours = int(seconds // 3600)
181
- minutes = int((seconds % 3600) // 60)
182
  secs = int(seconds % 60)
183
-
184
- if hours > 0:
185
- return f"{hours}:{minutes:02d}:{secs:02d}"
186
- return f"{minutes}:{secs:02d}"
187
-
188
- def get_file_size_mb(filepath):
189
- """Get file size in MB"""
190
- try:
191
- return os.path.getsize(filepath) / (1024 * 1024)
192
- except:
193
- return 0
194
 
195
- def upload_to_telegram_bot(file_path, bot_token, chat_id):
196
- """Upload video to Telegram using Bot API"""
197
  try:
198
  url = f"https://api.telegram.org/bot{bot_token}/sendVideo"
199
-
200
  with open(file_path, 'rb') as video:
201
  files = {'video': video}
202
  data = {
203
  'chat_id': chat_id,
204
- 'caption': f"📹 TikTok Live - @{st.session_state.username}\n📅 {datetime.now().strftime('%Y-%m-%d %H:%M')}\n⏱️ Duration: {format_duration(time.time() - st.session_state.start_time)}"
205
  }
206
-
207
  response = requests.post(url, files=files, data=data, timeout=300)
208
-
209
  if response.status_code == 200:
210
  result = response.json()
211
  if result.get('ok'):
212
- file_id = result['result']['video']['file_id']
213
- return {
214
- 'success': True,
215
- 'file_id': file_id,
216
- 'message': 'تم الرفع بنجاح إلى Telegram!'
217
- }
218
-
219
- return {'success': False, 'error': 'فشل الرفع'}
220
  except Exception as e:
221
  return {'success': False, 'error': str(e)}
222
 
223
- def prepare_internet_archive_upload(username, duration, stats):
224
- """Prepare metadata for Internet Archive upload"""
225
- identifier = f"tiktok-live-{username}-{int(time.time())}"
226
-
227
- metadata = {
228
- 'identifier': identifier,
229
- 'title': f'TikTok Live - @{username}',
230
- 'description': f'Recorded TikTok Live stream from @{username}',
231
- 'creator': username,
232
- 'date': datetime.now().isoformat(),
233
- 'subject': 'tiktok; live; stream',
234
- 'mediatype': 'movies',
235
- 'duration': format_duration(duration)
236
- }
237
-
238
- upload_url = f"https://archive.org/services/edit/{identifier}"
239
-
240
- return {
241
- 'success': True,
242
- 'identifier': identifier,
243
- 'upload_url': upload_url,
244
- 'metadata': metadata
245
- }
246
-
247
- # Main UI
248
  st.markdown('<h1 class="main-header">📹 TikTok Live Recorder</h1>', unsafe_allow_html=True)
249
- st.markdown('<p class="sub-header">تسجيل بثوث تيك توك المباشرة مع رفع مجاني غير محدود على Telegram</p>')
250
 
251
- # Main content
252
- col_main1, col_main2 = st.columns([2, 1])
253
 
254
- with col_main1:
255
- # Input section
256
- st.markdown("### 🔗 الاتصال بالبث المباشر")
257
-
258
- username_input = st.text_input(
259
- "اسم المستخدم في TikTok",
260
- placeholder="أدخل اليوزر بدون @"
261
- )
262
-
263
- col_btn1, col_btn2, col_btn3 = st.columns([1, 1, 1])
264
-
265
- with col_btn1:
266
- if st.button("🔍 التحقق من البث", use_container_width=True, key="check_live"):
267
- if username_input:
268
- with st.spinner("جاري التحقق..."):
269
- result = check_tiktok_live(username_input)
270
-
271
- if result.get('is_live'):
272
- st.success(f"✅ @{username_input} يبث الآن!")
273
- st.session_state.stream_url = result.get('stream_url')
274
- st.session_state.room_id = result.get('room_id')
275
- st.session_state.username = username_input
276
- st.session_state.stats['viewers'] = result.get('viewer_count', 0)
277
- st.rerun()
278
- else:
279
- st.error(f"❌ @{username_input} لا يبث الآن. {result.get('error', '')}")
280
-
281
- with col_btn2:
282
- if st.session_state.stream_url:
283
- if st.session_state.recording:
284
- if st.button("⏹️ إيقاف التسجيل", type="secondary", use_container_width=True):
285
- stop_ffmpeg_recording()
286
- st.success("تم إيقاف التسجيل!")
287
  st.rerun()
288
- else:
289
- if st.button("🔴 بدء التسجيل", type="primary", use_container_width=True):
290
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
291
- output_path = f"{OUTPUT_DIR}/tiktok_{st.session_state.username}_{timestamp}.mp4"
292
-
293
- st.session_state.process = start_ffmpeg_recording(st.session_state.stream_url, output_path)
294
-
295
- if st.session_state.process:
296
- st.session_state.recording = True
297
- st.session_state.start_time = time.time()
298
- st.session_state.video_path = output_path
299
- st.success("🎬 بدأ التسجيل!")
300
- st.rerun()
301
-
302
- with col_btn3:
303
  if st.session_state.recording:
304
- status = "🔴 جاري التسجيل"
305
- status_class = "status-recording"
306
- elif st.session_state.stream_url:
307
- status = "🟢 جاهز للتسجيل"
308
- status_class = "status-ready"
309
  else:
310
- status = "⚪ غير متصل"
311
- status_class = "status-offline"
312
-
313
- st.markdown(f'<div class="status-box {status_class}">{status}</div>', unsafe_allow_html=True)
314
-
315
- # Recording status
 
 
 
 
 
 
316
  if st.session_state.recording:
317
- st.divider()
318
-
319
- duration = time.time() - st.session_state.start_time if st.session_state.start_time else 0
320
-
321
- col_stat1, col_stat2, col_stat3 = st.columns(3)
322
-
323
- with col_stat1:
324
- st.metric("الحالة", "🔴 تسجيل", delta=None)
325
-
326
- with col_stat2:
327
- st.metric("المدة", format_duration(duration), delta=None)
328
-
329
- with col_stat3:
330
- st.metric("المستخدم", f"@{st.session_state.username}", delta=None)
331
-
332
- st.progress(1.0, text="جاري التسجيل...")
333
-
334
- # Upload section (after recording)
335
- if not st.session_state.recording and st.session_state.video_path and os.path.exists(st.session_state.video_path):
336
- st.divider()
337
- st.markdown("### ☁️ رفع التسجيل")
338
-
339
- file_size = get_file_size_mb(st.session_state.video_path)
340
- st.info(f"📊 حجم الملف: {file_size:.1f} MB")
341
-
342
- upload_method = st.radio(
343
- "اختر منصة الرفع",
344
- ["Telegram (تخزين غير محدود مجاني)", "Internet Archive (أرشيف دائم)", "تحميل محلي فقط"],
345
- index=0
346
- )
347
-
348
- if upload_method == "Telegram (تخزين غير محدود مجاني)":
349
- st.markdown("""
350
- **✅ مميزات Telegram:**
351
- - تخزين مجاني غير محدود
352
- - ملفات حتى 2 جيجابايت
353
- - روابط دائمة
354
- - سهولة المشاركة
355
- """)
356
-
357
- with st.expander("⚙️ إعدادات Telegram"):
358
- bot_token = st.text_input("Bot Token", placeholder="123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11", type="password")
359
- chat_id = st.text_input("Chat ID", placeholder="-1001234567890")
360
-
361
- st.info("""
362
- **للحصول على Bot Token و Chat ID:**
363
- 1. أنشئ بوت عبر @BotFather في Telegram
364
- 2. احصل على Token من @BotFather
365
- 3. أضف البوت لقناة أو مجموعة
366
- 4. احصل على Chat ID عبر @getmyid_bot
367
- """)
368
-
369
- if st.button("📤 رفع إلى Telegram", type="primary"):
370
- if bot_token and chat_id:
371
- with st.spinner("جاري الرفع إلى Telegram..."):
372
- result = upload_to_telegram_bot(st.session_state.video_path, bot_token, chat_id)
373
-
374
- if result.get('success'):
375
- st.success(f"✅ {result['message']}")
376
- st.code(f"File ID: {result['file_id']}")
377
- else:
378
- st.error(f"❌ {result.get('error', 'فشل الرفع')}")
379
- else:
380
- st.error("يرجى إدخال Bot Token و Chat ID")
381
-
382
- elif upload_method == "Internet Archive (أرشيف دائم)":
383
- st.markdown("""
384
- **✅ مميزات Internet Archive:**
385
- - أرشيف دائم ومجاني
386
- - مناسب للأبحاث والتوثيق
387
- - وصول عام أو خاص
388
- """)
389
-
390
- if st.button("📤 تحضير الرفع إلى Archive.org"):
391
- result = prepare_internet_archive_upload(
392
- st.session_state.username,
393
- time.time() - st.session_state.start_time,
394
- st.session_state.stats
395
- )
396
-
397
- if result.get('success'):
398
- st.success("تم تحضير بيانات الرفع!")
399
- st.link_button("🌐 الذهاب لصفحة الرفع", result['upload_url'])
400
- st.json(result['metadata'])
401
-
402
- # Download button (always available)
403
- st.divider()
404
- with open(st.session_state.video_path, 'rb') as f:
405
- st.download_button(
406
- label="⬇️ تحميل الفيديو (MP4)",
407
- data=f,
408
- file_name=f"tiktok_{st.session_state.username}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4",
409
- mime="video/mp4",
410
- use_container_width=True
411
- )
412
 
413
- with col_main2:
414
- # Statistics
415
- st.markdown("### 📊 الإحصائيات")
416
 
417
- col1, col2 = st.columns(2)
418
- with col1:
419
- st.markdown(f"""
420
- <div class="stat-card">
421
- <div class="stat-value">{st.session_state.stats['viewers']:,}</div>
422
- <div class="stat-label">👁️ المشاهدين</div>
423
- </div>
424
- """, unsafe_allow_html=True)
425
 
426
- with col2:
427
- st.markdown(f"""
428
- <div class="stat-card">
429
- <div class="stat-value">{st.session_state.stats['likes']:,}</div>
430
- <div class="stat-label">❤️ اللايكات</div>
431
- </div>
432
- """, unsafe_allow_html=True)
433
 
434
- col3, col4 = st.columns(2)
435
- with col3:
436
- st.markdown(f"""
437
- <div class="stat-card">
438
- <div class="stat-value">{st.session_state.stats['comments']}</div>
439
- <div class="stat-label">💬 التعليقات</div>
440
- </div>
441
- """, unsafe_allow_html=True)
 
 
 
 
 
 
 
 
442
 
443
- with col4:
444
- st.markdown(f"""
445
- <div class="stat-card">
446
- <div class="stat-value">{st.session_state.stats['gifts']}</div>
447
- <div class="stat-label">🎁 الجولات</div>
448
- </div>
449
- """, unsafe_allow_html=True)
450
 
451
- # Info box
452
  st.divider()
453
- st.markdown("### ℹ️ معلومات")
454
-
455
- st.info("""
456
- **كيفية الاستخدام:**
457
- 1. أدخل يوزر تيك توك
458
- 2. اضغط "التحقق من البث"
459
- 3. اضغط "بدء التسجيل"
460
- 4. اضغط "إيقاف التسجيل" عند الانتهاء
461
- 5. اختر منصة الرفع أو حمل محلياً
462
- """)
463
-
464
- st.warning("""
465
- **⚠️ ملاحظات:**
466
- - يجب أن يكون المستخدم يبث حالياً
467
- - التسجيلات مؤقتة - حملها أو ارفعها فوراً
468
- - احترم حقوق صناع المحتوى
469
- """)
470
 
471
- # Footer
472
  st.divider()
473
  st.markdown("""
474
- <div style="text-align: center; color: #666; padding: 1rem;">
475
- <p>📹 TikTok Live Recorder - Built with ❤️ on Hugging Face Spaces</p>
476
- <p style="font-size: 0.8rem;">
477
- ⚠️ هذه الأداة تستخدم APIs غير رسمية. يرجى احترام حقوق صناع المحتوى وشروط خدمة TikTok.
478
- </p>
479
- </div>
480
- """, unsafe_allow_html=True)
481
 
482
- # Help expander
483
- with st.expander("📖 دليل الاستخدام الكامل"):
484
  st.markdown("""
485
- ### 🎯 الميزات
486
-
487
- - ✅ **تسجيل حقيقي**: يستخدم FFmpeg لالتقاط البث مباشرة من خوادم TikTok
488
- - ✅ **أفضل جودة**: التسجيل بالجودة الأصلية بدون إعادة ترميز
489
- - ✅ **رفع مجاني**: Telegram يوفر تخزين مجاني غير محدود (حتى 2GB للملف)
490
- - ✅ **أرشيف دائم**: Internet Archive للأرشفة الدائمة
491
- - ✅ **تحميل محلي**: تحميل الفيديو كملف MP4
492
-
493
- ### 📤 منصات الرفع
494
-
495
- #### Telegram (موصى به)
496
- - تخزين مجاني غير محدود
497
- - ملفات حتى 2 جيجابايت (4GB للبريميوم)
498
- - روابط دائمة
499
- - سهولة المشاركة
500
-
501
- #### Internet Archive
502
- - أرشيف دائم ومجاني
503
- - مناسب للتوثيق والأبحاث
504
- - وصول عام أو خاص
505
-
506
- ### ⚠️ القيود
507
-
508
- - مساحة Hugging Face محدودة - احمل أو ارفع فوراً
509
- - يجب أن يكون المستخدم LIVE حالياً
510
- - بعض البثوث قد يكون لها قيود جغرافية
511
-
512
- ### 🔧 متطلبات Telegram
513
 
514
- للرفع على Telegram تحتاج:
515
- 1. **Bot Token**: أنشئ بوت عبر @BotFather
516
- 2. **Chat ID**: معرف القناة/المجموعة
517
 
518
- **الخطوات:**
519
- 1. افتح Telegram وابحث عن @BotFather
520
- 2. أرسل /newbot وأنشئ بوت جديد
521
- 3. احفظ Token الذي ستحصل عليه
522
- 4. أضف البوت لقناتك أو مجموعتك
523
- 5. احصل على Chat ID عبر @getmyid_bot
524
  """)
 
 
 
 
 
 
1
  import streamlit as st
2
  import subprocess
3
  import time
 
6
  from pathlib import Path
7
  import requests
8
 
 
9
  st.set_page_config(
10
  page_title="TikTok Live Recorder",
11
  page_icon="📹",
12
+ layout="wide"
 
13
  )
14
 
 
15
  st.markdown("""
16
  <style>
17
+ .main-header {
18
+ font-size: 2.5rem;
19
+ font-weight: bold;
20
+ background: linear-gradient(90deg, #ff0050, #00f2ea);
21
+ -webkit-background-clip: text;
22
+ -webkit-text-fill-color: transparent;
23
+ }
24
+ .status-box {
25
+ padding: 1rem;
26
+ border-radius: 0.5rem;
27
+ text-align: center;
28
+ font-weight: bold;
29
+ }
30
+ .status-recording {
31
+ background: linear-gradient(90deg, #ff6b6b, #ee5a5a);
32
+ color: white;
33
+ animation: pulse 1s infinite;
34
+ }
35
+ .status-ready {
36
+ background: linear-gradient(90deg, #00ff88, #00cc66);
37
+ color: black;
38
+ }
39
+ .status-offline {
40
+ background: linear-gradient(90deg, #444444, #222222);
41
+ color: #aaa;
42
+ }
43
+ @keyframes pulse {
44
+ 0%, 100% { opacity: 1; }
45
+ 50% { opacity: 0.7; }
46
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  </style>
48
  """, unsafe_allow_html=True)
49
 
 
50
  if 'recording' not in st.session_state:
51
  st.session_state.recording = False
52
  if 'process' not in st.session_state:
53
  st.session_state.process = None
 
 
54
  if 'stream_url' not in st.session_state:
55
  st.session_state.stream_url = None
 
 
 
 
56
  if 'username' not in st.session_state:
57
  st.session_state.username = None
58
  if 'video_path' not in st.session_state:
59
  st.session_state.video_path = None
60
+ if 'start_time' not in st.session_state:
61
+ st.session_state.start_time = None
62
 
 
63
  OUTPUT_DIR = "/tmp/tiktok_recordings"
64
  Path(OUTPUT_DIR).mkdir(parents=True, exist_ok=True)
65
 
66
  def check_tiktok_live(username):
 
67
  try:
68
  from TikTokLive import TikTokLiveClient
 
69
  client = TikTokLiveClient(unique_id=f"@{username}")
70
  is_live = client.fetch_is_live()
71
 
72
  if not is_live:
73
+ return {'is_live': False, 'error': 'User not live'}
74
 
75
  room_info = client.fetch_room_info()
 
76
  stream_url = None
77
+
78
  if hasattr(room_info, 'stream_url') and room_info.stream_url:
79
+ if hasattr(room_info.stream_url, 'hls_pull_url'):
80
  stream_url = room_info.stream_url.hls_pull_url
81
+ elif hasattr(room_info.stream_url, 'flv_pull_url'):
82
  stream_url = room_info.stream_url.flv_pull_url
83
 
84
  return {
85
  'is_live': True,
86
  'stream_url': stream_url,
87
  'room_id': getattr(room_info, 'webcast_room_id', None),
 
88
  'viewer_count': getattr(room_info, 'user_count', 0)
89
  }
90
  except Exception as e:
91
  return {'is_live': False, 'error': str(e)}
92
 
93
+ def start_recording(stream_url, output_path):
 
94
  try:
95
+ cmd = ['ffmpeg', '-y', '-re', '-i', stream_url, '-c:v', 'copy', '-c:a', 'copy', '-f', 'mp4', output_path]
96
+ process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
  return process
98
  except Exception as e:
99
+ st.error(f"Error: {e}")
100
  return None
101
 
102
+ def stop_recording():
 
103
  if st.session_state.process:
104
  try:
105
  st.session_state.process.terminate()
106
  st.session_state.process.wait(timeout=5)
107
  except:
108
  st.session_state.process.kill()
109
+ st.session_state.process = None
110
+ st.session_state.recording = False
 
111
 
112
+ def format_time(seconds):
113
+ mins = int(seconds // 60)
 
 
114
  secs = int(seconds % 60)
115
+ return f"{mins}:{secs:02d}"
 
 
 
 
 
 
 
 
 
 
116
 
117
+ def upload_to_telegram(file_path, bot_token, chat_id):
 
118
  try:
119
  url = f"https://api.telegram.org/bot{bot_token}/sendVideo"
 
120
  with open(file_path, 'rb') as video:
121
  files = {'video': video}
122
  data = {
123
  'chat_id': chat_id,
124
+ 'caption': f"📹 TikTok Live - @{st.session_state.username}"
125
  }
 
126
  response = requests.post(url, files=files, data=data, timeout=300)
 
127
  if response.status_code == 200:
128
  result = response.json()
129
  if result.get('ok'):
130
+ return {'success': True, 'file_id': result['result']['video']['file_id']}
131
+ return {'success': False, 'error': 'Upload failed'}
 
 
 
 
 
 
132
  except Exception as e:
133
  return {'success': False, 'error': str(e)}
134
 
135
+ # UI
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
  st.markdown('<h1 class="main-header">📹 TikTok Live Recorder</h1>', unsafe_allow_html=True)
137
+ st.markdown("Record TikTok live streams and upload to Telegram for free unlimited storage")
138
 
139
+ st.markdown("### 🔗 Connect to Live Stream")
140
+ username_input = st.text_input("TikTok Username", placeholder="Enter username without @")
141
 
142
+ col1, col2, col3 = st.columns(3)
143
+
144
+ with col1:
145
+ if st.button("🔍 Check Live", use_container_width=True):
146
+ if username_input:
147
+ with st.spinner("Checking..."):
148
+ result = check_tiktok_live(username_input)
149
+ if result.get('is_live'):
150
+ st.success(f"✅ @{username_input} is LIVE!")
151
+ st.session_state.stream_url = result.get('stream_url')
152
+ st.session_state.username = username_input
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
153
  st.rerun()
154
+ else:
155
+ st.error(f"❌ Not live: {result.get('error', '')}")
156
+
157
+ with col2:
158
+ if st.session_state.stream_url:
 
 
 
 
 
 
 
 
 
 
159
  if st.session_state.recording:
160
+ if st.button("⏹️ Stop", type="secondary", use_container_width=True):
161
+ stop_recording()
162
+ st.success("Recording stopped!")
163
+ st.rerun()
 
164
  else:
165
+ if st.button("🔴 Record", type="primary", use_container_width=True):
166
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
167
+ output_path = f"{OUTPUT_DIR}/tiktok_{st.session_state.username}_{timestamp}.mp4"
168
+ st.session_state.process = start_recording(st.session_state.stream_url, output_path)
169
+ if st.session_state.process:
170
+ st.session_state.recording = True
171
+ st.session_state.start_time = time.time()
172
+ st.session_state.video_path = output_path
173
+ st.success("🎬 Recording started!")
174
+ st.rerun()
175
+
176
+ with col3:
177
  if st.session_state.recording:
178
+ st.markdown('<div class="status-box status-recording">🔴 Recording</div>', unsafe_allow_html=True)
179
+ elif st.session_state.stream_url:
180
+ st.markdown('<div class="status-box status-ready">🟢 Ready</div>', unsafe_allow_html=True)
181
+ else:
182
+ st.markdown('<div class="status-box status-offline">⚪ Offline</div>', unsafe_allow_html=True)
183
+
184
+ if st.session_state.recording:
185
+ st.divider()
186
+ duration = time.time() - st.session_state.start_time
187
+ col1, col2, col3 = st.columns(3)
188
+ col1.metric("Status", "🔴 Recording")
189
+ col2.metric("Duration", format_time(duration))
190
+ col3.metric("User", f"@{st.session_state.username}")
191
+ st.progress(1.0)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
192
 
193
+ if not st.session_state.recording and st.session_state.video_path and os.path.exists(st.session_state.video_path):
194
+ st.divider()
195
+ st.markdown("### ☁️ Upload Video")
196
 
197
+ file_size = os.path.getsize(st.session_state.video_path) / (1024 * 1024)
198
+ st.info(f"📊 File size: {file_size:.1f} MB")
 
 
 
 
 
 
199
 
200
+ upload_method = st.radio("Choose upload method", ["Telegram (Unlimited Free)", "Internet Archive", "Download Only"], index=0)
 
 
 
 
 
 
201
 
202
+ if upload_method == "Telegram (Unlimited Free)":
203
+ with st.expander("⚙️ Telegram Settings", expanded=True):
204
+ bot_token = st.text_input("Bot Token", placeholder="123456:ABC-DEF...", type="password")
205
+ chat_id = st.text_input("Chat ID", placeholder="-1001234567890")
206
+ st.info("**Get Bot Token:** @BotFather on Telegram\n\n**Get Chat ID:** @getmyid_bot on Telegram")
207
+
208
+ if st.button("📤 Upload to Telegram", type="primary"):
209
+ if bot_token and chat_id:
210
+ with st.spinner("Uploading to Telegram..."):
211
+ result = upload_to_telegram(st.session_state.video_path, bot_token, chat_id)
212
+ if result.get('success'):
213
+ st.success(f"✅ Uploaded! File ID: {result['file_id']}")
214
+ else:
215
+ st.error(f"❌ {result.get('error', 'Failed')}")
216
+ else:
217
+ st.error("Please enter Bot Token and Chat ID")
218
 
219
+ elif upload_method == "Internet Archive":
220
+ if st.button("📤 Prepare Archive.org Upload"):
221
+ identifier = f"tiktok-{st.session_state.username}-{int(time.time())}"
222
+ upload_url = f"https://archive.org/services/edit/{identifier}"
223
+ st.success("Ready to upload!")
224
+ st.link_button("🌐 Go to Upload Page", upload_url)
 
225
 
 
226
  st.divider()
227
+ with open(st.session_state.video_path, 'rb') as f:
228
+ st.download_button(
229
+ label="⬇️ Download MP4",
230
+ data=f,
231
+ file_name=f"tiktok_{st.session_state.username}.mp4",
232
+ mime="video/mp4",
233
+ use_container_width=True
234
+ )
 
 
 
 
 
 
 
 
 
235
 
 
236
  st.divider()
237
  st.markdown("""
238
+ **⚠️ Notes:**
239
+ - Recordings are temporary - download or upload immediately
240
+ - User must be LIVE to record
241
+ - Respect content creators' rights
242
+ """)
 
 
243
 
244
+ with st.expander("📖 How to Setup Telegram"):
 
245
  st.markdown("""
246
+ ### Step 1: Create Bot
247
+ 1. Open Telegram
248
+ 2. Search @BotFather
249
+ 3. Send /newbot
250
+ 4. Save the Token
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
251
 
252
+ ### Step 2: Create Channel
253
+ 1. New Channel in Telegram
254
+ 2. Add your bot as Admin
255
 
256
+ ### Step 3: Get Chat ID
257
+ 1. Search @getmyid_bot
258
+ 2. Send any message
259
+ 3. Copy Current Chat ID
 
 
260
  """)