removed __cache__-thingy, that was only around to hold data from search results to forming the entries...

This commit is contained in:
Christoph Stahl 2024-10-14 18:22:31 +02:00
parent cd3e6d6d7c
commit fdf72ac4f7
11 changed files with 674 additions and 690 deletions

View file

@ -61,6 +61,7 @@ class Entry:
uuid: UUID = field(default_factory=uuid4) uuid: UUID = field(default_factory=uuid4)
uid: Optional[str] = None uid: Optional[str] = None
started_at: Optional[float] = None started_at: Optional[float] = None
incomplete_data: bool = False
def update(self, **kwargs: Any) -> None: def update(self, **kwargs: Any) -> None:
""" """

View file

@ -26,8 +26,8 @@ class Result:
ident: str ident: str
source: str source: str
title: str title: str
artist: str artist: Optional[str]
album: str album: Optional[str]
duration: Optional[str] = None duration: Optional[str] = None
@classmethod @classmethod
@ -58,9 +58,7 @@ class Result:
album = splitfile[2].strip() album = splitfile[2].strip()
return cls(ident=ident, source=source, title=title, artist=artist, album=album) return cls(ident=ident, source=source, title=title, artist=artist, album=album)
except IndexError: except IndexError:
return cls( return cls(ident=filename, source=source, title=basename, artist=None, album=None)
ident=filename, source=source, title=basename, artist="Unknown", album="Unknown"
)
@classmethod @classmethod
def from_dict(cls, values: dict[str, str]) -> Result: def from_dict(cls, values: dict[str, str]) -> Result:
@ -97,20 +95,22 @@ class Result:
- ident (str) - ident (str)
- source (str) - source (str)
- title (str) - title (str)
- artist (str) - album (str, if available)
- album (str) - artist (str, if available)
- duration (str, if available) - duration (str, if available)
:return: The dictionary with the values :return: The dictionary with the values
:rtype: dict[str, str] :rtype: dict[str, str]
""" """
output = { output: dict[str, str] = {
"ident": self.ident, "ident": self.ident,
"source": self.source, "source": self.source,
"title": self.title, "title": self.title,
"artist": self.artist,
"album": self.album,
} }
if self.album is not None:
output["album"] = self.album
if self.artist is not None:
output["artist"] = self.artist
if self.duration is not None: if self.duration is not None:
output["duration"] = self.duration output["duration"] = self.duration
return output return output

View file

