Initial commit - Telegram thumbnail uploader bot
This commit is contained in:
commit
b1a1f95e4e
|
|
@ -0,0 +1,7 @@
|
|||
.env
|
||||
sessions/
|
||||
temp/
|
||||
__pycache__/
|
||||
*.pyc
|
||||
venv/
|
||||
.env.*
|
||||
|
|
@ -0,0 +1,266 @@
|
|||
# Telegram Thumbnail Upload Bot
|
||||
|
||||
Bot ini untuk **upload video ke Telegram bersama thumbnail secara automatik**.
|
||||
|
||||
---
|
||||
|
||||
## Keperluan
|
||||
|
||||
Pastikan komputer sudah install:
|
||||
|
||||
Disarankan guna Python **3.12 atau 3.13**
|
||||
|
||||
Python 3.13
|
||||
https://www.python.org/downloads/release/python-3130/
|
||||
|
||||
Python 3.12
|
||||
https://www.python.org/downloads/release/python-3120/
|
||||
|
||||
**FFmpeg**
|
||||
https://ffmpeg.org/download.html
|
||||
|
||||
### Add FFmpeg to PATH (Windows)
|
||||
|
||||
Selepas download dan extract FFmpeg, pastikan folder `bin` dimasukkan ke dalam **System PATH**.
|
||||
|
||||
Contoh lokasi:
|
||||
|
||||
```
|
||||
C:\ffmpeg\bin
|
||||
```
|
||||
|
||||
Kalau folder ini tidak dimasukkan ke PATH, command `ffmpeg` dan `ffprobe` tidak akan dikenali oleh sistem.
|
||||
|
||||
Lepas install, cuba test:
|
||||
|
||||
```
|
||||
ffmpeg -version
|
||||
ffprobe -version
|
||||
```
|
||||
|
||||
Kalau keluar version info maksudnya sudah OK.
|
||||
|
||||
---
|
||||
|
||||
## Setup Telegram API
|
||||
|
||||
Pergi ke:
|
||||
|
||||
https://my.telegram.org
|
||||
|
||||
Login Telegram → masuk **API development tools**
|
||||
|
||||
Ambil:
|
||||
|
||||
```
|
||||
API_ID
|
||||
API_HASH
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Buat Bot
|
||||
|
||||
Pergi ke:
|
||||
|
||||
https://t.me/BotFather
|
||||
|
||||
Run command:
|
||||
|
||||
```
|
||||
/newbot
|
||||
```
|
||||
|
||||
Lepas siap BotFather akan bagi:
|
||||
|
||||
```
|
||||
BOT_TOKEN
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Setup `.env`
|
||||
|
||||
Dalam folder bot ada file:
|
||||
|
||||
```
|
||||
.env.example
|
||||
```
|
||||
|
||||
Rename jadi:
|
||||
|
||||
```
|
||||
.env
|
||||
```
|
||||
|
||||
Lepas tu isi macam ni:
|
||||
|
||||
```
|
||||
BOT_TOKEN=
|
||||
API_ID=
|
||||
API_HASH=
|
||||
```
|
||||
|
||||
Contoh:
|
||||
|
||||
```
|
||||
BOT_TOKEN=123456:ABCDEF
|
||||
API_ID=123456
|
||||
API_HASH=abcdef1234567890
|
||||
```
|
||||
|
||||
Jangan share file `.env` ni dengan orang lain.
|
||||
|
||||
---
|
||||
|
||||
## Jalankan Bot
|
||||
|
||||
Double click:
|
||||
|
||||
```
|
||||
Start.bat
|
||||
```
|
||||
|
||||
Script ni akan:
|
||||
|
||||
- create virtual environment
|
||||
- install dependency
|
||||
- start bot
|
||||
|
||||
Kalau berjaya akan nampak:
|
||||
|
||||
```
|
||||
Bot started successfully
|
||||
```
|
||||
|
||||
Selepas bot berjalan, buka Telegram dan cuba command berikut untuk test:
|
||||
|
||||
```
|
||||
/send_video
|
||||
```
|
||||
|
||||
atau
|
||||
|
||||
```
|
||||
/batch
|
||||
```
|
||||
|
||||
Bot akan menggunakan sample file yang sudah disediakan dalam folder:
|
||||
|
||||
```
|
||||
single/
|
||||
batch/
|
||||
```
|
||||
|
||||
## Test Bot (Sample Files)
|
||||
|
||||
Repository ini sudah menyediakan **sample video dan thumbnail** supaya anda boleh terus test bot.
|
||||
|
||||
---
|
||||
|
||||
## Upload Video (Single)
|
||||
|
||||
Letak video dalam folder:
|
||||
|
||||
```
|
||||
single/
|
||||
```
|
||||
|
||||
Contoh:
|
||||
|
||||
```
|
||||
single/
|
||||
sample_video.mp4
|
||||
sample_video.jpg
|
||||
```
|
||||
|
||||
Kemudian dalam Telegram hantar command:
|
||||
|
||||
```
|
||||
/send_video
|
||||
```
|
||||
|
||||
Bot akan upload semua video dalam folder tu.
|
||||
|
||||
Pastikan **nama thumbnail sama dengan nama video**.
|
||||
|
||||
Contoh:
|
||||
|
||||
```
|
||||
sample_video.mp4
|
||||
sample_video.jpg
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Upload Drama (Batch)
|
||||
|
||||
Struktur folder:
|
||||
|
||||
```
|
||||
batch/
|
||||
|
||||
drama1/
|
||||
sample_video.mp4
|
||||
sample_video.jpg
|
||||
```
|
||||
|
||||
Dalam Telegram run:
|
||||
|
||||
```
|
||||
/batch
|
||||
```
|
||||
|
||||
Bot akan upload semua video dalam setiap folder batch.
|
||||
|
||||
---
|
||||
|
||||
## Nota
|
||||
|
||||
Kali pertama bot run dia akan minta:
|
||||
|
||||
```
|
||||
Phone number
|
||||
OTP code
|
||||
```
|
||||
|
||||
Ini untuk create **user session** supaya boleh upload video besar.
|
||||
|
||||
Session akan disimpan dalam folder:
|
||||
|
||||
```
|
||||
sessions/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Converter (Optional)
|
||||
|
||||
Dalam folder `converter` ada tool kecil untuk convert image ke JPG kalau thumbnail format pelik macam:
|
||||
|
||||
```
|
||||
AVIF
|
||||
WEBP
|
||||
HEIC
|
||||
PNG
|
||||
```
|
||||
|
||||
Run saja:
|
||||
|
||||
```
|
||||
converter/Start.bat
|
||||
```
|
||||
|
||||
atau
|
||||
|
||||
```
|
||||
python converter/converter.py
|
||||
```
|
||||
|
||||
Letakkan image dalam folder `converter/` dan hasil convert akan disimpan dalam folder `output_images/`.
|
||||
|
||||
---
|
||||
|
||||
## Siap
|
||||
|
||||
Lepas setup semua ni, bot dah boleh guna untuk upload video ke Telegram dengan senang.
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
@echo off
|
||||
chcp 65001 >nul
|
||||
cd /d "%~dp0"
|
||||
|
||||
echo ==============================
|
||||
echo Telegram Thumbnail Bot
|
||||
echo ==============================
|
||||
|
||||
REM
|
||||
where python >nul 2>nul
|
||||
if %errorlevel% neq 0 (
|
||||
echo Python tidak dijumpai. Sila install Python dahulu.
|
||||
pause
|
||||
exit
|
||||
)
|
||||
|
||||
REM
|
||||
if not exist venv (
|
||||
echo First time setup detected...
|
||||
echo Creating virtual environment...
|
||||
python -m venv venv
|
||||
|
||||
call venv\Scripts\activate.bat
|
||||
|
||||
echo Updating pip...
|
||||
python -m pip install --upgrade pip setuptools wheel
|
||||
|
||||
echo Installing requirements...
|
||||
pip install -r requirements.txt
|
||||
) else (
|
||||
call venv\Scripts\activate.bat
|
||||
)
|
||||
|
||||
echo Starting bot...
|
||||
python main.py
|
||||
|
||||
pause
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 191 KiB |
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 191 KiB |
Binary file not shown.
|
|
@ -0,0 +1,26 @@
|
|||
@echo off
|
||||
chcp 65001 >nul
|
||||
cd /d "%~dp0"
|
||||
|
||||
echo ==============================
|
||||
echo Image Converter Tool
|
||||
echo ==============================
|
||||
echo.
|
||||
|
||||
REM
|
||||
cd ..
|
||||
|
||||
REM
|
||||
call venv\Scripts\activate.bat
|
||||
|
||||
REM
|
||||
cd converter
|
||||
|
||||
echo Starting converter...
|
||||
echo.
|
||||
|
||||
python converter.py
|
||||
|
||||
echo.
|
||||
echo Conversion finished.
|
||||
pause
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
import os
|
||||
import imageio.v3 as iio
|
||||
from PIL import Image
|
||||
|
||||
input_dir = os.getcwd()
|
||||
output_dir = os.path.join(input_dir, "output_images")
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
|
||||
supported_extensions = {
|
||||
".avif", ".webp", ".png", ".heif", ".heic",
|
||||
".bmp", ".tiff"
|
||||
}
|
||||
|
||||
files = [
|
||||
f for f in os.listdir(input_dir)
|
||||
if os.path.splitext(f)[1].lower() in supported_extensions
|
||||
]
|
||||
|
||||
total = len(files)
|
||||
count = 0
|
||||
|
||||
print(f"Found {total} convertible images\n")
|
||||
|
||||
for filename in files:
|
||||
filepath = os.path.join(input_dir, filename)
|
||||
name, ext = os.path.splitext(filename)
|
||||
ext = ext.lower()
|
||||
|
||||
count += 1
|
||||
|
||||
try:
|
||||
if ext == ".avif":
|
||||
img_array = iio.imread(filepath)
|
||||
img = Image.fromarray(img_array).convert("RGB")
|
||||
else:
|
||||
img = Image.open(filepath).convert("RGB")
|
||||
|
||||
output_path = os.path.join(output_dir, name + ".jpg")
|
||||
|
||||
if os.path.exists(output_path):
|
||||
print(f"[{count}/{total}] ⏭️ Skipped (exists): {name}.jpg")
|
||||
continue
|
||||
|
||||
img.thumbnail((320, 320))
|
||||
|
||||
img.save(output_path, "JPEG", quality=90, subsampling=0)
|
||||
|
||||
print(f"[{count}/{total}] ✅ Converted: {filename} → {name}.jpg")
|
||||
|
||||
except Exception as e:
|
||||
print(f"[{count}/{total}] ❌ Failed: {filename} ({e})")
|
||||
|
||||
print("\nConversion finished.")
|
||||
Binary file not shown.
|
|
@ -0,0 +1,231 @@
|
|||
import os
|
||||
import time
|
||||
import logging
|
||||
import subprocess
|
||||
import json
|
||||
from dotenv import load_dotenv
|
||||
from pyrogram import Client, filters
|
||||
from pyrogram.errors import FloodWait
|
||||
import asyncio
|
||||
|
||||
from utils import (
|
||||
get_local_videos,
|
||||
process_local_thumbnail,
|
||||
get_batch_directories,
|
||||
get_batch_videos
|
||||
)
|
||||
|
||||
load_dotenv()
|
||||
|
||||
BOT_TOKEN = os.getenv("BOT_TOKEN")
|
||||
API_ID = int(os.getenv("API_ID"))
|
||||
API_HASH = os.getenv("API_HASH")
|
||||
|
||||
if not BOT_TOKEN or not API_ID or not API_HASH:
|
||||
raise ValueError("Please configure BOT_TOKEN, API_ID and API_HASH in .env")
|
||||
|
||||
USER_SESSION = "user_session"
|
||||
|
||||
SESSION_DIR = os.path.join(os.path.dirname(__file__), "sessions")
|
||||
os.makedirs(SESSION_DIR, exist_ok=True)
|
||||
|
||||
TEMP_DIR = os.path.join(os.path.dirname(__file__), "temp")
|
||||
os.makedirs(TEMP_DIR, exist_ok=True)
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s - %(levelname)s - %(message)s"
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
Bot = Client(
|
||||
os.path.join(SESSION_DIR, "Thumb-Bot"),
|
||||
bot_token=BOT_TOKEN,
|
||||
api_id=API_ID,
|
||||
api_hash=API_HASH
|
||||
)
|
||||
|
||||
User = Client(
|
||||
os.path.join(SESSION_DIR, USER_SESSION),
|
||||
api_id=API_ID,
|
||||
api_hash=API_HASH
|
||||
)
|
||||
|
||||
|
||||
def remove_extension(name: str) -> str:
|
||||
return os.path.splitext(name)[0]
|
||||
|
||||
|
||||
def get_video_dimensions(path: str):
|
||||
cmd = [
|
||||
"ffprobe", "-v", "error",
|
||||
"-select_streams", "v:0",
|
||||
"-show_entries", "stream=width,height",
|
||||
"-of", "json",
|
||||
path
|
||||
]
|
||||
|
||||
try:
|
||||
result = subprocess.run(
|
||||
cmd,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False
|
||||
)
|
||||
|
||||
if not result.stdout:
|
||||
return 1280, 720
|
||||
|
||||
data = json.loads(result.stdout)
|
||||
streams = data.get("streams")
|
||||
|
||||
if not streams:
|
||||
return 1280, 720
|
||||
|
||||
width = streams[0].get("width", 1280)
|
||||
height = streams[0].get("height", 720)
|
||||
|
||||
return int(width), int(height)
|
||||
|
||||
except Exception:
|
||||
return 1280, 720
|
||||
|
||||
|
||||
def get_video_duration(path: str):
|
||||
cmd = [
|
||||
"ffprobe", "-v", "error",
|
||||
"-show_entries", "format=duration",
|
||||
"-of", "default=noprint_wrappers=1:nokey=1",
|
||||
path
|
||||
]
|
||||
|
||||
try:
|
||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||
return int(float(result.stdout.strip()))
|
||||
except:
|
||||
return 0
|
||||
|
||||
async def send_video_safe(
|
||||
client,
|
||||
chat_id,
|
||||
video_path,
|
||||
caption=None,
|
||||
thumb=None
|
||||
):
|
||||
width, height = get_video_dimensions(video_path)
|
||||
duration = get_video_duration(video_path)
|
||||
|
||||
while True:
|
||||
try:
|
||||
await client.send_video(
|
||||
chat_id=chat_id,
|
||||
video=video_path,
|
||||
caption=caption,
|
||||
thumb=thumb,
|
||||
width=width,
|
||||
height=height,
|
||||
duration=duration,
|
||||
supports_streaming=True
|
||||
)
|
||||
break
|
||||
|
||||
except FloodWait as e:
|
||||
logger.warning(
|
||||
f"FloodWait while uploading {os.path.basename(video_path)}. "
|
||||
f"Sleeping {e.value + 1} seconds..."
|
||||
)
|
||||
await asyncio.sleep(e.value + 1)
|
||||
|
||||
@Bot.on_message(filters.command("start"))
|
||||
async def start(_, m):
|
||||
await m.reply(
|
||||
"Video uploader bot ready.\n\n"
|
||||
"Commands:\n"
|
||||
"/send_video - Upload videos from single folder\n"
|
||||
"/batch - Upload videos from batch folders"
|
||||
)
|
||||
|
||||
|
||||
@Bot.on_message(filters.command("send_video"))
|
||||
async def send_local(_, m):
|
||||
|
||||
status = await m.reply("Scanning local videos...")
|
||||
|
||||
videos, _ = get_local_videos()
|
||||
|
||||
if not videos:
|
||||
await status.edit("No videos found.")
|
||||
return
|
||||
|
||||
for v in videos:
|
||||
|
||||
msg = await m.reply(f"Uploading {v['filename']}...")
|
||||
|
||||
thumb = None
|
||||
if v["thumb_path"]:
|
||||
thumb = process_local_thumbnail(v["thumb_path"])
|
||||
|
||||
await send_video_safe(
|
||||
User,
|
||||
m.chat.id,
|
||||
v["video_path"],
|
||||
caption=remove_extension(v["filename"]),
|
||||
thumb=thumb
|
||||
)
|
||||
|
||||
await msg.delete()
|
||||
|
||||
if thumb and os.path.exists(thumb):
|
||||
os.remove(thumb)
|
||||
|
||||
await status.edit("All videos uploaded.")
|
||||
|
||||
|
||||
@Bot.on_message(filters.command("batch"))
|
||||
async def batch(_, m):
|
||||
|
||||
status = await m.reply("Scanning batch folders...")
|
||||
|
||||
batches = get_batch_directories()
|
||||
|
||||
if not batches:
|
||||
await status.edit("No batch folders found.")
|
||||
return
|
||||
|
||||
for batch_dir in batches:
|
||||
|
||||
videos = get_batch_videos(batch_dir)
|
||||
|
||||
if not videos:
|
||||
continue
|
||||
|
||||
thumb = None
|
||||
|
||||
if videos[0]["thumb_path"]:
|
||||
thumb = process_local_thumbnail(videos[0]["thumb_path"])
|
||||
|
||||
for v in videos:
|
||||
|
||||
msg = await m.reply(f"Uploading {v['filename']}...")
|
||||
|
||||
await send_video_safe(
|
||||
User,
|
||||
m.chat.id,
|
||||
v["video_path"],
|
||||
caption=remove_extension(v["filename"]),
|
||||
thumb=thumb
|
||||
)
|
||||
|
||||
await msg.delete()
|
||||
|
||||
if thumb and os.path.exists(thumb):
|
||||
os.remove(thumb)
|
||||
|
||||
await status.edit("Batch upload complete.")
|
||||
|
||||
|
||||
print("Bot started successfully")
|
||||
|
||||
with User:
|
||||
Bot.run()
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
pyrofork>=2.3.69
|
||||
TgCrypto-pyrofork==1.2.8
|
||||
humanize==4.15.0
|
||||
Pillow>=12.1.0
|
||||
python-dotenv==1.2.1
|
||||
pillow-heif
|
||||
pillow-avif-plugin
|
||||
imageio
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 191 KiB |
Binary file not shown.
|
|
@ -0,0 +1,212 @@
|
|||
import os
|
||||
import re
|
||||
import time
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from PIL import Image
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
BASE_DIR = os.path.dirname(__file__)
|
||||
SINGLE_DIR = os.path.join(BASE_DIR, "single")
|
||||
BATCH_DIR = os.path.join(BASE_DIR, "batch")
|
||||
|
||||
TEMP_DIR = os.path.join(BASE_DIR, "temp")
|
||||
os.makedirs(TEMP_DIR, exist_ok=True)
|
||||
|
||||
VIDEO_FORMATS = (
|
||||
".mp4", ".mkv", ".webm", ".avi", ".mov",
|
||||
".m4v", ".flv", ".wmv", ".ts",
|
||||
".mpg", ".mpeg", ".3gp", ".mp4v", ".vob"
|
||||
)
|
||||
|
||||
THUMB_FORMATS = (".jpg", ".jpeg", ".png")
|
||||
|
||||
def find_matching_thumbnail(video_path):
|
||||
video_name = Path(video_path).stem.lower()
|
||||
|
||||
# Exact match
|
||||
for ext in THUMB_FORMATS:
|
||||
exact_match = os.path.join(SINGLE_DIR, f"{video_name}{ext}")
|
||||
if os.path.exists(exact_match):
|
||||
logger.info(f"Exact thumbnail found: {exact_match}")
|
||||
return exact_match
|
||||
|
||||
video_keywords = video_name.split(".")
|
||||
|
||||
for file in os.listdir(SINGLE_DIR):
|
||||
if file.lower().endswith(THUMB_FORMATS):
|
||||
|
||||
thumb_name = Path(file).stem.lower()
|
||||
|
||||
if (
|
||||
thumb_name in video_name
|
||||
or video_name in thumb_name
|
||||
or any(keyword in thumb_name for keyword in video_keywords)
|
||||
):
|
||||
return os.path.join(SINGLE_DIR, file)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def get_local_videos():
|
||||
video_files = []
|
||||
|
||||
logger.info(f"Scanning single folder: {SINGLE_DIR}")
|
||||
|
||||
videos = [
|
||||
f for f in os.listdir(SINGLE_DIR)
|
||||
if f.lower().endswith(VIDEO_FORMATS)
|
||||
]
|
||||
|
||||
videos.sort()
|
||||
|
||||
for video in videos:
|
||||
|
||||
video_path = os.path.join(SINGLE_DIR, video)
|
||||
|
||||
thumb_path = find_matching_thumbnail(video_path)
|
||||
|
||||
video_files.append({
|
||||
"video_path": video_path,
|
||||
"thumb_path": thumb_path,
|
||||
"filename": video,
|
||||
"size": os.path.getsize(video_path),
|
||||
"has_thumb": thumb_path is not None
|
||||
})
|
||||
|
||||
logger.info(
|
||||
f"Found video: {video} {'with thumbnail' if thumb_path else 'no thumbnail'}"
|
||||
)
|
||||
|
||||
return video_files, {}
|
||||
|
||||
def process_local_thumbnail(thumb_path):
|
||||
|
||||
try:
|
||||
img = Image.open(thumb_path).convert("RGB")
|
||||
|
||||
width, height = img.size
|
||||
|
||||
if width > height:
|
||||
new_width = 320
|
||||
new_height = int(320 * height / width)
|
||||
else:
|
||||
new_height = 320
|
||||
new_width = int(320 * width / height)
|
||||
|
||||
img = img.resize((new_width, new_height), Image.Resampling.LANCZOS)
|
||||
|
||||
output_path = os.path.join(
|
||||
TEMP_DIR,
|
||||
f"thumb_{int(time.time())}.jpg"
|
||||
)
|
||||
|
||||
img.save(output_path, "JPEG", quality=95)
|
||||
|
||||
return output_path
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Thumbnail processing error: {e}")
|
||||
return None
|
||||
|
||||
def find_batch_thumbnail(batch_dir):
|
||||
|
||||
for file in os.listdir(batch_dir):
|
||||
if file.lower().endswith(THUMB_FORMATS):
|
||||
return os.path.join(batch_dir, file)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def extract_episode_number(filename):
|
||||
|
||||
patterns = [
|
||||
r'[Ss](\d+)[Ee](\d+)',
|
||||
r'[Ee][Pp]?\.?\s*(\d+)',
|
||||
r'E(\d+)',
|
||||
r'(\d+)'
|
||||
]
|
||||
|
||||
filename = filename.lower()
|
||||
|
||||
for pattern in patterns:
|
||||
|
||||
match = re.search(pattern, filename)
|
||||
|
||||
if match:
|
||||
try:
|
||||
if len(match.groups()) == 2:
|
||||
return int(match.group(2))
|
||||
|
||||
return int(match.group(1))
|
||||
|
||||
except:
|
||||
continue
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
def get_batch_videos(batch_dir):
|
||||
|
||||
video_files = []
|
||||
|
||||
thumb_path = find_batch_thumbnail(batch_dir)
|
||||
|
||||
videos = []
|
||||
|
||||
for f in os.listdir(batch_dir):
|
||||
|
||||
if f.lower().endswith(VIDEO_FORMATS):
|
||||
|
||||
ep = extract_episode_number(f)
|
||||
|
||||
videos.append((f, ep))
|
||||
|
||||
videos.sort(key=lambda x: x[1])
|
||||
|
||||
for video, ep_num in videos:
|
||||
|
||||
video_path = os.path.join(batch_dir, video)
|
||||
|
||||
video_files.append({
|
||||
"video_path": video_path,
|
||||
"thumb_path": thumb_path,
|
||||
"filename": video,
|
||||
"episode": ep_num,
|
||||
"size": os.path.getsize(video_path)
|
||||
})
|
||||
|
||||
logger.info(f"Found video {video} episode {ep_num}")
|
||||
|
||||
return video_files
|
||||
|
||||
def natural_sort_key(s):
|
||||
return [
|
||||
int(text) if text.isdigit() else text.lower()
|
||||
for text in re.split(r'([0-9]+)', s)
|
||||
]
|
||||
|
||||
def get_batch_directories():
|
||||
|
||||
batch_dirs = []
|
||||
|
||||
if not os.path.exists(BATCH_DIR):
|
||||
return batch_dirs
|
||||
|
||||
has_root_videos = False
|
||||
|
||||
for item in sorted(os.listdir(BATCH_DIR), key=natural_sort_key):
|
||||
|
||||
full = os.path.join(BATCH_DIR, item)
|
||||
|
||||
if os.path.isdir(full):
|
||||
batch_dirs.append(full)
|
||||
|
||||
elif item.lower().endswith(VIDEO_FORMATS):
|
||||
has_root_videos = True
|
||||
|
||||
if has_root_videos:
|
||||
batch_dirs.insert(0, BATCH_DIR)
|
||||
|
||||
return batch_dirs
|
||||
Loading…
Reference in New Issue