Isse-bo commited on
Commit
e5f1708
·
verified ·
1 Parent(s): e10aadf

Delete plugins/muxer.py as part of folder deletion for plugins/

Browse files
Files changed (1) hide show
  1. plugins/muxer.py +0 -1131
plugins/muxer.py DELETED
@@ -1,1131 +0,0 @@
1
- from pyrogram import Client, filters
2
- from helper_func.progress_bar import progress_bar
3
- from helper_func.dbhelper import Database as Db
4
- from helper_func.mux import softmux_vid, hardmux_vid, format_time_duration, active_processes, check_active_process, set_process_status, clear_process_status
5
- from config import Config
6
- from encoding_defaults import EncodingDefaults
7
- import time
8
- import os
9
- import asyncio
10
- import logging
11
- import re
12
-
13
- db = Db()
14
-
15
- # Add a dictionary to store user encoding settings
16
- user_settings = {}
17
-
18
- # Add skip status tracking
19
- skipped_sub = set() # Store user IDs who skipped subtitle
20
-
21
- # Define valid presets and CRF ranges
22
- VALID_PRESETS = ['ultrafast', 'superfast', 'veryfast', 'faster', 'fast', 'medium', 'slow', 'slower', 'veryslow']
23
- MIN_CRF = 0
24
- MAX_CRF = 51
25
-
26
- # Define resolution options
27
- RESOLUTIONS = {
28
- '8k': '7680x4320',
29
- '4k': '3840x2160',
30
- '1440p': '2560x1440',
31
- '1080p': '1920x1080',
32
- '720p': '1280x720',
33
- '480p': '854x480',
34
- '360p': '640x360',
35
- '240p': '426x240',
36
- '144p': '256x144',
37
- 'original': 'original'
38
- }
39
-
40
- # Define codec options
41
- CODECS = {
42
- 'h264': {
43
- 'ffmpeg_codec': 'libx264',
44
- 'description': 'H.264 (AVC) - Widely compatible'
45
- },
46
- 'h265': {
47
- 'ffmpeg_codec': 'libx265',
48
- 'description': 'H.265 (HEVC) - Better compression, smaller file size'
49
- }
50
- }
51
-
52
- # Add frame rate options after CODECS definition
53
- FRAME_RATES = {
54
- '60': '60',
55
- '50': '50',
56
- '30': '30',
57
- '25': '25',
58
- '24': '24',
59
- 'original': 'original'
60
- }
61
-
62
- # At the top with other global variables
63
- active_processes = {} # Store user IDs and their current process (download/encode/upload)
64
-
65
- async def _check_user(filt, c, m):
66
- chat_id = str(m.from_user.id)
67
- if chat_id in Config.ALLOWED_USERS:
68
- return True
69
- else:
70
- return False
71
-
72
- check_user = filters.create(_check_user)
73
-
74
- @Client.on_message(filters.command('nosubs') & check_user & filters.private)
75
- async def nosubs(client, message):
76
- chat_id = message.from_user.id
77
-
78
- # Send initial processing message
79
- processing_msg = await client.send_message(chat_id, '⚙️ Processing...')
80
-
81
- # Check for active process
82
- if await check_active_process(client, chat_id, processing_msg.id):
83
- return
84
-
85
- if not db.check_video(chat_id):
86
- await processing_msg.edit("❌ Send video file first!")
87
- return
88
-
89
- # Check if subtitle exists and delete it
90
- sub_filename = db.get_sub_filename(chat_id)
91
- if sub_filename:
92
- # Delete subtitle file from server
93
- sub_path = os.path.join(Config.DOWNLOAD_DIR, sub_filename)
94
- if os.path.exists(sub_path):
95
- try:
96
- os.remove(sub_path)
97
- logging.info(f"Deleted subtitle file {sub_filename} after /nosubs command")
98
- except Exception as e:
99
- logging.error(f"Error deleting subtitle file after /nosubs: {e}")
100
-
101
- # Update database to remove subtitle reference properly
102
- # Get the current video filename and final filename to preserve them
103
- vid_filename = db.get_vid_filename(chat_id)
104
- final_filename = db.get_filename(chat_id)
105
-
106
- # First erase the complete record
107
- db.erase(chat_id)
108
-
109
- # Then restore just the video data without subtitle
110
- db.put_video(chat_id, vid_filename, final_filename)
111
-
112
- await processing_msg.edit("🗑️ Previous subtitle file deleted. Proceeding with encoding without subtitles...")
113
- await asyncio.sleep(2)
114
-
115
- # Add user to skipped subtitle set
116
- skipped_sub.add(chat_id)
117
-
118
- # Initialize encoding settings with defaults
119
- user_settings[chat_id] = {
120
- 'crf': '23',
121
- 'preset': 'medium',
122
- 'resolution': 'original',
123
- 'fps': 'original',
124
- 'codec': 'h264',
125
- 'audio_bitrate': '128k',
126
- 'audio_track': 'all',
127
- 'filename': None,
128
- 'waiting_for': 'resolution'
129
- }
130
-
131
- # Ask user for resolution
132
- resolution_text = (
133
- '🎬 <b>Choose Video Resolution:</b>\n\n'
134
- '• <code>8K</code> - 7680x4320\n'
135
- '• <code>4K</code> - 3840x2160\n'
136
- '• <code>1440p</code> - 2560x1440\n'
137
- '• <code>1080p</code> - 1920x1080\n'
138
- '• <code>720p</code> - 1280x720\n'
139
- '• <code>480p</code> - 854x480\n'
140
- '• <code>360p</code> - 640x360\n'
141
- '• <code>240p</code> - 426x240\n'
142
- '• <code>144p</code> - 256x144\n'
143
- '• <code>original</code> - Keep original resolution\n\n'
144
- '💡 Type resolution or /default for original'
145
- )
146
-
147
- await processing_msg.edit(resolution_text)
148
-
149
- @Client.on_message(filters.command('clear') & check_user & filters.private)
150
- async def clear_user_data(client, message):
151
- chat_id = message.from_user.id
152
-
153
- # Send initial processing message
154
- processing_msg = await client.send_message(chat_id, '⚙️ Processing...')
155
-
156
- # Check for active process
157
- if await check_active_process(client, chat_id, processing_msg.id):
158
- return
159
-
160
- # Remove from skip list if exists
161
- if chat_id in skipped_sub:
162
- skipped_sub.remove(chat_id)
163
-
164
- # Remove from encoding settings if exists
165
- if chat_id in user_settings:
166
- del user_settings[chat_id]
167
-
168
- # Delete files if they exist
169
- path = Config.DOWNLOAD_DIR + '/'
170
- files_deleted = False
171
-
172
- # Get filenames from database
173
- vid_file = db.get_vid_filename(chat_id)
174
- sub_file = db.get_sub_filename(chat_id)
175
-
176
- # Delete video file if exists
177
- if vid_file and os.path.exists(path + vid_file):
178
- try:
179
- os.remove(path + vid_file)
180
- files_deleted = True
181
- except Exception as e:
182
- print(f"Error deleting video file: {e}")
183
-
184
- # Delete subtitle file if exists
185
- if sub_file and os.path.exists(path + sub_file):
186
- try:
187
- os.remove(path + sub_file)
188
- files_deleted = True
189
- except Exception as e:
190
- print(f"Error deleting subtitle file: {e}")
191
-
192
- # Delete any in-progress encoding files
193
- try:
194
- for filename in os.listdir(path):
195
- # Check for temporary encoding files that match the pattern
196
- if filename.startswith(str(round(time.time()))[:5]) or \
197
- (vid_file and filename.startswith(vid_file.split('.')[0])) or \
198
- filename.endswith('_VidSub.mp4') or \
199
- filename.endswith('1.mkv'):
200
- try:
201
- os.remove(path + filename)
202
- files_deleted = True
203
- except Exception as e:
204
- print(f"Error deleting encoding file {filename}: {e}")
205
- except Exception as e:
206
- print(f"Error scanning directory: {e}")
207
-
208
- # Clear database entry
209
- db.erase(chat_id)
210
-
211
- # Clear active process status
212
- if chat_id in active_processes:
213
- del active_processes[chat_id]
214
-
215
- # Send appropriate message
216
- if files_deleted:
217
- await processing_msg.edit(
218
- '🗑 All your data has been cleared:\n'
219
- '• Video and subtitle files deleted\n'
220
- '• Encoding settings reset\n'
221
- '• Database entries removed\n\n'
222
- 'Send a new video file to start over!'
223
- )
224
- else:
225
- await processing_msg.edit(
226
- '🔄 Your session has been reset:\n'
227
- '• Encoding settings cleared\n'
228
- '• Database entries removed\n\n'
229
- 'Send a video file to start over!'
230
- )
231
-
232
- @Client.on_message(filters.command('softcode') & check_user & filters.private)
233
- async def softmux(client, message):
234
- chat_id = message.from_user.id
235
-
236
- # Send initial processing message
237
- processing_msg = await client.send_message(chat_id, '⚙️ Processing...')
238
-
239
- # Check for active process
240
- if await check_active_process(client, chat_id, processing_msg.id):
241
- return
242
-
243
- # Check if user skipped subtitle
244
- if chat_id in skipped_sub:
245
- await processing_msg.edit(
246
- '❌ Softcode encoding is not available because you skipped the subtitle file.\n\n'
247
- 'Use /clear to start over and upload both video and subtitle files.'
248
- )
249
- return
250
-
251
- og_vid_filename = db.get_vid_filename(chat_id)
252
- og_sub_filename = db.get_sub_filename(chat_id)
253
- text = ''
254
- if not og_vid_filename :
255
- text += '❌ No video file found! Please send a video first.\n'
256
- if not og_sub_filename :
257
- text += '❌ No subtitle file found! Please send subtitles.'
258
-
259
- if not (og_sub_filename and og_vid_filename):
260
- await processing_msg.edit(text)
261
- return
262
-
263
- text = (
264
- '🎬 <b>Starting Soft Subtitle Encoding</b>\n\n'
265
- '• Adding subtitles without re-encoding\n'
266
- '• This process should be quick\n'
267
- '• Please wait a moment...'
268
- )
269
- await processing_msg.edit(text)
270
- await asyncio.sleep(1)
271
-
272
- # Start encoding time tracking
273
- encoding_start = time.time()
274
- softmux_filename = await softmux_vid(og_vid_filename, og_sub_filename, processing_msg)
275
- if not softmux_filename:
276
- return
277
-
278
- # Calculate encoding time
279
- encoding_time = round(time.time() - encoding_start)
280
-
281
- # Create user-specific directory if it doesn't exist
282
- user_dir = os.path.join(Config.DOWNLOAD_DIR, str(chat_id))
283
- if not os.path.exists(user_dir):
284
- os.makedirs(user_dir)
285
-
286
- # Get the filename from the database
287
- final_filename = db.get_filename(chat_id)
288
-
289
- # Extract just the original filename part (remove timestamp_random_ prefix)
290
- if '_' in final_filename:
291
- # Find the second underscore (after timestamp_random_)
292
- parts = final_filename.split('_', 2) # Split on first 2 underscores
293
- if len(parts) >= 3:
294
- original_filename = parts[2] # Get everything after the second underscore
295
- else:
296
- original_filename = final_filename # Fallback if format is unexpected
297
- else:
298
- original_filename = final_filename # Fallback if no underscore
299
-
300
- # Get custom filename if available
301
- custom_filename = None
302
- if chat_id in user_settings and user_settings[chat_id].get('filename'):
303
- custom_filename = user_settings[chat_id].get('filename')
304
-
305
- # For softmux, always use .mkv extension
306
- if custom_filename:
307
- # Ensure the extension is .mkv
308
- if not custom_filename.lower().endswith('.mkv'):
309
- name_without_ext = os.path.splitext(custom_filename)[0]
310
- destination_filename = name_without_ext + '.mkv'
311
- else:
312
- destination_filename = custom_filename
313
- else:
314
- # Ensure the extension is .mkv
315
- if not original_filename.lower().endswith('.mkv'):
316
- name_without_ext = os.path.splitext(original_filename)[0]
317
- destination_filename = name_without_ext + '.mkv'
318
- else:
319
- destination_filename = original_filename
320
-
321
- # Move file to user directory with the appropriate filename
322
- source_path = os.path.join(Config.DOWNLOAD_DIR, softmux_filename)
323
- destination_path = os.path.join(user_dir, destination_filename)
324
-
325
- # Make sure the source file exists before trying to move it
326
- if os.path.exists(source_path):
327
- os.rename(source_path, destination_path)
328
- logging.info(f"Moved file from {source_path} to {destination_path}")
329
- else:
330
- logging.error(f"Source file not found: {source_path}")
331
- await client.send_message(
332
- chat_id,
333
- '❌ <b>Error</b>\n\n'
334
- 'Could not find the encoded file. Please try again.'
335
- )
336
- return
337
-
338
- # Clean up settings AFTER moving the file
339
- if chat_id in user_settings:
340
- del user_settings[chat_id]
341
-
342
- # Start upload time tracking
343
- upload_start = time.time()
344
- try:
345
- # Set active process to upload
346
- set_process_status(chat_id, 'upload')
347
-
348
- await client.send_document(
349
- chat_id,
350
- progress = progress_bar,
351
- progress_args = (
352
- 'Uploading',
353
- processing_msg,
354
- upload_start
355
- ),
356
- document = destination_path,
357
- caption = f'📁 {destination_filename}'
358
- )
359
- # Calculate upload time
360
- upload_time = round(time.time() - upload_start)
361
- text = (
362
- '✅ <b>File Successfully Uploaded!</b>\n\n'
363
- f'⚡ Encoding Time: {format_time_duration(encoding_time)}\n'
364
- f'📤 Upload Time: {format_time_duration(upload_time)}\n'
365
- f'⏱️ Total Time: {format_time_duration(encoding_time + upload_time)}'
366
- )
367
- await processing_msg.edit(text)
368
-
369
- # Cleanup inside try block AFTER successful upload
370
- path = Config.DOWNLOAD_DIR+'/'
371
-
372
- # Delete source files
373
- if og_sub_filename and os.path.exists(path + og_sub_filename):
374
- os.remove(path + og_sub_filename)
375
- logging.info(f"Deleted subtitle file: {og_sub_filename}")
376
-
377
- if og_vid_filename and os.path.exists(path + og_vid_filename):
378
- os.remove(path + og_vid_filename)
379
- logging.info(f"Deleted video file: {og_vid_filename}")
380
-
381
- # Delete the final encoded file from user directory
382
- if os.path.exists(destination_path):
383
- os.remove(destination_path)
384
- logging.info(f"Deleted final encoded file: {destination_path}")
385
-
386
- # Try to remove user directory if it exists
387
- try:
388
- if os.path.exists(user_dir):
389
- if len(os.listdir(user_dir)) == 0:
390
- os.rmdir(user_dir)
391
- logging.info(f"Removed empty user directory: {user_dir}")
392
- else:
393
- logging.warning(f"User directory not empty, cannot remove: {user_dir}")
394
- except Exception as e:
395
- logging.error(f"Error removing user directory: {e}")
396
-
397
- db.erase(chat_id)
398
-
399
- except Exception as e:
400
- print(e)
401
- await client.send_message(
402
- chat_id,
403
- '❌ <b>Upload Failed!</b>\n\n'
404
- 'An error occurred while uploading the file.\n'
405
- 'Please check logs for details.'
406
- )
407
- finally:
408
- # Clear process status at the end
409
- clear_process_status(chat_id)
410
-
411
- @Client.on_message(filters.command(['hardcode', 'quick']) & check_user & filters.private)
412
- async def hardmux(client, message):
413
- chat_id = message.from_user.id
414
- command = message.text.split()[0].lower() # Get the actual command used
415
-
416
- # Send initial processing message
417
- processing_msg = await client.send_message(chat_id, '⚙️ Processing...')
418
-
419
- # Check for active process
420
- if await check_active_process(client, chat_id, processing_msg.id):
421
- return
422
-
423
- # Handle /quick separately
424
- if command == '/quick':
425
- logging.info(f"User {chat_id} used /quick command")
426
-
427
- # Check if video exists
428
- if not db.check_video(chat_id):
429
- await processing_msg.edit("❌ Send video file first!")
430
- return
431
-
432
- # Apply default encoding settings
433
- user_settings[chat_id] = EncodingDefaults.DEFAULT_ENCODING.copy()
434
-
435
- # Add filename and waiting_for fields
436
- user_settings[chat_id]['filename'] = None
437
- user_settings[chat_id]['waiting_for'] = 'filename'
438
-
439
- # Get current filename from database to show as default
440
- final_filename = db.get_filename(chat_id)
441
-
442
- # Extract just the original filename part (remove timestamp_random_ prefix)
443
- if '_' in final_filename:
444
- # Find the second underscore (after timestamp_random_)
445
- parts = final_filename.split('_', 2) # Split on first 2 underscores
446
- if len(parts) >= 3:
447
- original_filename = parts[2] # Get everything after the second underscore
448
- else:
449
- original_filename = final_filename # Fallback if format is unexpected
450
- else:
451
- original_filename = final_filename # Fallback if no underscore
452
-
453
- filename_text = (
454
- '📝 <b>Choose Output Filename:</b>\n\n'
455
- '• You can change the output filename\n'
456
- '• Must end with .mp4 or .mkv or .webm extension\n'
457
- '• Maximum length: 60 characters\n\n'
458
- f'Current filename: <code>{original_filename}</code>\n\n'
459
- '💡 Type a new filename or /default to keep current'
460
- )
461
-
462
- await processing_msg.edit(filename_text)
463
- return
464
-
465
- # Original hardcode logic continues below
466
- og_vid_filename = db.get_vid_filename(chat_id)
467
- og_sub_filename = db.get_sub_filename(chat_id)
468
-
469
- # Check if video exists
470
- if not og_vid_filename:
471
- await processing_msg.edit('❌ No video file found! Please send a video first.')
472
- return
473
-
474
- # Check if subtitle exists or was skipped
475
- if not og_sub_filename and chat_id not in skipped_sub:
476
- await processing_msg.edit(
477
- '❌ No subtitle file found!\n\n'
478
- 'Either:\n'
479
- '• Send a subtitle file\n'
480
- '• Use /nosubs to encode without subtitles'
481
- )
482
- return
483
-
484
- # Initialize encoding settings with defaults
485
- user_settings[chat_id] = {
486
- 'crf': '23',
487
- 'preset': 'medium',
488
- 'resolution': 'original',
489
- 'fps': 'original',
490
- 'codec': 'h264',
491
- 'audio_bitrate': '128k',
492
- 'audio_track': 'all',
493
- 'filename': None,
494
- 'waiting_for': 'resolution'
495
- }
496
-
497
- # Ask user for resolution
498
- resolution_text = (
499
- '🎬 <b>Choose Video Resolution:</b>\n\n'
500
- '• <code>8K</code> - 7680x4320\n'
501
- '• <code>4K</code> - 3840x2160\n'
502
- '• <code>1440p</code> - 2560x1440\n'
503
- '• <code>1080p</code> - 1920x1080\n'
504
- '• <code>720p</code> - 1280x720\n'
505
- '• <code>480p</code> - 854x480\n'
506
- '• <code>360p</code> - 640x360\n'
507
- '• <code>240p</code> - 426x240\n'
508
- '• <code>144p</code> - 256x144\n'
509
- '• <code>original</code> - Keep original resolution\n\n'
510
- '💡 Type resolution or /default for original'
511
- )
512
-
513
- await processing_msg.edit(resolution_text)
514
- return
515
-
516
- @Client.on_message(filters.text & check_user & filters.private)
517
- async def handle_encoding_settings(client, message):
518
- chat_id = message.from_user.id
519
-
520
- # Check if we're waiting for encoding settings from this user
521
- if chat_id not in user_settings:
522
- return
523
-
524
- # Get what we're waiting for
525
- waiting_for = user_settings[chat_id]['waiting_for']
526
-
527
- # Handle resolution input
528
- if waiting_for == 'resolution':
529
- text = message.text.strip().lower()
530
-
531
- # Handle default case
532
- if text == '/default':
533
- resolution = 'original'
534
- else:
535
- # Validate resolution
536
- if text not in RESOLUTIONS:
537
- return await client.send_message(
538
- chat_id,
539
- '❌ <b>Invalid Resolution</b>\n\n'
540
- 'Please choose from the list or use /default\n'
541
- 'for original resolution.'
542
- )
543
- resolution = text
544
-
545
- # Save resolution and ask for frame rate
546
- user_settings[chat_id]['resolution'] = resolution
547
- user_settings[chat_id]['waiting_for'] = 'fps'
548
-
549
- # Ask user for frame rate
550
- fps_text = (
551
- '🎥 <b>Choose Frame Rate:</b>\n\n'
552
- '• <code>60</code> - 60 FPS\n'
553
- '• <code>50</code> - 50 FPS\n'
554
- '• <code>30</code> - 30 FPS\n'
555
- '• <code>25</code> - 25 FPS\n'
556
- '• <code>24</code> - 24 FPS\n'
557
- '• <code>original</code> - Keep original FPS\n'
558
- '• Custom FPS value between 1 and 120\n\n'
559
- '💡 Type FPS value or /default for original'
560
- )
561
-
562
- await client.send_message(chat_id, fps_text)
563
- return
564
-
565
- # Handle frame rate input
566
- elif waiting_for == 'fps':
567
- text = message.text.strip().lower()
568
-
569
- # Handle default case
570
- if text == '/default':
571
- fps = 'original'
572
- else:
573
- # Check if it's one of the preset options
574
- if text in FRAME_RATES:
575
- fps = text
576
- else:
577
- # Try to parse custom frame rate
578
- try:
579
- fps_value = float(text)
580
- if 1 <= fps_value <= 120:
581
- fps = str(fps_value)
582
- else:
583
- return await client.send_message(
584
- chat_id,
585
- '❌ <b>Invalid Frame Rate</b>\n\n'
586
- '• Must be between 1 and 120 FPS\n'
587
- '• Choose from the list or\n'
588
- '• Enter a custom value\n'
589
- '• Use /default for original FPS'
590
- )
591
- except ValueError:
592
- return await client.send_message(
593
- chat_id,
594
- '❌ <b>Invalid Frame Rate</b>\n\n'
595
- '• Must be between 1 and 120 FPS\n'
596
- '• Choose from the list or\n'
597
- '• Enter a custom value\n'
598
- '• Use /default for original FPS'
599
- )
600
-
601
- # Save frame rate and ask for audio bitrate
602
- user_settings[chat_id]['fps'] = fps
603
- user_settings[chat_id]['waiting_for'] = 'audio_bitrate'
604
-
605
- # Ask user for audio bitrate
606
- audio_bitrate_text = (
607
- '🔊 <b>Choose Audio Bitrate:</b>\n\n'
608
- '• <code>64k</code> - Lower quality, smaller file\n'
609
- '• <code>128k</code> - Standard quality (recommended)\n'
610
- '• <code>192k</code> - Higher quality\n'
611
- '• <code>256k</code> - High quality\n'
612
- '• <code>320k</code> - Very high quality\n\n'
613
- '💡 Type a value (e.g., 128k) or /default for 128k'
614
- )
615
-
616
- await client.send_message(chat_id, audio_bitrate_text)
617
- return
618
-
619
- # Handle audio bitrate input
620
- elif waiting_for == 'audio_bitrate':
621
- text = message.text.strip().lower()
622
-
623
- # Handle default case
624
- if text == '/default':
625
- audio_bitrate = '128k'
626
- else:
627
- # Validate audio bitrate format
628
- if re.match(r'^\d+k$', text):
629
- # Extract the number part
630
- bitrate_value = int(text.rstrip('k'))
631
- if 32 <= bitrate_value <= 320:
632
- audio_bitrate = text
633
- else:
634
- return await client.send_message(
635
- chat_id,
636
- '❌ <b>Invalid Audio Bitrate</b>\n\n'
637
- '• Must be between 32k and 320k\n'
638
- '• Format example: 128k\n'
639
- '• Use /default for 128k'
640
- )
641
- else:
642
- return await client.send_message(
643
- chat_id,
644
- '❌ <b>Invalid Audio Bitrate Format</b>\n\n'
645
- '• Must be in format like 128k\n'
646
- '• Use /default for 128k'
647
- )
648
-
649
- # Save audio bitrate and ask for codec
650
- user_settings[chat_id]['audio_bitrate'] = audio_bitrate
651
- user_settings[chat_id]['waiting_for'] = 'codec'
652
-
653
- # Ask user for codec
654
- codec_text = (
655
- '🎞️ <b>Choose Video Codec:</b>\n\n'
656
- '• H.264 (AVC) - Better compatibility\n'
657
- '• H.265 (HEVC) - Smaller file size\n\n'
658
- '💡 Type <code>h264</code>/<code>h265</code> or /default for H.264'
659
- )
660
-
661
- await client.send_message(chat_id, codec_text)
662
- return
663
-
664
- # Handle codec input
665
- elif waiting_for == 'codec':
666
- text = message.text.strip().lower()
667
-
668
- # Handle default case
669
- if text == '/default':
670
- codec = 'h264'
671
- else:
672
- # Validate codec
673
- if text not in CODECS:
674
- return await client.send_message(
675
- chat_id,
676
- '❌ <b>Invalid Codec</b>\n\n'
677
- 'Please type <code>h264</code>/<code>h265</code> or /default.'
678
- )
679
- codec = text
680
-
681
- # Save codec and ask for CRF
682
- user_settings[chat_id]['codec'] = codec
683
- user_settings[chat_id]['waiting_for'] = 'crf'
684
-
685
- # Ask user for CRF value
686
- crf_text = (
687
- '📊 <b>Set Video Quality (CRF):</b>\n\n'
688
- '• Lower value = Higher quality, larger file\n'
689
- '• Higher value = Lower quality, smaller file\n'
690
- '• Recommended: 18-28\n'
691
- '• Range: 0-51\n\n'
692
- '💡 Type a number or /default for 23'
693
- )
694
-
695
- await client.send_message(chat_id, crf_text)
696
- return
697
-
698
- # Handle CRF input
699
- elif waiting_for == 'crf':
700
- text = message.text.strip()
701
-
702
- # Handle default case
703
- if text.lower() == '/default':
704
- crf = '23'
705
- else:
706
- # Validate CRF value
707
- try:
708
- crf_value = int(text)
709
- if crf_value < MIN_CRF or crf_value > MAX_CRF:
710
- return await client.send_message(
711
- chat_id,
712
- '❌ <b>Invalid CRF Value</b>\n\n'
713
- f'• Must be between {MIN_CRF} and {MAX_CRF}\n'
714
- '• Lower = Better quality\n'
715
- '• Higher = Smaller file\n'
716
- '• Use /default for 23'
717
- )
718
- crf = str(crf_value)
719
- except ValueError:
720
- return await client.send_message(
721
- chat_id,
722
- '❌ <b>Invalid CRF Value</b>\n\n'
723
- f'• Must be between {MIN_CRF} and {MAX_CRF}\n'
724
- '• Lower = Better quality\n'
725
- '• Higher = Smaller file\n'
726
- '• Use /default for 23'
727
- )
728
-
729
- # Save CRF value and ask for preset
730
- user_settings[chat_id]['crf'] = crf
731
- user_settings[chat_id]['waiting_for'] = 'preset'
732
-
733
- preset_text = (
734
- '⚙️ <b>Choose Encoding Preset:</b>\n\n'
735
- '• <code>ultrafast</code> - Fastest, lowest compression\n'
736
- '• <code>superfast</code> - Very fast encoding\n'
737
- '• <code>veryfast</code> - Fast encoding\n'
738
- '• <code>faster</code> - Quick encoding\n'
739
- '• <code>fast</code> - Good balance\n'
740
- '• <code>medium</code> - Default preset\n'
741
- '• <code>slow</code> - Better compression\n'
742
- '• <code>slower</code> - High compression\n'
743
- '• <code>veryslow</code> - Best compression\n\n'
744
- '💡 Type preset or /default for medium'
745
- )
746
-
747
- await client.send_message(chat_id, preset_text)
748
- return
749
-
750
- # Handle preset input
751
- elif waiting_for == 'preset':
752
- text = message.text.strip().lower()
753
-
754
- # Handle default case
755
- if text == '/default':
756
- preset = 'medium'
757
- else:
758
- # Validate preset
759
- if text not in VALID_PRESETS:
760
- return await client.send_message(
761
- chat_id,
762
- '❌ <b>Invalid Preset</b>\n\n'
763
- 'Please choose from the list or use /default\n'
764
- 'for medium encoding.'
765
- )
766
- preset = text
767
-
768
- # Save preset and now ask for audio track
769
- user_settings[chat_id]['preset'] = preset
770
- user_settings[chat_id]['waiting_for'] = 'audio_track'
771
-
772
- # Ask user for audio track
773
- audio_track_text = (
774
- '🔊 <b>Choose Audio Track:</b>\n\n'
775
- '• <code>all</code> - Keep all audio tracks\n'
776
- '• <code>1</code> - First audio track only\n'
777
- '• <code>2</code> - Second audio track only\n'
778
- '• <code>3</code> - Third audio track only\n'
779
- '• etc.\n\n'
780
- '💡 Type <code>all</code> or track number or /default for all tracks'
781
- )
782
-
783
- await client.send_message(chat_id, audio_track_text)
784
- return
785
-
786
- # Add handler for audio track input
787
- elif waiting_for == 'audio_track':
788
- text = message.text.strip().lower()
789
-
790
- # Handle default case
791
- if text == '/default':
792
- audio_track = 'all'
793
- elif text == 'all':
794
- audio_track = 'all'
795
- else:
796
- # Try to parse as a track number
797
- try:
798
- track_num = int(text)
799
- if track_num < 1:
800
- return await client.send_message(
801
- chat_id,
802
- '❌ <b>Invalid Track Number</b>\n\n'
803
- 'Track numbers start from 1.\n'
804
- 'Use <code>all</code> or /default to keep all tracks.'
805
- )
806
- audio_track = str(track_num)
807
- except ValueError:
808
- return await client.send_message(
809
- chat_id,
810
- '❌ <b>Invalid Audio Track Selection</b>\n\n'
811
- 'Please choose <code>all</code> or enter a track number (1, 2, 3, etc.)\n'
812
- 'Use /default to keep all tracks.'
813
- )
814
-
815
- # Save audio track and ask for filename
816
- user_settings[chat_id]['audio_track'] = audio_track
817
- user_settings[chat_id]['waiting_for'] = 'filename'
818
-
819
- # Get current filename from database to show as default
820
- final_filename = db.get_filename(chat_id)
821
-
822
- # Extract just the original filename part (remove timestamp_random_ prefix)
823
- if '_' in final_filename:
824
- # Find the second underscore (after timestamp_random_)
825
- parts = final_filename.split('_', 2) # Split on first 2 underscores
826
- if len(parts) >= 3:
827
- original_filename = parts[2] # Get everything after the second underscore
828
- else:
829
- original_filename = final_filename # Fallback if format is unexpected
830
- else:
831
- original_filename = final_filename # Fallback if no underscore
832
-
833
- filename_text = (
834
- '📝 <b>Choose Output Filename:</b>\n\n'
835
- '• You can change the output filename\n'
836
- '• Must end with .mp4 or .mkv or .webm extension\n'
837
- '• Maximum length: 60 characters\n\n'
838
- f'Current filename: <code>{original_filename}</code>\n\n'
839
- '💡 Type a new filename or /default to keep current'
840
- )
841
-
842
- await client.send_message(chat_id, filename_text)
843
- return
844
-
845
- # Modify the filename handler to go directly to confirmation
846
- elif waiting_for == 'filename':
847
- text = message.text.strip()
848
-
849
- # Handle default case
850
- if text.lower() == '/default':
851
- # Get current filename from database
852
- final_filename = db.get_filename(chat_id)
853
-
854
- # Extract just the original filename part
855
- if '_' in final_filename:
856
- parts = final_filename.split('_', 2)
857
- if len(parts) >= 3:
858
- original_filename = parts[2]
859
- else:
860
- original_filename = final_filename
861
- else:
862
- original_filename = final_filename
863
-
864
- user_settings[chat_id]['filename'] = original_filename
865
- else:
866
- # Validate filename
867
- if len(text) > 60:
868
- return await client.send_message(
869
- chat_id,
870
- '❌ <b>Filename Too Long</b>\n\n'
871
- 'Please keep the filename under 60 characters.'
872
- )
873
-
874
- # Check for valid extension
875
- if not (text.lower().endswith('.mp4') or text.lower().endswith('.mkv') or text.lower().endswith('.webm')):
876
- return await client.send_message(
877
- chat_id,
878
- '❌ <b>Invalid Filename Extension</b>\n\n'
879
- 'Filename must end with .mp4 or .mkv or .webm extension.'
880
- )
881
-
882
- # Check for illegal characters
883
- illegal_chars = ['\\', '/', ':', '*', '?', '"', '<', '>', '|']
884
- if any(char in text for char in illegal_chars):
885
- return await client.send_message(
886
- chat_id,
887
- '❌ <b>Invalid Filename</b>\n\n'
888
- 'Filename cannot contain any of these characters:\n'
889
- '<code>\\ / : * ? " < > |</code>'
890
- )
891
-
892
- # Save the new filename
893
- user_settings[chat_id]['filename'] = text
894
-
895
- # Move to confirmation
896
- user_settings[chat_id]['waiting_for'] = 'confirmation'
897
-
898
- # Show settings and ask for confirmation
899
- resolution_display = user_settings[chat_id]['resolution']
900
- codec_display = user_settings[chat_id]['codec'].upper()
901
- fps_display = user_settings[chat_id]['fps']
902
- audio_bitrate_display = user_settings[chat_id]['audio_bitrate']
903
- audio_track_display = user_settings[chat_id]['audio_track']
904
- filename_display = user_settings[chat_id]['filename']
905
-
906
- # Check if subtitles will be included
907
- has_subtitle = db.check_sub(chat_id) and chat_id not in skipped_sub
908
- subtitle_status = "Will be hardcoded" if has_subtitle else "No subtitles"
909
-
910
- # Create confirmation text with conditional skip option
911
- confirm_text = (
912
- '📝 <b>Review Encoding Settings:</b>\n\n'
913
- f'• Resolution: {resolution_display}\n'
914
- f'• Frame Rate: {fps_display}\n'
915
- f'• Audio Bitrate: {audio_bitrate_display}\n'
916
- f'• Audio Track: {audio_track_display}\n'
917
- f'• Codec: {codec_display}\n'
918
- f'• CRF: {user_settings[chat_id]["crf"]}\n'
919
- f'• Preset: {user_settings[chat_id]["preset"]}\n'
920
- f'• Subtitles: {subtitle_status}\n'
921
- f'• Filename: {filename_display}\n\n'
922
- '⚡️ <b>Available Commands:</b>\n'
923
- '• /confirm - Start encoding\n'
924
- '• /reset - Choose settings again\n'
925
- )
926
-
927
- # Only add skip option if subtitle exists
928
- if has_subtitle:
929
- confirm_text += '• /nosubs - Encode without subtitles\n'
930
-
931
- # Add the last option always
932
- confirm_text += '• /clear - Clear all data'
933
-
934
- await client.send_message(chat_id, confirm_text)
935
- return
936
-
937
- # Handle confirmation
938
- elif waiting_for == 'confirmation':
939
- command = message.text.strip().lower()
940
-
941
- if command == '/confirm':
942
- # Update waiting_for to None to prevent further processing
943
- user_settings[chat_id]['waiting_for'] = None
944
-
945
- # Send starting message
946
- sent_msg = await client.send_message(
947
- chat_id,
948
- '🎬 <b>Starting Encoding Process</b>\n\nThis might take some time depending on your settings...'
949
- )
950
- await asyncio.sleep(1)
951
-
952
- # Start encoding with the selected settings
953
- og_vid_filename = db.get_vid_filename(chat_id)
954
- og_sub_filename = db.get_sub_filename(chat_id)
955
-
956
- encoding_start = time.time()
957
- hardmux_filename = await hardmux_vid(
958
- og_vid_filename,
959
- og_sub_filename,
960
- sent_msg,
961
- crf=user_settings[chat_id]['crf'],
962
- preset=user_settings[chat_id]['preset'],
963
- resolution=user_settings[chat_id]['resolution'],
964
- codec=user_settings[chat_id]['codec'],
965
- fps=user_settings[chat_id]['fps'],
966
- audio_bitrate=user_settings[chat_id]['audio_bitrate'],
967
- audio_track=user_settings[chat_id]['audio_track'],
968
- custom_filename=user_settings[chat_id]['filename']
969
- )
970
-
971
- # Calculate encoding time
972
- encoding_time = round(time.time() - encoding_start)
973
-
974
- # Get custom filename before cleaning up settings
975
- custom_filename = user_settings[chat_id].get('filename')
976
-
977
- # Clean up settings
978
- del user_settings[chat_id]
979
-
980
- if not hardmux_filename:
981
- return
982
-
983
- # Create user-specific directory if it doesn't exist
984
- user_dir = os.path.join(Config.DOWNLOAD_DIR, str(chat_id))
985
- if not os.path.exists(user_dir):
986
- os.makedirs(user_dir)
987
-
988
- # Move file to user-specific directory with the custom filename
989
- source_path = os.path.join(Config.DOWNLOAD_DIR, hardmux_filename)
990
-
991
- # Use custom filename if provided, otherwise use extracted original filename
992
- if custom_filename:
993
- destination_filename = custom_filename
994
- else:
995
- # Get the filename from the database
996
- final_filename = db.get_filename(chat_id)
997
-
998
- # Extract just the original filename part (remove timestamp_random_ prefix)
999
- if '_' in final_filename:
1000
- # Find the second underscore (after timestamp_random_)
1001
- parts = final_filename.split('_', 2) # Split on first 2 underscores
1002
- if len(parts) >= 3:
1003
- destination_filename = parts[2] # Get everything after the second underscore
1004
- else:
1005
- destination_filename = final_filename # Fallback if format is unexpected
1006
- else:
1007
- destination_filename = final_filename # Fallback if no underscore
1008
-
1009
- destination_path = os.path.join(user_dir, destination_filename)
1010
- os.rename(source_path, destination_path)
1011
-
1012
- # Start upload time tracking
1013
- upload_start = time.time()
1014
- try:
1015
- # Set active process to upload
1016
- set_process_status(chat_id, 'upload')
1017
-
1018
- await client.send_document(
1019
- chat_id,
1020
- progress = progress_bar,
1021
- progress_args = (
1022
- 'Uploading',
1023
- sent_msg,
1024
- upload_start
1025
- ),
1026
- document = destination_path,
1027
- caption = f'📁 {destination_filename}'
1028
- )
1029
- upload_time = round(time.time() - upload_start)
1030
- text = (
1031
- '✅ <b>File Successfully Uploaded!</b>\n\n'
1032
- f'⚡ Encoding Time: {format_time_duration(encoding_time)}\n'
1033
- f'📤 Upload Time: {format_time_duration(upload_time)}\n'
1034
- f'⏱️ Total Time: {format_time_duration(encoding_time + upload_time)}'
1035
- )
1036
- await sent_msg.edit(text)
1037
-
1038
- # Cleanup inside try block AFTER successful upload
1039
- path = Config.DOWNLOAD_DIR+'/'
1040
-
1041
- # Delete source files
1042
- if og_vid_filename and os.path.exists(path + og_vid_filename):
1043
- os.remove(path + og_vid_filename)
1044
- logging.info(f"Deleted video file: {og_vid_filename}")
1045
-
1046
- if og_sub_filename and os.path.exists(path + og_sub_filename):
1047
- os.remove(path + og_sub_filename)
1048
- logging.info(f"Deleted subtitle file: {og_sub_filename}")
1049
-
1050
- # Delete the final encoded file from user directory
1051
- if os.path.exists(destination_path):
1052
- os.remove(destination_path)
1053
- logging.info(f"Deleted final encoded file: {destination_path}")
1054
-
1055
- # Try to remove user directory if it exists
1056
- try:
1057
- if os.path.exists(user_dir):
1058
- if len(os.listdir(user_dir)) == 0:
1059
- os.rmdir(user_dir)
1060
- logging.info(f"Removed empty user directory: {user_dir}")
1061
- else:
1062
- logging.warning(f"User directory not empty, cannot remove: {user_dir}")
1063
- except Exception as e:
1064
- logging.error(f"Error removing user directory: {e}")
1065
-
1066
- db.erase(chat_id)
1067
- if chat_id in skipped_sub:
1068
- skipped_sub.remove(chat_id)
1069
-
1070
- except Exception as e:
1071
- print(e)
1072
- await client.send_message(
1073
- chat_id,
1074
- '❌ <b>Upload Failed!</b>\n\n'
1075
- 'An error occurred while uploading the file.\n'
1076
- 'Please check logs for details.'
1077
- )
1078
- finally:
1079
- # Clear process status at the end
1080
- clear_process_status(chat_id)
1081
-
1082
- elif command == '/reset':
1083
- # Reset to initial settings and start over with resolution
1084
- user_settings[chat_id] = {
1085
- 'crf': '23',
1086
- 'preset': 'medium',
1087
- 'resolution': 'original',
1088
- 'fps': 'original',
1089
- 'codec': 'h264',
1090
- 'audio_bitrate': '128k',
1091
- 'audio_track': 'all',
1092
- 'filename': None,
1093
- 'waiting_for': 'resolution'
1094
- }
1095
-
1096
- # Show resolution options again
1097
- resolution_text = (
1098
- '🎬 <b>Choose Video Resolution:</b>\n\n'
1099
- '• <code>8K</code> - 7680x4320\n'
1100
- '• <code>4K</code> - 3840x2160\n'
1101
- '• <code>1440p</code> - 2560x1440\n'
1102
- '• <code>1080p</code> - 1920x1080\n'
1103
- '• <code>720p</code> - 1280x720\n'
1104
- '• <code>480p</code> - 854x480\n'
1105
- '• <code>360p</code> - 640x360\n'
1106
- '• <code>240p</code> - 426x240\n'
1107
- '• <code>144p</code> - 256x144\n'
1108
- '• <code>original</code> - Keep original resolution\n\n'
1109
- '💡 Type resolution or /default for original'
1110
- )
1111
-
1112
- await client.send_message(chat_id, resolution_text)
1113
- return
1114
-
1115
- elif command == '/clear':
1116
- # Call the existing clear function
1117
- await clear_user_data(client, message)
1118
- return
1119
-
1120
- else:
1121
- # Invalid command
1122
- await client.send_message(
1123
- chat_id,
1124
- '⚠️ <b>Invalid Command</b>\n\n'
1125
- '📝 <b>Available Commands:</b>\n'
1126
- '• /confirm - Start encoding\n'
1127
- '• /reset - Choose settings again\n'
1128
- '• /nosubs - Encode without subtitles\n'
1129
- '• /clear - Clear all data'
1130
- )
1131
- return