status bar in queue and cleaned up embeds, slash command on help
This commit is contained in:
@@ -18,7 +18,7 @@ BASE_FFMPEG_OPTS = {
|
||||
# Audio effects configurations
|
||||
def get_effect_options(effect_name):
|
||||
"""Get FFmpeg options for a specific audio effect"""
|
||||
|
||||
|
||||
effects = {
|
||||
'none': {
|
||||
**BASE_FFMPEG_OPTS
|
||||
@@ -35,20 +35,10 @@ def get_effect_options(effect_name):
|
||||
**BASE_FFMPEG_OPTS,
|
||||
'options': '-vn -af "atempo=0.8,asetrate=48000*0.8,aecho=0.8:0.9:1000:0.3"'
|
||||
},
|
||||
'earrape': {
|
||||
**BASE_FFMPEG_OPTS,
|
||||
# Aggressive compression + hard clipping + bitcrushing for maximum distortion
|
||||
'options': '-vn -af "volume=8,acompressor=threshold=0.001:ratio=30:attack=0.1:release=5,acrusher=bits=8:mix=0.7,volume=2,alimiter=limit=0.8"'
|
||||
},
|
||||
'deepfry': {
|
||||
**BASE_FFMPEG_OPTS,
|
||||
# Extreme bitcrushing + bass boost + compression (meme audio effect)
|
||||
'options': '-vn -af "acrusher=bits=4:mode=log:aa=1,bass=g=15,acompressor=threshold=0.001:ratio=20,volume=3"'
|
||||
},
|
||||
'distortion': {
|
||||
**BASE_FFMPEG_OPTS,
|
||||
# Pure bitcrushing distortion
|
||||
'options': '-vn -af "acrusher=bits=6:mix=0.9,acompressor=threshold=0.01:ratio=15,volume=2"'
|
||||
'options': '-vn -af "acrusher=bits=6:mix=0.9,acompressor=threshold=0.01:ratio=15"'
|
||||
},
|
||||
'reverse': {
|
||||
**BASE_FFMPEG_OPTS,
|
||||
@@ -86,18 +76,8 @@ def get_effect_options(effect_name):
|
||||
**BASE_FFMPEG_OPTS,
|
||||
'options': '-vn -af "aecho=0.8:0.88:60:0.4"'
|
||||
},
|
||||
'phone': {
|
||||
**BASE_FFMPEG_OPTS,
|
||||
# Sounds like a phone call (bandpass filter)
|
||||
'options': '-vn -af "bandpass=f=1500:width_type=h:w=1000,volume=2"'
|
||||
},
|
||||
'megaphone': {
|
||||
**BASE_FFMPEG_OPTS,
|
||||
# Megaphone/radio effect
|
||||
'options': '-vn -af "highpass=f=300,lowpass=f=3000,volume=2,acompressor=threshold=0.1:ratio=8"'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return effects.get(effect_name, effects['none'])
|
||||
|
||||
|
||||
@@ -114,28 +94,40 @@ def initialize_tables():
|
||||
song_name TEXT,
|
||||
loop_mode TEXT DEFAULT 'off',
|
||||
volume INTEGER DEFAULT 100,
|
||||
effect TEXT DEFAULT 'none'
|
||||
effect TEXT DEFAULT 'none',
|
||||
song_start_time REAL DEFAULT 0,
|
||||
song_duration INTEGER DEFAULT 0
|
||||
);''')
|
||||
|
||||
# Set all to not playing
|
||||
cursor.execute("UPDATE servers SET is_playing = 0;")
|
||||
|
||||
|
||||
# Add new columns if they don't exist (for existing databases)
|
||||
try:
|
||||
cursor.execute("ALTER TABLE servers ADD COLUMN loop_mode TEXT DEFAULT 'off';")
|
||||
except sqlite3.OperationalError:
|
||||
pass # Column already exists
|
||||
|
||||
|
||||
try:
|
||||
cursor.execute("ALTER TABLE servers ADD COLUMN volume INTEGER DEFAULT 100;")
|
||||
except sqlite3.OperationalError:
|
||||
pass # Column already exists
|
||||
|
||||
|
||||
try:
|
||||
cursor.execute("ALTER TABLE servers ADD COLUMN effect TEXT DEFAULT 'none';")
|
||||
except sqlite3.OperationalError:
|
||||
pass # Column already exists
|
||||
|
||||
try:
|
||||
cursor.execute("ALTER TABLE servers ADD COLUMN song_start_time REAL DEFAULT 0;")
|
||||
except sqlite3.OperationalError:
|
||||
pass # Column already exists
|
||||
|
||||
try:
|
||||
cursor.execute("ALTER TABLE servers ADD COLUMN song_duration INTEGER DEFAULT 0;")
|
||||
except sqlite3.OperationalError:
|
||||
pass # Column already exists
|
||||
|
||||
# Create queue table if it doesn't exist
|
||||
cursor.execute('''CREATE TABLE IF NOT EXISTS songs (
|
||||
server_id TEXT NOT NULL,
|
||||
@@ -241,17 +233,17 @@ async def pop(server_id, ignore=False):
|
||||
else:
|
||||
song = song[0]
|
||||
|
||||
await set_current_song(server_id, song['title'])
|
||||
|
||||
await set_current_song(server_id, song['title'], song.get('duration', 0))
|
||||
|
||||
# Check loop mode before removing
|
||||
loop_mode = await get_loop_mode(server_id)
|
||||
if loop_mode != 'song': # Only remove if not looping song
|
||||
await mark_song_as_finished(server_id, result[3])
|
||||
|
||||
|
||||
return song['url']
|
||||
|
||||
await set_current_song(server_id, result[4])
|
||||
|
||||
await set_current_song(server_id, result[4], result[6]) # result[6] is duration
|
||||
|
||||
# Check loop mode before removing
|
||||
loop_mode = await get_loop_mode(server_id)
|
||||
if loop_mode != 'song': # Only remove if not looping song
|
||||
@@ -294,15 +286,18 @@ async def mark_song_as_finished(server_id, order_num):
|
||||
|
||||
|
||||
# set the current playing song of the server
|
||||
async def set_current_song(server_id, title):
|
||||
async def set_current_song(server_id, title, duration=0):
|
||||
# Connect to the database
|
||||
conn = sqlite3.connect(db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
import time
|
||||
start_time = time.time()
|
||||
|
||||
cursor.execute(''' UPDATE servers
|
||||
SET song_name = ?
|
||||
SET song_name = ?, song_start_time = ?, song_duration = ?
|
||||
WHERE server_id = ?''',
|
||||
(title, server_id))
|
||||
(title, start_time, duration, server_id))
|
||||
|
||||
# Close connection
|
||||
conn.commit()
|
||||
@@ -319,7 +314,7 @@ async def get_current_song(server_id):
|
||||
WHERE server_id = ?
|
||||
LIMIT 1;''',
|
||||
(server_id,))
|
||||
|
||||
|
||||
result = cursor.fetchone()
|
||||
|
||||
# Close connection
|
||||
@@ -329,6 +324,38 @@ async def get_current_song(server_id):
|
||||
return result[0] if result else "Nothing"
|
||||
|
||||
|
||||
async def get_current_progress(server_id):
|
||||
"""Get current playback progress (elapsed, duration, percentage)"""
|
||||
conn = sqlite3.connect(db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
cursor.execute('''SELECT song_start_time, song_duration, is_playing
|
||||
FROM servers
|
||||
WHERE server_id = ?
|
||||
LIMIT 1;''',
|
||||
(server_id,))
|
||||
|
||||
result = cursor.fetchone()
|
||||
conn.close()
|
||||
|
||||
if not result or result[2] == 0: # Not playing
|
||||
return 0, 0, 0.0
|
||||
|
||||
start_time, duration, _ = result
|
||||
|
||||
if duration == 0:
|
||||
return 0, 0, 0.0
|
||||
|
||||
import time
|
||||
elapsed = int(time.time() - start_time)
|
||||
elapsed = min(elapsed, duration) # Cap at duration
|
||||
percentage = (elapsed / duration) * 100 if duration > 0 else 0
|
||||
|
||||
return elapsed, duration, percentage
|
||||
|
||||
return result[0] if result else "Nothing"
|
||||
|
||||
|
||||
# Grab max order from server
|
||||
async def get_max(server_id, cursor):
|
||||
cursor.execute(f"""
|
||||
@@ -433,18 +460,18 @@ async def get_loop_mode(server_id):
|
||||
"""Get the current loop mode: 'off', 'song', or 'queue'"""
|
||||
conn = sqlite3.connect(db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
|
||||
await add_server(server_id, cursor, conn)
|
||||
|
||||
|
||||
cursor.execute("""SELECT loop_mode
|
||||
FROM servers
|
||||
WHERE server_id = ?""",
|
||||
(server_id,))
|
||||
result = cursor.fetchone()
|
||||
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
|
||||
return result[0] if result else 'off'
|
||||
|
||||
|
||||
@@ -452,17 +479,17 @@ async def set_loop_mode(server_id, mode):
|
||||
"""Set loop mode: 'off', 'song', or 'queue'"""
|
||||
if mode not in ['off', 'song', 'queue']:
|
||||
return False
|
||||
|
||||
|
||||
conn = sqlite3.connect(db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
|
||||
await add_server(server_id, cursor, conn)
|
||||
|
||||
|
||||
cursor.execute("""UPDATE servers
|
||||
SET loop_mode = ?
|
||||
WHERE server_id = ?""",
|
||||
(mode, server_id))
|
||||
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return True
|
||||
@@ -473,35 +500,35 @@ async def get_volume(server_id):
|
||||
"""Get the current volume (0-200)"""
|
||||
conn = sqlite3.connect(db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
|
||||
await add_server(server_id, cursor, conn)
|
||||
|
||||
|
||||
cursor.execute("""SELECT volume
|
||||
FROM servers
|
||||
WHERE server_id = ?""",
|
||||
(server_id,))
|
||||
result = cursor.fetchone()
|
||||
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
|
||||
return result[0] if result else 100
|
||||
|
||||
|
||||
async def set_volume(server_id, volume):
|
||||
"""Set volume (0-200)"""
|
||||
volume = max(0, min(200, volume)) # Clamp between 0-200
|
||||
|
||||
|
||||
conn = sqlite3.connect(db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
|
||||
await add_server(server_id, cursor, conn)
|
||||
|
||||
|
||||
cursor.execute("""UPDATE servers
|
||||
SET volume = ?
|
||||
WHERE server_id = ?""",
|
||||
(volume, server_id))
|
||||
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return volume
|
||||
@@ -512,32 +539,32 @@ async def shuffle_queue(server_id):
|
||||
"""Randomize the order of songs in the queue"""
|
||||
conn = sqlite3.connect(db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
|
||||
await add_server(server_id, cursor, conn)
|
||||
|
||||
|
||||
# Get all songs
|
||||
cursor.execute('''SELECT position, song_link, queued_by, title, thumbnail, duration
|
||||
FROM songs
|
||||
WHERE server_id = ?
|
||||
ORDER BY position''', (server_id,))
|
||||
songs = cursor.fetchall()
|
||||
|
||||
|
||||
if len(songs) <= 1:
|
||||
conn.close()
|
||||
return False # Nothing to shuffle
|
||||
|
||||
|
||||
# Shuffle the songs (keep positions but randomize order)
|
||||
random.shuffle(songs)
|
||||
|
||||
|
||||
# Delete all current songs
|
||||
cursor.execute('''DELETE FROM songs WHERE server_id = ?''', (server_id,))
|
||||
|
||||
|
||||
# Re-insert in shuffled order
|
||||
for i, song in enumerate(songs):
|
||||
cursor.execute("""INSERT INTO songs (server_id, song_link, queued_by, position, title, thumbnail, duration)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)""",
|
||||
(server_id, song[1], song[2], i, song[3], song[4], song[5]))
|
||||
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return True
|
||||
@@ -549,18 +576,18 @@ async def get_effect(server_id):
|
||||
"""Get the current audio effect"""
|
||||
conn = sqlite3.connect(db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
|
||||
await add_server(server_id, cursor, conn)
|
||||
|
||||
|
||||
cursor.execute("""SELECT effect
|
||||
FROM servers
|
||||
WHERE server_id = ?""",
|
||||
(server_id,))
|
||||
result = cursor.fetchone()
|
||||
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
|
||||
return result[0] if result else 'none'
|
||||
|
||||
|
||||
@@ -568,14 +595,14 @@ async def set_effect(server_id, effect_name):
|
||||
"""Set the audio effect"""
|
||||
conn = sqlite3.connect(db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
|
||||
await add_server(server_id, cursor, conn)
|
||||
|
||||
|
||||
cursor.execute("""UPDATE servers
|
||||
SET effect = ?
|
||||
WHERE server_id = ?""",
|
||||
(effect_name, server_id))
|
||||
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return True
|
||||
@@ -597,8 +624,6 @@ def get_effect_emoji(effect_name):
|
||||
'bassboost': '🔉💥',
|
||||
'nightcore': '⚡🎀',
|
||||
'slowed': '🐌💤',
|
||||
'earrape': '💀📢',
|
||||
'deepfry': '🍟💥',
|
||||
'distortion': '⚡🔊',
|
||||
'reverse': '⏪🔄',
|
||||
'chipmunk': '🐿️',
|
||||
@@ -609,8 +634,6 @@ def get_effect_emoji(effect_name):
|
||||
'vibrato': '〰️',
|
||||
'tremolo': '📳',
|
||||
'echo': '🗣️💭',
|
||||
'phone': '📞',
|
||||
'megaphone': '📢📣'
|
||||
}
|
||||
return emojis.get(effect_name, '🔊')
|
||||
|
||||
@@ -622,8 +645,6 @@ def get_effect_description(effect_name):
|
||||
'bassboost': 'MAXIMUM BASS 🔊',
|
||||
'nightcore': 'Speed + pitch up (anime vibes)',
|
||||
'slowed': 'Slowed + reverb (TikTok aesthetic)',
|
||||
'earrape': '⚠️ Aggressive compression + distortion + clipping ⚠️',
|
||||
'deepfry': '🍟 EXTREME bitcrushing + bass (meme audio) 🍟',
|
||||
'distortion': 'Heavy bitcrushing distortion',
|
||||
'reverse': 'Plays audio BACKWARDS',
|
||||
'chipmunk': 'High pitched and fast (Alvin mode)',
|
||||
@@ -634,8 +655,6 @@ def get_effect_description(effect_name):
|
||||
'vibrato': 'Warbling pitch effect',
|
||||
'tremolo': 'Volume oscillation',
|
||||
'echo': 'Echo/reverb effect',
|
||||
'phone': 'Sounds like a phone call',
|
||||
'megaphone': 'Megaphone/radio effect'
|
||||
}
|
||||
return descriptions.get(effect_name, 'Unknown effect')
|
||||
|
||||
@@ -667,14 +686,14 @@ async def play(ctx):
|
||||
# Get volume and effect settings
|
||||
volume_percent = await get_volume(server_id)
|
||||
volume = volume_percent / 100.0 # Convert to 0.0-2.0 range
|
||||
|
||||
|
||||
current_effect = await get_effect(server_id)
|
||||
ffmpeg_opts = get_effect_options(current_effect)
|
||||
|
||||
|
||||
# Create audio source with effect and volume control
|
||||
audio_source = discord.FFmpegPCMAudio(url, **ffmpeg_opts)
|
||||
audio_source = discord.PCMVolumeTransformer(audio_source, volume=volume)
|
||||
|
||||
|
||||
# Play with callback to continue queue
|
||||
def after_playing(error):
|
||||
if error:
|
||||
@@ -690,7 +709,7 @@ async def play(ctx):
|
||||
print(f"Error playing next song: {e}")
|
||||
|
||||
voice_client.play(audio_source, after=after_playing)
|
||||
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error starting playback: {e}")
|
||||
# Try to continue with next song
|
||||
|
||||
Reference in New Issue
Block a user