initial push
This commit is contained in:
139
util/sing_util.py
Normal file
139
util/sing_util.py
Normal file
@ -0,0 +1,139 @@
|
||||
#!/usr/bin/env python3.12
|
||||
import logging
|
||||
import regex
|
||||
import aiohttp
|
||||
import textwrap
|
||||
import traceback
|
||||
from typing import Optional
|
||||
from discord import Activity
|
||||
|
||||
class Utility:
|
||||
"""Sing Utility"""
|
||||
def __init__(self):
|
||||
self.api_url: str = "http://127.0.0.1:52111/lyric/search"
|
||||
self.api_src: str = "DISC-HAVOC"
|
||||
|
||||
def parse_song_input(self, song: Optional[str] = None,
|
||||
activity: Optional[Activity] = None) -> bool|tuple:
|
||||
"""Parse Song (Sing Command) Input
|
||||
Args:
|
||||
song (Optional[str]): Song to search
|
||||
activity (Optional[discord.Activity]): Discord activity, used to attempt lookup if no song is provided
|
||||
Returns:
|
||||
bool|tuple
|
||||
"""
|
||||
logging.debug("Activity? %s", activity)
|
||||
try:
|
||||
if (not song or len(song) < 2) and not activity:
|
||||
# pylint: disable=superfluous-parens
|
||||
return False
|
||||
# pylint: enable=superfluous-parens
|
||||
if not song and activity:
|
||||
match activity.name.lower():
|
||||
case "codey toons" | "cider" | "sonixd":
|
||||
search_artist: str = " ".join(str(activity.state)\
|
||||
.strip().split(" ")[1:])
|
||||
search_artist: str = regex.sub(r"(\s{0,})(\[(spotify|tidal|sonixd|browser|yt music)])$", "",
|
||||
search_artist.strip(), flags=regex.IGNORECASE)
|
||||
search_song: str = str(activity.details)
|
||||
song: str = f"{search_artist} : {search_song}"
|
||||
case "tidal hi-fi":
|
||||
search_artist: str = str(activity.state)
|
||||
search_song: str = str(activity.details)
|
||||
song: str = f"{search_artist} : {search_song}"
|
||||
case "spotify":
|
||||
search_artist: str = str(activity.title)
|
||||
search_song: str = str(activity.artist)
|
||||
song: str = f"{search_artist} : {search_song}"
|
||||
case "serious.fm" | "cocks.fm" | "something":
|
||||
if not activity.details:
|
||||
song: str = str(activity.state)
|
||||
else:
|
||||
search_artist: str = str(activity.state)
|
||||
search_song: str = str(activity.details)
|
||||
song: str = f"{search_artist} : {search_song}"
|
||||
case _:
|
||||
return False # Unsupported activity detected
|
||||
|
||||
search_split_by: str = ":" if not(song) or len(song.split(":")) > 1\
|
||||
else "-" # Support either : or - to separate artist/track
|
||||
search_artist: str = song.split(search_split_by)[0].strip()
|
||||
search_song: str = "".join(song.split(search_split_by)[1:]).strip()
|
||||
search_subsearch: Optional[str] = None
|
||||
if search_split_by == ":" and len(song.split(":")) > 2: # Support sub-search if : is used (per instructions)
|
||||
search_song: str = song.split(search_split_by)[1].strip() # Reduce search_song to only the 2nd split of : [the rest is meant to be lyric text]
|
||||
search_subsearch: str = "".join(song.split(search_split_by)[2:]) # Lyric text from split index 2 and beyond
|
||||
return (search_artist, search_song, search_subsearch)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
return False
|
||||
|
||||
async def lyric_search(self, artist: str, song: str,
|
||||
sub: Optional[str] = None) -> list[str]:
|
||||
"""
|
||||
Lyric Search
|
||||
Args:
|
||||
artist (str): Artist to search
|
||||
song (str): Song to search
|
||||
sub (Optional[str]): Lyrics for subsearch
|
||||
"""
|
||||
try:
|
||||
if not artist or not song:
|
||||
return ["FAIL! Artist/Song not provided"]
|
||||
|
||||
search_obj: dict = {
|
||||
'a': artist.strip(),
|
||||
's': song.strip(),
|
||||
'extra': True,
|
||||
'src': self.api_src,
|
||||
}
|
||||
|
||||
if len(song.strip()) < 1:
|
||||
search_obj.pop('a')
|
||||
search_obj.pop('s')
|
||||
search_obj['t'] = artist.strip() # Parse failed, try title without sep
|
||||
|
||||
if sub and len(sub) >= 2:
|
||||
search_obj['sub'] = sub.strip()
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with await session.post(self.api_url,
|
||||
json=search_obj,
|
||||
timeout=aiohttp.ClientTimeout(connect=5, sock_read=10)) as request:
|
||||
request.raise_for_status()
|
||||
response: dict = await request.json()
|
||||
if response.get('err'):
|
||||
return [f"ERR: {response.get('errorText')}"]
|
||||
|
||||
out_lyrics = regex.sub(r'<br>', '\u200B\n', response.get('lyrics'))
|
||||
response_obj: dict = {
|
||||
'artist': response.get('artist'),
|
||||
'song': response.get('song'),
|
||||
'lyrics': out_lyrics,
|
||||
'src': response.get('src'),
|
||||
'confidence': float(response.get('confidence')),
|
||||
'time': float(response.get('time')),
|
||||
}
|
||||
|
||||
lyrics = response_obj.get('lyrics')
|
||||
response_obj['lyrics'] = textwrap.wrap(text=lyrics.strip(),
|
||||
width=4000, drop_whitespace=False,
|
||||
replace_whitespace=False, break_long_words=True,
|
||||
break_on_hyphens=True, max_lines=8)
|
||||
response_obj['lyrics_short'] = textwrap.wrap(text=lyrics.strip(),
|
||||
width=750, drop_whitespace=False,
|
||||
replace_whitespace=False, break_long_words=True,
|
||||
break_on_hyphens=True, max_lines=1)
|
||||
|
||||
return [
|
||||
(
|
||||
response_obj.get('artist'), response_obj.get('song'), response_obj.get('src'),
|
||||
f"{int(response_obj.get('confidence'))}%",
|
||||
f"{response_obj.get('time', -666.0):.4f}s",
|
||||
),
|
||||
response_obj.get('lyrics'),
|
||||
response_obj.get('lyrics_short'),
|
||||
]
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
return [f"Retrieval failed: {str(e)}"]
|
Reference in New Issue
Block a user