diff --git a/actions/ad_schedule.py b/actions/ad_schedule.py new file mode 100644 index 0000000..ecb9b07 --- /dev/null +++ b/actions/ad_schedule.py @@ -0,0 +1,39 @@ +import threading +import time +import os + +from loguru import logger as log + +from plugins.com_imdevinc_StreamControllerTwitchPlugin.TwitchActionBase import TwitchActionBase + +# Currently an issue with TwitchPy that gets the wrong time format, can't use this yet + + +class NextAd(TwitchActionBase): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.__ad_thread: threading.Thread = None + + def on_ready(self): + self.set_media(media_path=os.path.join( + self.plugin_base.PATH, "assets", "view.png"), size=0.85) + if not self.__ad_thread or not self.__ad_thread.is_alive(): + self.__ad_thread = threading.Thread( + target=self.ad_thread, daemon=True, name="ad_thread") + self.__ad_thread.start() + + def ad_thread(self): + while True: + self.get_next_ad() + time.sleep(1) + + def get_next_ad(self): + try: + next_ad = self.plugin_base.backend.get_next_ad() + if not next_ad: + next_ad = "-" + self.set_bottom_label(str(next_ad)) + self.hide_error() + except Exception as ex: + log.error(ex) + self.show_error() diff --git a/actions/play_ad.py b/actions/play_ad.py new file mode 100644 index 0000000..fd01aa7 --- /dev/null +++ b/actions/play_ad.py @@ -0,0 +1,65 @@ +import os +import gi +from gi.repository import Adw, Gtk + +from loguru import logger as log + +from plugins.com_imdevinc_StreamControllerTwitchPlugin.TwitchActionBase import TwitchActionBase + +gi.require_version("Gtk", "4.0") +gi.require_version("Adw", "1") + +options = [30, 60, 90, 120] + + +class PlayAd(TwitchActionBase): + _time: int = 30 + + def on_ready(self): + self.set_media(media_path=os.path.join( + self.plugin_base.PATH, "assets", "money.png"), size=0.85) + + def _load_config(self): + settings = self.get_settings() + time = settings.get("time") + for value in options: + if value == time: + self._time = value + return + self._time = 30 + + def _on_change_time(self, *_): + settings = self.get_settings() + selected_index = self.time_row.get_selected() + time = self.action_model[selected_index].get_string() + settings["time"] = int(time) + self.set_settings(settings) + + def get_config_rows(self): + super_rows = super().get_config_rows() + self.action_model = Gtk.StringList() + self.time_row = Adw.ComboRow( + model=self.action_model, title=self.plugin_base.lm.get("actions.play_ad.time.label")) + self._load_config() + + index = 0 + found = -1 + for value in options: + self.action_model.append(str(value)) + if value == self._time: + found = index + index += 1 + if found < 0: + self.time_row.set_selected(0) + + self.time_row.connect("notify::selected", self._on_change_time) + self.time_row.set_selected(found) + super_rows.append(self.time_row) + return super_rows + + def on_key_down(self): + try: + self.plugin_base.backend.play_ad(self._time) + except Exception as ex: + log.error(ex) + self.show_error(3) diff --git a/actions/snooze_ad.py b/actions/snooze_ad.py new file mode 100644 index 0000000..7e6c060 --- /dev/null +++ b/actions/snooze_ad.py @@ -0,0 +1,19 @@ + +import os + +from loguru import logger as log + +from plugins.com_imdevinc_StreamControllerTwitchPlugin.TwitchActionBase import TwitchActionBase + + +class SnoozeAd(TwitchActionBase): + def on_ready(self): + self.set_media(media_path=os.path.join( + self.plugin_base.PATH, "assets", "delay.png"), size=0.85) + + def on_key_down(self): + try: + self.plugin_base.backend.snooze_ad() + except Exception as ex: + log.error(ex) + self.show_error(3) diff --git a/assets/delay.png b/assets/delay.png new file mode 100644 index 0000000..d4ec147 Binary files /dev/null and b/assets/delay.png differ diff --git a/assets/money.png b/assets/money.png new file mode 100644 index 0000000..903d73d Binary files /dev/null and b/assets/money.png differ diff --git a/locales/en_US.json b/locales/en_US.json index 7837e91..3f57611 100644 --- a/locales/en_US.json +++ b/locales/en_US.json @@ -11,5 +11,6 @@ "actions.message.channel": "Channel", "actions.chat_mode.mode_row.label": "Chat Mode", "actions.info.link.label": "Checkout how to configure this plugin on", - "actions.info.link.text": "GitHub" + "actions.info.link.text": "GitHub", + "actions.play_ad.time.label": "Ad Length" } diff --git a/main.py b/main.py index c49100d..0973063 100644 --- a/main.py +++ b/main.py @@ -11,6 +11,9 @@ from .actions.clip import Clip from .actions.marker import Marker from .actions.viewers import Viewers +from .actions.play_ad import PlayAd +from .actions.snooze_ad import SnoozeAd +from .actions.ad_schedule import NextAd class PluginTemplate(PluginBase): @@ -82,6 +85,30 @@ def __init__(self): ) self.add_action_holder(self.clip_action_holder) + self.snooze_ad_action_holder = ActionHolder( + plugin_base=self, + action_base=SnoozeAd, + action_id="com_imdevinc_StreamControllerTwitchPlugin::SnoozeAd", + action_name="Snooze Ad" + ) + self.add_action_holder(self.snooze_ad_action_holder) + + self.play_ad_action_holder = ActionHolder( + plugin_base=self, + action_base=PlayAd, + action_id="com_imdevinc_StreamControllerTwitchPlugin::PlayAd", + action_name="Play Ad" + ) + self.add_action_holder(self.play_ad_action_holder) + + # self.next_ad_action_holder = ActionHolder( + # plugin_base=self, + # action_base=NextAd, + # action_id="com_imdevinc_StreamControllerTwitchPlugin::NextAd", + # action_name="Next Ad" + # ) + # self.add_action_holder(self.next_ad_action_holder) + # Register plugin self.register( plugin_name="Twitch Integration", diff --git a/twitch_backend.py b/twitch_backend.py index 237d3b8..e37966a 100644 --- a/twitch_backend.py +++ b/twitch_backend.py @@ -3,6 +3,7 @@ from urllib.parse import urlparse, parse_qs, urlencode import threading import requests +from datetime import datetime from loguru import logger as log from twitchpy.client import Client @@ -129,6 +130,25 @@ def send_message(self, message: str, user_name: str) -> None: channel_id = self.get_channel_id(user_name) or self.user_id self.twitch.send_chat_message(channel_id, self.user_id, message) + def snooze_ad(self) -> None: + if not self.twitch: + return + self.validate_auth() + self.twitch.snooze_next_ad(self.user_id) + + def play_ad(self, length: int) -> None: + if not self.twitch: + return + self.validate_auth() + self.twitch.start_commercial(self.user_id, length) + + def get_next_ad(self) -> datetime: + if not self.twitch: + return "Not Live" + self.validate_auth() + schedule = self.twitch.get_ad_schedule(self.user_id) + return schedule.next_ad_at + def update_client_credentials(self, client_id: str, client_secret: str) -> None: if None in (client_id, client_secret) or "" in (client_id, client_secret): return @@ -138,7 +158,7 @@ def update_client_credentials(self, client_id: str, client_secret: str) -> None: 'client_id': client_id, 'redirect_uri': 'http://localhost:3000/auth', 'response_type': 'code', - 'scope': 'user:write:chat channel:manage:broadcast moderator:manage:chat_settings clips:edit channel:read:subscriptions' + 'scope': 'user:write:chat channel:manage:broadcast moderator:manage:chat_settings clips:edit channel:read:subscriptions channel:edit:commercial channel:manage:ads channel:read:ads' } encoded_params = urlencode(params) if not self.httpd: