playlist_of_the_day: add plugin

Simply support adding lines from other plugins, the playlist can't be
posted yet.
This commit is contained in:
dece 2022-09-09 19:08:46 +02:00
parent e601a77f72
commit a7d3a18ea7
3 changed files with 110 additions and 13 deletions

View file

@ -147,6 +147,9 @@
"positive": ["I like it."], "positive": ["I like it."],
"negative": ["I don't like it."] "negative": ["I don't like it."]
}, },
"playlistoftheday": {
"commands": ["playlist of the day"]
},
"plus": { "plus": {
"commands": ["plus"], "commands": ["plus"],
"aliases": { "aliases": {

View file

@ -0,0 +1,66 @@
import datetime
from edmond.plugin import Plugin
class PlaylistOfTheDayPlugin(Plugin):
"""Collect music links from other platforms.
Plugins that want to feed the playlist must do so by calling the add_link method of
the plugin. Later this plugin may feed its playlist using links unhandled by other,
more specialized plugins (bandcamp, soundcloud, ).
The current behaviour for the playlist is that links are added for a same day, so
the date at which the latest link was added is also stored. If a link is the first
to be added on a new day (by using this date as reference), the previous list is
emptied. But if the list is requested even if no links have been posted today and
the playlist is therefore outdated, it is still posted by the bot, along with a
message explaining that it's not the current day's playlist.
Note that the playlist itself is just of bunch of lines, which should of course
contain links, but can also contain titles and other metadata.
"""
REQUIRED_CONFIGS = ["commands"]
DATE_KEY = "date_of_latest_link"
PLAYLIST_KEY = "current_playlist"
def __init__(self, bot):
super().__init__(bot)
def on_pubmsg(self, event):
if not self.should_handle_command(event.arguments[0], no_content=True):
return False
# self.post_playlist(event.target)
return True
def add_line(self, line: str) -> None:
"""Add this line to the current playlist."""
today = datetime.date.today()
last_update_str = self.get_storage_value(self.DATE_KEY)
if last_update_str:
last_update_date = datetime.date.fromisoformat(last_update_str)
else:
last_update_date = today
if last_update_date == today:
current_playlist: list[str] = self.get_storage_value(
self.PLAYLIST_KEY,
default=[],
)
current_playlist.append(line)
else:
current_playlist = [line]
self.set_storage_value(
self.PLAYLIST_KEY,
current_playlist,
skip_save=True, # save one write
)
self.set_storage_value(
self.DATE_KEY,
today.isoformat(),
)
# def post_playlist(self):
# pass

View file

@ -1,13 +1,16 @@
import re import re
from typing import cast, Optional
try: try:
from googleapiclient.errors import Error as GoogleApiError from googleapiclient.errors import Error as GoogleApiError # type: ignore
DEPENDENCIES_FOUND = True DEPENDENCIES_FOUND = True
except ImportError: except ImportError:
DEPENDENCIES_FOUND = False DEPENDENCIES_FOUND = False
from edmond.plugin import Plugin from edmond.plugin import Plugin
from edmond.plugins.playlist_of_the_day import PlaylistOfTheDayPlugin
from edmond.plugins.youtube import YoutubePlugin
class YoutubeParserPlugin(Plugin): class YoutubeParserPlugin(Plugin):
@ -20,14 +23,29 @@ class YoutubeParserPlugin(Plugin):
def __init__(self, bot): def __init__(self, bot):
super().__init__(bot) super().__init__(bot)
self.priority = -3 self.priority = -3
self._youtube_plugin = None self._youtube_plugin: Optional[YoutubePlugin] = None
self._playlist_of_the_day_plugin: Optional[
PlaylistOfTheDayPlugin
] = None
@property @property
def youtube_plugin(self): def youtube_plugin(self) -> Optional[YoutubePlugin]:
if self._youtube_plugin is None: if self._youtube_plugin is None:
self._youtube_plugin = self.bot.get_plugin("youtube") self._youtube_plugin = cast(
YoutubePlugin,
self.bot.get_plugin("youtube"),
)
return self._youtube_plugin return self._youtube_plugin
@property
def playlist_of_the_day_plugin(self) -> Optional[PlaylistOfTheDayPlugin]:
if self._playlist_of_the_day_plugin is None:
self._playlist_of_the_day_plugin = cast(
PlaylistOfTheDayPlugin,
self.bot.get_plugin("playlistoftheday"),
)
return self._playlist_of_the_day_plugin
def on_welcome(self, _): def on_welcome(self, _):
if not (self.youtube_plugin and self.youtube_plugin.is_ready): if not (self.youtube_plugin and self.youtube_plugin.is_ready):
self.bot.log_w("Youtube plugin is not available.") self.bot.log_w("Youtube plugin is not available.")
@ -40,14 +58,22 @@ class YoutubeParserPlugin(Plugin):
for word in words: for word in words:
matched = self.VIDEO_URL_RE.match(word) matched = self.VIDEO_URL_RE.match(word)
if matched: if matched:
return self.handle_match(matched, event.target) title = self.get_video_title(matched)
if title:
self.bot.say(event.target, title)
self.add_to_playlist(word, title)
return True
else:
self.signal_failure(event.target)
return False return False
def handle_match(self, matched, target): def get_video_title(self, matched) -> Optional[str]:
if self.youtube_plugin is None:
return None
groupdict = matched.groupdict() groupdict = matched.groupdict()
code = groupdict.get("code1") or groupdict.get("code2") code = groupdict.get("code1") or groupdict.get("code2")
if not code: if not code:
return False return None
try: try:
search_response = ( search_response = (
self.youtube_plugin.youtube.videos() self.youtube_plugin.youtube.videos()
@ -55,15 +81,17 @@ class YoutubeParserPlugin(Plugin):
.execute() .execute()
) )
except GoogleApiError: except GoogleApiError:
self.signal_failure(target) return None
return False
title = "" title = ""
for result in search_response.get("items", []): for result in search_response.get("items", []):
if result["kind"] == "youtube#video": if result["kind"] == "youtube#video":
title = result["snippet"]["title"] title = result["snippet"]["title"]
break break
else: else:
self.signal_failure(target) return None
return False return title
self.bot.say(target, title)
return True def add_to_playlist(self, url: str, title: str):
if self.playlist_of_the_day_plugin is None:
return
self.playlist_of_the_day_plugin.add_line(f"{url} {title}")