init
This commit is contained in:
parent
616babf4c7
commit
4dbeebdfcf
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
conf.json
|
||||||
|
__pycache__
|
||||||
|
*.mp3
|
19
db.py
Normal file
19
db.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import os
|
||||||
|
import json
|
||||||
|
|
||||||
|
if not os.path.exists('conf.json'):
|
||||||
|
db = {'api_token': 'None'}
|
||||||
|
js = json.dumps(db, indent=2)
|
||||||
|
with open("conf.json", "w") as outfile:
|
||||||
|
outfile.write(js)
|
||||||
|
print('Created new conf.json')
|
||||||
|
|
||||||
|
def read(file = 'conf.json'):
|
||||||
|
with open(file, "r", encoding="utf-8") as openfile:
|
||||||
|
db = json.load(openfile)
|
||||||
|
return db
|
||||||
|
|
||||||
|
def write(db, file = 'conf.json'):
|
||||||
|
js = json.dumps(db, indent=2, ensure_ascii=False)
|
||||||
|
with open(file, "w", encoding="utf-8") as outfile:
|
||||||
|
outfile.write(js)
|
159
yt_downloader.py
Normal file
159
yt_downloader.py
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
import json
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
import telebot
|
||||||
|
from telebot import types
|
||||||
|
from db import *
|
||||||
|
import subprocess
|
||||||
|
import time
|
||||||
|
from threading import Lock
|
||||||
|
|
||||||
|
# Configure logging
|
||||||
|
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO)
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# Bot token from @BotFather
|
||||||
|
CONFIG = read()
|
||||||
|
BOT_TOKEN = CONFIG['api_token']
|
||||||
|
|
||||||
|
bot = telebot.TeleBot(BOT_TOKEN)
|
||||||
|
|
||||||
|
# Global lock to enforce delay between operations
|
||||||
|
operation_lock = Lock()
|
||||||
|
|
||||||
|
def delayed_operation():
|
||||||
|
"""Enforce a 1-second delay between any two operations."""
|
||||||
|
with operation_lock:
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
@bot.message_handler(func=lambda message: 'music.youtube.com/watch?v=' in message.text)
|
||||||
|
def handle_youtube_music_link(message):
|
||||||
|
"""Handle YouTube Music links in messages"""
|
||||||
|
user = message.from_user
|
||||||
|
text = message.text
|
||||||
|
|
||||||
|
# Extract all YouTube Music URLs from the message
|
||||||
|
urls = [word for word in text.split() if 'music.youtube.com/watch?v=' in word]
|
||||||
|
|
||||||
|
for url in urls:
|
||||||
|
try:
|
||||||
|
# Notify user that download has started
|
||||||
|
progress_msg = bot.reply_to(message, "🎵 Fetching music metadata...")
|
||||||
|
|
||||||
|
# Delay before fetching metadata
|
||||||
|
delayed_operation()
|
||||||
|
|
||||||
|
# Step 1: Get metadata using yt-dlp in simulate mode
|
||||||
|
result = subprocess.run(
|
||||||
|
["yt-dlp", "--print-json", "--skip-download", "--restrict-filenames", url],
|
||||||
|
capture_output=True,
|
||||||
|
text=True
|
||||||
|
)
|
||||||
|
|
||||||
|
if result.returncode != 0:
|
||||||
|
logger.error(f"yt-dlp metadata fetch error: {result.stderr}")
|
||||||
|
bot.reply_to(message, "❌ Failed to fetch music info. Please try again.")
|
||||||
|
continue
|
||||||
|
|
||||||
|
metadata = json.loads(result.stdout)
|
||||||
|
title = metadata.get("title", "Unknown Title")
|
||||||
|
uploader = metadata.get("uploader", "Unknown Artist")
|
||||||
|
|
||||||
|
# Update message
|
||||||
|
bot.edit_message_text(
|
||||||
|
chat_id=progress_msg.chat.id,
|
||||||
|
message_id=progress_msg.message_id,
|
||||||
|
text="🎵 Downloading your music..."
|
||||||
|
)
|
||||||
|
|
||||||
|
# Delay before downloading
|
||||||
|
delayed_operation()
|
||||||
|
|
||||||
|
# Step 2: Download the audio
|
||||||
|
output_template = f"{user.id}_%(title)s.%(ext)s"
|
||||||
|
download_result = subprocess.run(
|
||||||
|
[
|
||||||
|
"yt-dlp",
|
||||||
|
"-x", "--audio-format", "mp3", "--audio-quality", "0",
|
||||||
|
"--output", output_template,
|
||||||
|
"--restrict-filenames",
|
||||||
|
url
|
||||||
|
],
|
||||||
|
capture_output=True,
|
||||||
|
text=True
|
||||||
|
)
|
||||||
|
|
||||||
|
if download_result.returncode != 0:
|
||||||
|
logger.error(f"yt-dlp download error: {download_result.stderr}")
|
||||||
|
bot.reply_to(message, "❌ Failed to download audio. Please check the link and try again.")
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Step 3: Find the downloaded file
|
||||||
|
downloaded_files = [f for f in os.listdir('.') if f.startswith(str(user.id)) and f.endswith('.mp3')]
|
||||||
|
|
||||||
|
if not downloaded_files:
|
||||||
|
bot.reply_to(message, "❌ Failed to download audio. Please try again.")
|
||||||
|
return
|
||||||
|
|
||||||
|
audio_file = downloaded_files[0]
|
||||||
|
|
||||||
|
# Step 4: Send audio with metadata as caption
|
||||||
|
with open(audio_file, 'rb') as audio:
|
||||||
|
bot.send_audio(
|
||||||
|
chat_id=message.chat.id,
|
||||||
|
audio=audio,
|
||||||
|
caption=f"🎧 {title}\n👤 {uploader}",
|
||||||
|
title=title,
|
||||||
|
performer=uploader,
|
||||||
|
reply_to_message_id=message.message_id
|
||||||
|
)
|
||||||
|
|
||||||
|
# Delete progress message
|
||||||
|
bot.delete_message(message.chat.id, progress_msg.message_id)
|
||||||
|
|
||||||
|
# Clean up downloaded file
|
||||||
|
os.remove(audio_file)
|
||||||
|
logger.info(f"Sent audio to user {user.first_name} (@{user.username})")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error processing request: {e}")
|
||||||
|
bot.reply_to(message, "❌ An error occurred while processing your request. Please try again later.")
|
||||||
|
|
||||||
|
@bot.message_handler(commands=['start', 'help'])
|
||||||
|
def send_welcome(message):
|
||||||
|
"""Send welcome message"""
|
||||||
|
bot.reply_to(message,
|
||||||
|
"🎵 YouTube Music Downloader Bot\n\n"
|
||||||
|
"Send me a link from music.youtube.com and I'll download the audio for you!\n\n"
|
||||||
|
"Example: https://music.youtube.com/watch?v=dQw4w9WgXcQ\n\n"
|
||||||
|
"Source code: https://gitea.del.pw/justuser-31/just_ytmusic_downloader",
|
||||||
|
disable_web_page_preview=True
|
||||||
|
)
|
||||||
|
|
||||||
|
# # Handler for when bot is added to a group
|
||||||
|
# @bot.my_chat_member_handler()
|
||||||
|
# def my_chat_member_handler(message: types.ChatMemberUpdated):
|
||||||
|
# """
|
||||||
|
# Triggered when the bot's membership status changes in a chat.
|
||||||
|
# If added to a group but is not admin, notify that it needs admin rights.
|
||||||
|
# """
|
||||||
|
# new_status = message.new_chat_member.status
|
||||||
|
# chat_type = message.chat.type
|
||||||
|
#
|
||||||
|
# # Only respond if added to a group or supergroup
|
||||||
|
# if chat_type in ["group", "supergroup"] and new_status == "member":
|
||||||
|
# # Check if bot is admin
|
||||||
|
# try:
|
||||||
|
# bot_member = bot.get_chat_member(message.chat.id, bot.get_me().id)
|
||||||
|
# if not bot_member.status == "administrator":
|
||||||
|
# bot.send_message(
|
||||||
|
# chat_id=message.chat.id,
|
||||||
|
# text="⚠️ This bot must be an administrator in the group to work properly.",
|
||||||
|
# disable_notification=True
|
||||||
|
# )
|
||||||
|
# except Exception as e:
|
||||||
|
# logger.warning(f"Could not check admin status: {e}")
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
logger.info("Bot is running...")
|
||||||
|
bot.infinity_polling()
|
Loading…
Reference in New Issue
Block a user