#!/usr/bin/env python3 import threading import time from typing import NewType import pychromecast import wx import wx.lib.scrolledpanel as scrolled import wx.media from ChannelProvider import SVT, ChannelProvider ChannelProvider = NewType('ChannelProvider', ChannelProvider) class Cast(wx.Frame): def __init__(self, *args, **kw): """__init__. :param args: :param kw: """ super().__init__(*args, **kw) self.m_selected_chromecast = None self.SetSizeHints(480,480,maxW=480) self.m_index = 0 self.m_chromecast_thr = threading.Thread(target=self.get_chromecasts, args=(), kwargs={}) self.m_chromecast_thr.start() self.m_sizer: wx.Sizer = wx.BoxSizer(wx.VERTICAL) self.m_panel: wx.lib.scrolledpanel.ScrolledPanel = scrolled.ScrolledPanel( self, -1, style=wx.VSCROLL) self.m_control = None self.m_panel.SetupScrolling() self.m_panel.SetSizer(self.m_sizer) self.m_channels: list[ChannelProvider] = [SVT.SVT()] self.show_list(None) def get_chromecasts(self): self.m_chromecasts, self.m_browser = pychromecast.get_chromecasts() def show_list(self, _): self.m_sizer.Clear(delete_windows=True) self.m_sizer = wx.BoxSizer(wx.VERTICAL) for channel in self.m_channels: if channel.wait(): with wx.BusyInfo("Please wait, working..."): index = 0 while channel.wait(): time.sleep(1) wx.GetApp().Yield() for item in channel.get_items(): title = wx.StaticText(self.m_panel, -1) title.SetLabelMarkup("{}".format(item['title'])) description = wx.StaticText(self.m_panel, -1, item['description']) description.Wrap(478) bitmap = item['thumbnail'] btn = wx.BitmapButton(self.m_panel, id=self.m_index, bitmap=bitmap) btn.Bind(wx.EVT_BUTTON, lambda event, link=item['link']: self.show_player( event, link)) self.m_sizer.Add(title) self.m_sizer.Add(btn) self.m_sizer.Add(description) self.m_sizer.AddSpacer(10) self.m_index = self.m_index + 1 self.m_panel.SetSizer(self.m_sizer) self.m_sizer.Fit(self) self.m_panel.Layout() def show_player(self, _, uri): self.m_sizer.Clear(delete_windows=True) self.m_sizer = wx.GridBagSizer() self.m_control = wx.media.MediaCtrl( self.m_panel, size=(480, 480), style=wx.SIMPLE_BORDER, szBackend=wx.media.MEDIABACKEND_GSTREAMER) play_button = wx.Button(self.m_panel, -1, "Play") pause_button = wx.Button(self.m_panel, -1, "Pause") back_button = wx.Button(self.m_panel, -1, "Back") back_button.Bind(wx.EVT_BUTTON, self.show_list) self.m_sizer.Add(self.m_control, (0, 0)) self.m_sizer.SetItemSpan(0, (0, 6)) self.m_sizer.Add(play_button, (1, 1)) self.m_sizer.Add(pause_button, (1, 2)) self.m_sizer.Add(back_button, (1, 3)) if not self.m_chromecast_thr.is_alive() and not self.m_selected_chromecast: chromecast_button = wx.Button(self.m_panel, -1, "Cast") chromecast_button.Bind( wx.EVT_BUTTON, lambda event, muri=uri: self.select_chromecast(event, muri)) self.m_sizer.Add(chromecast_button, (2, 2)) if self.m_selected_chromecast: self.Bind(wx.media.EVT_MEDIA_LOADED, lambda event, muri=uri: self.cast(event, muri)) play_button.Bind(wx.EVT_BUTTON, self.play_cast) pause_button.Bind(wx.EVT_BUTTON, self.pause_cast) else: self.Bind(wx.media.EVT_MEDIA_LOADED, self.play) play_button.Bind(wx.EVT_BUTTON, self.play) pause_button.Bind(wx.EVT_BUTTON, self.pause) self.Bind(wx.media.EVT_MEDIA_FINISHED, self.show_list) self.load_uri(uri) self.m_panel.SetSizer(self.m_sizer) self.m_sizer.Fit(self) self.m_panel.Layout() self.m_panel.ScrollChildIntoView(self.m_control) def select_chromecast(self, _, uri): self.m_sizer.Clear(delete_windows=True) self.m_sizer = wx.BoxSizer(wx.VERTICAL) for cast in self.m_chromecasts: friendly_name = cast.cast_info.friendly_name btn = wx.Button(self.m_panel, id=-1, label=friendly_name) btn.Bind(wx.EVT_BUTTON, lambda event, chromecast=cast, muri=uri: self. set_chromecast(event, chromecast, muri)) self.m_sizer.Add(btn) self.m_panel.SetSizer(self.m_sizer) self.m_sizer.Fit(self) self.m_panel.Layout() def set_chromecast(self, event, chromecast, uri): self.m_selected_chromecast = chromecast self.show_player(event, uri) def cast(self, event, uri): mimetype = 'video/mp4' cast = self.m_selected_chromecast cast.wait() cast.media_controller.play_media(uri, mimetype) # Wait for player_state PLAYING player_state = None has_played = False while True: if player_state != cast.media_controller.status.player_state: player_state = cast.media_controller.status.player_state print("Player state:", player_state) if player_state == "PLAYING": has_played = True break if cast.socket_client.is_connected and has_played and player_state != "PLAYING": has_played = False cast.media_controller.play_media(uri, mimetype) time.sleep(0.1) self.m_browser.stop_discovery() def pause_cast(self, event): cast = self.m_selected_chromecast cast.media_controller.pause() def play_cast(self, event): cast = self.m_selected_chromecast cast.media_controller.play() def load_uri(self, uri): self.m_control.LoadURI(uri) def play(self, _): self.m_control.Play() def pause(self, _): self.m_control.Pause() def quit(self, _): self.Destroy() if __name__ == '__main__': # When this module is run (not imported) then create the app, the # frame, show it, and start the event loop. app: wx.App = wx.App() frm: Cast = Cast(None, title='Cast') frm.Show() app.MainLoop()