Commit 6488f911 authored by Todd Weaver's avatar Todd Weaver
Browse files

Speed is still disabled until implemented in gstreamer pipeline

Added "mode_switcher" to offer playing one video (default), loop single video, or autoplay all videos
Changed pinning size of window (and videos) at search to having two fixed sizes and snapping to them based on the threshold set
Adding clamping around video box from those two snapped sizes
Adding dynamically resizing video thumbnail pixbuf based on resizing
Restored error messages (from previous switch adding playlists) to use a "lists_stack" that includes the error message stack
Added '{' and '}' keystrokes for speed adjustments stub
Replaced 'Autoplay' toggle for "mode switcher" toggle buttons
Replaced 'Speed' button with 'Share' button stub (to be added later)
Added a size-allocate handler to determine if resizing window crosses threshold to resize results
Changed default app size to 960x720
Added default thresholds to .ui files for clamping text overrun...
parent 9cab34d7
......@@ -26,9 +26,11 @@ from .help import Help
class Menu(Gtk.PopoverMenu):
__gtype_name__ = 'Menu'
autoplay_toggle = Gtk.Template.Child()
volume = Gtk.Template.Child()
volume_icon = Gtk.Template.Child()
speed = Gtk.Template.Child()
mode_switcher = Gtk.Template.Child()
def __init__(self, app_window, **kwargs):
super().__init__(**kwargs)
......@@ -39,6 +41,10 @@ class Menu(Gtk.PopoverMenu):
def volume_change(self, event):
self.volume_setting(event.get_value())
@Gtk.Template.Callback()
def speed_change(self, event):
self.speed_setting(event.get_value())
@Gtk.Template.Callback()
def mute_toggle(self, toggle_button):
if toggle_button.get_active():
......@@ -56,6 +62,13 @@ class Menu(Gtk.PopoverMenu):
for child in children:
child.get_child().player.set_property("volume", volume_decimal)
def speed_setting(self, speed_value):
list = self.app_window.get_scroller_list()
children = list.get_children()
# for child in children:
# # something like this sudo code:
# child.get_child().player.set_property("speed", speed_value)
@Gtk.Template.Callback()
def show_about(self, data):
about = About()
......
......@@ -47,7 +47,7 @@ class ResultsBox(Gtk.Box):
audio_dl_image = Gtk.Template.Child()
video_dl = Gtk.Template.Child()
video_dl_image = Gtk.Template.Child()
speed = Gtk.Template.Child()
fullscreen = Gtk.Template.Child()
unfullscreen = Gtk.Template.Child()
......@@ -67,22 +67,40 @@ class ResultsBox(Gtk.Box):
# listen for motion on the player box for controls show/hide
self.event_box.add_events(Gdk.EventMask.POINTER_MOTION_MASK)
# do ratio calculation from width (16:9 or 1.77)
self.video_box_width = int(self.app_window.app_orig_width - self.window_to_player_box_margin)
self.video_box_height = int(self.video_box_width / 1.77)
self.player_box.set_size_request(self.video_box_width, self.video_box_height)
self.poster_image.set_size_request(self.video_box_width, self.video_box_height)
# init gstreamer player
self.player = Gst.ElementFactory.make("playbin", "player")
self.sink = Gst.ElementFactory.make("gtksink")
self.video_widget = self.sink.get_property("widget")
self.video_widget.set_size_request(self.video_box_width, self.video_box_height)
self.player_box.add(self.video_widget)
self.set_player_box_size()
def set_player_box_size(self):
# setup player box sizing based on app window (re)size
size = self.app_window.get_size()
self.app_last_width = size.width
self.app_last_height = size.height
# do ratio calculation from width (16:9 or 1.77)
self.video_box_width = self.app_window.video_size_active
self.video_box_height = int(self.video_box_width / 1.77)
self.player_box.set_size_request(self.video_box_width, self.video_box_height)
self.poster_image.set_size_request(self.video_box_width, self.video_box_height)
self.video_widget.set_size_request(self.video_box_width, self.video_box_height)
# this is a HdyClamp to tighten the box around the dynamic
# player box size (determined by window size at time of search)
# not allowing long text titles to expand the results window
results_width = int(self.app_window.video_size_active + self.window_to_player_box_margin)
self.app_window.results_clamp.set_property('maximum-size', results_width)
self.app_window.results_clamp.set_property('tightening-threshold', results_width)
self.app_window.playlist_clamp.set_property('maximum-size', results_width)
self.app_window.playlist_clamp.set_property('tightening-threshold', results_width)
def get_readable_seconds(self, seconds):
m, s = divmod(seconds, 60)
h, m = divmod(m, 60)
......@@ -106,10 +124,8 @@ class ResultsBox(Gtk.Box):
None) # user_data
def on_stream_load(self, source, async_res, context):
pixbuf = GdkPixbuf.Pixbuf.new_from_stream_finish(async_res)
self.poster_image.clear()
self.poster_image.set_from_pixbuf(pixbuf)
self.pixbuf = GdkPixbuf.Pixbuf.new_from_stream_finish(async_res)
self.poster_image.set_from_pixbuf(self.pixbuf)
def stream_at_scale_async(self, poster_file):
stream = poster_file.read_async(0, None,
......@@ -195,7 +211,7 @@ class ResultsBox(Gtk.Box):
if int(position / Gst.SECOND) >= int(duration / Gst.SECOND):
GLib.timeout_add(500, self.null_out_player)
GLib.timeout_add(600, self.app_window.autoplay_next)
GLib.timeout_add(700, self.app_window.next_playback_action)
try:
# block seek slider function so it doesn't loop itself
......@@ -336,6 +352,11 @@ class ResultsBox(Gtk.Box):
app_volume = self.app_window.menu.volume.get_value() / 100
self.player.set_property("volume", app_volume)
# grab the speed from menu
speed = self.app_window.menu.speed.get_value()
# something like this sudo code:
# self.player.set_property("speed", speed)
# update slider to track video time in slider
GLib.timeout_add_seconds(1, self.update_slider)
......@@ -360,9 +381,19 @@ class ResultsBox(Gtk.Box):
self.app_window.is_playing = False
self.app_window.uninhibit_app()
@Gtk.Template.Callback()
def speed_button(self, button):
print("speed_button")
def resize_results(self):
self.poster_image.clear()
# resize the player boxes to the new window size
self.set_player_box_size()
# check if there is an original pixbuf to resize
if hasattr(self, "pixbuf"):
resize_pixbuf = GdkPixbuf.Pixbuf.scale_simple(self.pixbuf,
self.video_box_width, self.video_box_height,
GdkPixbuf.InterpType.BILINEAR)
self.poster_image.set_from_pixbuf(resize_pixbuf)
def resize_player(self, width, height):
self.poster_image.set_size_request(width, height)
......@@ -411,11 +442,8 @@ class ResultsBox(Gtk.Box):
results_context.remove_class("fullscreen")
results_context.add_class("results")
set_width = int(self.app_window.app_orig_width - self.window_to_player_box_margin)
set_height = int(set_width / 1.77)
self.resize_player(set_width, set_height)
self.app_window.resize(self.app_window.app_orig_width, self.app_window.app_orig_height)
self.resize_player(self.video_box_width, self.video_box_height)
self.app_window.resize(self.app_last_width, self.app_last_height)
scroller = self.app_window.scroller
scroller.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
......
......@@ -27,7 +27,7 @@ class Search:
# for internal plugins only
self.app_window = kwargs.get('app_window', None)
self.toggle_status_spinner = kwargs.get('toggle_status_spinner', None)
self.scroller = kwargs.get('scroller', None)
self.lists_stack = kwargs.get('lists_stack', None)
self.si_index = 0
self.this_instance = self.app_window.strong_instances[self.si_index]
......@@ -77,8 +77,8 @@ class Search:
first_video_meta = meta['videos'][0]
self.get_poster_url(first_video_meta, meta)
self.append_playlist(meta)
elif meta['type'] == 'channel':
print('channel')
# elif meta['type'] == 'channel':
# print('channel')
def do_playlist(self, playlist_id, page):
self.toggle_status_spinner(True)
......@@ -186,7 +186,7 @@ class Search:
self.search_video_ids.append(video_meta['videoId'])
self.toggle_status_spinner(False)
self.scroller.set_visible(True)
self.lists_stack.set_visible(True)
def append_playlist(self, playlist_meta):
# add the playlist to the list
......@@ -197,7 +197,7 @@ class Search:
self.search_playlist_ids.append(playlist_meta['playlistId'])
self.toggle_status_spinner(False)
self.scroller.set_visible(True)
self.lists_stack.set_visible(True)
def get_download_uris(self, video_meta):
# get download link urls based on (future) user-config
......
......@@ -24,6 +24,20 @@
<property name="accelerator">Down</property>
</object>
</child>
<child>
<object class="GtkShortcutsShortcut">
<property name="visible">True</property>
<property name="title" translatable="yes" context="shortcut window">Speed Slower</property>
<property name="accelerator">bracketleft</property>
</object>
</child>
<child>
<object class="GtkShortcutsShortcut">
<property name="visible">True</property>
<property name="title" translatable="yes" context="shortcut window">Speed Faster</property>
<property name="accelerator">bracketright</property>
</object>
</child>
</object>
</child>
<child>
......
......@@ -2,6 +2,13 @@
<!-- Generated with glade 3.38.2 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkAdjustment" id="speed_adjustmet">
<property name="lower">0.25</property>
<property name="upper">2</property>
<property name="value">1</property>
<property name="step-increment">0.25</property>
<property name="page-increment">0.5</property>
</object>
<object class="GtkAdjustment" id="volume_adjustment">
<property name="upper">100</property>
<property name="value">90</property>
......@@ -83,7 +90,8 @@
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="margin-start">6</property>
<property name="label" translatable="yes">Autoplay</property>
<property name="margin-end">8</property>
<property name="label" translatable="yes">Speed</property>
</object>
<packing>
<property name="expand">True</property>
......@@ -92,12 +100,21 @@
</packing>
</child>
<child>
<object class="GtkSwitch" id="autoplay_toggle">
<object class="GtkSpinButton" id="speed">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can-focus">True</property>
<property name="margin-start">12</property>
<property name="max-length">4</property>
<property name="text" translatable="yes">90</property>
<property name="adjustment">speed_adjustmet</property>
<property name="digits">2</property>
<property name="numeric">True</property>
<property name="value">1</property>
<signal name="value-changed" handler="speed_change" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
......@@ -110,9 +127,78 @@
</packing>
</child>
<child>
<object class="GtkSeparator">
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<object class="GtkStack" id="mode_toggle_stack">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="margin-start">6</property>
<property name="margin-end">8</property>
<child>
<object class="GtkLabel" id="one_video_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">One Video</property>
</object>
<packing>
<property name="name">one</property>
<property name="title" translatable="yes">One Video</property>
<property name="icon-name">media-playlist-consecutive-symbolic</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="loop_video_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Loop Video</property>
</object>
<packing>
<property name="name">loop</property>
<property name="title" translatable="yes">Loop Video</property>
<property name="icon-name">media-playlist-repeat-symbolic</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="autoplay_all">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Autoplay All</property>
</object>
<packing>
<property name="name">auto</property>
<property name="title" translatable="yes">Autoplay All</property>
<property name="icon-name">view-list-bullet-symbolic</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkStackSwitcher" id="mode_switcher">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">end</property>
<property name="hexpand">True</property>
<property name="stack">mode_toggle_stack</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
......@@ -120,6 +206,17 @@
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can-focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkModelButton" id="preferences_button">
<property name="visible">True</property>
......@@ -132,7 +229,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
<property name="position">4</property>
</packing>
</child>
<child>
......@@ -147,7 +244,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">4</property>
<property name="position">5</property>
</packing>
</child>
<child>
......@@ -162,7 +259,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">5</property>
<property name="position">6</property>
</packing>
</child>
</object>
......
......@@ -298,26 +298,21 @@
<property name="can-focus">False</property>
<property name="spacing">8</property>
<child>
<object class="GtkButton" id="speed">
<object class="GtkButton" id="share">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can-focus">False</property>
<property name="receives-default">False</property>
<property name="tooltip-text" translatable="yes">Adjust Speed</property>
<property name="tooltip-text" translatable="yes">Share Url</property>
<property name="opacity">0.80</property>
<property name="halign">center</property>
<property name="valign">center</property>
<signal name="clicked" handler="speed_button" swapped="no"/>
<signal name="clicked" handler="share_button" swapped="no"/>
<child>
<object class="GtkLabel">
<object class="GtkImage">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label">1x</property>
<property name="justify">center</property>
<attributes>
<attribute name="weight" value="bold"/>
<attribute name="scale" value="0.80000000000000004"/>
</attributes>
<property name="icon-name">emblem-shared-symbolic</property>
</object>
</child>
<style>
......
......@@ -7,10 +7,11 @@
<property name="can-focus">False</property>
<property name="hexpand">False</property>
<property name="vexpand">True</property>
<property name="default-width">882</property>
<property name="default-height">652</property>
<property name="default-width">960</property>
<property name="default-height">720</property>
<property name="icon-name">sm.puri.Stream</property>
<signal name="key-press-event" handler="keypress_listener" swapped="no"/>
<signal name="size-allocate" handler="screen_changed" swapped="no"/>
<child>
<object class="GtkBox">
<property name="visible">True</property>
......@@ -104,7 +105,7 @@
<packing>
<property name="position">1</property>
</packing>
</child>
</child>
</object>
<packing>
<property name="pack-type">end</property>
......@@ -190,11 +191,11 @@
</child>
<child>
<object class="GtkStack" id="lists_stack">
<property name="visible">True</property>
<property name="visible">False</property>
<property name="can-focus">False</property>
<child>
<object class="GtkScrolledWindow" id="scroller">
<property name="visible">False</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="hexpand">False</property>
<property name="vexpand">False</property>
......@@ -205,6 +206,8 @@
<object class="HdyClamp" id="results_clamp">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="maximum-size">882</property>
<property name="tightening-threshold">882</property>
<child>
<object class="GtkFlowBox" id="results_list">
<property name="can-focus">True</property>
......@@ -235,6 +238,8 @@
<object class="HdyClamp" id="playlist_clamp">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="maximum-size">882</property>
<property name="tightening-threshold">882</property>
<child>
<object class="GtkFlowBox" id="playlist_list">
<property name="can-focus">True</property>
......@@ -307,6 +312,30 @@
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkButton" id="error_action_button">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="margin-top">12</property>
<property name="tooltip-text" translatable="yes">Reload</property>
<property name="valign">center</property>
<signal name="clicked" handler="reload_instances" swapped="no"/>
<child>
<object class="GtkImage" id="error_action_icon">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">view-refresh-symbolic</property>
<property name="icon_size">1</property>
<style>
<class name="image-button"/>
</style>
</object>
</child>
</object>
<packing>
<property name="position">3</property>
</packing>
</child>
</object>
<packing>
<property name="position">1</property>
......
......@@ -48,6 +48,11 @@ class StreamWindow(Handy.ApplicationWindow):
error_box = Gtk.Template.Child()
error_heading = Gtk.Template.Child()
error_text = Gtk.Template.Child()
error_action_button = Gtk.Template.Child()
# lists stack holds both the scroller ScrollWindow
# and the playlist_scroller ScrollWindow
lists_stack = Gtk.Template.Child()
scroller = Gtk.Template.Child()
results_list = Gtk.Template.Child()
......@@ -57,6 +62,18 @@ class StreamWindow(Handy.ApplicationWindow):
playlist_list = Gtk.Template.Child()
playlist_clamp = Gtk.Template.Child()
# magic sizes:
# 16:9 (1.77)
# initial window width 1920 / 2 = 960
# initial video size 854x480
# small video size 332x186
# resize threshold 854 + margin (28) = 882
window_resize_threshold = 882
video_large_width = 854
video_small_width = 332
video_size_active = video_small_width
window_last_size = 'big'
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.application = kwargs.get('application', None)
......@@ -67,8 +84,8 @@ class StreamWindow(Handy.ApplicationWindow):
self.strong_instances = []
self.results_meta = []
self.playlist_results_meta = []
instances = Instances(app_window = self)
instances.get_strong_instances()
self.instances = Instances(app_window = self)
self.instances.get_strong_instances()
self.menu = Menu(app_window = self)
self.menu_button.set_popover(self.menu)
......@@ -111,7 +128,7 @@ class StreamWindow(Handy.ApplicationWindow):
child.destroy()
self.hide_error_box()
self.scroller.set_visible(False)
self.lists_stack.set_visible(False)
self.status_page.set_visible(True)
self.clear_playlist(0, 0, None)
......@@ -130,31 +147,16 @@ class StreamWindow(Handy.ApplicationWindow):
self.status_page.set_visible(False)
self.hide_error_box()
self.scroller.set_visible(False)
self.lists_stack.set_visible(False)
self.search_query = search_box.get_text()
# determine app window size at time of search
size = self.get_size()
self.app_orig_width = size.width
self.app_orig_height = size.height
# this is a HdyClamp to tighten the box around the dynamic
# player box size (determined by window size at time of search)
# not allowing long text titles to expand the results window
results_width = int(self.app_orig_width)
self.results_clamp.set_property('maximum-size', results_width)
self.results_clamp.set_property('tightening-threshold', results_width)
self.playlist_clamp.set_property('maximum-size', results_width)
self.playlist_clamp.set_property('tightening-threshold', results_width)
if not self.strong_instances:
self.show_error_box("Service Failure",
"No strong video server instances found yet. Try again shortly.")
self.instances.get_strong_instances()
else:
self.search = Search(app_window = self,
toggle_status_spinner = self.toggle_status_spinner,
scroller = self.scroller,
lists_stack = self.lists_stack,
add_result_meta = self.add_result_meta)
self.search.do_search(query = self.search_query, page = self.page_results)
......@@ -233,11 +235,21 @@ class StreamWindow(Handy.ApplicationWindow):
else:
return self.results_list
def autoplay_next(self):
if self.menu.autoplay_toggle.get_active():
list = self.get_scroller_list()
focus_child = list.get_focus_child()
if focus_child:
def next_playback_action(self):
list = self.get_scroller_list()
focus_child = list.get_focus_child()
if focus_child:
action = "one"
stack = self.menu.mode_switcher.get_stack()
if stack:
action = stack.get_visible_child_name()
# check if video loop override is on
if action == "loop":
self.play_pause_toggle(focus_child)
# otherwise, check if user wants to auto play through list
elif action == "auto":
focus_index = focus_child.get_index()
focus_next = list.get_child_at_index(focus_index + 1)
if focus_next:
......@@ -268,6 +280,12 @@ class StreamWindow(Handy.ApplicationWindow):
elif key == "Right":