cast/src/main.py

355 lines
13 KiB
Python
Raw Normal View History

2021-12-07 09:51:53 +01:00
#!/usr/bin/env python3
import json
import os
2021-12-13 17:22:42 +01:00
import threading
2021-12-07 22:19:20 +01:00
import time
from typing import Callable
from urllib.parse import urlparse
2021-12-07 09:51:53 +01:00
2021-12-07 22:19:20 +01:00
import pychromecast
2021-12-07 09:51:53 +01:00
import wx
import wx.lib.scrolledpanel as scrolled
2021-12-07 09:51:53 +01:00
import wx.media
2022-01-06 13:31:45 +01:00
from Channel import SVT, YouTube
from ChannelProvider import ChannelProvider
2022-01-07 18:16:13 +01:00
from Utils import get_subscriptions, import_from_newpipe, make_sized_button
2021-12-07 09:51:53 +01:00
2022-01-07 18:16:13 +01:00
WIDTH = int(720 / 2)
HEIGHT = int(1440 / 2)
2022-01-06 13:31:45 +01:00
BTN_HEIGHT = 40
SPACER_HEIGHT = 10
2022-01-06 17:59:24 +01:00
SCROLL_RATE = 5
2022-01-06 13:31:45 +01:00
2022-01-06 15:34:07 +01:00
BM_BTN_STYLE = wx.BOTTOM | wx.EXPAND | wx.LEFT | wx.TOP
2022-01-06 16:08:10 +01:00
BTN_STYLE = wx.BORDER_NONE | wx.BU_AUTODRAW | wx.BU_EXACTFIT | wx.BU_NOTEXT
2022-01-06 15:34:07 +01:00
2021-12-07 09:51:53 +01:00
class Cast(wx.Frame):
def __init__(self, *args, **kw):
"""__init__.
:param args:
:param kw:
"""
super().__init__(*args, **kw)
2021-12-19 12:55:59 +01:00
self.m_selected_chromecast = None
2022-01-06 13:31:45 +01:00
self.SetSizeHints(WIDTH, HEIGHT, maxW=WIDTH)
self.m_index = 0
self.m_chromecast_thr = threading.Thread(target=self.get_chromecasts,
args=(),
kwargs={})
2021-12-13 17:22:42 +01:00
self.m_chromecast_thr.start()
self.m_sizer: wx.Sizer = wx.BoxSizer(wx.VERTICAL)
self.m_panel: wx.lib.scrolledpanel.ScrolledPanel = scrolled.ScrolledPanel( # type: ignore
self, -1, style=wx.VSCROLL)
self.m_control = None
self.m_panel.SetupScrolling(rate_y=SCROLL_RATE, scrollToTop=True)
self.m_panel.SetSizer(self.m_sizer)
self.m_providers: list[ChannelProvider] = self.get_providers()
2021-12-22 17:05:53 +01:00
self.m_selected_channel = None
self.m_selected_provider_index = None
self.show_provider_list(None)
2021-12-22 17:05:53 +01:00
2022-01-06 15:34:07 +01:00
def add_back_button(self, callback: Callable) -> None:
backbtn = wx.Button(self.m_panel,
-1,
label="Go back",
size=(WIDTH, BTN_HEIGHT))
backbtn.Bind(wx.EVT_BUTTON, callback)
2022-01-06 15:34:07 +01:00
self.m_sizer.Add(backbtn)
2021-12-22 17:05:53 +01:00
def get_chromecasts(self) -> None:
"""
[TODO:description]
2021-12-22 17:05:53 +01:00
:rtype None: [TODO:description]
"""
2021-12-13 17:22:42 +01:00
self.m_chromecasts, self.m_browser = pychromecast.get_chromecasts()
def get_providers(self) -> list[ChannelProvider]:
providers = list()
2022-01-08 21:21:34 +01:00
channels = list()
svt = ChannelProvider(
"SVT",
channels=[
SVT.SVT("feed"),
SVT.SVT("ch-svt1"),
SVT.SVT("ch-svt2"),
SVT.SVT("ch-svt24"),
SVT.SVT("ch-barnkanalen"),
SVT.SVT("ch-kunskapskanalen"),
],
)
providers.append(svt)
2022-01-08 21:21:34 +01:00
subscriptions = get_subscriptions()
if subscriptions:
2022-01-07 18:16:13 +01:00
for channel in subscriptions:
2022-01-08 21:21:34 +01:00
channels.append(YouTube.YouTube(channel[0], channel[1]))
else:
channels.append(YouTube.YouTube("UCs6A_0Jm21SIvpdKyg9Gmxw", "Pine 64"))
youtube = ChannelProvider("YouTube", channels=channels)
providers.append(youtube)
return providers
def show_provider_list(self, _) -> None:
2021-12-22 17:05:53 +01:00
self.m_sizer.Clear(delete_windows=True)
self.m_sizer = wx.BoxSizer(wx.VERTICAL)
2022-01-08 21:21:34 +01:00
# self.m_sizer.AddSpacer(SPACER_HEIGHT * 4)
closebtn = wx.Button(self.m_panel,
-1,
label="Close",
size=(WIDTH, BTN_HEIGHT))
2022-01-06 16:08:10 +01:00
closebtn.Bind(wx.EVT_BUTTON, lambda event: self.Destroy())
self.m_sizer.Add(closebtn)
provider_index = 0
2021-12-22 17:05:53 +01:00
for provider in self.m_providers:
bitmap = provider.get_logo_as_bitmap()
callback = lambda event, index=provider_index: self.show_channel_list(
event, index)
btn_sizer: wx.BoxSizer = make_sized_button(self.m_panel, bitmap,
provider.get_name(),
callback)
self.m_sizer.Add(btn_sizer)
provider_index += 1
self.m_panel.SetupScrolling(rate_y=SCROLL_RATE, scrollToTop=True)
self.m_panel.SetSizer(self.m_sizer)
2022-01-06 16:08:10 +01:00
self.m_sizer.Fit(self)
self.m_sizer.Layout()
def show_channel_list(self, _, provider_index) -> None:
self.m_selected_provider_index = provider_index
self.m_selected_provider = self.m_providers[provider_index]
self.m_sizer.Clear(delete_windows=True)
self.m_sizer = wx.BoxSizer(wx.VERTICAL)
2022-01-08 21:21:34 +01:00
#self.m_sizer.AddSpacer(SPACER_HEIGHT * 4)
2022-01-06 15:34:07 +01:00
bck_callback = lambda event: self.show_provider_list(event)
self.add_back_button(bck_callback)
channel_index = 0
for channel in self.m_selected_provider.get_channels():
bitmap = channel.get_logo_as_bitmap()
callback = lambda event, index=channel_index: self.show_video_list(
event, index)
btn_sizer: wx.BoxSizer = make_sized_button(self.m_panel, bitmap,
channel.get_name(),
callback)
self.m_sizer.Add(btn_sizer)
2021-12-22 17:05:53 +01:00
channel_index += 1
self.m_panel.SetupScrolling(rate_y=SCROLL_RATE, scrollToTop=True)
2021-12-22 17:05:53 +01:00
self.m_panel.SetSizer(self.m_sizer)
2022-01-06 16:08:10 +01:00
self.m_sizer.Fit(self)
self.m_sizer.Layout()
2021-12-22 17:05:53 +01:00
def show_video_list(self, _, index=0) -> None:
self.m_sizer.Clear(delete_windows=True)
self.m_sizer = wx.BoxSizer(wx.VERTICAL)
2022-01-08 21:21:34 +01:00
# self.m_sizer.AddSpacer(SPACER_HEIGHT * 4)
channel = self.m_selected_provider.get_channel_by_index(index)
2021-12-22 17:05:53 +01:00
if channel.wait():
with wx.BusyInfo("Please wait, working..."):
index = 0
2021-12-22 17:05:53 +01:00
while channel.wait():
time.sleep(1)
wx.GetApp().Yield()
callback = lambda event: self.show_channel_list(
event, self.m_selected_provider_index)
2022-01-06 15:34:07 +01:00
self.add_back_button(callback)
for item in channel.get_items(): # type: ignore
2022-01-06 15:34:07 +01:00
inner_sizer = wx.BoxSizer(wx.VERTICAL)
2021-12-22 17:05:53 +01:00
title = wx.StaticText(self.m_panel, -1)
title.SetLabelMarkup("<span weight='bold' >{}</span>".format(
item["title"]))
2021-12-31 15:12:02 +01:00
description = wx.StaticText(self.m_panel, -1, item["description"])
2021-12-22 17:05:53 +01:00
description.Wrap(WIDTH - 2)
2021-12-31 15:12:02 +01:00
bitmap = item["thumbnail"]
btn = wx.BitmapButton(self.m_panel,
id=self.m_index,
bitmap=bitmap,
style=BM_BTN_STYLE)
2021-12-31 15:12:02 +01:00
btn.Bind(
wx.EVT_BUTTON,
lambda event, link=item["link"], provider_index=index: self.
show_player(event, link, provider_index),
2021-12-31 15:12:02 +01:00
)
2022-01-06 15:34:07 +01:00
inner_sizer.Add(title)
inner_sizer.Add(btn)
inner_sizer.Add(description)
self.m_sizer.Add(inner_sizer)
2022-01-06 13:31:45 +01:00
self.m_sizer.AddSpacer(SPACER_HEIGHT)
2021-12-22 17:05:53 +01:00
self.m_index = self.m_index + 1
self.m_panel.SetupScrolling(rate_y=SCROLL_RATE, scrollToTop=True)
self.m_panel.SetSizer(self.m_sizer)
2022-01-06 16:08:10 +01:00
self.m_sizer.Fit(self)
self.m_sizer.Layout()
2021-12-22 17:05:53 +01:00
def show_player(self, _, uri, provider_index: int):
"""
Show the video player
:param _ event: unused
:param uri str: the link to the video stream
"""
self.m_sizer.Clear(delete_windows=True)
2022-01-06 17:34:35 +01:00
self.m_sizer = wx.BoxSizer(wx.VERTICAL)
2022-01-08 21:21:34 +01:00
# self.m_sizer.AddSpacer(SPACER_HEIGHT * 4)
2022-01-06 17:34:35 +01:00
inner_sizer = wx.GridBagSizer()
2021-12-07 09:51:53 +01:00
self.m_control = wx.media.MediaCtrl(
self.m_panel,
2022-01-07 18:16:13 +01:00
size=(WIDTH, int(HEIGHT / 2)),
style=wx.SIMPLE_BORDER,
2021-12-31 15:12:02 +01:00
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")
2021-12-31 15:12:02 +01:00
back_button.Bind(
wx.EVT_BUTTON,
lambda event, index=provider_index: self.show_video_list(
event, index),
2021-12-31 15:12:02 +01:00
)
2022-01-06 17:34:35 +01:00
inner_sizer.Add(self.m_control, (0, 0))
inner_sizer.SetItemSpan(0, (0, 6))
inner_sizer.Add(play_button, (1, 1))
inner_sizer.Add(pause_button, (1, 2))
inner_sizer.Add(back_button, (1, 3))
if not self.m_chromecast_thr.is_alive(
) and not self.m_selected_chromecast:
2021-12-07 22:19:20 +01:00
chromecast_button = wx.Button(self.m_panel, -1, "Cast")
chromecast_button.Bind(
wx.EVT_BUTTON,
lambda event, muri=uri, index=provider_index: self.
select_chromecast(event, muri, index),
2021-12-31 15:12:02 +01:00
)
2022-01-06 17:34:35 +01:00
inner_sizer.Add(chromecast_button, (2, 2))
self.m_sizer.Add(inner_sizer)
2021-12-07 22:19:20 +01:00
if self.m_selected_chromecast:
2021-12-31 15:12:02 +01:00
self.Bind(
wx.media.EVT_MEDIA_LOADED,
lambda event, muri=uri: self.cast(event, muri),
)
2021-12-07 22:19:20 +01:00
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_video_list)
self.load_uri(uri)
self.m_panel.SetupScrolling(rate_y=SCROLL_RATE, scrollToTop=True)
self.m_panel.SetSizer(self.m_sizer)
2022-01-06 16:08:10 +01:00
self.m_sizer.Fit(self)
self.m_sizer.Layout()
self.m_panel.ScrollChildIntoView(self.m_control)
def select_chromecast(self, _, uri, provider_index):
2021-12-07 22:19:20 +01:00
self.m_sizer.Clear(delete_windows=True)
self.m_sizer = wx.BoxSizer(wx.VERTICAL)
2022-01-08 21:21:34 +01:00
# self.m_sizer.AddSpacer(SPACER_HEIGHT * 4)
2021-12-07 22:19:20 +01:00
cancel_btn = wx.Button(self.m_panel,
-1,
"Cancel",
size=(WIDTH, BTN_HEIGHT),
style=wx.BU_EXACTFIT)
2022-01-06 15:34:07 +01:00
cancel_btn.Bind(
wx.EVT_BUTTON,
lambda event, index=provider_index: self.show_video_list(
event, index),
2022-01-06 15:34:07 +01:00
)
self.m_sizer.Add(cancel_btn) #, wx.ALIGN_CENTER_VERTICAL)
2021-12-07 22:19:20 +01:00
for cast in self.m_chromecasts:
friendly_name = cast.cast_info.friendly_name
btn = wx.Button(self.m_panel,
id=-1,
label=friendly_name,
size=(WIDTH, BTN_HEIGHT))
2021-12-28 17:55:24 +01:00
btn.Bind(
wx.EVT_BUTTON,
lambda event, chromecast=cast, muri=uri, index=provider_index:
self.set_chromecast(event, chromecast, muri, index),
2021-12-31 15:12:02 +01:00
)
self.m_sizer.Add(btn) #, wx.ALIGN_CENTER_VERTICAL)
self.m_panel.SetupScrolling(rate_y=SCROLL_RATE, scrollToTop=True)
2021-12-07 22:19:20 +01:00
self.m_panel.SetSizer(self.m_sizer)
2022-01-06 16:08:10 +01:00
self.m_sizer.Fit(self)
self.m_sizer.Layout()
2021-12-07 22:19:20 +01:00
def set_chromecast(self, event, chromecast, uri, provider_index):
2021-12-07 22:19:20 +01:00
self.m_selected_chromecast = chromecast
self.show_player(event, uri, provider_index)
2021-12-07 22:19:20 +01:00
2022-01-06 15:34:07 +01:00
def cast(self, _, uri):
2021-12-31 15:12:02 +01:00
mimetype = "video/mp4"
2021-12-07 22:19:20 +01:00
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
2021-12-13 17:22:42 +01:00
2021-12-07 22:19:20 +01:00
break
if (cast.socket_client.is_connected and has_played
and player_state != "PLAYING"):
2021-12-07 22:19:20 +01:00
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):
2021-12-07 09:51:53 +01:00
self.m_control.LoadURI(uri)
def play(self, _):
2021-12-07 09:51:53 +01:00
self.m_control.Play()
def pause(self, _):
2021-12-07 09:51:53 +01:00
self.m_control.Pause()
def quit(self, _):
2021-12-07 09:51:53 +01:00
self.Destroy()
2021-12-31 15:12:02 +01:00
if __name__ == "__main__":
2021-12-07 09:51:53 +01:00
# 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()
2021-12-31 15:12:02 +01:00
frm: Cast = Cast(None, title="Cast")
2021-12-07 09:51:53 +01:00
frm.Show()
app.MainLoop()