diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..57d80c8 --- /dev/null +++ b/install.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +if [[ "${1}" == "-u" ]]; then + echo "Uninstalling cast" + sudo rm /usr/local/bin/cast /usr/share/applications/cast.desktop + echo "If you wish you can now manually remove the dependencies" + exit 0 +elif [[ "${1}" == "-h" ]]; then + echo "Usage: $0 [-u|-h] + -h show this message + -u uninstall cast" + exit 0 +fi + + sudo apt install python3-pip \ + python3-bs4 \ + gir1.2-gstreamer-1.0 \ + gstreamer1.0-alsa \ + gstreamer1.0-clutter-3.0 \ + gstreamer1.0-gl \ + gstreamer1.0-gtk3 \ + gstreamer1.0-libav \ + gstreamer1.0-nice \ + gstreamer1.0-packagekit \ + gstreamer1.0-pipewire \ + gstreamer1.0-plugins-bad \ + gstreamer1.0-plugins-base \ + gstreamer1.0-plugins-base-apps \ + gstreamer1.0-plugins-good \ + gstreamer1.0-plugins-rtp \ + gstreamer1.0-plugins-ugly \ + gstreamer1.0-pulseaudio \ + gstreamer1.0-tools \ + gstreamer1.0-x \ + libgstreamer-gl1.0-0 \ + libgstreamer-plugins-bad1.0-0 \ + libgstreamer-plugins-base1.0-0 \ + libgstreamer-plugins-good1.0-0 \ + libgstreamer1.0-0 \ + python3-wxgtk-media4.0 \ + python3-wxgtk4.0 \ + python3-feedparser \ + youtube-dl +sudo pip3 install pychromecast +sudo cp src/main.py /usr/local/bin/cast +sudo cp -a src/{Channel,ChannelProvider,Items} /usr/lib/python3.9/site-packages/ +sudo cp -a src/{Channel,ChannelProvider,Items} /usr/lib/python3.9/ +sudo cp src/cast.desktop /usr/share/applications/ +exit 0 diff --git a/Channel/SVT/__init__.py b/src/Channel/SVT/__init__.py similarity index 100% rename from Channel/SVT/__init__.py rename to src/Channel/SVT/__init__.py diff --git a/Channel/YouTube/__init__.py b/src/Channel/YouTube/__init__.py similarity index 100% rename from Channel/YouTube/__init__.py rename to src/Channel/YouTube/__init__.py diff --git a/Channel/__init__.py b/src/Channel/__init__.py similarity index 100% rename from Channel/__init__.py rename to src/Channel/__init__.py diff --git a/ChannelProvider/__init__.py b/src/ChannelProvider/__init__.py similarity index 100% rename from ChannelProvider/__init__.py rename to src/ChannelProvider/__init__.py diff --git a/Items/__init__.py b/src/Items/__init__.py similarity index 100% rename from Items/__init__.py rename to src/Items/__init__.py diff --git a/src/cast.desktop b/src/cast.desktop new file mode 100644 index 0000000..5737a92 --- /dev/null +++ b/src/cast.desktop @@ -0,0 +1,11 @@ +[Desktop Entry] +Type=Application +Name=Cast +Version=0.0.1 +GenericName=Video Player +Comment=Video Player +Exec=/usr/local/bin/cast +Icon=video-single-display-symbolic +Terminal=false +Categories=Security;Utility; +Keywords=YouTube;SVT; diff --git a/main.py b/src/main.py similarity index 71% rename from main.py rename to src/main.py index cc2a31f..2d16902 100644 --- a/main.py +++ b/src/main.py @@ -22,31 +22,36 @@ class Cast(wx.Frame): 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 = 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, -1, style=wx.VSCROLL + ) self.m_control = None self.m_panel.SetupScrolling() self.m_panel.SetSizer(self.m_sizer) self.m_providers: list[ChannelProvider] = [ - ChannelProvider('SVT', - channels=[ - SVT.SVT('feed'), - SVT.SVT('ch-svt1'), - SVT.SVT('ch-svt2'), - SVT.SVT('ch-svt24') - ]), - ChannelProvider('YouTube', - channels=[ - YouTube.YouTube('UCtESv1e7ntJaLJYKIO1FoYw'), - YouTube.YouTube('UC9-y-6csu5WGm29I7JiwpnA'), - YouTube.YouTube('UCoxcjq-8xIDTYp3uz647V5A'), - YouTube.YouTube('UCu6mSoMNzHQiBIOCkHUa2Aw') - ]) + ChannelProvider( + "SVT", + channels=[ + SVT.SVT("feed"), + SVT.SVT("ch-svt1"), + SVT.SVT("ch-svt2"), + SVT.SVT("ch-svt24"), + ], + ), + ChannelProvider( + "YouTube", + channels=[ + YouTube.YouTube("UCtESv1e7ntJaLJYKIO1FoYw"), + YouTube.YouTube("UC9-y-6csu5WGm29I7JiwpnA"), + YouTube.YouTube("UCoxcjq-8xIDTYp3uz647V5A"), + YouTube.YouTube("UCu6mSoMNzHQiBIOCkHUa2Aw"), + ], + ), ] self.m_selected_channel = None self.m_selected_provider_index = None @@ -66,12 +71,13 @@ class Cast(wx.Frame): provider_index = 0 for provider in self.m_providers: bitmap = provider.get_logo_as_bitmap() - btn = wx.BitmapButton(self.m_panel, - id=provider_index, - bitmap=bitmap) - btn.Bind(wx.EVT_BUTTON, - lambda event, index=provider_index: self. - show_channel_list(event, index)) + btn = wx.BitmapButton(self.m_panel, id=provider_index, bitmap=bitmap) + btn.Bind( + wx.EVT_BUTTON, + lambda event, index=provider_index: self.show_channel_list( + event, index + ), + ) self.m_sizer.Add(btn) provider_index += 1 @@ -85,19 +91,17 @@ class Cast(wx.Frame): self.m_sizer.Clear(delete_windows=True) self.m_sizer = wx.BoxSizer(wx.VERTICAL) backbtn = wx.Button(self.m_panel, -1, label="Go back", size=(480, 40)) - backbtn.Bind(wx.EVT_BUTTON, - lambda event: self.show_provider_list(event)) + backbtn.Bind(wx.EVT_BUTTON, lambda event: self.show_provider_list(event)) self.m_sizer.Add(backbtn, wx.ALIGN_CENTER_VERTICAL) self.m_sizer.AddSpacer(10) channel_index = 0 for channel in self.m_selected_provider.get_channels(): bitmap = channel.get_logo_as_bitmap() - btn = wx.BitmapButton(self.m_panel, - id=channel_index, - bitmap=bitmap) - btn.Bind(wx.EVT_BUTTON, - lambda event, index=channel_index: self.show_video_list( - event, index)) + btn = wx.BitmapButton(self.m_panel, id=channel_index, bitmap=bitmap) + btn.Bind( + wx.EVT_BUTTON, + lambda event, index=channel_index: self.show_video_list(event, index), + ) self.m_sizer.Add(btn) channel_index += 1 @@ -125,22 +129,25 @@ class Cast(wx.Frame): backbtn = wx.Button(self.m_panel, -1, label="Go back", size=(480, 40)) backbtn.Bind( - wx.EVT_BUTTON, lambda event: self.show_channel_list( - event, self.m_selected_provider_index)) + wx.EVT_BUTTON, + lambda event: self.show_channel_list(event, self.m_selected_provider_index), + ) self.m_sizer.Add(backbtn, wx.ALIGN_CENTER_VERTICAL) self.m_sizer.AddSpacer(10) 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']) + title.SetLabelMarkup("{}".format(item["title"])) + description = wx.StaticText(self.m_panel, -1, item["description"]) description.Wrap(478) - bitmap = item['thumbnail'] + 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'], provider_index=index: - self.show_player(event, link, provider_index)) + btn.Bind( + wx.EVT_BUTTON, + lambda event, link=item["link"], provider_index=index: self.show_player( + event, link, provider_index + ), + ) self.m_sizer.Add(title) self.m_sizer.Add(btn) self.m_sizer.Add(description) @@ -163,15 +170,17 @@ class Cast(wx.Frame): self.m_panel, size=(480, 480), style=wx.SIMPLE_BORDER, - szBackend=wx.media.MEDIABACKEND_GSTREAMER) + 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, - lambda event, index=provider_index: self. - show_video_list(event, index)) + back_button.Bind( + wx.EVT_BUTTON, + lambda event, index=provider_index: self.show_video_list(event, index), + ) self.m_sizer.Add(self.m_control, (0, 0)) self.m_sizer.SetItemSpan(0, (0, 6)) @@ -179,18 +188,21 @@ class Cast(wx.Frame): 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: + 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, index=provider_index: self. - select_chromecast(event, muri, index)) + lambda event, muri=uri, index=provider_index: self.select_chromecast( + event, muri, index + ), + ) 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)) + 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: @@ -211,14 +223,13 @@ class Cast(wx.Frame): 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=(480, 40)) + btn = wx.Button(self.m_panel, id=-1, label=friendly_name, size=(480, 40)) btn.Bind( wx.EVT_BUTTON, - lambda event, chromecast=cast, muri=uri, index=provider_index: - self.set_chromecast(event, chromecast, muri, index)) + lambda event, chromecast=cast, muri=uri, index=provider_index: self.set_chromecast( + event, chromecast, muri, index + ), + ) self.m_sizer.Add(btn, wx.ALIGN_CENTER_VERTICAL) self.m_panel.SetSizer(self.m_sizer) self.m_sizer.Fit(self) @@ -229,7 +240,7 @@ class Cast(wx.Frame): self.show_player(event, uri, provider_index) def cast(self, event, uri): - mimetype = 'video/mp4' + mimetype = "video/mp4" cast = self.m_selected_chromecast cast.wait() cast.media_controller.play_media(uri, mimetype) @@ -247,7 +258,11 @@ class Cast(wx.Frame): break - if cast.socket_client.is_connected and has_played and player_state != "PLAYING": + if ( + cast.socket_client.is_connected + and has_played + and player_state != "PLAYING" + ): has_played = False cast.media_controller.play_media(uri, mimetype) @@ -275,10 +290,10 @@ class Cast(wx.Frame): self.Destroy() -if __name__ == '__main__': +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: Cast = Cast(None, title="Cast") frm.Show() app.MainLoop()