Add support for importing subscriptions from NewPipe
This commit is contained in:
parent
b0b05cbecc
commit
ed4ddf1d01
3 changed files with 148 additions and 99 deletions
|
@ -1,42 +1,46 @@
|
||||||
import hashlib
|
import hashlib
|
||||||
import io
|
|
||||||
import os
|
import os
|
||||||
import pickle
|
import pickle
|
||||||
import threading
|
import threading
|
||||||
|
import time
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
import feedparser
|
import feedparser
|
||||||
import requests
|
|
||||||
import wx
|
import wx
|
||||||
from bs4 import BeautifulSoup
|
|
||||||
from youtube_dl import YoutubeDL as yt
|
|
||||||
|
|
||||||
from Channel import Channel
|
from Channel import Channel
|
||||||
from Items import Item
|
from Items import Item
|
||||||
from Utils import get_default_logo, make_bitmap_from_url
|
from Utils import get_default_logo, make_bitmap_from_url
|
||||||
|
from youtube_dl import YoutubeDL as yt
|
||||||
|
from youtube_dl.utils import DownloadError, ExtractorError
|
||||||
|
|
||||||
|
|
||||||
class YouTube(Channel):
|
class YouTube(Channel):
|
||||||
m_cache: dict = dict()
|
m_cache: dict = dict()
|
||||||
m_cachefile = '/tmp/yt_cache'
|
|
||||||
|
|
||||||
def __init__(self, channel_id) -> None:
|
def __init__(self, channel_id: str, name: str) -> None:
|
||||||
self.m_channel_id = channel_id
|
self.m_channel_id = channel_id
|
||||||
self.m_info = get_info(channel_id)
|
self.m_name = name
|
||||||
rss_url = 'https://www.youtube.com/feeds/videos.xml?channel_id={}'.format(
|
rss_url = 'https://www.youtube.com/feeds/videos.xml?channel_id={}'.format(
|
||||||
channel_id)
|
channel_id)
|
||||||
|
self.m_cachefile = '/tmp/yt_cache_{}'.format(channel_id)
|
||||||
self.m_logo = get_default_logo('YouTube')
|
self.m_logo = get_default_logo('YouTube')
|
||||||
super().__init__(channel_id, rss_url, self.m_logo, self.m_info['title'])
|
super().__init__(channel_id, rss_url, self.m_logo,
|
||||||
|
self.m_name)
|
||||||
self.m_items: Union[list[Item], None] = None
|
self.m_items: Union[list[Item], None] = None
|
||||||
|
|
||||||
if os.path.exists(self.m_cachefile):
|
if os.path.exists(self.m_cachefile):
|
||||||
with open(self.m_cachefile, 'rb') as cachehandle:
|
with open(self.m_cachefile, 'rb') as cachehandle:
|
||||||
|
try:
|
||||||
self.m_cache = pickle.load(cachehandle)
|
self.m_cache = pickle.load(cachehandle)
|
||||||
|
except EOFError or pickle.UnpicklingError:
|
||||||
|
pass
|
||||||
|
|
||||||
self.m_thr = threading.Thread(target=self.parse_feed,
|
self.m_thr = threading.Thread(target=self.parse_feed,
|
||||||
args=(),
|
args=(),
|
||||||
kwargs={})
|
kwargs={})
|
||||||
self.m_thr.start()
|
self.m_thr.start()
|
||||||
|
|
||||||
|
self.pickle()
|
||||||
|
|
||||||
def wait(self) -> bool:
|
def wait(self) -> bool:
|
||||||
return self.m_thr.is_alive()
|
return self.m_thr.is_alive()
|
||||||
|
@ -68,6 +72,7 @@ class YouTube(Channel):
|
||||||
description = str(entry['description'])
|
description = str(entry['description'])
|
||||||
link = ''
|
link = ''
|
||||||
with yt(ydl_opts) as ydl:
|
with yt(ydl_opts) as ydl:
|
||||||
|
try:
|
||||||
video = ydl.extract_info(entry['link'], download=False)
|
video = ydl.extract_info(entry['link'], download=False)
|
||||||
|
|
||||||
for form in video['formats']:
|
for form in video['formats']:
|
||||||
|
@ -75,6 +80,8 @@ class YouTube(Channel):
|
||||||
if form['height'] < 480 and form[
|
if form['height'] < 480 and form[
|
||||||
'acodec'] != 'none':
|
'acodec'] != 'none':
|
||||||
link = form['url']
|
link = form['url']
|
||||||
|
except ExtractorError or DownloadError:
|
||||||
|
pass
|
||||||
|
|
||||||
resolved_link = link
|
resolved_link = link
|
||||||
|
|
||||||
|
@ -87,19 +94,16 @@ class YouTube(Channel):
|
||||||
self.m_cache[key]['description'] = description
|
self.m_cache[key]['description'] = description
|
||||||
self.m_cache[key]['published_parsed'] = published_parsed
|
self.m_cache[key]['published_parsed'] = published_parsed
|
||||||
self.m_cache[key]['title'] = title
|
self.m_cache[key]['title'] = title
|
||||||
thumbnail = make_bitmap_from_url(thumbnail_link, wx.Size(self.m_screen_width,150))
|
thumbnail = make_bitmap_from_url(thumbnail_link,
|
||||||
|
wx.Size(self.m_screen_width, 150))
|
||||||
item = Item(description, resolved_link, self.m_provider_name,
|
item = Item(description, resolved_link, self.m_provider_name,
|
||||||
published_parsed, thumbnail, title)
|
published_parsed, thumbnail, title)
|
||||||
self.m_items.append(item)
|
self.m_items.append(item)
|
||||||
|
|
||||||
|
def pickle(self) -> None:
|
||||||
|
while self.wait():
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
# write to cache file
|
# write to cache file
|
||||||
with open(self.m_cachefile, 'wb') as cachehandle:
|
with open(self.m_cachefile, 'wb') as cachehandle:
|
||||||
pickle.dump(self.m_cache, cachehandle)
|
pickle.dump(self.m_cache, cachehandle)
|
||||||
|
|
||||||
|
|
||||||
def get_info(channel_id: str) -> dict:
|
|
||||||
info: dict = {'title': 'unknown'}
|
|
||||||
link = 'https://www.youtube.com/channel/{}'.format(channel_id)
|
|
||||||
ydl_opts = {'extract_flat': True, 'skip_download': True}
|
|
||||||
with yt(ydl_opts) as ydl:
|
|
||||||
info = ydl.extract_info(link, download=False)
|
|
||||||
return info
|
|
||||||
|
|
|
@ -89,7 +89,7 @@ def resolve_svt_channel(svt_id: str) -> dict:
|
||||||
"SVT 24",
|
"SVT 24",
|
||||||
"thumbnail": make_bitmap_from_file('{}/assets/SVT24.png'.format(MYPATH))
|
"thumbnail": make_bitmap_from_file('{}/assets/SVT24.png'.format(MYPATH))
|
||||||
},
|
},
|
||||||
"kunskapskanalen": {
|
"ch-kunskapskanalen": {
|
||||||
"name":
|
"name":
|
||||||
"Kunskapskanalen",
|
"Kunskapskanalen",
|
||||||
"thumbnail": make_bitmap_from_file('{}/assets/Kunskapskanalen.png'.format(MYPATH))
|
"thumbnail": make_bitmap_from_file('{}/assets/Kunskapskanalen.png'.format(MYPATH))
|
||||||
|
|
167
src/main.py
167
src/main.py
|
@ -1,14 +1,16 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
import json
|
||||||
|
import os
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
|
from typing import Callable
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
import pychromecast
|
import pychromecast
|
||||||
import wx
|
import wx
|
||||||
import wx.lib.scrolledpanel as scrolled
|
import wx.lib.scrolledpanel as scrolled
|
||||||
import wx.media
|
import wx.media
|
||||||
|
|
||||||
from typing import Callable
|
|
||||||
|
|
||||||
from Channel import SVT, YouTube
|
from Channel import SVT, YouTube
|
||||||
from ChannelProvider import ChannelProvider
|
from ChannelProvider import ChannelProvider
|
||||||
from Utils import make_sized_button
|
from Utils import make_sized_button
|
||||||
|
@ -22,6 +24,7 @@ SCROLL_RATE = 5
|
||||||
BM_BTN_STYLE = wx.BOTTOM | wx.EXPAND | wx.LEFT | wx.TOP
|
BM_BTN_STYLE = wx.BOTTOM | wx.EXPAND | wx.LEFT | wx.TOP
|
||||||
BTN_STYLE = wx.BORDER_NONE | wx.BU_AUTODRAW | wx.BU_EXACTFIT | wx.BU_NOTEXT
|
BTN_STYLE = wx.BORDER_NONE | wx.BU_AUTODRAW | wx.BU_EXACTFIT | wx.BU_NOTEXT
|
||||||
|
|
||||||
|
|
||||||
class Cast(wx.Frame):
|
class Cast(wx.Frame):
|
||||||
def __init__(self, *args, **kw):
|
def __init__(self, *args, **kw):
|
||||||
"""__init__.
|
"""__init__.
|
||||||
|
@ -32,47 +35,27 @@ class Cast(wx.Frame):
|
||||||
self.m_selected_chromecast = None
|
self.m_selected_chromecast = None
|
||||||
self.SetSizeHints(WIDTH, HEIGHT, maxW=WIDTH)
|
self.SetSizeHints(WIDTH, HEIGHT, maxW=WIDTH)
|
||||||
self.m_index = 0
|
self.m_index = 0
|
||||||
self.m_chromecast_thr = threading.Thread(
|
self.m_chromecast_thr = threading.Thread(target=self.get_chromecasts,
|
||||||
target=self.get_chromecasts, args=(), kwargs={}
|
args=(),
|
||||||
)
|
kwargs={})
|
||||||
self.m_chromecast_thr.start()
|
self.m_chromecast_thr.start()
|
||||||
self.m_sizer: wx.Sizer = wx.BoxSizer(wx.VERTICAL)
|
self.m_sizer: wx.Sizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
self.m_panel: wx.lib.scrolledpanel.ScrolledPanel = scrolled.ScrolledPanel( # type: ignore
|
self.m_panel: wx.lib.scrolledpanel.ScrolledPanel = scrolled.ScrolledPanel( # type: ignore
|
||||||
self, -1, style=wx.VSCROLL
|
self, -1, style=wx.VSCROLL)
|
||||||
)
|
|
||||||
self.m_control = None
|
self.m_control = None
|
||||||
self.m_panel.SetupScrolling(rate_y=SCROLL_RATE, scrollToTop=True)
|
self.m_panel.SetupScrolling(rate_y=SCROLL_RATE, scrollToTop=True)
|
||||||
self.m_panel.SetSizer(self.m_sizer)
|
self.m_panel.SetSizer(self.m_sizer)
|
||||||
self.m_providers: list[ChannelProvider] = [
|
self.m_providers: list[ChannelProvider] = self.get_providers()
|
||||||
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_channel = None
|
||||||
self.m_selected_provider_index = None
|
self.m_selected_provider_index = None
|
||||||
self,
|
|
||||||
self.show_provider_list(None)
|
self.show_provider_list(None)
|
||||||
|
|
||||||
def add_back_button(self, callback: Callable) -> None:
|
def add_back_button(self, callback: Callable) -> None:
|
||||||
backbtn = wx.Button(self.m_panel, -1, label="Go back", size=(WIDTH, BTN_HEIGHT))
|
backbtn = wx.Button(self.m_panel,
|
||||||
backbtn.Bind(
|
-1,
|
||||||
wx.EVT_BUTTON, callback
|
label="Go back",
|
||||||
)
|
size=(WIDTH, BTN_HEIGHT))
|
||||||
|
backbtn.Bind(wx.EVT_BUTTON, callback)
|
||||||
self.m_sizer.Add(backbtn)
|
self.m_sizer.Add(backbtn)
|
||||||
|
|
||||||
def get_chromecasts(self) -> None:
|
def get_chromecasts(self) -> None:
|
||||||
|
@ -83,18 +66,64 @@ class Cast(wx.Frame):
|
||||||
"""
|
"""
|
||||||
self.m_chromecasts, self.m_browser = pychromecast.get_chromecasts()
|
self.m_chromecasts, self.m_browser = pychromecast.get_chromecasts()
|
||||||
|
|
||||||
|
def get_providers(self) -> list[ChannelProvider]:
|
||||||
|
|
||||||
|
providers = 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)
|
||||||
|
youtube = ChannelProvider(
|
||||||
|
"YouTube",
|
||||||
|
channels=[
|
||||||
|
YouTube.YouTube("UCs6A_0Jm21SIvpdKyg9Gmxw", "Pine 64"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
subfile = 'yt_subs.json'
|
||||||
|
|
||||||
|
if os.path.isfile(subfile):
|
||||||
|
with open(subfile, 'r') as subs:
|
||||||
|
janson = json.loads(subs.read())
|
||||||
|
|
||||||
|
for channel in janson['subscriptions']:
|
||||||
|
if channel['service_id'] == 0:
|
||||||
|
channel_id = urlparse(
|
||||||
|
channel['url']).path.split('/').pop()
|
||||||
|
youtube.append_channel(
|
||||||
|
YouTube.YouTube(channel_id, channel['name']))
|
||||||
|
|
||||||
|
providers.append(youtube)
|
||||||
|
|
||||||
|
return providers
|
||||||
|
|
||||||
def show_provider_list(self, _) -> None:
|
def show_provider_list(self, _) -> None:
|
||||||
self.m_sizer.Clear(delete_windows=True)
|
self.m_sizer.Clear(delete_windows=True)
|
||||||
self.m_sizer = wx.BoxSizer(wx.VERTICAL)
|
self.m_sizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
self.m_sizer.AddSpacer(SPACER_HEIGHT * 4)
|
self.m_sizer.AddSpacer(SPACER_HEIGHT * 4)
|
||||||
closebtn = wx.Button(self.m_panel, -1, label="Close", size=(WIDTH, BTN_HEIGHT))
|
closebtn = wx.Button(self.m_panel,
|
||||||
|
-1,
|
||||||
|
label="Close",
|
||||||
|
size=(WIDTH, BTN_HEIGHT))
|
||||||
closebtn.Bind(wx.EVT_BUTTON, lambda event: self.Destroy())
|
closebtn.Bind(wx.EVT_BUTTON, lambda event: self.Destroy())
|
||||||
self.m_sizer.Add(closebtn)
|
self.m_sizer.Add(closebtn)
|
||||||
provider_index = 0
|
provider_index = 0
|
||||||
|
|
||||||
for provider in self.m_providers:
|
for provider in self.m_providers:
|
||||||
bitmap = provider.get_logo_as_bitmap()
|
bitmap = provider.get_logo_as_bitmap()
|
||||||
callback = lambda event, index=provider_index: self.show_channel_list(event, index)
|
callback = lambda event, index=provider_index: self.show_channel_list(
|
||||||
btn_sizer: wx.BoxSizer = make_sized_button(self.m_panel,bitmap,provider.get_name(),callback)
|
event, index)
|
||||||
|
btn_sizer: wx.BoxSizer = make_sized_button(self.m_panel, bitmap,
|
||||||
|
provider.get_name(),
|
||||||
|
callback)
|
||||||
self.m_sizer.Add(btn_sizer)
|
self.m_sizer.Add(btn_sizer)
|
||||||
provider_index += 1
|
provider_index += 1
|
||||||
|
|
||||||
|
@ -112,10 +141,14 @@ class Cast(wx.Frame):
|
||||||
bck_callback = lambda event: self.show_provider_list(event)
|
bck_callback = lambda event: self.show_provider_list(event)
|
||||||
self.add_back_button(bck_callback)
|
self.add_back_button(bck_callback)
|
||||||
channel_index = 0
|
channel_index = 0
|
||||||
|
|
||||||
for channel in self.m_selected_provider.get_channels():
|
for channel in self.m_selected_provider.get_channels():
|
||||||
bitmap = channel.get_logo_as_bitmap()
|
bitmap = channel.get_logo_as_bitmap()
|
||||||
callback = lambda event, index=channel_index: self.show_video_list(event, index)
|
callback = lambda event, index=channel_index: self.show_video_list(
|
||||||
btn_sizer: wx.BoxSizer = make_sized_button(self.m_panel,bitmap,channel.get_name(),callback)
|
event, index)
|
||||||
|
btn_sizer: wx.BoxSizer = make_sized_button(self.m_panel, bitmap,
|
||||||
|
channel.get_name(),
|
||||||
|
callback)
|
||||||
self.m_sizer.Add(btn_sizer)
|
self.m_sizer.Add(btn_sizer)
|
||||||
channel_index += 1
|
channel_index += 1
|
||||||
|
|
||||||
|
@ -129,30 +162,36 @@ class Cast(wx.Frame):
|
||||||
self.m_sizer = wx.BoxSizer(wx.VERTICAL)
|
self.m_sizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
self.m_sizer.AddSpacer(SPACER_HEIGHT * 4)
|
self.m_sizer.AddSpacer(SPACER_HEIGHT * 4)
|
||||||
channel = self.m_selected_provider.get_channel_by_index(index)
|
channel = self.m_selected_provider.get_channel_by_index(index)
|
||||||
|
|
||||||
if channel.wait():
|
if channel.wait():
|
||||||
with wx.BusyInfo("Please wait, working..."):
|
with wx.BusyInfo("Please wait, working..."):
|
||||||
index = 0
|
index = 0
|
||||||
|
|
||||||
while channel.wait():
|
while channel.wait():
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
wx.GetApp().Yield()
|
wx.GetApp().Yield()
|
||||||
|
|
||||||
callback = lambda event: self.show_channel_list(event, self.m_selected_provider_index)
|
callback = lambda event: self.show_channel_list(
|
||||||
|
event, self.m_selected_provider_index)
|
||||||
self.add_back_button(callback)
|
self.add_back_button(callback)
|
||||||
|
|
||||||
for item in channel.get_items(): # type: ignore
|
for item in channel.get_items(): # type: ignore
|
||||||
inner_sizer = wx.BoxSizer(wx.VERTICAL)
|
inner_sizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
title = wx.StaticText(self.m_panel, -1)
|
title = wx.StaticText(self.m_panel, -1)
|
||||||
title.SetLabelMarkup("<span weight='bold' >{}</span>".format(item["title"]))
|
title.SetLabelMarkup("<span weight='bold' >{}</span>".format(
|
||||||
|
item["title"]))
|
||||||
description = wx.StaticText(self.m_panel, -1, item["description"])
|
description = wx.StaticText(self.m_panel, -1, item["description"])
|
||||||
|
|
||||||
description.Wrap(WIDTH - 2)
|
description.Wrap(WIDTH - 2)
|
||||||
bitmap = item["thumbnail"]
|
bitmap = item["thumbnail"]
|
||||||
btn = wx.BitmapButton(self.m_panel, id=self.m_index, bitmap=bitmap, style= BM_BTN_STYLE)
|
btn = wx.BitmapButton(self.m_panel,
|
||||||
|
id=self.m_index,
|
||||||
|
bitmap=bitmap,
|
||||||
|
style=BM_BTN_STYLE)
|
||||||
btn.Bind(
|
btn.Bind(
|
||||||
wx.EVT_BUTTON,
|
wx.EVT_BUTTON,
|
||||||
lambda event, link=item["link"], provider_index=index: self.show_player(
|
lambda event, link=item["link"], provider_index=index: self.
|
||||||
event, link, provider_index
|
show_player(event, link, provider_index),
|
||||||
),
|
|
||||||
)
|
)
|
||||||
inner_sizer.Add(title)
|
inner_sizer.Add(title)
|
||||||
inner_sizer.Add(btn)
|
inner_sizer.Add(btn)
|
||||||
|
@ -178,8 +217,8 @@ class Cast(wx.Frame):
|
||||||
inner_sizer = wx.GridBagSizer()
|
inner_sizer = wx.GridBagSizer()
|
||||||
self.m_control = wx.media.MediaCtrl(
|
self.m_control = wx.media.MediaCtrl(
|
||||||
self.m_panel,
|
self.m_panel,
|
||||||
# size=(WIDTH, HEIGHT/2),
|
size=(WIDTH, HEIGHT / 2),
|
||||||
# style=wx.SIMPLE_BORDER,
|
style=wx.SIMPLE_BORDER,
|
||||||
szBackend=wx.media.MEDIABACKEND_GSTREAMER,
|
szBackend=wx.media.MEDIABACKEND_GSTREAMER,
|
||||||
)
|
)
|
||||||
play_button = wx.Button(self.m_panel, -1, "Play")
|
play_button = wx.Button(self.m_panel, -1, "Play")
|
||||||
|
@ -189,7 +228,8 @@ class Cast(wx.Frame):
|
||||||
back_button = wx.Button(self.m_panel, -1, "Back")
|
back_button = wx.Button(self.m_panel, -1, "Back")
|
||||||
back_button.Bind(
|
back_button.Bind(
|
||||||
wx.EVT_BUTTON,
|
wx.EVT_BUTTON,
|
||||||
lambda event, index=provider_index: self.show_video_list(event, index),
|
lambda event, index=provider_index: self.show_video_list(
|
||||||
|
event, index),
|
||||||
)
|
)
|
||||||
|
|
||||||
inner_sizer.Add(self.m_control, (0, 0))
|
inner_sizer.Add(self.m_control, (0, 0))
|
||||||
|
@ -198,13 +238,13 @@ class Cast(wx.Frame):
|
||||||
inner_sizer.Add(pause_button, (1, 2))
|
inner_sizer.Add(pause_button, (1, 2))
|
||||||
inner_sizer.Add(back_button, (1, 3))
|
inner_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 = wx.Button(self.m_panel, -1, "Cast")
|
||||||
chromecast_button.Bind(
|
chromecast_button.Bind(
|
||||||
wx.EVT_BUTTON,
|
wx.EVT_BUTTON,
|
||||||
lambda event, muri=uri, index=provider_index: self.select_chromecast(
|
lambda event, muri=uri, index=provider_index: self.
|
||||||
event, muri, index
|
select_chromecast(event, muri, index),
|
||||||
),
|
|
||||||
)
|
)
|
||||||
inner_sizer.Add(chromecast_button, (2, 2))
|
inner_sizer.Add(chromecast_button, (2, 2))
|
||||||
self.m_sizer.Add(inner_sizer)
|
self.m_sizer.Add(inner_sizer)
|
||||||
|
@ -234,20 +274,28 @@ class Cast(wx.Frame):
|
||||||
self.m_sizer = wx.BoxSizer(wx.VERTICAL)
|
self.m_sizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
self.m_sizer.AddSpacer(SPACER_HEIGHT * 4)
|
self.m_sizer.AddSpacer(SPACER_HEIGHT * 4)
|
||||||
|
|
||||||
cancel_btn = wx.Button(self.m_panel, -1, "Cancel", size=(WIDTH, BTN_HEIGHT), style=wx.BU_EXACTFIT)
|
cancel_btn = wx.Button(self.m_panel,
|
||||||
|
-1,
|
||||||
|
"Cancel",
|
||||||
|
size=(WIDTH, BTN_HEIGHT),
|
||||||
|
style=wx.BU_EXACTFIT)
|
||||||
cancel_btn.Bind(
|
cancel_btn.Bind(
|
||||||
wx.EVT_BUTTON,
|
wx.EVT_BUTTON,
|
||||||
lambda event, index=provider_index: self.show_video_list(event, index),
|
lambda event, index=provider_index: self.show_video_list(
|
||||||
|
event, index),
|
||||||
)
|
)
|
||||||
self.m_sizer.Add(cancel_btn) #, wx.ALIGN_CENTER_VERTICAL)
|
self.m_sizer.Add(cancel_btn) #, wx.ALIGN_CENTER_VERTICAL)
|
||||||
|
|
||||||
for cast in self.m_chromecasts:
|
for cast in self.m_chromecasts:
|
||||||
friendly_name = cast.cast_info.friendly_name
|
friendly_name = cast.cast_info.friendly_name
|
||||||
btn = wx.Button(self.m_panel, id=-1, label=friendly_name, size=(WIDTH, BTN_HEIGHT))
|
btn = wx.Button(self.m_panel,
|
||||||
|
id=-1,
|
||||||
|
label=friendly_name,
|
||||||
|
size=(WIDTH, BTN_HEIGHT))
|
||||||
btn.Bind(
|
btn.Bind(
|
||||||
wx.EVT_BUTTON,
|
wx.EVT_BUTTON,
|
||||||
lambda event, chromecast=cast, muri=uri, index=provider_index: self.set_chromecast(
|
lambda event, chromecast=cast, muri=uri, index=provider_index:
|
||||||
event, chromecast, muri, index
|
self.set_chromecast(event, chromecast, muri, index),
|
||||||
),
|
|
||||||
)
|
)
|
||||||
self.m_sizer.Add(btn) #, wx.ALIGN_CENTER_VERTICAL)
|
self.m_sizer.Add(btn) #, wx.ALIGN_CENTER_VERTICAL)
|
||||||
self.m_panel.SetupScrolling(rate_y=SCROLL_RATE, scrollToTop=True)
|
self.m_panel.SetupScrolling(rate_y=SCROLL_RATE, scrollToTop=True)
|
||||||
|
@ -278,11 +326,8 @@ class Cast(wx.Frame):
|
||||||
|
|
||||||
break
|
break
|
||||||
|
|
||||||
if (
|
if (cast.socket_client.is_connected and has_played
|
||||||
cast.socket_client.is_connected
|
and player_state != "PLAYING"):
|
||||||
and has_played
|
|
||||||
and player_state != "PLAYING"
|
|
||||||
):
|
|
||||||
has_played = False
|
has_played = False
|
||||||
cast.media_controller.play_media(uri, mimetype)
|
cast.media_controller.play_media(uri, mimetype)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue