@ -1,5 +1,4 @@
#!/usr/bin/env python3
import os
import threading
import time
from typing import Callable
@ -8,13 +7,11 @@ import pychromecast
import wx
import wx . lib . scrolledpanel as scrolled
import wx . media
from youtubesearchpython import ChannelsSearch
from vlc import Instance
from Channel import SVT , YouTube
from Channel import SVT
from ChannelProvider import ChannelProvider
from Utils import ( get_default_logo , get_subscriptions , import_from_newpipe ,
make_bitmap_from_file , make_bitmap_from_url ,
make_sized_button , resolve_youtube_link )
from Utils import MYPATH , make_bitmap_from_file , make_sized_button
WIDTH = int ( 720 / 2 )
HEIGHT = int ( 1440 / 2 )
@ -42,15 +39,17 @@ class Cast(wx.Frame):
self . m_chromecast_thr = threading . Thread ( target = self . get_chromecasts ,
args = ( ) ,
kwargs = { } )
self . m_vlc = Instance ( )
self . m_vlc_medialist = self . m_vlc . media_list_new ( )
self . m_vlc_listplayer = self . m_vlc . media_list_player_new ( )
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 ( )
self . show_ provider_list( None )
self . show_ channel_list( None , 0 )
def add_back_button ( self , callback : Callable ) - > None :
backbtn = wx . Button ( self . m_panel ,
@ -60,38 +59,6 @@ class Cast(wx.Frame):
backbtn . Bind ( wx . EVT_BUTTON , callback )
self . m_sizer . Add ( backbtn )
def add_youtube_buttons ( self ) - > None :
def search_callback ( event , text : wx . TextCtrl ) - > None :
reply = text . GetLineText ( 0 )
text . Clear ( )
channels_search = ChannelsSearch (
reply , limit = 1 ) . result ( ) [ ' result ' ] [ 0 ] #type: ignore
if ' id ' in channels_search :
found_channel = YouTube . YouTube (
channels_search [ ' id ' ] ,
channels_search [ ' title ' ] ,
logo = make_bitmap_from_url (
' https: ' + channels_search [ ' thumbnails ' ] [ 0 ] [ ' url ' ] ,
size = wx . Size ( 68 , 100 ) ) ) #type: ignore
self . m_selected_provider . append_channel ( found_channel )
text : wx . TextCtrl = wx . TextCtrl ( self . m_panel , size = ( WIDTH , BTN_HEIGHT ) )
self . m_sizer . Add ( text )
searchbtn = wx . Button ( self . m_panel ,
- 1 ,
label = " Search " ,
size = ( WIDTH , BTN_HEIGHT ) )
self . m_sizer . Add ( searchbtn )
searchbtn . Bind (
wx . EVT_BUTTON ,
lambda event , textctl = text : search_callback ( event , textctl ) )
importbtn = wx . Button ( self . m_panel ,
- 1 ,
label = " Import from NewPipe " ,
size = ( WIDTH , BTN_HEIGHT ) )
importbtn . Bind ( wx . EVT_BUTTON , lambda event : self . show_importer ( event ) )
self . m_sizer . Add ( importbtn )
def get_chromecasts ( self ) - > None :
"""
[ TODO : description ]
@ -122,44 +89,45 @@ class Cast(wx.Frame):
event , cindex ) ,
)
stop_button = wx . Button ( self . m_panel ,
- 1 ,
" Stop " ,
size = ( WIDTH / 4 , BTN_HEIGHT ) )
inner_sizer . Add ( play_button , FLAGS )
inner_sizer . Add ( pause_button , FLAGS )
inner_sizer . Add ( back_button , FLAGS )
inner_sizer . Add ( stop_button , FLAGS )
if not self . m_chromecast_thr . is_alive (
) and not self . m_selected_chromecast :
chromecast_button = wx . Button ( self . m_panel ,
btm = make_bitmap_from_file ( ' {} /assets/Cast.png ' . format ( MYPATH ) , wx . Size ( 24 , 24 ) )
cast_button = wx . BitmapButton ( self . m_panel ,
- 1 ,
" Cast " ,
bitmap = btm ,
size = ( WIDTH / 4 , BTN_HEIGHT ) )
c hromec ast_button. Bind (
c ast_button. Bind (
wx . EVT_BUTTON ,
lambda event , muri = uri , cindex = channel_index : self .
select_chromecast ( event , muri , cindex ) ,
)
inner_sizer . Add ( chromecast_button , FLAGS )
elif self . m_selected_chromecast :
inner_sizer . Add ( cast_button , FLAGS )
chromecast_button = wx . Button ( self . m_panel ,
- 1 ,
" Stop Cast " ,
size = ( WIDTH / 4 , BTN_HEIGHT ) )
chromecast_button . Bind (
wx . EVT_BUTTON ,
lambda event , muri = uri , cindex = channel_index : self .
stop_callback ( event , muri , cindex ) ,
)
inner_sizer . Add ( chromecast_button , FLAGS )
if self . m_selected_chromecast :
self . cast ( wx . media . EVT_MEDIA_LOADED , uri ) ,
play_button . Bind ( wx . EVT_BUTTON , self . play_cast )
pause_button . Bind ( wx . EVT_BUTTON , self . pause_cast )
stop_button . Bind (
wx . EVT_BUTTON ,
lambda event , muri = uri , cindex = channel_index : self .
stop_callback ( event , muri , cindex ) ,
)
else :
self . Bind ( wx . media . EVT_MEDIA_LOADED ,
lambda event : wx . Frame . SetFocus ( self ) )
play_button . Bind ( wx . EVT_BUTTON , self . play )
pause_button . Bind ( wx . EVT_BUTTON , self . pause )
stop_button . Bind ( wx . EVT_BUTTON , self . stop )
inner_sizer . Fit ( self )
inner_sizer . Layout ( )
@ -168,7 +136,6 @@ class Cast(wx.Frame):
def get_providers ( self ) - > list [ ChannelProvider ] :
providers = list ( )
channels = list ( )
svt = ChannelProvider (
" SVT " ,
channels = [
@ -182,87 +149,21 @@ class Cast(wx.Frame):
)
providers . append ( svt )
subscriptions = get_subscriptions ( )
if subscriptions :
for channel in subscriptions :
logo = make_bitmap_from_file ( channel [ 2 ] )
channels . append ( YouTube . YouTube ( channel [ 0 ] , channel [ 1 ] , logo ) )
else :
logo = make_bitmap_from_url (
' https://yt3.ggpht.com/ytc/AKedOLQ5L9xUSDxB2j6V3VC8L_HEwiKeHM21CgbSUyqe=s88-c-k-c0x00ffffff-no-rj '
)
channels . append (
YouTube . YouTube ( " UCs6A_0Jm21SIvpdKyg9Gmxw " , " Pine 64 " , logo ) )
youtube = ChannelProvider ( " YouTube " , channels = channels )
providers . append ( youtube )
return providers
def show_importer ( self , _ ) - > None :
with wx . FileDialog ( self ,
" Open Newpipe json file " ,
wildcard = " Json files (*.json)|*.json " ,
style = wx . FD_OPEN
| wx . FD_FILE_MUST_EXIST ) as file_dialog :
if file_dialog . ShowModal ( ) == wx . ID_CANCEL :
return # the user changed their mind
# Proceed loading the file chosen by the user
subfile = file_dialog . GetPath ( )
channels = list ( )
logo = get_default_logo ( ' YouTube ' )
if os . path . isfile ( subfile ) :
import_from_newpipe ( subfile )
subscriptions = get_subscriptions ( )
for channel in subscriptions :
yt_chan = YouTube . YouTube ( channel [ 0 ] , channel [ 1 ] , logo )
yt_chan . refresh ( )
channels . append ( yt_chan )
# Index 1 is YouTube
self . m_providers [ 1 ] . set_channels ( channels )
self . m_providers [ 1 ] . make_latest ( )
self . show_channel_list ( None , self . m_selected_provider_index )
def show_provider_list ( self , _ ) - > None :
self . m_sizer . Clear ( delete_windows = True )
self . m_sizer = wx . BoxSizer ( wx . VERTICAL )
closebtn = wx . Button ( self . m_panel ,
- 1 ,
label = " Close " ,
size = ( WIDTH , BTN_HEIGHT ) )
closebtn . Bind ( wx . EVT_BUTTON , lambda event : self . Destroy ( ) )
self . m_sizer . Add ( closebtn , 0 , wx . ALL , 1 )
provider_index = 0
for provider in self . m_providers :
bitmap = provider . get_logo_as_bitmap ( )
callback = lambda event , pindex = provider_index : self . show_channel_list (
event , pindex )
btn_sizer : wx . BoxSizer = make_sized_button ( self . m_panel , bitmap ,
provider . get_name ( ) ,
callback )
self . m_sizer . Add ( btn_sizer , 0 , wx . ALL , 1 )
provider_index + = 1
self . m_panel . SetupScrolling ( rate_y = SCROLL_RATE , scrollToTop = True )
self . m_panel . SetSizer ( self . m_sizer )
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 )
#self.m_sizer.AddSpacer(SPACER_HEIGHT * 4)
bck_callback = lambda event : self . show_provider_list ( event )
self . add_back_button ( bck_callback )
if self . m_selected_provider . get_name ( ) == " YouTube " :
self . add_youtube_buttons ( )
closebtn = wx . Button ( self . m_panel ,
- 1 ,
label = " Close " ,
size = ( WIDTH , BTN_HEIGHT ) )
closebtn . Bind ( wx . EVT_BUTTON , lambda event : self . Destroy ( ) )
self . m_sizer . Add ( closebtn , 0 , wx . ALL , 1 )
channel_index = 0
@ -290,27 +191,10 @@ class Cast(wx.Frame):
back_callback = lambda event : self . show_channel_list (
event , self . m_selected_provider_index )
self . add_back_button ( back_callback )
if self . m_selected_provider . get_name ( ) == ' YouTube ' or (
self . m_selected_provider . get_name ( ) == ' SVT '
and self . m_selected_channel . get_id ( ) == ' feed ' ) :
if self . m_selected_provider . get_name ( ) == ' SVT ' and self . m_selected_channel . get_id ( ) == ' feed ' :
def refresh_callback ( event ) :
if self . m_selected_provider . get_name (
) == ' YouTube ' and channel_index == 0 :
with wx . BusyInfo ( " Please wait, working... " ) :
for chan in self . m_selected_provider . get_channels ( ) :
chan . refresh ( )
wait = 1
while wait > 0 :
wait = 0
for chan in self . m_selected_provider . get_channels (
) :
if chan . wait ( ) :
wait + = 1
time . sleep ( 1 )
wx . GetApp ( ) . Yield ( )
else :
self . m_selected_channel . refresh ( )
self . m_selected_channel . refresh ( )
self . show_video_list ( event , channel_index )
refreshbtn = wx . Button ( self . m_panel ,
@ -386,22 +270,12 @@ class Cast(wx.Frame):
: param _ event : unused
: param uri str : the link to the video stream
"""
if ' youtube ' in uri :
uri = resolve_youtube_link ( uri )
media = self . m_vlc . media_new ( uri )
self . m_vlc_medialist . add_media ( media )
self . m_vlc_listplayer . set_media_list ( self . m_vlc_medialist )
self . m_sizer . Clear ( delete_windows = True )
self . m_sizer = wx . BoxSizer ( wx . VERTICAL )
if not self . m_selected_chromecast :
self . m_control = wx . media . MediaCtrl (
self . m_panel ,
size = ( WIDTH , HEIGHT / 2 ) ,
szBackend = wx . media . MEDIABACKEND_GSTREAMER ,
)
self . m_sizer . Add ( self . m_control , FLAGS )
self . Bind ( wx . media . EVT_MEDIA_FINISHED ,
lambda event : self . show_video_list ( event , 0 ) )
self . Bind ( wx . EVT_POWER_SUSPENDING ,
lambda event : wx . EVT_POWER_SUSPENDING . Veto ( event ) )
self . load_uri ( uri )
self . m_sizer . Add ( self . get_player_controls ( channel_index , uri ) , FLAGS )
self . m_panel . SetupScrolling ( rate_y = SCROLL_RATE , scrollToTop = True )
self . m_panel . SetSizer ( self . m_sizer )
@ -424,7 +298,7 @@ class Cast(wx.Frame):
self . m_sizer . Add ( cancel_btn )
for cast in self . m_chromecasts :
friendly_name = cast . cast_info . friendly_name
friendly_name = cast . device . friendly_name
btn = wx . Button ( self . m_panel ,
id = - 1 ,
label = friendly_name ,
@ -498,14 +372,14 @@ class Cast(wx.Frame):
self . m_sizer . Fit ( self )
self . m_sizer . Layout ( )
def load_uri ( self , uri ) :
self . m_control . LoadURI ( uri )
def play ( self , _ ) :
self . m_ control. P lay( )
self . m_vlc_listplayer . play ( )
def pause ( self , _ ) :
self . m_control . Pause ( )
self . m_vlc_listplayer . pause ( )
def stop ( self , _ ) :
self . m_vlc_listplayer . stop ( )
def quit ( self , _ ) :
self . Destroy ( )