black -> line-length=100

This commit is contained in:
Christoph Stahl 2024-07-16 14:52:04 +02:00
parent f4e15908cc
commit 411ccdd2c9
13 changed files with 56 additions and 137 deletions

View file

@ -79,3 +79,6 @@ ignore_missing_imports = true
[tool.ruff] [tool.ruff]
line-length = 100 line-length = 100
[tool.black]
line-length = 100

View file

@ -356,9 +356,7 @@ async def handle_request_config(data: dict[str, Any]) -> None:
:rtype: None :rtype: None
""" """
if data["source"] in sources: if data["source"] in sources:
config: dict[str, Any] | list[dict[str, Any]] = await sources[ config: dict[str, Any] | list[dict[str, Any]] = await sources[data["source"]].get_config()
data["source"]
].get_config()
if isinstance(config, list): if isinstance(config, list):
num_chunks: int = len(config) num_chunks: int = len(config)
for current, chunk in enumerate(config): for current, chunk in enumerate(config):

View file

@ -76,8 +76,7 @@ class Entry:
def normalize(performers: str) -> set[str]: def normalize(performers: str) -> set[str]:
return set( return set(
filter( filter(
lambda x: len(x) > 0 lambda x: len(x) > 0 and x not in ["der", "die", "das", "alle", "und"],
and x not in ["der", "die", "das", "alle", "und"],
re.sub( re.sub(
r"[^a-zA-Z0-9\s]", r"[^a-zA-Z0-9\s]",
"", "",

View file

@ -68,9 +68,7 @@ class DateAndTimePickerWindow(customtkinter.CTkToplevel): # type: ignore
self.timepicker.pack(expand=True, fill="both") self.timepicker.pack(expand=True, fill="both")
button = customtkinter.CTkButton( button = customtkinter.CTkButton(self, text="Ok", command=partial(self.insert, input_field))
self, text="Ok", command=partial(self.insert, input_field)
)
button.pack(expand=True, fill="x") button.pack(expand=True, fill="x")
def insert(self, input_field: customtkinter.CTkTextbox) -> None: def insert(self, input_field: customtkinter.CTkTextbox) -> None:
@ -121,12 +119,8 @@ class OptionFrame(customtkinter.CTkScrollableFrame): # type:ignore
if value is None: if value is None:
value = "" value = ""
self.string_options[name] = customtkinter.CTkTextbox( self.string_options[name] = customtkinter.CTkTextbox(self, wrap="none", height=1)
self, wrap="none", height=1 self.string_options[name].grid(column=1, row=self.number_of_options, sticky="EW")
)
self.string_options[name].grid(
column=1, row=self.number_of_options, sticky="EW"
)
self.string_options[name].insert("0.0", value) self.string_options[name].insert("0.0", value)
if callback is not None: if callback is not None:
self.string_options[name].bind("<KeyRelease>", callback) self.string_options[name].bind("<KeyRelease>", callback)
@ -196,22 +190,16 @@ class OptionFrame(customtkinter.CTkScrollableFrame): # type:ignore
) -> None: ) -> None:
self.add_option_label(description) self.add_option_label(description)
self.choose_options[name] = customtkinter.CTkOptionMenu(self, values=values) self.choose_options[name] = customtkinter.CTkOptionMenu(self, values=values)
self.choose_options[name].grid( self.choose_options[name].grid(column=1, row=self.number_of_options, sticky="EW")
column=1, row=self.number_of_options, sticky="EW"
)
self.choose_options[name].set(value) self.choose_options[name].set(value)
self.number_of_options += 1 self.number_of_options += 1
def open_date_and_time_picker( def open_date_and_time_picker(self, name: str, input_field: customtkinter.CTkTextbox) -> None:
self, name: str, input_field: customtkinter.CTkTextbox
) -> None:
if ( if (
name not in self.date_and_time_pickers name not in self.date_and_time_pickers
or not self.date_and_time_pickers[name].winfo_exists() or not self.date_and_time_pickers[name].winfo_exists()
): ):
self.date_and_time_pickers[name] = DateAndTimePickerWindow( self.date_and_time_pickers[name] = DateAndTimePickerWindow(self, input_field)
self, input_field
)
else: else:
self.date_and_time_pickers[name].focus() self.date_and_time_pickers[name].focus()
@ -307,9 +295,7 @@ class GeneralConfig(OptionFrame):
str(config["waiting_room_policy"]).lower(), str(config["waiting_room_policy"]).lower(),
) )
self.add_date_time_option("last_song", "Time of last song", config["last_song"]) self.add_date_time_option("last_song", "Time of last song", config["last_song"])
self.add_string_option( self.add_string_option("preview_duration", "Preview Duration", config["preview_duration"])
"preview_duration", "Preview Duration", config["preview_duration"]
)
def get_config(self) -> dict[str, Any]: def get_config(self) -> dict[str, Any]:
config = super().get_config() config = super().get_config()
@ -346,9 +332,7 @@ class SyngGui(customtkinter.CTk): # type:ignore
self.syng_server: Optional[Process] = None self.syng_server: Optional[Process] = None
self.syng_client: Optional[Process] = None self.syng_client: Optional[Process] = None
self.configfile = os.path.join( self.configfile = os.path.join(platformdirs.user_config_dir("syng"), "config.yaml")
platformdirs.user_config_dir("syng"), "config.yaml"
)
try: try:
with open(self.configfile, encoding="utf8") as cfile: with open(self.configfile, encoding="utf8") as cfile:
@ -380,13 +364,9 @@ class SyngGui(customtkinter.CTk): # type:ignore
self.startsyng_serverbutton = customtkinter.CTkButton( self.startsyng_serverbutton = customtkinter.CTkButton(
button_line, text="Start Local Server", command=self.start_syng_server button_line, text="Start Local Server", command=self.start_syng_server
) )
self.startsyng_serverbutton.pack( self.startsyng_serverbutton.pack(side="left", expand=True, anchor="w", padx=10, pady=5)
side="left", expand=True, anchor="w", padx=10, pady=5
)
savebutton = customtkinter.CTkButton( savebutton = customtkinter.CTkButton(button_line, text="Save", command=self.save_config)
button_line, text="Save", command=self.save_config
)
savebutton.pack(side="left", padx=10, pady=5) savebutton.pack(side="left", padx=10, pady=5)
# open_web_button = customtkinter.CTkButton( # open_web_button = customtkinter.CTkButton(
@ -430,9 +410,7 @@ class SyngGui(customtkinter.CTk): # type:ignore
except (KeyError, TypeError): except (KeyError, TypeError):
source_config = {} source_config = {}
self.tabs[source_name] = SourceTab( self.tabs[source_name] = SourceTab(tabview.tab(source_name), source_name, source_config)
tabview.tab(source_name), source_name, source_config
)
self.tabs[source_name].pack(ipadx=10, expand=True, fill="both") self.tabs[source_name].pack(ipadx=10, expand=True, fill="both")
self.update_qr() self.update_qr()

View file

@ -1,4 +1,5 @@
"""Wraps the ``json`` module, so that own classes get encoded.""" """Wraps the ``json`` module, so that own classes get encoded."""
import json import json
from dataclasses import asdict from dataclasses import asdict
from typing import Any from typing import Any

View file

@ -1,4 +1,5 @@
"""A async queue with synchronization.""" """A async queue with synchronization."""
import asyncio import asyncio
from collections import deque from collections import deque
from collections.abc import Callable, Iterable from collections.abc import Callable, Iterable

View file

@ -1,4 +1,5 @@
"""Module for search results.""" """Module for search results."""
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass from dataclasses import dataclass
from typing import Optional from typing import Optional

View file

@ -138,9 +138,7 @@ class State:
recent: list[Entry] recent: list[Entry]
sid: str sid: str
client: Client client: Client
last_seen: datetime.datetime = field( last_seen: datetime.datetime = field(init=False, default_factory=datetime.datetime.now)
init=False, default_factory=datetime.datetime.now
)
clients: dict[str, State] = {} clients: dict[str, State] = {}
@ -163,9 +161,7 @@ async def send_state(state: State, sid: str) -> None:
:rtype: None :rtype: None
""" """
safe_config = { safe_config = {k: v for k, v in state.client.config.items() if k not in ["secret", "key"]}
k: v for k, v in state.client.config.items() if k not in ["secret", "key"]
}
await sio.emit( await sio.emit(
"state", "state",
@ -216,18 +212,13 @@ async def handle_waiting_room_append(sid: str, data: dict[str, Any]) -> None:
if entry is None: if entry is None:
await sio.emit( await sio.emit(
"msg", "msg",
{ {"msg": f"Unable to add to the waiting room: {data['ident']}. Maybe try again?"},
"msg": f"Unable to add to the waiting room: {data['ident']}. Maybe try again?"
},
room=sid, room=sid,
) )
return return
if "uid" not in data or ( if "uid" not in data or (
( (data["uid"] is not None and len(list(state.queue.find_by_uid(data["uid"]))) == 0)
data["uid"] is not None
and len(list(state.queue.find_by_uid(data["uid"]))) == 0
)
or (data["uid"] is None and state.queue.find_by_name(data["performer"]) is None) or (data["uid"] is None and state.queue.find_by_name(data["performer"]) is None)
): ):
await append_to_queue(room, entry, sid) await append_to_queue(room, entry, sid)
@ -244,9 +235,7 @@ async def handle_waiting_room_append(sid: str, data: dict[str, Any]) -> None:
) )
async def append_to_queue( async def append_to_queue(room: str, entry: Entry, report_to: Optional[str] = None) -> None:
room: str, entry: Entry, report_to: Optional[str] = None
) -> None:
""" """
Append a song to the queue for a given session. Append a song to the queue for a given session.
@ -270,10 +259,7 @@ async def append_to_queue(
start_time = first_song.started_at start_time = first_song.started_at
start_time = state.queue.fold( start_time = state.queue.fold(
lambda item, time: time lambda item, time: time + item.duration + state.client.config["preview_duration"] + 1,
+ item.duration
+ state.client.config["preview_duration"]
+ 1,
start_time, start_time,
) )
@ -395,15 +381,11 @@ async def handle_append(sid: str, data: dict[str, Any]) -> None:
state = clients[room] state = clients[room]
if len(data["performer"]) > 50: if len(data["performer"]) > 50:
await sio.emit( await sio.emit("err", {"type": "NAME_LENGTH", "name": data["performer"]}, room=sid)
"err", {"type": "NAME_LENGTH", "name": data["performer"]}, room=sid
)
return return
if predict([data["performer"]]) == [1]: if predict([data["performer"]]) == [1]:
await sio.emit( await sio.emit("err", {"type": "PROFANITY", "name": data["performer"]}, room=sid)
"err", {"type": "PROFANITY", "name": data["performer"]}, room=sid
)
return return
if state.client.config["waiting_room_policy"] and ( if state.client.config["waiting_room_policy"] and (
@ -462,15 +444,11 @@ async def handle_append_anyway(sid: str, data: dict[str, Any]) -> None:
state = clients[room] state = clients[room]
if len(data["performer"]) > 50: if len(data["performer"]) > 50:
await sio.emit( await sio.emit("err", {"type": "NAME_LENGTH", "name": data["performer"]}, room=sid)
"err", {"type": "NAME_LENGTH", "name": data["performer"]}, room=sid
)
return return
if predict([data["performer"]]) == [1]: if predict([data["performer"]]) == [1]:
await sio.emit( await sio.emit("err", {"type": "PROFANITY", "name": data["performer"]}, room=sid)
"err", {"type": "PROFANITY", "name": data["performer"]}, room=sid
)
return return
if state.client.config["waiting_room_policy"].lower() == "forced": if state.client.config["waiting_room_policy"].lower() == "forced":
@ -582,11 +560,7 @@ async def handle_waiting_room_to_queue(sid: str, data: dict[str, Any]) -> None:
if is_admin: if is_admin:
entry = next( entry = next(
( (wr_entry for wr_entry in state.waiting_room if str(wr_entry.uuid) == data["uuid"]),
wr_entry
for wr_entry in state.waiting_room
if str(wr_entry.uuid) == data["uuid"]
),
None, None,
) )
if entry is not None: if entry is not None:
@ -718,9 +692,7 @@ async def handle_register_client(sid: str, data: dict[str, Any]) -> None:
""" """
def gen_id(length: int = 4) -> str: def gen_id(length: int = 4) -> str:
client_id = "".join( client_id = "".join([random.choice(string.ascii_letters) for _ in range(length)])
[random.choice(string.ascii_letters) for _ in range(length)]
)
if client_id in clients: if client_id in clients:
client_id = gen_id(length + 1) client_id = gen_id(length + 1)
return client_id return client_id
@ -732,8 +704,7 @@ async def handle_register_client(sid: str, data: dict[str, Any]) -> None:
if ( if (
"key" not in data["config"] "key" not in data["config"]
or hashlib.sha256(data["config"]["key"].encode()).hexdigest() or hashlib.sha256(data["config"]["key"].encode()).hexdigest() not in keys
not in keys
): ):
await sio.emit( await sio.emit(
"client-registered", "client-registered",
@ -743,9 +714,7 @@ async def handle_register_client(sid: str, data: dict[str, Any]) -> None:
return return
room: str = ( room: str = (
data["config"]["room"] data["config"]["room"] if "room" in data["config"] and data["config"]["room"] else gen_id()
if "room" in data["config"] and data["config"]["room"]
else gen_id()
) )
async with sio.session(sid) as session: async with sio.session(sid) as session:
session["room"] = room session["room"] = room
@ -761,15 +730,11 @@ async def handle_register_client(sid: str, data: dict[str, Any]) -> None:
config=DEFAULT_CONFIG | data["config"], config=DEFAULT_CONFIG | data["config"],
) )
await sio.enter_room(sid, room) await sio.enter_room(sid, room)
await sio.emit( await sio.emit("client-registered", {"success": True, "room": room}, room=sid)
"client-registered", {"success": True, "room": room}, room=sid
)
await send_state(clients[room], sid) await send_state(clients[room], sid)
else: else:
logger.warning("Got wrong secret for %s", room) logger.warning("Got wrong secret for %s", room)
await sio.emit( await sio.emit("client-registered", {"success": False, "room": room}, room=sid)
"client-registered", {"success": False, "room": room}, room=sid
)
else: else:
logger.info("Registerd new client %s", room) logger.info("Registerd new client %s", room)
initial_entries = [Entry(**entry) for entry in data["queue"]] initial_entries = [Entry(**entry) for entry in data["queue"]]
@ -860,9 +825,7 @@ async def handle_config_chunk(sid: str, data: dict[str, Any]) -> None:
return return
if data["source"] not in state.client.sources: if data["source"] not in state.client.sources:
state.client.sources[data["source"]] = available_sources[data["source"]]( state.client.sources[data["source"]] = available_sources[data["source"]](data["config"])
data["config"]
)
else: else:
state.client.sources[data["source"]].add_to_config(data["config"]) state.client.sources[data["source"]].add_to_config(data["config"])
@ -891,9 +854,7 @@ async def handle_config(sid: str, data: dict[str, Any]) -> None:
if sid != state.sid: if sid != state.sid:
return return
state.client.sources[data["source"]] = available_sources[data["source"]]( state.client.sources[data["source"]] = available_sources[data["source"]](data["config"])
data["config"]
)
@sio.on("register-web") @sio.on("register-web")
@ -1078,17 +1039,10 @@ async def handle_search(sid: str, data: dict[str, Any]) -> None:
query = data["query"] query = data["query"]
results_list = await asyncio.gather( results_list = await asyncio.gather(
*[ *[state.client.sources[source].search(query) for source in state.client.sources_prio]
state.client.sources[source].search(query)
for source in state.client.sources_prio
]
) )
results = [ results = [search_result for source_result in results_list for search_result in source_result]
search_result
for source_result in results_list
for search_result in source_result
]
await sio.emit( await sio.emit(
"search-results", "search-results",
{"results": results}, {"results": results},
@ -1152,9 +1106,7 @@ def run_server(args: Namespace) -> None:
app["root_folder"] = args.root_folder app["root_folder"] = args.root_folder
app.add_routes( app.add_routes([web.static("/assets/", os.path.join(app["root_folder"], "assets/"))])
[web.static("/assets/", os.path.join(app["root_folder"], "assets/"))]
)
app.router.add_route("*", "/", root_handler) app.router.add_route("*", "/", root_handler)
app.router.add_route("*", "/{room}", root_handler) app.router.add_route("*", "/{room}", root_handler)

View file

@ -2,6 +2,7 @@
Imports all sources, so that they add themselves to the Imports all sources, so that they add themselves to the
``available_sources`` dictionary. ``available_sources`` dictionary.
""" """
# pylint: disable=useless-import-alias # pylint: disable=useless-import-alias
from typing import Any from typing import Any

View file

@ -1,4 +1,5 @@
"""Module for the files Source.""" """Module for the files Source."""
import asyncio import asyncio
import os import os
from typing import Any, Optional from typing import Any, Optional

View file

@ -64,13 +64,9 @@ class S3Source(FileBasedSource):
secure=(config["secure"] if "secure" in config else True), secure=(config["secure"] if "secure" in config else True),
) )
self.bucket: str = config["bucket"] self.bucket: str = config["bucket"]
self.tmp_dir: str = ( self.tmp_dir: str = config["tmp_dir"] if "tmp_dir" in config else "/tmp/syng"
config["tmp_dir"] if "tmp_dir" in config else "/tmp/syng"
)
self.index_file: Optional[str] = ( self.index_file: Optional[str] = config["index_file"] if "index_file" in config else None
config["index_file"] if "index_file" in config else None
)
self.extra_mpv_arguments = ["--scale=oversample"] self.extra_mpv_arguments = ["--scale=oversample"]
async def get_file_list(self) -> list[str]: async def get_file_list(self) -> list[str]:
@ -93,8 +89,7 @@ class S3Source(FileBasedSource):
file_list = [ file_list = [
obj.object_name obj.object_name
for obj in self.minio.list_objects(self.bucket, recursive=True) for obj in self.minio.list_objects(self.bucket, recursive=True)
if obj.object_name is not None if obj.object_name is not None and self.has_correct_extension(obj.object_name)
and self.has_correct_extension(obj.object_name)
] ]
if self.index_file is not None and not os.path.isfile(self.index_file): if self.index_file is not None and not os.path.isfile(self.index_file):
with open(self.index_file, "w", encoding="utf8") as index_file_handle: with open(self.index_file, "w", encoding="utf8") as index_file_handle:
@ -140,9 +135,7 @@ class S3Source(FileBasedSource):
video_dl_path: str = os.path.join(self.tmp_dir, video_path) video_dl_path: str = os.path.join(self.tmp_dir, video_path)
os.makedirs(os.path.dirname(video_dl_path), exist_ok=True) os.makedirs(os.path.dirname(video_dl_path), exist_ok=True)
video_dl_task: asyncio.Task[Any] = asyncio.create_task( video_dl_task: asyncio.Task[Any] = asyncio.create_task(
asyncio.to_thread( asyncio.to_thread(self.minio.fget_object, self.bucket, entry.ident, video_dl_path)
self.minio.fget_object, self.bucket, entry.ident, video_dl_path
)
) )
audio_dl_path: Optional[str] audio_dl_path: Optional[str]
@ -150,9 +143,7 @@ class S3Source(FileBasedSource):
audio_dl_path = os.path.join(self.tmp_dir, audio_path) audio_dl_path = os.path.join(self.tmp_dir, audio_path)
audio_dl_task: asyncio.Task[Any] = asyncio.create_task( audio_dl_task: asyncio.Task[Any] = asyncio.create_task(
asyncio.to_thread( asyncio.to_thread(self.minio.fget_object, self.bucket, audio_path, audio_dl_path)
self.minio.fget_object, self.bucket, audio_path, audio_dl_path
)
) )
else: else:
audio_dl_path = None audio_dl_path = None

View file

@ -4,6 +4,7 @@ Abstract class for sources.
Also defines the dictionary of available sources. Each source should add itself Also defines the dictionary of available sources. Each source should add itself
to this dictionary in its module. to this dictionary in its module.
""" """
from __future__ import annotations from __future__ import annotations
import asyncio import asyncio

View file

@ -41,9 +41,7 @@ class YouTube:
self._infos = YouTube.__cache__[url] self._infos = YouTube.__cache__[url]
else: else:
try: try:
self._infos = YoutubeDL({"quiet": True}).extract_info( self._infos = YoutubeDL({"quiet": True}).extract_info(url, download=False)
url, download=False
)
except DownloadError: except DownloadError:
self.length = 300 self.length = 300
self._title = None self._title = None
@ -104,7 +102,9 @@ class Search:
else: else:
if channel[0] == "/": if channel[0] == "/":
channel = channel[1:] channel = channel[1:]
query_url = f"https://www.youtube.com/{channel}/search?{urlencode({'query': query, 'sp':sp})}" query_url = (
f"https://www.youtube.com/{channel}/search?{urlencode({'query': query, 'sp':sp})}"
)
results = YoutubeDL( results = YoutubeDL(
{ {
@ -118,9 +118,7 @@ class Search:
) )
self.results = [] self.results = []
if results is not None: if results is not None:
filtered_entries = filter( filtered_entries = filter(lambda entry: "short" not in entry["url"], results["entries"])
lambda entry: "short" not in entry["url"], results["entries"]
)
for r in filtered_entries: for r in filtered_entries:
try: try:
@ -169,8 +167,7 @@ class YoutubeSource(Source):
config["start_streaming"] if "start_streaming" in config else False config["start_streaming"] if "start_streaming" in config else False
) )
self.formatstring = ( self.formatstring = (
f"bestvideo[height<={self.max_res}]+" f"bestvideo[height<={self.max_res}]+" f"bestaudio/best[height<={self.max_res}]"
f"bestaudio/best[height<={self.max_res}]"
) )
self._yt_dlp = YoutubeDL( self._yt_dlp = YoutubeDL(
params={ params={
@ -278,15 +275,10 @@ class YoutubeSource(Source):
results: list[YouTube] = [] results: list[YouTube] = []
results_lists: list[list[YouTube]] = await asyncio.gather( results_lists: list[list[YouTube]] = await asyncio.gather(
*[ *[asyncio.to_thread(self._channel_search, query, channel) for channel in self.channels],
asyncio.to_thread(self._channel_search, query, channel)
for channel in self.channels
],
asyncio.to_thread(self._yt_search, query), asyncio.to_thread(self._yt_search, query),
) )
results = [ results = [search_result for yt_result in results_lists for search_result in yt_result]
search_result for yt_result in results_lists for search_result in yt_result
]
results.sort(key=partial(_contains_index, query)) results.sort(key=partial(_contains_index, query))