use sql for queue

This commit is contained in:
insert 2024-12-01 17:06:09 -05:00
parent c21175622a
commit 045ec9ff66
Signed by: insert
GPG key ID: A70775C389ACF105

144
newbot.py
View file

@ -9,27 +9,21 @@ from disnake.ext import commands, tasks
from disnake import TextInputStyle from disnake import TextInputStyle
import asyncio import asyncio
from dotenv import load_dotenv from dotenv import load_dotenv
import os import os, sys
import datetime import datetime
import logging import logging
import math import math
from tempfile import TemporaryDirectory from tempfile import TemporaryDirectory
from pathlib import Path from pathlib import Path
#https://stackoverflow.com/a/11706463 import sqlite3
def randshuffle(x, start=0, stop=None, seed=10):
if stop is None:
stop = len(x)
loop = 0 if len(sys.argv) > 1:
for i in reversed(range(start + 1, stop)): if sys.argv[1] == "--clear-queue":
random.seed(seed+loop) os.remove("queue.db")
j = random.randint(start, i) print("Queue cleared!")
x[i], x[j] = x[j], x[i]
loop = loop + 1
load_dotenv() load_dotenv()
queue = [] queue = []
user_queue = []
skip_list = [] skip_list = []
shuffle = True shuffle = True
retries = 0 retries = 0
@ -40,6 +34,29 @@ vid_dir = Path(temp_dir.name)
download_dir = TemporaryDirectory() download_dir = TemporaryDirectory()
full_dl_dir = Path(download_dir.name) full_dl_dir = Path(download_dir.name)
vid_details = {"title": "", "channel": ""} vid_details = {"title": "", "channel": ""}
con = sqlite3.connect("queue.db", check_same_thread=False)
cur = con.cursor()
def countuser(usrid):
res = cur.execute(f"SELECT ROWID FROM queue WHERE " + ("hasplayed = false AND " if not (os.getenv("PERMANENT_MAX_QUEUE","FALSE") == "TRUE") else "") + f"user = {usrid}")
rowid = res.fetchall()
print(len(rowid))
return len(rowid)
def sqllen():
res = cur.execute(f"SELECT ROWID FROM queue WHERE hasplayed = false")
rowid = res.fetchall()
return len(rowid)
def propagate_queue(times):
res = cur.execute(f"SELECT ROWID,link FROM queue WHERE hasplayed = false " + ("ORDER BY RANDOM() " if shuffle else "") + f"LIMIT {times}")
for rowid,link in res.fetchall():
if len(queue) > 2:
print(f"The queue is larger than two videos this WILL cause issues: {queue}")
queue.append(link)
cur.execute(f"UPDATE queue SET hasplayed = true WHERE ROWID='{rowid}'")
print("Updated a line!!")
con.commit()
def video_check(info, *, incomplete): def video_check(info, *, incomplete):
duration = info.get('duration') duration = info.get('duration')
@ -49,8 +66,7 @@ def video_check(info, *, incomplete):
print(info.get("title")) print(info.get("title"))
if duration and duration >= ((float(os.getenv("MAX_MIN")) * 60) + 480): if duration and duration >= ((float(os.getenv("MAX_MIN")) * 60) + 480):
queue.pop(0) queue.pop(0)
if not (os.getenv("PERMANENT_MAX_QUEUE","FALSE") == "TRUE"): propagate_queue(1)
user_queue.pop(0)
skip_list.clear() skip_list.clear()
if queue: if queue:
ydl.download(queue[0]) ydl.download(queue[0])
@ -60,7 +76,7 @@ ydl_opts = {
'match_filter': video_check, 'match_filter': video_check,
'hls_prefer_native': True, 'hls_prefer_native': True,
'extract_flat': 'discard_in_playlist', 'extract_flat': 'discard_in_playlist',
'format': 'bestvideo[height<=1080][ext=mp4]+bestaudio[abr<=256][ext=m4a]/best[ext=mp4]/best', 'format': 'bestvideo[height<=1080][vcodec!*=av01][ext=mp4]+bestaudio[abr<=256][ext=m4a]/best[ext=mp4]/best',
'fragment_retries': 10, 'fragment_retries': 10,
'noplaylist': True, 'noplaylist': True,
'restrictfilenames': True, 'restrictfilenames': True,
@ -107,13 +123,18 @@ print(f"OBS Version: {ver.obs_version}")
@bot.event @bot.event
async def on_ready(): async def on_ready():
res = cur.execute("SELECT name FROM sqlite_master WHERE name='queue'")
if res.fetchone() is None:
cur.execute("CREATE TABLE queue(link, user, hasplayed)")
con.commit()
obs.set_current_program_scene("waiting") obs.set_current_program_scene("waiting")
obse.callback.register(on_media_input_playback_ended) obse.callback.register(on_media_input_playback_ended)
videotimer.start() videotimer.start()
ensurewaiting.start() ensurewaiting.start()
titlehandler.start() titlehandler.start()
if (not os.path.isfile(f"{vid_dir}/{vidcounter}.mp4")) and len(queue) > 1: if (not os.path.isfile(f"{vid_dir}/{vidcounter}.mp4")) and sqllen() > 1:
print("predefined queue!") print("predefined queue!")
propagate_queue(2)
loop = asyncio.get_running_loop() loop = asyncio.get_running_loop()
await loop.run_in_executor(None, cold_run) await loop.run_in_executor(None, cold_run)
@ -131,8 +152,7 @@ def download_video(index):
global failures global failures
if retries % 2 == 1: if retries % 2 == 1:
queue.pop(index) queue.pop(index)
if not (os.getenv("PERMANENT_MAX_QUEUE","FALSE") == "TRUE"): propagate_queue(1)
user_queue.pop(index)
failures = failures + 1 failures = failures + 1
retries = retries + 1 retries = retries + 1
download_video(index) download_video(index)
@ -176,11 +196,10 @@ def on_media_input_playback_ended(data):
global vidcounter global vidcounter
try: try:
queue.pop(0) queue.pop(0)
except IndexError: propagate_queue(1)
print("Video ended but somehow it wasn't in the queue? Resetting") except IndexError as e:
print(f"Video ended but somehow it wasn't in the queue? Resetting {e}")
obs.set_current_program_scene("waiting") obs.set_current_program_scene("waiting")
if not (os.getenv("PERMANENT_MAX_QUEUE","FALSE") == "TRUE"):
user_queue.pop(0)
skip_list.clear() skip_list.clear()
if not queue: if not queue:
obs.set_current_program_scene("waiting") obs.set_current_program_scene("waiting")
@ -189,14 +208,6 @@ def on_media_input_playback_ended(data):
return return
wait_for_next_video() wait_for_next_video()
print(queue) print(queue)
print(user_queue)
if shuffle:
seed = time.time()
randshuffle(queue,1,None,seed)
if not (os.getenv("PERMANENT_MAX_QUEUE","FALSE") == "TRUE"):
randshuffle(user_queue,1,None,seed)
print(queue)
print(user_queue)
#obs.set_current_program_scene("youtube2") #obs.set_current_program_scene("youtube2")
os.remove(f"{vid_dir}/{vidcounter}.mp4") os.remove(f"{vid_dir}/{vidcounter}.mp4")
vidcounter = vidcounter + 1 vidcounter = vidcounter + 1
@ -233,10 +244,15 @@ async def stats(inter: disnake.AppCmdInter):
message = message + f"Failures: {failures}\n" message = message + f"Failures: {failures}\n"
nowplaying = obs.get_input_settings("nowplaying") nowplaying = obs.get_input_settings("nowplaying")
message = message + f"Title: {nowplaying.input_settings['text']}\n" message = message + f"Title: {nowplaying.input_settings['text']}\n"
message = message + f"Link: <{queue[0]}>\n"
playing = obs.get_media_input_status("player") playing = obs.get_media_input_status("player")
message = message + f"Video Duration: {str(datetime.timedelta(seconds=(round(playing.media_cursor/1000))))}/{str(datetime.timedelta(seconds=(round(playing.media_duration/1000))))}\n" message = message + f"Video Duration: {str(datetime.timedelta(seconds=(round(playing.media_cursor/1000))))}/{str(datetime.timedelta(seconds=(round(playing.media_duration/1000))))}\n"
if inter.permissions.moderate_members and not (os.getenv("PERMANENT_MAX_QUEUE","FALSE") == "TRUE"): if inter.permissions.moderate_members and not (os.getenv("PERMANENT_MAX_QUEUE","FALSE") == "TRUE"):
message = message + f"Queued by <@{user_queue[0]}>" res = cur.execute(f"SELECT user FROM queue WHERE hasplayed = true AND link = '{queue[0]}'")
res = res.fetchall() #We can't gaurentee the result so just show likely possibilites
message = message + f"Users who have queued this video: "
for [usrid] in list(set(res)):
message = message + f"<@{usrid}>, "
await inter.edit_original_response(message) await inter.edit_original_response(message)
@ -246,15 +262,20 @@ async def stats(inter: disnake.AppCmdInter):
) )
async def play(inter: disnake.AppCmdInter, link: str): async def play(inter: disnake.AppCmdInter, link: str):
await inter.response.defer(ephemeral=True) await inter.response.defer(ephemeral=True)
if user_queue.count(inter.user.id) >= (int(os.getenv("MAX_QUEUE"))): if countuser(inter.user.id) >= (int(os.getenv("MAX_QUEUE"))):
await inter.edit_original_response(f"You have reached the queue limit of {os.getenv('MAX_QUEUE')}, " + ("try again after one of your videos has played." if not (os.getenv("PERMANENT_MAX_QUEUE","FALSE") == "TRUE") else "you may not queue videos for the rest of the session.")) await inter.edit_original_response(f"You have reached the queue limit of {os.getenv('MAX_QUEUE')}, " + ("try again after one of your videos has played." if not (os.getenv("PERMANENT_MAX_QUEUE","FALSE") == "TRUE") else "you may not queue videos for the rest of the session."))
return return
if urlparse(link).netloc == 'youtube.com' or urlparse(link).netloc == 'www.youtube.com' or urlparse(link).netloc == 'youtu.be': if urlparse(link).netloc == 'youtube.com' or urlparse(link).netloc == 'www.youtube.com' or urlparse(link).netloc == 'youtu.be':
queue.append(link) cur.execute(f"""INSERT INTO queue VALUES
user_queue.append(inter.user.id) ('{link}',{inter.user.id},false)
""")
con.commit()
await inter.edit_original_response(f"added to queue!") await inter.edit_original_response(f"added to queue!")
if (not os.path.isfile(f"{vid_dir}/{vidcounter}.mp4")) and len(queue) > 1: if (not os.path.isfile(f"{vid_dir}/{vidcounter}.mp4")) and sqllen() > 1 and len(queue) == 0:
loop = asyncio.get_running_loop() loop = asyncio.get_running_loop()
queue.clear() #safety
propagate_queue(2)
print(queue)
await loop.run_in_executor(None, cold_run) await loop.run_in_executor(None, cold_run)
return return
else: else:
@ -290,16 +311,20 @@ async def getqueue(inter: disnake.AppCmdInter):
await inter.edit_original_response("There are no items in queue") await inter.edit_original_response("There are no items in queue")
return return
message = f"Now Playing: <{queue[0]}>\n" message = f"Now Playing: <{queue[0]}>\n"
message = message + f"Up Next: <{queue[1]}>\n"
message = message + f"Shuffle is currently " + ("off\n" if not shuffle else "on!\n") message = message + f"Shuffle is currently " + ("off\n" if not shuffle else "on!\n")
for i in range(11): res = cur.execute(f"SELECT link FROM queue WHERE hasplayed = false")
links = [x[0] for x in res.fetchall()]
links.insert(0,"dummy")
for i in range(10):
if i == 0: if i == 0:
continue continue
try: try:
message = message + f"{i}. <{queue[i]}>\n" message = message + f"{i}. <{links[i]}>\n"
except IndexError: except IndexError:
break break
message = message + f"1 of {math.ceil((len(queue)-1)/10) if (len(queue)-1)/10 > 1 else 1}" message = message + f"1 of {math.ceil((len(links)-1)/10) if (len(links)-1)/10 > 1 else 1}"
if math.ceil((len(queue)-1)/10) > 1: if math.ceil((len(links)-1)/10) > 1:
await inter.edit_original_response(message, components=[disnake.ui.Button(label=">>", style=disnake.ButtonStyle.primary, custom_id="Forward"),]) await inter.edit_original_response(message, components=[disnake.ui.Button(label=">>", style=disnake.ButtonStyle.primary, custom_id="Forward"),])
else: else:
await inter.edit_original_response(message) await inter.edit_original_response(message)
@ -314,16 +339,20 @@ async def button_listener(inter: disnake.MessageInteraction):
page = page[-1].split(" of ") page = page[-1].split(" of ")
if inter.component.custom_id == "Forward": if inter.component.custom_id == "Forward":
message = f"Now Playing: <{queue[0]}>\n" message = f"Now Playing: <{queue[0]}>\n"
message = message + f"Up Next: <{queue[1]}>\n"
message = message + f"Shuffle is currently " + ("off\n" if not shuffle else "on!\n") message = message + f"Shuffle is currently " + ("off\n" if not shuffle else "on!\n")
res = cur.execute(f"SELECT link FROM queue WHERE hasplayed = false")
links = [x[0] for x in res.fetchall()]
links.insert(0,"dummy")
offset = int(int(page[0]) * 10) offset = int(int(page[0]) * 10)
for i in range(11): for i in range(11):
if i == 0: if i == 0:
continue continue
try: try:
message = message + f"{int(i+offset)}. <{queue[int(i+offset)]}>\n" message = message + f"{int(i+offset)}. <{links[int(i+offset)]}>\n"
except IndexError: except IndexError:
break break
message = message + f"{int(page[0])+1} of {math.ceil((len(queue)-1)/10) if (len(queue)-1)/10 > 1 else 1}" message = message + f"{int(page[0])+1} of {math.ceil((len(links)-1)/10) if (len(links)-1)/10 > 1 else 1}"
if (int(page[0])+1) >= int(page[1]): if (int(page[0])+1) >= int(page[1]):
await inter.response.edit_message(message, components=[disnake.ui.Button(label="<<", style=disnake.ButtonStyle.primary, custom_id="Backward"),]) await inter.response.edit_message(message, components=[disnake.ui.Button(label="<<", style=disnake.ButtonStyle.primary, custom_id="Backward"),])
else: else:
@ -332,16 +361,20 @@ async def button_listener(inter: disnake.MessageInteraction):
return return
if inter.component.custom_id == "Backward": if inter.component.custom_id == "Backward":
message = f"Now Playing: <{queue[0]}>\n" message = f"Now Playing: <{queue[0]}>\n"
message = message + f"Up Next: <{queue[1]}>\n"
message = message + f"Shuffle is currently " + ("off\n" if not shuffle else "on!\n") message = message + f"Shuffle is currently " + ("off\n" if not shuffle else "on!\n")
res = cur.execute(f"SELECT link FROM queue WHERE hasplayed = false")
links = [x[0] for x in res.fetchall()]
links.insert(0,"dummy")
offset = int((int(page[0]) - 2) * 10) offset = int((int(page[0]) - 2) * 10)
for i in range(11): for i in range(11):
if i == 0: if i == 0:
continue continue
try: try:
message = message + f"{int(i+offset)}. <{queue[int(i+offset)]}>\n" message = message + f"{int(i+offset)}. <{links[int(i+offset)]}>\n"
except IndexError: except IndexError:
break break
message = message + f"{int(page[0])-1} of {math.ceil((len(queue)-1)/10) if (len(queue)-1)/10 > 1 else 1}" message = message + f"{int(page[0])-1} of {math.ceil((len(links)-1)/10) if (len(links)-1)/10 > 1 else 1}"
if (int(page[0])-1) <= 1 and int(int(page[1]) == 1): if (int(page[0])-1) <= 1 and int(int(page[1]) == 1):
await inter.response.edit_message(message) await inter.response.edit_message(message)
elif (int(page[0])-1) <= 1 and int(int(page[1]) > 1): elif (int(page[0])-1) <= 1 and int(int(page[1]) > 1):
@ -378,8 +411,7 @@ async def skip(inter: disnake.AppCmdInter):
loop = asyncio.get_running_loop() loop = asyncio.get_running_loop()
global vidcounter global vidcounter
queue.pop(0) queue.pop(0)
if not (os.getenv("PERMANENT_MAX_QUEUE","FALSE") == "TRUE"): propagate_queue(1)
user_queue.pop(0)
skip_list.clear() skip_list.clear()
if not queue: if not queue:
obs.set_current_program_scene("waiting") obs.set_current_program_scene("waiting")
@ -436,8 +468,7 @@ async def voteskip(inter: disnake.AppCmdInter):
loop = asyncio.get_running_loop() loop = asyncio.get_running_loop()
global vidcounter global vidcounter
queue.pop(0) queue.pop(0)
if not (os.getenv("PERMANENT_MAX_QUEUE","FALSE") == "TRUE"): propagate_queue(1)
user_queue.pop(0)
skip_list.clear() skip_list.clear()
if not queue: if not queue:
obs.set_current_program_scene("waiting") obs.set_current_program_scene("waiting")
@ -463,22 +494,6 @@ async def voteskip(inter: disnake.AppCmdInter):
await inter.edit_original_response(f"**{inter.user.display_name}** has voted to skip the video, {len(skip_list)}/{math.floor(len(vc)/2)}") await inter.edit_original_response(f"**{inter.user.display_name}** has voted to skip the video, {len(skip_list)}/{math.floor(len(vc)/2)}")
@bot.slash_command(
name="remove",
description="removes a video from the queue",
default_member_permissions=disnake.Permissions(8192),
)
async def remove(inter: disnake.AppCmdInter, toremove: int):
await inter.response.defer(ephemeral=True)
if toremove == 0 or toremove == 1:
await inter.edit_original_response("that is the currently playing video!", ephemeral=True)
return
else:
queue.pop(toremove)
if not (os.getenv("PERMANENT_MAX_QUEUE","FALSE") == "TRUE"):
user_queue.pop(toremove)
await inter.edit_original_response("removed!")
@tasks.loop(seconds=5.0) @tasks.loop(seconds=5.0)
async def videotimer(): async def videotimer():
@ -497,8 +512,7 @@ async def videotimer():
print("skip return") print("skip return")
return return
queue.pop(0) queue.pop(0)
if not (os.getenv("PERMANENT_MAX_QUEUE","FALSE") == "TRUE"): propagate_queue(1)
user_queue.pop(0)
skip_list.clear() skip_list.clear()
if not queue: if not queue:
obs.set_current_program_scene("waiting") obs.set_current_program_scene("waiting")