@ -23,7 +23,7 @@ from json.decoder import JSONDecodeError
from argparse import Namespace from argparse import Namespace
from dataclasses import dataclass from dataclasses import dataclass
from dataclasses import field from dataclasses import field
from typing import Any, Callable, overload from typing import Any, Callable
from typing import AsyncGenerator from typing import AsyncGenerator
from typing import Optional from typing import Optional
@ -32,7 +32,6 @@ from aiohttp import web
from profanity_check import predict from profanity_check import predict
from .result import Result from .result import Result
from .sources.youtube import YouTube
from . import jsonencoder from . import jsonencoder
from .log import logger from .log import logger
@ -298,7 +297,9 @@ class Server:
:rtype: None :rtype: None
""" """
source_obj = state.client.sources[data["source"]] source_obj = state.client.sources[data["source"]]
entry = await source_obj.get_entry(data["performer"], data["ident"]) entry = await source_obj.get_entry(
data["performer"], data["ident"], artist=data["artist"], title=data["title"]
)
if entry is None: if entry is None:
await self.sio.emit( await self.sio.emit(
@ -476,6 +477,8 @@ class Server:
"source": data["source"], "source": data["source"],
"performer": data["performer"], "performer": data["performer"],
"ident": data["ident"], "ident": data["ident"],
"artist": data["artist"],
"title": data["title"],
}, },
"old_entry": { "old_entry": {
"artist": old_entry.artist, "artist": old_entry.artist,
@ -489,7 +492,9 @@ class Server:
source_obj = state.client.sources[data["source"]] source_obj = state.client.sources[data["source"]]
entry = await source_obj.get_entry(data["performer"], data["ident"]) entry = await source_obj.get_entry(
data["performer"], data["ident"], artist=data["artist"], title=data["title"]
)
if entry is None: if entry is None:
await self.sio.emit( await self.sio.emit(
@ -531,7 +536,9 @@ class Server:
source_obj = state.client.sources[data["source"]] source_obj = state.client.sources[data["source"]]
entry = await source_obj.get_entry(data["performer"], data["ident"]) entry = await source_obj.get_entry(
data["performer"], data["ident"], artist=data["artist"], title=data["title"]
)
if entry is None: if entry is None:
await self.sio.emit( await self.sio.emit(
@ -564,14 +571,15 @@ class Server:
:type data: dict[str, Any] :type data: dict[str, Any]
:rtype: None :rtype: None
""" """
print(data)
state.queue.update( state.queue.update(
data["uuid"], data["uuid"],
lambda item: item.update(**data["meta"]), lambda item: item.update(**data["meta"], incomplete_data=False),
) )
for entry in state.waiting_room: for entry in state.waiting_room:
if entry.uuid == data["uuid"] or str(entry.uuid) == data["uuid"]: if entry.uuid == data["uuid"] or str(entry.uuid) == data["uuid"]:
entry.update(**data["meta"]) entry.update(**data["meta"], incomplete_data=False)
await self.broadcast_state(state) await self.broadcast_state(state)
@ -1111,21 +1119,6 @@ class Server:
web_sid = data["sid"] web_sid = data["sid"]
results = [Result.from_dict(result) for result in data["results"]] results = [Result.from_dict(result) for result in data["results"]]
# TODO: we convert the results to YouTube objects. This
# adds them to the cache to prevent YouTube from blocking us.
__unused_yt_list = [
YouTube.from_result(
{
"duration": result.duration,
"title": result.title,
"channel": result.artist,
"url": result.ident,
}
)
for result in results
if "youtube" == result.source
]
await self.send_search_results(web_sid, results) await self.send_search_results(web_sid, results)
async def send_search_results(self, sid: str, results: list[Result]) -> None: async def send_search_results(self, sid: str, results: list[Result]) -> None:

View file

@ -157,7 +157,14 @@ class Source(ABC):
) )
return await mpv_process return await mpv_process
async def get_entry(self, performer: str, ident: str) -> Optional[Entry]: async def get_entry(
self,
performer: str,
ident: str,
/,
artist: Optional[str] = None,
title: Optional[str] = None,
) -> Optional[Entry]:
""" """
Create an :py:class:`syng.entry.Entry` from a given identifier. Create an :py:class:`syng.entry.Entry` from a given identifier.
@ -185,10 +192,11 @@ class Source(ABC):
ident=ident, ident=ident,
source=self.source_name, source=self.source_name,
duration=180, duration=180,
album=res.album, album=res.album if res.album else "Unknown",
title=res.title, title=res.title if res.title else title if title else "Unknown",
artist=res.artist, artist=res.artist if res.artist else artist if artist else "Unknown",
performer=performer, performer=performer,
incomplete_data=True,
) )
async def search(self, query: str) -> list[Result]: async def search(self, query: str) -> list[Result]:

View file

@ -30,10 +30,6 @@ class YouTube:
A minimal compatibility layer for the YouTube object of pytube, implemented via yt-dlp A minimal compatibility layer for the YouTube object of pytube, implemented via yt-dlp
""" """
__cache__: dict[str, Any] = (
{}
) # TODO: this may grow fast... but atm it fixed youtubes anti bot measures
def __init__(self, url: Optional[str] = None): def __init__(self, url: Optional[str] = None):
""" """
Construct a YouTube object from a url. Construct a YouTube object from a url.
@ -48,9 +44,6 @@ class YouTube:
self._author: Optional[str] self._author: Optional[str]
if url is not None: if url is not None:
if url in YouTube.__cache__:
self._infos = YouTube.__cache__[url]
else:
try: try:
self._infos = YoutubeDL({"quiet": True}).extract_info(url, download=False) self._infos = YoutubeDL({"quiet": True}).extract_info(url, download=False)
except DownloadError: except DownloadError:
@ -107,12 +100,12 @@ class YouTube:
:type search_result: dict[str, Any] :type search_result: dict[str, Any]
""" """
url = search_result["url"] url = search_result["url"]
cls.__cache__[url] = { # cls.__cache__[url] = {
"duration": int(search_result["duration"]), # "duration": int(search_result["duration"]),
"title": search_result["title"], # "title": search_result["title"],
"channel": search_result["channel"], # "channel": search_result["channel"],
"url": url, # "url": url,
} # }
return cls(url) return cls(url)
@ -268,7 +261,14 @@ class YoutubeSource(Source):
else: else:
await super().play(entry, mpv_options) await super().play(entry, mpv_options)
async def get_entry(self, performer: str, ident: str) -> Optional[Entry]: async def get_entry(
self,
performer: str,
ident: str,
/,
artist: Optional[str] = None,
title: Optional[str] = None,
) -> Optional[Entry]:
""" """
Create an :py:class:`syng.entry.Entry` for the identifier. Create an :py:class:`syng.entry.Entry` for the identifier.
@ -283,34 +283,17 @@ class YoutubeSource(Source):
:rtype: Optional[Entry] :rtype: Optional[Entry]
""" """
def _get_entry(performer: str, url: str) -> Optional[Entry]:
"""
Create the entry in a thread.
:param performer: The person singing.
:type performer: str
:param url: A url to a YouTube video.
:type url: str
:return: An entry with the data.
:rtype: Optional[Entry]
"""
yt_song = YouTube(url)
try:
length = yt_song.length
except TypeError:
length = 180
return Entry( return Entry(
ident=url, ident=ident,
source="youtube", source="youtube",
duration=180,
album="YouTube", album="YouTube",
duration=length, title=title,
title=yt_song._title, artist=artist,
artist=yt_song._author,
performer=performer, performer=performer,
incomplete_data=True,
) )
return await asyncio.to_thread(_get_entry, performer, ident)
async def search(self, query: str) -> list[Result]: async def search(self, query: str) -> list[Result]:
""" """
Search YouTube and the configured channels for the query. Search YouTube and the configured channels for the query.
@ -392,7 +375,7 @@ class YoutubeSource(Source):
Video metadata should be read on the client to avoid banning Video metadata should be read on the client to avoid banning
the server. the server.
""" """
if entry.title is None or entry.artist is None: if entry.incomplete_data:
print(f"Looking up {entry.ident}") print(f"Looking up {entry.ident}")
youtube_video: YouTube = await asyncio.to_thread(YouTube, entry.ident) youtube_video: YouTube = await asyncio.to_thread(YouTube, entry.ident)
return { return {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -5,8 +5,8 @@
<link rel="icon" href="/favicon.ico"> <link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Syng Rocks!</title> <title>Syng Rocks!</title>
<script type="module" crossorigin src="/assets/index.953ecce8.js"></script> <script type="module" crossorigin src="/assets/index.38362a39.js"></script>
<link rel="stylesheet" href="/assets/index.ceeebb4f.css"> <link rel="stylesheet" href="/assets/index.e5e245ad.css">
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>