| 
									
										
										
										
											2025-02-13 14:51:35 -05:00
										 |  |  | import logging | 
					
						
							|  |  |  | import regex | 
					
						
							|  |  |  | import aiohttp | 
					
						
							|  |  |  | import textwrap | 
					
						
							|  |  |  | import traceback | 
					
						
							|  |  |  | from discord import Activity | 
					
						
							| 
									
										
										
										
											2025-02-16 20:07:02 -05:00
										 |  |  | from typing import Optional, Union | 
					
						
							| 
									
										
										
										
											2025-02-13 14:51:35 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-17 14:35:56 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-13 14:51:35 -05:00
										 |  |  | class Utility: | 
					
						
							|  |  |  |     """Sing Utility""" | 
					
						
							| 
									
										
										
										
											2025-04-17 14:35:56 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-16 20:07:02 -05:00
										 |  |  |     def __init__(self) -> None: | 
					
						
							| 
									
										
										
										
											2025-03-14 10:47:46 -04:00
										 |  |  |         self.api_url: str = "http://127.0.0.1:52111/lyric/search" | 
					
						
							| 
									
										
										
										
											2025-02-13 14:51:35 -05:00
										 |  |  |         self.api_src: str = "DISC-HAVOC" | 
					
						
							| 
									
										
										
										
											2025-04-17 14:35:56 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def parse_song_input( | 
					
						
							|  |  |  |         self, song: Optional[str] = None, activity: Optional[Activity] = None | 
					
						
							|  |  |  |     ) -> Union[bool, tuple]: | 
					
						
							| 
									
										
										
										
											2025-02-16 20:07:02 -05:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         Parse Song (Sing Command) Input | 
					
						
							| 
									
										
										
										
											2025-04-17 14:35:56 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-13 14:51:35 -05:00
										 |  |  |         Args: | 
					
						
							|  |  |  |             song (Optional[str]): Song to search | 
					
						
							|  |  |  |             activity (Optional[discord.Activity]): Discord activity, used to attempt lookup if no song is provided | 
					
						
							| 
									
										
										
										
											2025-04-17 14:35:56 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-13 14:51:35 -05:00
										 |  |  |         Returns: | 
					
						
							| 
									
										
										
										
											2025-02-16 20:07:02 -05:00
										 |  |  |             Union[bool, tuple] | 
					
						
							| 
									
										
										
										
											2025-02-13 14:51:35 -05:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             if (not song or len(song) < 2) and not activity: | 
					
						
							|  |  |  |                 return False | 
					
						
							| 
									
										
										
										
											2025-03-01 07:56:47 -05:00
										 |  |  |             if not song and activity: | 
					
						
							| 
									
										
										
										
											2025-02-16 20:07:02 -05:00
										 |  |  |                 if not activity.name: | 
					
						
							| 
									
										
										
										
											2025-04-17 14:35:56 -04:00
										 |  |  |                     return False  # No valid activity found | 
					
						
							| 
									
										
										
										
											2025-02-13 14:51:35 -05:00
										 |  |  |                 match activity.name.lower(): | 
					
						
							|  |  |  |                     case "codey toons" | "cider" | "sonixd": | 
					
						
							| 
									
										
										
										
											2025-04-17 14:35:56 -04:00
										 |  |  |                         search_artist: str = " ".join( | 
					
						
							|  |  |  |                             str(activity.state).strip().split(" ")[1:] | 
					
						
							|  |  |  |                         ) | 
					
						
							|  |  |  |                         search_artist = regex.sub( | 
					
						
							|  |  |  |                             r"(\s{0,})(\[(spotify|tidal|sonixd|browser|yt music)])$", | 
					
						
							|  |  |  |                             "", | 
					
						
							|  |  |  |                             search_artist.strip(), | 
					
						
							|  |  |  |                             flags=regex.IGNORECASE, | 
					
						
							|  |  |  |                         ) | 
					
						
							| 
									
										
										
										
											2025-02-16 20:07:02 -05:00
										 |  |  |                         search_song = str(activity.details) | 
					
						
							|  |  |  |                         song = f"{search_artist} : {search_song}" | 
					
						
							| 
									
										
										
										
											2025-02-13 14:51:35 -05:00
										 |  |  |                     case "tidal hi-fi": | 
					
						
							| 
									
										
										
										
											2025-02-16 20:07:02 -05:00
										 |  |  |                         search_artist = str(activity.state) | 
					
						
							|  |  |  |                         search_song = str(activity.details) | 
					
						
							|  |  |  |                         song = f"{search_artist} : {search_song}" | 
					
						
							| 
									
										
										
										
											2025-02-13 14:51:35 -05:00
										 |  |  |                     case "spotify": | 
					
						
							| 
									
										
										
										
											2025-04-17 14:35:56 -04:00
										 |  |  |                         if not activity.title or not activity.artist:  # type: ignore | 
					
						
							| 
									
										
										
										
											2025-02-16 20:07:02 -05:00
										 |  |  |                             """
 | 
					
						
							|  |  |  |                             Attributes exist, but mypy does not recognize them.  Ignored. | 
					
						
							|  |  |  |                             """
 | 
					
						
							|  |  |  |                             return False | 
					
						
							| 
									
										
										
										
											2025-05-17 12:33:27 -04:00
										 |  |  |                         search_artist = str(activity.artist)  # type: ignore | 
					
						
							|  |  |  |                         search_song = str(activity.title)  # type: ignore | 
					
						
							| 
									
										
										
										
											2025-02-16 20:07:02 -05:00
										 |  |  |                         song = f"{search_artist} : {search_song}" | 
					
						
							| 
									
										
										
										
											2025-02-13 14:51:35 -05:00
										 |  |  |                     case "serious.fm" | "cocks.fm" | "something": | 
					
						
							|  |  |  |                         if not activity.details: | 
					
						
							| 
									
										
										
										
											2025-02-16 20:07:02 -05:00
										 |  |  |                             song = str(activity.state) | 
					
						
							| 
									
										
										
										
											2025-02-13 14:51:35 -05:00
										 |  |  |                         else: | 
					
						
							| 
									
										
										
										
											2025-04-17 14:35:56 -04:00
										 |  |  |                             search_artist = str(activity.state).rsplit("[", maxsplit=1)[ | 
					
						
							|  |  |  |                                 0 | 
					
						
							|  |  |  |                             ]  # Strip genre | 
					
						
							| 
									
										
										
										
											2025-02-16 20:07:02 -05:00
										 |  |  |                             search_song = str(activity.details) | 
					
						
							|  |  |  |                             song = f"{search_artist} : {search_song}" | 
					
						
							| 
									
										
										
										
											2025-02-13 14:51:35 -05:00
										 |  |  |                     case _: | 
					
						
							| 
									
										
										
										
											2025-04-17 14:35:56 -04:00
										 |  |  |                         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 | 
					
						
							| 
									
										
										
										
											2025-02-16 20:07:02 -05:00
										 |  |  |             if not song: | 
					
						
							|  |  |  |                 return False | 
					
						
							|  |  |  |             search_artist = song.split(search_split_by)[0].strip() | 
					
						
							|  |  |  |             search_song = "".join(song.split(search_split_by)[1:]).strip() | 
					
						
							| 
									
										
										
										
											2025-02-13 14:51:35 -05:00
										 |  |  |             search_subsearch: Optional[str] = None | 
					
						
							| 
									
										
										
										
											2025-04-17 14:35:56 -04:00
										 |  |  |             if ( | 
					
						
							|  |  |  |                 search_split_by == ":" and len(song.split(":")) > 2 | 
					
						
							|  |  |  |             ):  # Support sub-search if : is used (per instructions) | 
					
						
							| 
									
										
										
										
											2025-05-15 15:49:28 -04:00
										 |  |  |                 search_song = song.split( | 
					
						
							|  |  |  |                     search_split_by | 
					
						
							|  |  |  |                 )[ | 
					
						
							| 
									
										
										
										
											2025-04-17 14:35:56 -04:00
										 |  |  |                     1 | 
					
						
							|  |  |  |                 ].strip()  # Reduce search_song to only the 2nd split of : [the rest is meant to be lyric text] | 
					
						
							|  |  |  |                 search_subsearch = "".join( | 
					
						
							|  |  |  |                     song.split(search_split_by)[2:] | 
					
						
							|  |  |  |                 )  # Lyric text from split index 2 and beyond | 
					
						
							| 
									
										
										
										
											2025-02-13 14:51:35 -05:00
										 |  |  |             return (search_artist, search_song, search_subsearch) | 
					
						
							| 
									
										
										
										
											2025-04-26 21:59:46 -04:00
										 |  |  |         except Exception as e: | 
					
						
							|  |  |  |             logging.debug("Exception: %s", str(e)) | 
					
						
							| 
									
										
										
										
											2025-02-13 14:51:35 -05:00
										 |  |  |             traceback.print_exc() | 
					
						
							|  |  |  |             return False | 
					
						
							| 
									
										
										
										
											2025-04-17 14:35:56 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     async def lyric_search( | 
					
						
							|  |  |  |         self, artist: str, song: str, sub: Optional[str] = None | 
					
						
							|  |  |  |     ) -> Optional[list]: | 
					
						
							| 
									
										
										
										
											2025-02-13 14:51:35 -05:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         Lyric Search | 
					
						
							| 
									
										
										
										
											2025-04-17 14:35:56 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-13 14:51:35 -05:00
										 |  |  |         Args: | 
					
						
							|  |  |  |             artist (str): Artist to search | 
					
						
							|  |  |  |             song (str): Song to search | 
					
						
							| 
									
										
										
										
											2025-04-17 14:35:56 -04:00
										 |  |  |             sub (Optional[str]): Lyrics for subsearch | 
					
						
							| 
									
										
										
										
											2025-02-16 20:07:02 -05:00
										 |  |  |         Returns: | 
					
						
							|  |  |  |             Optional[list] | 
					
						
							| 
									
										
										
										
											2025-02-13 14:51:35 -05:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             if not artist or not song: | 
					
						
							| 
									
										
										
										
											2025-02-16 20:07:02 -05:00
										 |  |  |                 return [("FAIL! Artist/Song not provided",)] | 
					
						
							| 
									
										
										
										
											2025-04-17 14:35:56 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-13 14:51:35 -05:00
										 |  |  |             search_obj: dict = { | 
					
						
							| 
									
										
										
										
											2025-04-17 14:35:56 -04:00
										 |  |  |                 "a": artist.strip(), | 
					
						
							|  |  |  |                 "s": song.strip(), | 
					
						
							|  |  |  |                 "extra": True, | 
					
						
							|  |  |  |                 "src": self.api_src, | 
					
						
							| 
									
										
										
										
											2025-02-13 14:51:35 -05:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2025-04-17 14:35:56 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-13 14:51:35 -05:00
										 |  |  |             if len(song.strip()) < 1: | 
					
						
							| 
									
										
										
										
											2025-04-17 14:35:56 -04:00
										 |  |  |                 search_obj.pop("a") | 
					
						
							|  |  |  |                 search_obj.pop("s") | 
					
						
							|  |  |  |                 search_obj["t"] = artist.strip()  # Parse failed, try title without sep | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-13 14:51:35 -05:00
										 |  |  |             if sub and len(sub) >= 2: | 
					
						
							| 
									
										
										
										
											2025-04-17 14:35:56 -04:00
										 |  |  |                 search_obj["sub"] = sub.strip() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-13 14:51:35 -05:00
										 |  |  |             async with aiohttp.ClientSession() as session: | 
					
						
							| 
									
										
										
										
											2025-04-17 14:35:56 -04:00
										 |  |  |                 async with await session.post( | 
					
						
							|  |  |  |                     self.api_url, | 
					
						
							|  |  |  |                     json=search_obj, | 
					
						
							|  |  |  |                     timeout=aiohttp.ClientTimeout(connect=5, sock_read=10), | 
					
						
							|  |  |  |                 ) as request: | 
					
						
							| 
									
										
										
										
											2025-02-13 14:51:35 -05:00
										 |  |  |                     request.raise_for_status() | 
					
						
							|  |  |  |                     response: dict = await request.json() | 
					
						
							| 
									
										
										
										
											2025-04-17 14:35:56 -04:00
										 |  |  |                     if response.get("err"): | 
					
						
							| 
									
										
										
										
											2025-02-16 20:07:02 -05:00
										 |  |  |                         return [(f"ERR: {response.get('errorText')}",)] | 
					
						
							| 
									
										
										
										
											2025-04-17 14:35:56 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |                     out_lyrics = regex.sub( | 
					
						
							|  |  |  |                         r"<br>", "\u200b\n", response.get("lyrics", "") | 
					
						
							|  |  |  |                     ) | 
					
						
							| 
									
										
										
										
											2025-02-13 14:51:35 -05:00
										 |  |  |                     response_obj: dict = { | 
					
						
							| 
									
										
										
										
											2025-04-17 14:35:56 -04:00
										 |  |  |                         "artist": response.get("artist"), | 
					
						
							|  |  |  |                         "song": response.get("song"), | 
					
						
							|  |  |  |                         "lyrics": out_lyrics, | 
					
						
							|  |  |  |                         "src": response.get("src"), | 
					
						
							|  |  |  |                         "confidence": float(response.get("confidence", 0.0)), | 
					
						
							|  |  |  |                         "time": float(response.get("time", -1.0)), | 
					
						
							| 
									
										
										
										
											2025-02-13 14:51:35 -05:00
										 |  |  |                     } | 
					
						
							| 
									
										
										
										
											2025-04-17 14:35:56 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |                     lyrics = response_obj.get("lyrics") | 
					
						
							| 
									
										
										
										
											2025-02-16 20:07:02 -05:00
										 |  |  |                     if not lyrics: | 
					
						
							|  |  |  |                         return None | 
					
						
							| 
									
										
										
										
											2025-04-17 14:35:56 -04:00
										 |  |  |                     response_obj["lyrics"] = textwrap.wrap( | 
					
						
							|  |  |  |                         text=lyrics.strip(), | 
					
						
							|  |  |  |                         width=1500, | 
					
						
							|  |  |  |                         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, | 
					
						
							|  |  |  |                     ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-13 14:51:35 -05:00
										 |  |  |                     return [ | 
					
						
							|  |  |  |                         ( | 
					
						
							| 
									
										
										
										
											2025-04-17 14:35:56 -04:00
										 |  |  |                             response_obj.get("artist"), | 
					
						
							|  |  |  |                             response_obj.get("song"), | 
					
						
							|  |  |  |                             response_obj.get("src"), | 
					
						
							| 
									
										
										
										
											2025-02-16 20:07:02 -05:00
										 |  |  |                             f"{int(response_obj.get('confidence', -1.0))}%", | 
					
						
							| 
									
										
										
										
											2025-02-13 14:51:35 -05:00
										 |  |  |                             f"{response_obj.get('time', -666.0):.4f}s", | 
					
						
							|  |  |  |                         ), | 
					
						
							| 
									
										
										
										
											2025-04-17 14:35:56 -04:00
										 |  |  |                         response_obj.get("lyrics"), | 
					
						
							|  |  |  |                         response_obj.get("lyrics_short"), | 
					
						
							|  |  |  |                     ] | 
					
						
							| 
									
										
										
										
											2025-02-13 14:51:35 -05:00
										 |  |  |         except Exception as e: | 
					
						
							|  |  |  |             traceback.print_exc() | 
					
						
							| 
									
										
										
										
											2025-04-17 14:35:56 -04:00
										 |  |  |             return [f"Retrieval failed: {str(e)}"] |