aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cogs/compression.py145
1 files changed, 88 insertions, 57 deletions
diff --git a/cogs/compression.py b/cogs/compression.py
index f39b56a..b684f9f 100644
--- a/cogs/compression.py
+++ b/cogs/compression.py
@@ -1,6 +1,7 @@
1import mimetypes 1import mimetypes
2import os 2import os
3import subprocess 3import subprocess
4from typing import List, Optional
4 5
5import discord 6import discord
6from discord.ext import commands 7from discord.ext import commands
@@ -19,21 +20,95 @@ def filetype(filename):
19 return "Image" 20 return "Image"
20 return "Unknown" 21 return "Unknown"
21 22
22
23class Compression(commands.Cog): 23class Compression(commands.Cog):
24 def __init__(self, bot): 24 def __init__(self, bot):
25 self.bot = bot 25 self.bot = bot
26 self.temp_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "temp")
27 if not os.path.exists(self.temp_dir):
28 os.makedirs(self.temp_dir)
29
30 async def _compress_attachment(self, attachment: discord.Attachment) -> Optional[discord.File]:
31 """Compress a single attachment and return the compressed file"""
32 try:
33 file_path = os.path.join(self.temp_dir, attachment.filename)
34 output_path = os.path.join(self.temp_dir, f"better_{attachment.filename}")
35
36 # Save the original file
37 await attachment.save(file_path)
38
39 # Process the file
40 file_type = filetype(file_path)
41 logger.info(f"Compression started for: {file_type}")
42
43 if file_type == "Video":
44 command = f"ffmpeg -i {file_path} -c:v libx264 -crf 51 -b:v 100 -vf 'fps=10,scale=trunc(iw*0.5/2)*2:trunc(ih*0.5/2)*2' -preset ultrafast -c:a libopus -b:a 500 {output_path}"
45 elif file_type == "Audio":
46 command = f"ffmpeg -i {file_path} -ac 1 -b:a 500 -acodec libopus {output_path}"
47 elif file_type == "Image":
48 ext = os.path.splitext(file_path)[1].lower()
49 if ext in [".jpg", ".jpeg"]:
50 command = f"magick {file_path} -strip -resize 30% -quality 5 {output_path}"
51 elif ext == ".gif":
52 command = f"magick {file_path} -coalesce -resize 30% -colors 32 -strip -layers Optimize {output_path}"
53 else:
54 output_path = os.path.splitext(output_path)[0] + ".jpg"
55 command = f"magick {file_path} -strip -resize 30% -quality 5 {output_path}"
56 else:
57 logger.info(f"Unsupported file type: {file_type}")
58 os.remove(file_path)
59 return None
60
61 # Run the compression
62 subprocess.run(command, check=True, shell=True)
63
64 if os.path.exists(output_path):
65 logger.info(f"Successfully compressed: {attachment.filename}")
66 return discord.File(output_path, filename=f"compressed_{attachment.filename}")
67 return None
68
69 except Exception as e:
70 logger.error(f"Error processing {attachment.filename}: {str(e)}")
71 return None
72
73 finally:
74 # Clean up temporary files
75 if 'file_path' in locals() and os.path.exists(file_path):
76 os.remove(file_path)
77
78 async def _compress_attachments(self, attachments: List[discord.Attachment]) -> List[discord.File]:
79 """Compress multiple attachments and return list of compressed files"""
80 compressed_files = []
81 for attachment in attachments:
82 compressed_file = await self._compress_attachment(attachment)
83 if compressed_file:
84 compressed_files.append(compressed_file)
85 return compressed_files
26 86
27 @commands.message_command( 87 @commands.message_command(
28 name="kompresja", 88 name="kompresja",
29 description="skompresuj wideło albo dźwienk :)", 89 description="skompresuj wideło albo dźwienk :)",
30 ) 90 )
31 async def message_compression(self, ctx, message: discord.Message): 91 async def message_compression(self, ctx, message: discord.Message):
32 if message.attachments: 92 if not message.attachments:
33 for attachment in message.attachments:
34 await self.compression(ctx, attachment)
35 else:
36 await ctx.respond("Brak załączników w wiadomości", ephemeral=True) 93 await ctx.respond("Brak załączników w wiadomości", ephemeral=True)
94 return
95
96 await ctx.defer()
97 compressed_files = await self._compress_attachments(message.attachments)
98
99 # Send all compressed files in one message
100 if compressed_files:
101 await ctx.respond(
102 f"Skompresowano {len(compressed_files)} z {len(message.attachments)} plików:",
103 files=compressed_files
104 )
105
106 # Clean up compressed files
107 for file in compressed_files:
108 if os.path.exists(file.fp.name):
109 os.remove(file.fp.name)
110 else:
111 await ctx.respond("Nie udało się skompresować żadnych plików.", ephemeral=True)
37 112
38 @commands.slash_command( 113 @commands.slash_command(
39 name="kompresja", 114 name="kompresja",
@@ -45,60 +120,16 @@ class Compression(commands.Cog):
45 required=True, 120 required=True,
46 type=discord.Attachment 121 type=discord.Attachment
47 ) 122 )
48 async def compression(self, ctx, plik): 123 async def compression(self, ctx, plik: discord.Attachment):
49 await ctx.defer() 124 await ctx.defer()
50 # Save files to the temp directory inside the bot's root directory 125 compressed_file = await self._compress_attachment(plik)
51 bot_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 126
52 temp_dir = os.path.join(bot_dir, "temp") 127 if compressed_file:
53 if not os.path.exists(temp_dir): 128 await ctx.respond(file=compressed_file)
54 os.makedirs(temp_dir) 129 if os.path.exists(compressed_file.fp.name):
55 file_path = os.path.join(temp_dir, plik.filename) 130 os.remove(compressed_file.fp.name)
56 await plik.save(file_path)
57
58 input_path = file_path
59 output_path = os.path.join(temp_dir, f"better_{plik.filename}")
60 file_type = filetype(file_path)
61 logger.info(f"Compression started for: {file_type}")
62 if file_type == "Video":
63 # Execute ffmpeg command to compress video
64 ffmpeg_command = f"ffmpeg -i {input_path} -c:v libx264 -crf 51 -b:v 100 -vf 'fps=10,scale=trunc(iw*0.5/2)*2:trunc(ih*0.5/2)*2' -preset ultrafast -c:a libopus -b:a 500 {output_path}"
65 elif file_type == "Audio":
66 ffmpeg_command = f"ffmpeg -i {input_path} -ac 1 -b:a 500 -acodec libopus {output_path}"
67 elif file_type == "Image":
68 # Use ImageMagick's magick to heavily compress image (JPEG/PNG/GIF)
69 # For JPEG: quality 5, strip metadata, resize to 30% of the original size
70 # For PNG: magick to JPEG, then compress
71 # For GIF: optimize and reduce colors
72 ext = os.path.splitext(input_path)[1].lower()
73 if ext in [".jpg", ".jpeg"]:
74 img_command = f"magick {input_path} -strip -resize 30% -quality 5 {output_path}"
75 elif ext == ".gif":
76 # Optimize GIF: reduce colors, optimize layers, and resize
77 img_command = f"magick {input_path} -coalesce -resize 30% -colors 32 -strip -layers Optimize {output_path}"
78 else:
79 # magick non-JPEG images to JPEG for maximum compression
80 output_path = os.path.splitext(output_path)[0] + ".jpg"
81 img_command = f"magick {input_path} -strip -resize 30% -quality 5 {output_path}"
82 ffmpeg_command = img_command
83 else: 131 else:
84 await ctx.respond("Plik jest nieobsługiwany", ephemeral=True) 132 await ctx.respond("Nie udało się skompresować pliku.", ephemeral=True)
85 logger.info("Unsupported file type")
86 return
87
88 try:
89 subprocess.run(ffmpeg_command, check=True, shell=True)
90 # Upload the compressed file
91 await ctx.respond(file=discord.File(output_path))
92 logger.info(f"Compression completed for: {file_type}")
93 # Delete compressed file
94 os.remove(input_path)
95 os.remove(output_path)
96 except subprocess.CalledProcessError as e:
97 await ctx.respond(f"Compression failed: {str(e)}", ephemeral=True)
98 if os.path.exists(input_path):
99 os.remove(input_path)
100 if os.path.exists(output_path):
101 os.remove(output_path)
102 133
103 134
104def setup(bot): 135def setup(bot):