mirror of
https://github.com/Cog-Creators/Red-DiscordBot.git
synced 2025-11-21 10:17:59 -05:00
[Audio] One PR to rule them all, One PR to find them, One PR to bring them all, and in the darkness bind them (all-in-one pr) (#2904)
* More changes Signed-off-by: Guy <guyreis96@gmail.com> * Fixed auto play defaulting to playlist Signed-off-by: Guy <guyreis96@gmail.com> * Localtrack fix Signed-off-by: Guy <guyreis96@gmail.com> * Updated deps .. since for some reason aiosqlite is not being auto installed for everyone Signed-off-by: Guy <guyreis96@gmail.com> * Yupo Signed-off-by: Guy <guyreis96@gmail.com> * Fixed a crash in [p]now Signed-off-by: Guy <guyreis96@gmail.com> * Fixed crash on playlist save Signed-off-by: Guy <guyreis96@gmail.com> * Debugging Commit Signed-off-by: Guy <guyreis96@gmail.com> * Yet more prints Signed-off-by: Guy <guyreis96@gmail.com> * Even more spammy debug Signed-off-by: Guy <guyreis96@gmail.com> * Debugging commit + NEw Dispatches Signed-off-by: Guy <guyreis96@gmail.com> * Debugging commit + NEw Dispatches Signed-off-by: Guy <guyreis96@gmail.com> * Fixed localpath checks Signed-off-by: Guy <guyreis96@gmail.com> * more fixes for Localpaths Signed-off-by: Guy <guyreis96@gmail.com> * Spelling mistake on method Signed-off-by: Guy <guyreis96@gmail.com> * Fixed Crash on event handler Signed-off-by: Guy <guyreis96@gmail.com> * Fixed Crash on local search Signed-off-by: Guy <guyreis96@gmail.com> * Reduced fuzzy match percentage threshold for local tracks to account for nested folders Signed-off-by: Guy <guyreis96@gmail.com> * Fixed a crash on queue end Signed-off-by: Guy <guyreis96@gmail.com> * Sigh ... Removed a duplicate dispatch Signed-off-by: Guy <guyreis96@gmail.com> * Sigh i removed this before ... Signed-off-by: Guy <guyreis96@gmail.com> * Reorder dispatch signatures so all 3 new dispatch have matching signature Signed-off-by: Guy <guyreis96@gmail.com> * Formatting Signed-off-by: Guy <guyreis96@gmail.com> * Edited Error Event to support localtracks Signed-off-by: Guy <guyreis96@gmail.com> * Fix a Crash on track crash :awesome: Signed-off-by: Guy <guyreis96@gmail.com> * Yikes soo much spam Signed-off-by: Guy <guyreis96@gmail.com> * Remove spam and improve existance check Signed-off-by: Guy <guyreis96@gmail.com> * Repeat and Auto-play are mutually exclusive now Signed-off-by: Guy <guyreis96@gmail.com> * DEBUGS for Preda Signed-off-by: Guy <guyreis96@gmail.com> * Vimeo tracks can be from both these domains "vimeo.com", "beam.pro" Signed-off-by: Guy <guyreis96@gmail.com> * I mean Mixer can be from those 2 domains .... Signed-off-by: Guy <guyreis96@gmail.com> * Fixed `search sc` command Signed-off-by: Guy <guyreis96@gmail.com> * Run everything though lints. rename localtracks module to dataclasses Clear lock on errors Signed-off-by: Draper <guyreis96@gmail.com> * Try to speed up long playlist loading Signed-off-by: Draper <guyreis96@gmail.com> * Im an idiot Signed-off-by: Draper <guyreis96@gmail.com> * Im an idiot Signed-off-by: Draper <guyreis96@gmail.com> * Added logging for writes Signed-off-by: Draper <guyreis96@gmail.com> * Fix crash on cog reload Signed-off-by: Draper <guyreis96@gmail.com> * Fix for runtimewarning ? Signed-off-by: Draper <guyreis96@gmail.com> * Fix for Local Track cache Signed-off-by: Draper <guyreis96@gmail.com> * Remove broken tracks from queue on exception Theoretically do not auto play if track stop reason is Stopped or cleanup Signed-off-by: Draper <guyreis96@gmail.com> * Previous commit was a fluke ... ignore it Signed-off-by: Draper <guyreis96@gmail.com> * Change from cleanup to Replaced Signed-off-by: Draper <guyreis96@gmail.com> * Fixed AttributeError: 'Track' object has no attribute 'info'. [p]skip will only work for autoplay is there a track being played. Fixed Console spam if query saving failed in the background while reloading bot. Autoplay now respect [p]stop command Signed-off-by: Guy <guyreis96@gmail.com> * Black formatting Fix Issue with auto play working when there is songs in the queue Stop notifying queue ended if autoplay is on Signed-off-by: Guy <guyreis96@gmail.com> * Fixed a crash on track load timeout Signed-off-by: Guy <guyreis96@gmail.com> * [p]playlist start will now show the playlist name in embed body Improved Logic for handling broken tracks when repeat is on. Signed-off-by: Draper <guyreis96@gmail.com> * Enqueue tracks as soon as we have the youtube URL .... This basically changes how spotify urls are handled Need to test saving spotify playlist Need to test loading a spotify playlist from file Need to test enqueuing a spotify playlist Signed-off-by: Draper <guyreis96@gmail.com> * Updated a track whrn enqueuing spotify playlist Signed-off-by: Draper <guyreis96@gmail.com> * Debug Signed-off-by: Draper <guyreis96@gmail.com> * Debug Signed-off-by: Draper <guyreis96@gmail.com> * Debug Signed-off-by: Draper <guyreis96@gmail.com> * Debug Signed-off-by: Draper <guyreis96@gmail.com> * Debug Signed-off-by: Draper <guyreis96@gmail.com> * Debug Signed-off-by: Draper <guyreis96@gmail.com> * Debug Signed-off-by: Draper <guyreis96@gmail.com> * Debug Signed-off-by: Draper <guyreis96@gmail.com> * Debug Signed-off-by: Draper <guyreis96@gmail.com> * Debug Signed-off-by: Draper <guyreis96@gmail.com> * Revert spotify_enqueue changes Signed-off-by: Draper <guyreis96@gmail.com> * Revert spotify_enqueue changes Signed-off-by: Draper <guyreis96@gmail.com> * Allow to set Lavalink jar version from Environment vars Signed-off-by: Draper <guyreis96@gmail.com> * Allow to set Lavalink jar version from Environment vars Signed-off-by: Draper <guyreis96@gmail.com> * Fix for a crash on Equalizer, Merge Spotify_enqueue changes and revert manager changes Signed-off-by: Draper <guyreis96@gmail.com> * Break playlist enqueue after 10 consecutive failures Signed-off-by: Draper <guyreis96@gmail.com> * Auto DC, is not compatible with Auto Play Signed-off-by: Draper <guyreis96@gmail.com> * Make notifier aware of guild its being called for Signed-off-by: Draper <guyreis96@gmail.com> * Type checking Signed-off-by: Draper <guyreis96@gmail.com> * Remove lock from 2 exits that i didn't before Signed-off-by: Draper <guyreis96@gmail.com> * Fixed TypeError: spotify_enqueue() got an unexpected keyword argument 'notify' Signed-off-by: Guy <guyreis96@gmail.com> * Reorder toggles to alphabetical order Signed-off-by: Guy <guyreis96@gmail.com> * Update Query to handle spotify URIs Signed-off-by: Guy <guyreis96@gmail.com> * update database Signed-off-by: Guy <guyreis96@gmail.com> * Dont say tracks enqued on invalid link Make autop lay a mod only setting Signed-off-by: Draper <guyreis96@gmail.com> * Dont say tracks enqued on invalid spotify link Signed-off-by: Draper <guyreis96@gmail.com> * Set default age to 365 days Signed-off-by: Draper <guyreis96@gmail.com> * Allow Audio mods to set auto play playlists. Save playlists songs to cache when migrating Signed-off-by: Guy <guyreis96@gmail.com> * Black formatting Signed-off-by: Guy <guyreis96@gmail.com> * [p]eq cooldown is not triggered is player check fails (i.e if nothing is currently playing) Adding and removing reaction is no longer a blocking action Signed-off-by: Guy <guyreis96@gmail.com> * changelog for non blocking reaction handles Signed-off-by: Guy <guyreis96@gmail.com> * Show auto dc and auto play settings by default Signed-off-by: Guy <guyreis96@gmail.com> * lint is being a bitch Signed-off-by: Guy <guyreis96@gmail.com> * lint changes Signed-off-by: Draper <guyreis96@gmail.com> * stop caching local tracks Signed-off-by: Draper <guyreis96@gmail.com> * List of Lavalink.Tracks natively added to Playlist Objects Signed-off-by: Draper <guyreis96@gmail.com> * Fix UX changes and should fix autoplay Signed-off-by: Draper <guyreis96@gmail.com> * Fixed Skip x number of tracks Signed-off-by: Draper <guyreis96@gmail.com> * Lint changes Signed-off-by: Draper <guyreis96@gmail.com> * Remvoe dead code Signed-off-by: Draper <guyreis96@gmail.com> * Update playlist embed formatting to reflect Preda's suggestions Signed-off-by: Draper <guyreis96@gmail.com> * Update change logs Signed-off-by: Draper <guyreis96@gmail.com> * Add `async with ctx.typing():` to queue and to local folder Signed-off-by: Draper <guyreis96@gmail.com> * Stop queuing now when queue is empty with [p]queue Signed-off-by: Draper <guyreis96@gmail.com> * fix ctx.typing() Signed-off-by: Draper <guyreis96@gmail.com> * fix ctx.typing() Signed-off-by: Draper <guyreis96@gmail.com> * Part 1 Signed-off-by: Draper <guyreis96@gmail.com> * Dont check local track author and name if title is Unknown Signed-off-by: Guy <guyreis96@gmail.com> * Makes auto play more random Signed-off-by: Guy <guyreis96@gmail.com> * Fixes local play Fixed missing format Signed-off-by: Guy <guyreis96@gmail.com> * Query.process_input accept lavalink.Track objects Signed-off-by: Draper <guyreis96@gmail.com> * docstrings Signed-off-by: Draper <guyreis96@gmail.com> * Add TODO for timestamp support Signed-off-by: Draper <guyreis96@gmail.com> * Improve autoplay from cache logic (possibly slightly slower but more efficient overall) Signed-off-by: Draper <guyreis96@gmail.com> * Add My Lavalink PR as a dependency Remember to remove this .... The PR will bump it to 0.3.2 Signed-off-by: Draper <guyreis96@gmail.com> * Add My Lavalink PR as a dependency Remember to remove this .... The PR will bump it to 0.3.2 Signed-off-by: Draper <guyreis96@gmail.com> * Add My Lavalink PR as a dependency Remember to remove this .... The PR will bump it to 0.3.2 Signed-off-by: Draper <guyreis96@gmail.com> * Compile all regex at runtime Signed-off-by: Draper <guyreis96@gmail.com> * Fixes local play Fixed missing format Signed-off-by: Guy <guyreis96@gmail.com> * Revert Dep error Signed-off-by: Guy <guyreis96@gmail.com> * black Signed-off-by: Guy <guyreis96@gmail.com> * Fixed attribute error Signed-off-by: Guy <guyreis96@gmail.com> * add `self.bot.dispatch("audio_disconnect", ctx.guild)` dispatch when the player is disconnected Signed-off-by: Guy <guyreis96@gmail.com> * Removed shuffle lock on skip Signed-off-by: Guy <guyreis96@gmail.com> * Better logic for auto seek (timestamps) Signed-off-by: Guy <guyreis96@gmail.com> * Better logic for auto seek (timestamps) Signed-off-by: Guy <guyreis96@gmail.com> * Fixes timestamps on spotify tracks Signed-off-by: Guy <guyreis96@gmail.com> * Add ctx typing to playlist enqueue Signed-off-by: Guy <guyreis96@gmail.com> * Fix Deps Signed-off-by: Guy <guyreis96@gmail.com> * Black formatting + Using new lavalink methods for shuffling Signed-off-by: Guy <guyreis96@gmail.com> * remove ctx.typing from playlist start Signed-off-by: Guy <guyreis96@gmail.com> * Fixes typerror when enqueuing spotify playlists Signed-off-by: Guy <guyreis96@gmail.com> * Fix keyerror Signed-off-by: Guy <guyreis96@gmail.com> * black formatting, + embed for [p]audioset cache as I forgot it before Signed-off-by: Guy <guyreis96@gmail.com> * Fix Error on playlist upload Signed-off-by: Guy <guyreis96@gmail.com> * Fix Text help for bump Signed-off-by: Guy <guyreis96@gmail.com> * Allow track bumping while shuffle is on Signed-off-by: Guy <guyreis96@gmail.com> * Edit bump embed to be consistent with other embed Hyperlink tracks and removed dynamic title Signed-off-by: Guy <guyreis96@gmail.com> * Black Signed-off-by: Guy <guyreis96@gmail.com> * Errors not printing fix? Signed-off-by: Guy <guyreis96@gmail.com> * Errors not printing fix? Signed-off-by: Guy <guyreis96@gmail.com> * Track enqueued footer now shows correct track position when shuffle is on Signed-off-by: Guy <guyreis96@gmail.com> * Update changelogs Signed-off-by: Guy <guyreis96@gmail.com> * Fix is_owner check in audioset settings Signed-off-by: Guy <guyreis96@gmail.com> * Changelogs Signed-off-by: Guy <guyreis96@gmail.com> * Dont store searches with no results in cache, fix malformated playlist to cache upon settings migration Signed-off-by: Guy <guyreis96@gmail.com> * _clear_lock_on_error > Needs to be reviewed to see if it has been done correctly Signed-off-by: Guy <guyreis96@gmail.com> * _clear_lock_on_error > Needs to be reviewed to see if it has been done correctly Signed-off-by: Guy <guyreis96@gmail.com> * Fix Query search so that it works with absolute paths for localtracks Signed-off-by: Guy <guyreis96@gmail.com> * Extra error if lavalink is set to external and the query is a localtrack and nothing is found Signed-off-by: Guy <guyreis96@gmail.com> * Black Signed-off-by: Guy <guyreis96@gmail.com> * More detailed error message Signed-off-by: Guy <guyreis96@gmail.com> * [p]seek and [p]skip can be used by user if they are the song requester while DJ mode is enabled, if votes are disabled. , [p]queue shuffle can be used to shuffle the queue manually. and [p]queue clean self can be used to remove all songs you requested from the queue. Signed-off-by: Guy <guyreis96@gmail.com> * black Signed-off-by: Guy <guyreis96@gmail.com> * All the fixes + a `should_auto_play` dispatch for the tech savy peeps Signed-off-by: Guy <guyreis96@gmail.com> * Spellchecker + Pythonic changes Signed-off-by: Guy <guyreis96@gmail.com> * NO spam for logs Signed-off-by: Guy <guyreis96@gmail.com> * Pass Current voice channel to `red_audio_should_auto_play` dispatch Signed-off-by: Guy <guyreis96@gmail.com> * Black Signed-off-by: Guy <guyreis96@gmail.com> * playlist upload also updates cache in the background Signed-off-by: Guy <guyreis96@gmail.com> * playlist upload also updates cache in the background Signed-off-by: Guy <guyreis96@gmail.com> * Add scope to playlist picker Signed-off-by: Guy <guyreis96@gmail.com> * Delete Playlist picker message once something is selected Signed-off-by: Guy <guyreis96@gmail.com> * OCD Fix Signed-off-by: Guy <guyreis96@gmail.com> * Facepalm Signed-off-by: Guy <guyreis96@gmail.com> * Fix a Potential crash Signed-off-by: Guy <guyreis96@gmail.com> * Update my stupidity Signed-off-by: Guy <guyreis96@gmail.com> * Auto Pause + Skip tracks already in playlist upon playlist append + a command to remove duplicated tracks from playlist Signed-off-by: Guy <guyreis96@gmail.com> * Fix DJ mode when Role is deleted - Credits go to Neuro Assassin#4779 Fix an issue where auto play MAY not trigger Signed-off-by: Guy <guyreis96@gmail.com> * Change log to Neuro Assassin#4779 fix Signed-off-by: Guy <guyreis96@gmail.com> * Black Signed-off-by: Guy <guyreis96@gmail.com> * Dont auto pause manual pauses Signed-off-by: Guy <guyreis96@gmail.com> * Adds `[p]autoplay` that can be run by mods or higher Signed-off-by: Guy <guyreis96@gmail.com> * 🤦 Signed-off-by: Guy <guyreis96@gmail.com> * 2x 🤦 Signed-off-by: Guy <guyreis96@gmail.com> * Fixed wrong import Signed-off-by: Guy <guyreis96@gmail.com> * Added Autoplay notify Signed-off-by: Guy <guyreis96@gmail.com> * Added Autoplay notify Signed-off-by: Guy <guyreis96@gmail.com> * Black Signed-off-by: Guy <guyreis96@gmail.com> * Store Track object as prev song instead of URI Signed-off-by: Guy <guyreis96@gmail.com> * Black why do u hate me Signed-off-by: Guy <guyreis96@gmail.com> * Fix command name Signed-off-by: Guy <guyreis96@gmail.com> * Fix Autoplay notify Signed-off-by: Guy <guyreis96@gmail.com> * Fix missing await and TypeError, Thanks Flame Signed-off-by: Guy <guyreis96@gmail.com> * Add a list of tracks to show as a menu Signed-off-by: Guy <guyreis96@gmail.com> * adds the `[p]genre` command which uses the Spotify and Youtube API Signed-off-by: Guy <guyreis96@gmail.com> * Enqueue Playlists from genre command Signed-off-by: Guy <guyreis96@gmail.com> * Pretify `[p]genre` Signed-off-by: Guy <guyreis96@gmail.com> * Fix a Typo and correct jukebox charge order Signed-off-by: Guy <guyreis96@gmail.com> * Add genre command to error handling Signed-off-by: Guy <guyreis96@gmail.com> * Type checking Signed-off-by: Guy <guyreis96@gmail.com> * Update naming scheme for `[p]genre` Signed-off-by: Guy <guyreis96@gmail.com> * Black why do you hate me Signed-off-by: Guy <guyreis96@gmail.com> * Fixed `[p]local start` Playlist picker auto selects if theres just 1 playlist found `[p]queue cleanself` added Signed-off-by: Guy <guyreis96@gmail.com> * *sigh* back compatibility with old localtrack paths Signed-off-by: Guy <guyreis96@gmail.com> * *sigh* back compatibility with old localtrack paths, even more Signed-off-by: Guy <guyreis96@gmail.com> * *sigh* back compatibility with old localtrack paths Even more Signed-off-by: Guy <guyreis96@gmail.com> * Fixes localtracks in playlist info command Signed-off-by: Guy <guyreis96@gmail.com> * Debug Local Strings Signed-off-by: Guy <guyreis96@gmail.com> * Debug Local Strings Signed-off-by: Guy <guyreis96@gmail.com> * Fixes `[p]playlist info` for local tracks + fixed error in `[p]remove` Signed-off-by: Guy <guyreis96@gmail.com> * Black Signed-off-by: Guy <guyreis96@gmail.com> * Fixes formatting in `[p]playlist info` Signed-off-by: Guy <guyreis96@gmail.com> * Fix an issue with User Scope playlists were not being deleted Signed-off-by: Guy <guyreis96@gmail.com> * Typechecking Signed-off-by: Guy <guyreis96@gmail.com> * Black Signed-off-by: Guy <guyreis96@gmail.com> * Fix the logic of `delegate_autoplay` Signed-off-by: Guy <guyreis96@gmail.com> * Fix a Crash on Load due to type hinting Signed-off-by: Guy <guyreis96@gmail.com> * Fix a Crash on Load due to type hintingBlack + fix order of `red_audio_should_auto_play` Signed-off-by: Guy <guyreis96@gmail.com> * Add `red_audio_initialized` dispatch so that ownership of auto play can be maintained after a reload Signed-off-by: Guy <guyreis96@gmail.com> * Check if the current owner is loaded before raising an error Signed-off-by: Guy <guyreis96@gmail.com> * Fixes the Existence Check in `delegate_autoplay` Signed-off-by: Guy <guyreis96@gmail.com> * Turns `own_autoplay` in a property of Audio and improves `delegate_autoplay` Thanks Sinbad! Signed-off-by: Guy <guyreis96@gmail.com> * Fix for Localtracks playlists Signed-off-by: Guy <guyreis96@gmail.com> * When disconnecting send `Disconnecting...` Fix Stop after a skip Fix UX discrepancy on Playlist IDs Fixed Exception when theres a track error Signed-off-by: Guy <guyreis96@gmail.com> * add `on_red_audio_unload` dispatch Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com> * Fix a crash on track start where `player.current` can be none? Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com> * Missing new line Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com> * Allow `--author` for playlist to be used to filter playlist for an specific author. Plus a few bugfixes for UX Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com> * Rename `remdupe` to `dedupe` Make global scope always be referenced as Global add missing backwards quotes around the Playlist ID for 1 string Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com> * Towncrier entries for dep changes Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com> * Remove track index when shuffle is on Fix Progress bar for livestreams Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com> * Trigger autoplay on `QUEUE_END` event instead of `TRACK_END` Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com> * Can't reproduce Ians bug but here a safeguard agaisnt it just in case Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com> * Fixes 2 Messages that had the wrong formatting Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com> * standerdize playlist naming scheme Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com> * Fix `[p]autoplay` message when Notify is enabled Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com> * y u h8 me black Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com> * Fix an issue with `[p]audioset localpath` where the localtracks folder was incorrect Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com> * Pythonic formatting Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com> * Ugh Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com> * Fix a typo Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com> * Silently try to delete messages + fixes error Ian found with `[p]genre` Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com> * sigh black Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com> * Add humanize_number usage correctly Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com> * Bump RLL to 0.4.0 Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com> * Update changelog entries Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com> * Change `bot.db` to new API's added by #2967 Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com> * Additional reformatting Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com> * Remove PyCharm noise + Fixes a few Pycharm warnings Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com> * Rework `index` parsing for youtube urls Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com> * Addess Aika's review Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com> * Fix a potential crash, saves guild ID to playlists to avoid an scheme change in the future Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com> * Add handling for Python installs without sqlite3. Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com> * Address Flame's review Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com> * Fix ma stupidity Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com> * Address Aika's latest review. 1. Update docstring for `[p]playlist rename`. 2. Fix punctuation for playlist matching. 3. `[p]playlist update` now respect playlist management perms 4. Playlist management errors now shows playlist name, id and scope where possible 5. Remove duplicated code and dead code. Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com> * Pluralize string Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com>
This commit is contained in:
425
redbot/cogs/audio/utils.py
Normal file
425
redbot/cogs/audio/utils.py
Normal file
@@ -0,0 +1,425 @@
|
||||
import asyncio
|
||||
import contextlib
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
from typing import NoReturn
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import discord
|
||||
import lavalink
|
||||
|
||||
from redbot.core import Config, commands
|
||||
from redbot.core.bot import Red
|
||||
from . import dataclasses
|
||||
|
||||
from .converters import _pass_config_to_converters
|
||||
|
||||
from .playlists import _pass_config_to_playlist
|
||||
|
||||
__all__ = [
|
||||
"pass_config_to_dependencies",
|
||||
"track_limit",
|
||||
"queue_duration",
|
||||
"draw_time",
|
||||
"dynamic_time",
|
||||
"match_url",
|
||||
"clear_react",
|
||||
"match_yt_playlist",
|
||||
"remove_react",
|
||||
"get_description",
|
||||
"track_creator",
|
||||
"time_convert",
|
||||
"url_check",
|
||||
"userlimit",
|
||||
"is_allowed",
|
||||
"CacheLevel",
|
||||
"Notifier",
|
||||
]
|
||||
_re_time_converter = re.compile(r"(?:(\d+):)?([0-5]?[0-9]):([0-5][0-9])")
|
||||
re_yt_list_playlist = re.compile(
|
||||
r"^(https?://)?(www\.)?(youtube\.com|youtu\.?be)(/playlist\?).*(list=)(.*)(&|$)"
|
||||
)
|
||||
|
||||
_config = None
|
||||
_bot = None
|
||||
|
||||
|
||||
def pass_config_to_dependencies(config: Config, bot: Red, localtracks_folder: str):
|
||||
global _bot, _config
|
||||
_bot = bot
|
||||
_config = config
|
||||
_pass_config_to_playlist(config, bot)
|
||||
_pass_config_to_converters(config, bot)
|
||||
dataclasses._pass_config_to_dataclasses(config, bot, localtracks_folder)
|
||||
|
||||
|
||||
def track_limit(track, maxlength):
|
||||
try:
|
||||
length = round(track.length / 1000)
|
||||
except AttributeError:
|
||||
length = round(track / 1000)
|
||||
|
||||
if maxlength < length <= 900000000000000: # livestreams return 9223372036854775807ms
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
async def is_allowed(guild: discord.Guild, query: str):
|
||||
query = query.lower().strip()
|
||||
whitelist = set(await _config.guild(guild).url_keyword_whitelist())
|
||||
if whitelist:
|
||||
return any(i in query for i in whitelist)
|
||||
blacklist = set(await _config.guild(guild).url_keyword_blacklist())
|
||||
return not any(i in query for i in blacklist)
|
||||
|
||||
|
||||
async def queue_duration(ctx):
|
||||
player = lavalink.get_player(ctx.guild.id)
|
||||
duration = []
|
||||
for i in range(len(player.queue)):
|
||||
if not player.queue[i].is_stream:
|
||||
duration.append(player.queue[i].length)
|
||||
queue_dur = sum(duration)
|
||||
if not player.queue:
|
||||
queue_dur = 0
|
||||
try:
|
||||
if not player.current.is_stream:
|
||||
remain = player.current.length - player.position
|
||||
else:
|
||||
remain = 0
|
||||
except AttributeError:
|
||||
remain = 0
|
||||
queue_total_duration = remain + queue_dur
|
||||
return queue_total_duration
|
||||
|
||||
|
||||
async def draw_time(ctx):
|
||||
player = lavalink.get_player(ctx.guild.id)
|
||||
paused = player.paused
|
||||
pos = player.position
|
||||
dur = player.current.length
|
||||
sections = 12
|
||||
loc_time = round((pos / dur) * sections)
|
||||
bar = "\N{BOX DRAWINGS HEAVY HORIZONTAL}"
|
||||
seek = "\N{RADIO BUTTON}"
|
||||
if paused:
|
||||
msg = "\N{DOUBLE VERTICAL BAR}"
|
||||
else:
|
||||
msg = "\N{BLACK RIGHT-POINTING TRIANGLE}"
|
||||
for i in range(sections):
|
||||
if i == loc_time:
|
||||
msg += seek
|
||||
else:
|
||||
msg += bar
|
||||
return msg
|
||||
|
||||
|
||||
def dynamic_time(seconds):
|
||||
m, s = divmod(seconds, 60)
|
||||
h, m = divmod(m, 60)
|
||||
d, h = divmod(h, 24)
|
||||
|
||||
if d > 0:
|
||||
msg = "{0}d {1}h"
|
||||
elif d == 0 and h > 0:
|
||||
msg = "{1}h {2}m"
|
||||
elif d == 0 and h == 0 and m > 0:
|
||||
msg = "{2}m {3}s"
|
||||
elif d == 0 and h == 0 and m == 0 and s > 0:
|
||||
msg = "{3}s"
|
||||
else:
|
||||
msg = ""
|
||||
return msg.format(d, h, m, s)
|
||||
|
||||
|
||||
def match_url(url):
|
||||
try:
|
||||
query_url = urlparse(url)
|
||||
return all([query_url.scheme, query_url.netloc, query_url.path])
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def match_yt_playlist(url):
|
||||
if re_yt_list_playlist.match(url):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
async def remove_react(message, react_emoji, react_user):
|
||||
with contextlib.suppress(discord.HTTPException):
|
||||
await message.remove_reaction(react_emoji, react_user)
|
||||
|
||||
|
||||
async def clear_react(bot: Red, message: discord.Message, emoji: dict = None):
|
||||
try:
|
||||
await message.clear_reactions()
|
||||
except discord.Forbidden:
|
||||
if not emoji:
|
||||
return
|
||||
with contextlib.suppress(discord.HTTPException):
|
||||
for key in emoji.values():
|
||||
await asyncio.sleep(0.2)
|
||||
await message.remove_reaction(key, bot.user)
|
||||
except discord.HTTPException:
|
||||
return
|
||||
|
||||
|
||||
async def get_description(track):
|
||||
if any(x in track.uri for x in [f"{os.sep}localtracks", f"localtracks{os.sep}"]):
|
||||
local_track = dataclasses.LocalPath(track.uri)
|
||||
if track.title != "Unknown title":
|
||||
return "**{} - {}**\n{}".format(
|
||||
track.author, track.title, local_track.to_string_hidden()
|
||||
)
|
||||
else:
|
||||
return local_track.to_string_hidden()
|
||||
else:
|
||||
return "**[{}]({})**".format(track.title, track.uri)
|
||||
|
||||
|
||||
def track_creator(player, position=None, other_track=None):
|
||||
if position == "np":
|
||||
queued_track = player.current
|
||||
elif position is None:
|
||||
queued_track = other_track
|
||||
else:
|
||||
queued_track = player.queue[position]
|
||||
track_keys = queued_track._info.keys()
|
||||
track_values = queued_track._info.values()
|
||||
track_id = queued_track.track_identifier
|
||||
track_info = {}
|
||||
for k, v in zip(track_keys, track_values):
|
||||
track_info[k] = v
|
||||
keys = ["track", "info"]
|
||||
values = [track_id, track_info]
|
||||
track_obj = {}
|
||||
for key, value in zip(keys, values):
|
||||
track_obj[key] = value
|
||||
return track_obj
|
||||
|
||||
|
||||
def time_convert(length):
|
||||
match = re.compile(_re_time_converter).match(length)
|
||||
if match is not None:
|
||||
hr = int(match.group(1)) if match.group(1) else 0
|
||||
mn = int(match.group(2)) if match.group(2) else 0
|
||||
sec = int(match.group(3)) if match.group(3) else 0
|
||||
pos = sec + (mn * 60) + (hr * 3600)
|
||||
return pos
|
||||
else:
|
||||
try:
|
||||
return int(length)
|
||||
except ValueError:
|
||||
return 0
|
||||
|
||||
|
||||
def url_check(url):
|
||||
valid_tld = [
|
||||
"youtube.com",
|
||||
"youtu.be",
|
||||
"soundcloud.com",
|
||||
"bandcamp.com",
|
||||
"vimeo.com",
|
||||
"beam.pro",
|
||||
"mixer.com",
|
||||
"twitch.tv",
|
||||
"spotify.com",
|
||||
"localtracks",
|
||||
]
|
||||
query_url = urlparse(url)
|
||||
url_domain = ".".join(query_url.netloc.split(".")[-2:])
|
||||
if not query_url.netloc:
|
||||
url_domain = ".".join(query_url.path.split("/")[0].split(".")[-2:])
|
||||
return True if url_domain in valid_tld else False
|
||||
|
||||
|
||||
def userlimit(channel):
|
||||
if channel.user_limit == 0 or channel.user_limit > len(channel.members) + 1:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class CacheLevel:
|
||||
__slots__ = ("value",)
|
||||
|
||||
def __init__(self, level=0):
|
||||
if not isinstance(level, int):
|
||||
raise TypeError(
|
||||
f"Expected int parameter, received {level.__class__.__name__} instead."
|
||||
)
|
||||
elif level < 0:
|
||||
level = 0
|
||||
elif level > 0b11111:
|
||||
level = 0b11111
|
||||
|
||||
self.value = level
|
||||
|
||||
def __eq__(self, other):
|
||||
return isinstance(other, CacheLevel) and self.value == other.value
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self.__eq__(other)
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.value)
|
||||
|
||||
def __add__(self, other):
|
||||
return CacheLevel(self.value + other.value)
|
||||
|
||||
def __radd__(self, other):
|
||||
return CacheLevel(other.value + self.value)
|
||||
|
||||
def __sub__(self, other):
|
||||
return CacheLevel(self.value - other.value)
|
||||
|
||||
def __rsub__(self, other):
|
||||
return CacheLevel(other.value - self.value)
|
||||
|
||||
def __str__(self):
|
||||
return "{0:b}".format(self.value)
|
||||
|
||||
def __format__(self, format_spec):
|
||||
return "{r:{f}}".format(r=self.value, f=format_spec)
|
||||
|
||||
def __repr__(self):
|
||||
return f"<CacheLevel value={self.value}>"
|
||||
|
||||
def is_subset(self, other):
|
||||
"""Returns ``True`` if self has the same or fewer caching levels as other."""
|
||||
return (self.value & other.value) == self.value
|
||||
|
||||
def is_superset(self, other):
|
||||
"""Returns ``True`` if self has the same or more caching levels as other."""
|
||||
return (self.value | other.value) == self.value
|
||||
|
||||
def is_strict_subset(self, other):
|
||||
"""Returns ``True`` if the caching level on other are a strict subset of those on self."""
|
||||
return self.is_subset(other) and self != other
|
||||
|
||||
def is_strict_superset(self, other):
|
||||
"""Returns ``True`` if the caching level on
|
||||
other are a strict superset of those on self."""
|
||||
return self.is_superset(other) and self != other
|
||||
|
||||
__le__ = is_subset
|
||||
__ge__ = is_superset
|
||||
__lt__ = is_strict_subset
|
||||
__gt__ = is_strict_superset
|
||||
|
||||
@classmethod
|
||||
def all(cls):
|
||||
"""A factory method that creates a :class:`CacheLevel` with max caching level."""
|
||||
return cls(0b11111)
|
||||
|
||||
@classmethod
|
||||
def none(cls):
|
||||
"""A factory method that creates a :class:`CacheLevel` with no caching."""
|
||||
return cls(0)
|
||||
|
||||
@classmethod
|
||||
def set_spotify(cls):
|
||||
"""A factory method that creates a :class:`CacheLevel` with Spotify caching level."""
|
||||
return cls(0b00011)
|
||||
|
||||
@classmethod
|
||||
def set_youtube(cls):
|
||||
"""A factory method that creates a :class:`CacheLevel` with YouTube caching level."""
|
||||
return cls(0b00100)
|
||||
|
||||
@classmethod
|
||||
def set_lavalink(cls):
|
||||
"""A factory method that creates a :class:`CacheLevel` with lavalink caching level."""
|
||||
return cls(0b11000)
|
||||
|
||||
def _bit(self, index):
|
||||
return bool((self.value >> index) & 1)
|
||||
|
||||
def _set(self, index, value):
|
||||
if value is True:
|
||||
self.value |= 1 << index
|
||||
elif value is False:
|
||||
self.value &= ~(1 << index)
|
||||
else:
|
||||
raise TypeError("Value to set for CacheLevel must be a bool.")
|
||||
|
||||
@property
|
||||
def lavalink(self):
|
||||
""":class:`bool`: Returns ``True`` if a user can deafen other users."""
|
||||
return self._bit(4)
|
||||
|
||||
@lavalink.setter
|
||||
def lavalink(self, value):
|
||||
self._set(4, value)
|
||||
|
||||
@property
|
||||
def youtube(self):
|
||||
""":class:`bool`: Returns ``True`` if a user can move users between other voice
|
||||
channels."""
|
||||
return self._bit(2)
|
||||
|
||||
@youtube.setter
|
||||
def youtube(self, value):
|
||||
self._set(2, value)
|
||||
|
||||
@property
|
||||
def spotify(self):
|
||||
""":class:`bool`: Returns ``True`` if a user can use voice activation in voice channels."""
|
||||
return self._bit(1)
|
||||
|
||||
@spotify.setter
|
||||
def spotify(self, value):
|
||||
self._set(1, value)
|
||||
|
||||
|
||||
class Notifier:
|
||||
def __init__(self, ctx: commands.Context, message: discord.Message, updates: dict, **kwargs):
|
||||
self.context = ctx
|
||||
self.message = message
|
||||
self.updates = updates
|
||||
self.color = None
|
||||
self.last_msg_time = 0
|
||||
self.cooldown = 5
|
||||
|
||||
async def notify_user(
|
||||
self,
|
||||
current: int = None,
|
||||
total: int = None,
|
||||
key: str = None,
|
||||
seconds_key: str = None,
|
||||
seconds: str = None,
|
||||
) -> NoReturn:
|
||||
"""
|
||||
This updates an existing message.
|
||||
Based on the message found in :variable:`Notifier.updates` as per the `key` param
|
||||
"""
|
||||
if self.last_msg_time + self.cooldown > time.time() and not current == total:
|
||||
return
|
||||
if self.color is None:
|
||||
self.color = await self.context.embed_colour()
|
||||
embed2 = discord.Embed(
|
||||
colour=self.color,
|
||||
title=self.updates.get(key).format(num=current, total=total, seconds=seconds),
|
||||
)
|
||||
if seconds and seconds_key:
|
||||
embed2.set_footer(text=self.updates.get(seconds_key).format(seconds=seconds))
|
||||
try:
|
||||
await self.message.edit(embed=embed2)
|
||||
self.last_msg_time = time.time()
|
||||
except discord.errors.NotFound:
|
||||
pass
|
||||
|
||||
async def update_text(self, text: str) -> NoReturn:
|
||||
embed2 = discord.Embed(colour=self.color, title=text)
|
||||
try:
|
||||
await self.message.edit(embed=embed2)
|
||||
except discord.errors.NotFound:
|
||||
pass
|
||||
|
||||
async def update_embed(self, embed: discord.Embed) -> NoReturn:
|
||||
try:
|
||||
await self.message.edit(embed=embed)
|
||||
self.last_msg_time = time.time()
|
||||
except discord.errors.NotFound:
|
||||
pass
|
||||
Reference in New Issue
Block a user