From ce61bf6bd5949046d15f58609f572fedcb8a073e Mon Sep 17 00:00:00 2001 From: salmonik Date: Wed, 4 Feb 2026 16:16:26 +0100 Subject: refactor music --- cogs/music/media_processing.py | 124 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 cogs/music/media_processing.py (limited to 'cogs/music/media_processing.py') diff --git a/cogs/music/media_processing.py b/cogs/music/media_processing.py new file mode 100644 index 0000000..430b51f --- /dev/null +++ b/cogs/music/media_processing.py @@ -0,0 +1,124 @@ +# This file is part of Senator Sperma +# Licensed under the GPL-3.0 License +# Copyright (C) 2026 Salmonik @ salmonik@tuta.io +# https://codeberg.org/salmonik/senator-sperma + +import os +import asyncio +import logging +from pathlib import Path + +import yt_dlp +from youtubesearchpython import VideosSearch + +logger = logging.getLogger(__name__) + +class MediaDownloader: + """Handles downloading media from various sources.""" + + @staticmethod + async def download(ctx, url): + """Download audio from YouTube URL.""" + # Create temp directory if it doesn't exist + temp_dir = os.path.join('cogs', 'temp', 'music') + os.makedirs(temp_dir, exist_ok=True) + + ytdl_opts = { + "outtmpl": os.path.join(temp_dir, "%(title)s.%(ext)s"), + "postprocessors": [ + { + "key": "FFmpegExtractAudio", + "preferredcodec": "mp3", + "preferredquality": "192", + } + ], + "force_ipv4": True, + } + + try: + with yt_dlp.YoutubeDL(ytdl_opts) as ydl: + info_dict = ydl.extract_info(url, download=True) + # Get the base filename without extension + base_filename = os.path.splitext(ydl.prepare_filename(info_dict))[0] + # Add .mp3 extension since we're extracting audio + filename = f"{base_filename}.mp3" + logger.info(f"Downloaded file: {filename}") + return filename + except Exception as e: + logger.error(f"Error downloading video: {e}") + await ctx.respond("eror pobirajync wideło", ephemeral=True) + + @staticmethod + async def get_url(ctx, query): + """Search YouTube and get URL for a query.""" + search = VideosSearch(query, limit=1) + if search.result()["result"]: + title = search.result()["result"][0]["title"] + url = search.result()["result"][0]["link"] + logger.info(f"Found video: {title}") + return url, title + else: + logger.warning("No results found for the query.") + await ctx.respond("nic ni ma", ephemeral=True) + return None + +class AudioCompressor: + """Handles audio compression using FFmpeg.""" + + @staticmethod + async def compress(ctx, filename): + """Compress audio file to opus format.""" + if not filename: + await ctx.respond("dzie wideło", ephemeral=True) + return None + + # Create temp directory if it doesn't exist + temp_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'temp', 'compressed') + os.makedirs(temp_dir, exist_ok=True) + + # Create a temporary output filename in the temp directory + base_name = os.path.basename(filename) + output_filename = os.path.join(temp_dir, f"{os.path.splitext(base_name)[0]}_compressed.opus") + + # Ensure the output directory exists + os.makedirs(os.path.dirname(output_filename) or '.', exist_ok=True) + + # Use proper quoting for filenames with spaces + ffmpeg_command = [ + 'ffmpeg', + '-y', # Overwrite output file if it exists + '-i', filename, + '-ac', '1', # Mono audio + '-b:a', '500', # 500k bitrate + '-acodec', 'libopus', + output_filename + ] + + try: + logger.info(f"Running FFmpeg command: {' '.join(ffmpeg_command)}") + process = await asyncio.create_subprocess_exec( + *ffmpeg_command, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE + ) + + _, stderr = await process.communicate() + + if process.returncode != 0: + error_msg = stderr.decode() + logger.error(f"FFmpeg error ({process.returncode}): {error_msg}") + await ctx.respond("eror kompresujync pilk", ephemeral=True) + return None + + if not os.path.exists(output_filename): + logger.error(f"Output file was not created: {output_filename}") + await ctx.respond("eror kompresujync pilk - output file not created", ephemeral=True) + return None + + logger.info(f"Successfully compressed file: {output_filename}") + return output_filename + + except Exception as e: + logger.error(f"Error compressing file: {str(e)}", exc_info=True) + await ctx.respond(f"eror kompresujync pilk: {str(e)}", ephemeral=True) + return None -- cgit v1.2.3