reworking of built-in player [GStreamer support]

This commit is contained in:
DYefremov
2021-02-26 12:36:19 +03:00
parent a46c6ae816
commit e1a5b8e39d
4 changed files with 363 additions and 382 deletions

View File

@@ -6,103 +6,102 @@ from app.commons import run_task, log, _DATE_FORMAT
class Player:
""" Simple wrapper for VLC media player. """
__VLC_INSTANCE = None
""" Simple wrapper for GStreamer playbin. """
__INSTANCE = None
def __init__(self, mode, rewind_cb, position_cb, error_cb, playing_cb):
try:
from app.tools import vlc
from app.tools.vlc import EventType
import gi
gi.require_version("Gst", "1.0")
gi.require_version("GstVideo", "1.0")
from gi.repository import Gst, GstVideo
except OSError as e:
log("{}: Load library error: {}".format(__class__.__name__, e))
raise ImportError
else:
# initialize GStreamer
Gst.init(sys.argv)
self.STATE = Gst.State
self.STAT_RETURN = Gst.StateChangeReturn
self._mode = mode
self._is_playing = False
args = "--quiet {}".format("" if sys.platform == "darwin" else "--no-xlib")
self._player = vlc.Instance(args).media_player_new()
ev_mgr = self._player.event_manager()
self._player = Gst.ElementFactory.make("playbin", "player")
if rewind_cb:
# TODO look other EventType options
ev_mgr.event_attach(EventType.MediaPlayerBuffering,
lambda et, p: rewind_cb(p.get_media().get_duration()),
self._player)
if position_cb:
ev_mgr.event_attach(EventType.MediaPlayerTimeChanged,
lambda et, p: position_cb(p.get_time()),
self._player)
if error_cb:
ev_mgr.event_attach(EventType.MediaPlayerEncounteredError,
lambda et, p: error_cb(),
self._player)
if playing_cb:
ev_mgr.event_attach(EventType.MediaPlayerPlaying,
lambda et, p: playing_cb(),
self._player)
bus = self._player.get_bus()
bus.add_signal_watch()
bus.connect("message::error", error_cb)
bus.connect("message::state-changed", playing_cb)
@classmethod
def get_instance(cls, mode, rewind_cb=None, position_cb=None, error_cb=None, playing_cb=None):
if not cls.__VLC_INSTANCE:
cls.__VLC_INSTANCE = Player(mode, rewind_cb, position_cb, error_cb, playing_cb)
return cls.__VLC_INSTANCE
if not cls.__INSTANCE:
cls.__INSTANCE = Player(mode, rewind_cb, position_cb, error_cb, playing_cb)
return cls.__INSTANCE
def get_play_mode(self):
return self._mode
@run_task
def play(self, mrl=None):
if mrl:
self._player.set_mrl(mrl)
self._player.play()
self._is_playing = True
self.stop()
if mrl:
self._player.set_property("uri", mrl)
ret = self._player.set_state(self.STATE.PLAYING)
if ret == self.STAT_RETURN.FAILURE:
log("ERROR: Unable to set the 'PLAYING' state for '{}'.".format(mrl))
else:
self._is_playing = True
@run_task
def stop(self):
if self._is_playing:
self._player.stop()
self._player.set_state(self.STATE.READY)
self._is_playing = False
def pause(self):
self._player.pause()
self._player.set_state(self.STATE.PAUSED)
def set_time(self, time):
self._player.set_time(time)
pass
@run_task
def release(self):
if self._player:
self._is_playing = False
self._player.stop()
self._player.release()
self.__VLC_INSTANCE = None
self.stop()
self._player.set_state(self.STATE.NULL)
self.__INSTANCE = None
def set_xwindow(self, xid):
self._player.set_xwindow(xid)
def set_nso(self, widget):
""" Used on MacOS to set NSObject.
def set_handle(self, widget):
""" Used on Windows to set window pointer.
Based on gtkvlc.py[get_window_pointer] example from here:
https://github.com/oaubert/python-vlc/tree/master/examples
"""
try:
import ctypes
g_dll = ctypes.CDLL("libgdk-3.0.dylib")
libgdk = ctypes.CDLL("libgdk-3-0.dll")
except OSError as e:
log("{}: Load library error: {}".format(__class__.__name__, e))
else:
get_nsview = g_dll.gdk_quartz_window_get_nsview
get_nsview.restype, get_nsview.argtypes = ctypes.c_void_p, [ctypes.c_void_p]
# https://gitlab.gnome.org/GNOME/pygobject/-/issues/112
ctypes.pythonapi.PyCapsule_GetPointer.restype = ctypes.c_void_p
ctypes.pythonapi.PyCapsule_GetPointer.argtypes = [ctypes.py_object]
# Get the C void* pointer to the window
pointer = ctypes.pythonapi.PyCapsule_GetPointer(widget.get_window().__gpointer__, None)
self._player.set_nsobject(get_nsview(pointer))
gpointer = ctypes.pythonapi.PyCapsule_GetPointer(widget.get_window().__gpointer__, None)
libgdk.gdk_win32_window_get_handle.restype = ctypes.c_void_p
libgdk.gdk_win32_window_get_handle.argtypes = [ctypes.c_void_p]
handle = libgdk.gdk_win32_window_get_handle(gpointer)
self._player.set_window_handle(handle)
def set_mrl(self, mrl):
self._player.set_mrl(mrl)
self._player.set_property("uri", mrl)
def is_playing(self):
return self._is_playing

View File

@@ -11,6 +11,7 @@ from urllib.parse import unquote
from urllib.request import Request, urlopen, urlretrieve
from app.commons import log
from app.settings import SEP
from app.ui.uicommons import show_notification
_YT_PATTERN = re.compile(r"https://www.youtube.com/.+(?:v=)([\w-]{11}).*")
@@ -169,7 +170,7 @@ class PlayListParser(HTMLParser):
try:
resp = json.loads(data)
except JSONDecodeError as e:
except YouTubeException as e:
log("{}: Parsing data error: {}".format(__class__.__name__, e))
else:
sb = resp.get("sidebar", None)
@@ -229,7 +230,7 @@ class YouTubeDL:
"cookiefile": "cookies.txt"} # File name where cookies should be read from and dumped to.
def __init__(self, settings, callback):
self._path = settings.default_data_path + "tools/"
self._path = settings.default_data_path + "tools{}".format(SEP)
self._update = settings.enable_yt_dl_update
self._supported = {"22", "18"}
self._dl = None
@@ -246,7 +247,7 @@ class YouTubeDL:
return cls._DL_INSTANCE
def init(self):
if not os.path.isfile(self._path + "youtube_dl/version.py"):
if not os.path.isfile(self._path + "youtube_dl{}version.py".format(SEP)):
self.get_latest_release()
if self._path not in sys.path:
@@ -313,7 +314,7 @@ class YouTubeDL:
os.makedirs(os.path.dirname(self._path), exist_ok=True)
for info in arch.infolist():
pref, sep, f = info.filename.partition("/youtube_dl/")
pref, sep, f = info.filename.partition("{}youtube_dl{}".format(SEP, SEP))
if sep:
arch.extract(info.filename)
shutil.move(info.filename, "{}{}{}".format(self._path, sep, f))

View File

@@ -17,7 +17,7 @@ from app.eparser.ecommons import CAS, Flag, BouquetService
from app.eparser.enigma.bouquets import BqServiceType
from app.eparser.iptv import export_to_m3u
from app.eparser.neutrino.bouquets import BqType
from app.settings import SettingsType, Settings, SettingsException, PlayStreamsMode, SettingsReadException
from app.settings import SettingsType, Settings, SettingsException, PlayStreamsMode, SettingsReadException, IS_WIN
from app.tools.media import Player, Recorder
from app.ui.epg_dialog import EpgDialog
from app.ui.transmitter import LinksTransmitter
@@ -304,12 +304,11 @@ class Application(Gtk.Application):
self._player_tool_bar = builder.get_object("player_tool_bar")
self._player_prev_button = builder.get_object("player_prev_button")
self._player_next_button = builder.get_object("player_next_button")
self._player_box.bind_property("visible", tool_bar, "visible", 4)
self._player_box.bind_property("visible", self._services_main_box, "visible", 4)
self._player_box.bind_property("visible", self._bouquets_main_box, "visible", 4)
self._player_box.bind_property("visible", builder.get_object("fav_pos_column"), "visible", 4)
self._player_box.bind_property("visible", builder.get_object("fav_pos_column"), "visible", 4)
self._player_box.bind_property("visible", self._profile_combo_box, "sensitive", 4)
self._player_box.bind_property("visible", tool_bar, "sensitive", 4)
self._fav_view.bind_property("sensitive", self._player_prev_button, "sensitive")
self._fav_view.bind_property("sensitive", self._player_next_button, "sensitive")
# Record
@@ -333,21 +332,23 @@ class Application(Gtk.Application):
self._status_bar_box.get_style_context().add_provider_for_screen(Gdk.Screen.get_default(), style_provider,
Gtk.STYLE_PROVIDER_PRIORITY_USER)
# Menu bar
main_box = builder.get_object("main_window_box")
builder.add_from_file(UI_RESOURCES_PATH + "app_menu_bar.ui")
menu_bar = Gtk.MenuBar.new_from_model(builder.get_object("menu_bar"))
menu_bar.set_visible(True)
main_box.pack_start(menu_bar, False, False, 0)
main_box.reorder_child(menu_bar, 0)
self._main_data_box.bind_property("visible", menu_bar, "visible")
self._player_box.bind_property("visible", menu_bar, "sensitive", 4)
if self._settings.get("telnet"):
self.init_telnet(builder)
def do_startup(self):
Gtk.Application.do_startup(self)
self.init_keys()
self.set_accels()
builder = Gtk.Builder()
builder.set_translation_domain("demon-editor")
builder.add_from_file(UI_RESOURCES_PATH + "app_menu_bar.ui")
self.set_menubar(builder.get_object("menu_bar"))
self.set_app_menu(builder.get_object("app-menu"))
if self._settings.get("telnet"):
self.init_telnet(builder)
self.update_profile_label()
self.init_drag_and_drop()
self.init_colors()
@@ -2445,7 +2446,7 @@ class Application(Gtk.Application):
def on_player_stop(self, item=None):
if self._player:
self._player.stop()
GLib.idle_add(self._player.stop)
def on_player_previous(self, item):
if self._fav_view.do_move_cursor(self._fav_view, Gtk.MovementStep.DISPLAY_LINES, -1):
@@ -2498,12 +2499,12 @@ class Application(Gtk.Application):
if not self._full_screen and self._player_rewind_box.get_visible():
GLib.idle_add(self._player_current_time_label.set_text, self.get_time_str(t), priority=GLib.PRIORITY_LOW)
def on_player_error(self):
def on_player_error(self, bus=None, msg=None):
self.set_playback_elms_active()
self.show_error_dialog("Can't Playback!")
@run_idle
def set_playback_elms_active(self):
def set_playback_elms_active(self, bus=None, msg=None):
self._fav_view.set_sensitive(True)
self._fav_view.do_grab_focus(self._fav_view)
@@ -2525,8 +2526,8 @@ class Application(Gtk.Application):
self.show_error_dialog("No VLC is found. Check that it is installed!")
return True
else:
if self._settings.is_darwin:
self._player.set_nso(widget)
if IS_WIN:
self._player.set_handle(widget)
else:
self._player.set_xwindow(widget.get_window().get_xid())
self._player.play(self._current_mrl)
@@ -2541,20 +2542,15 @@ class Application(Gtk.Application):
widget.set_size_request(w * 0.6, -1)
def on_player_drawing_area_draw(self, widget, cr):
""" Used for black background drawing in the player drawing area.
""" Used for black background drawing in the player drawing area. """
if not self._player.is_playing:
allocation = widget.get_allocation()
Required for Gtk >= 3.20.
More info: https://developer.gnome.org/gtk3/stable/ch32s10.html,
https://developer.gnome.org/gtk3/stable/GtkStyleContext.html#gtk-render-background
"""
context = widget.get_style_context()
width = widget.get_allocated_width()
height = widget.get_allocated_height()
Gtk.render_background(context, cr, 0, 0, width, height)
r, g, b, a = 0, 0, 0, 1 # black color
cr.set_source_rgba(r, g, b, a)
cr.rectangle(0, 0, width, height)
cr.fill()
cr.set_source_rgb(0, 0, 0)
cr.rectangle(0, 0, allocation.width, allocation.height)
cr.fill()
return False
def on_player_press(self, area, event):
if event.button == Gdk.BUTTON_PRIMARY:

View File

@@ -1005,305 +1005,22 @@ Author: Dmitriy Yefremov
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkBox" id="top_box">
<object class="GtkBox" id="top_toolbar">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="valign">center</property>
<property name="margin_left">10</property>
<property name="margin_top">10</property>
<property name="margin_bottom">10</property>
<property name="spacing">10</property>
<child>
<object class="GtkBox" id="top_toolbar">
<object class="GtkComboBoxText" id="profile_combo_box">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="margin_left">10</property>
<property name="margin_top">10</property>
<property name="margin_bottom">10</property>
<property name="spacing">10</property>
<child>
<object class="GtkComboBoxText" id="profile_combo_box">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Profile</property>
<property name="active">0</property>
<property name="focus_on_click">False</property>
<signal name="changed" handler="on_profile_changed" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButtonBox" id="toolbar_main_box">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="layout_style">expand</property>
<child>
<object class="GtkButton" id="open_tool_button">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Open</property>
<property name="action_name">app.on_data_open</property>
<property name="image">open_image</property>
<property name="always_show_image">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="download_tool_button">
<property name="label" translatable="yes"> </property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">FTP-transfer</property>
<property name="action_name">app.on_download</property>
<property name="image">ftp_image</property>
<property name="always_show_image">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButton" id="telnet_tool_button">
<property name="label" translatable="yes"> </property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Telnet</property>
<property name="action_name">app.on_telnet_client_show</property>
<property name="image">telnet_image</property>
<property name="always_show_image">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkButton" id="save_tool_button">
<property name="label" translatable="yes"> </property>
<property name="can_focus">False</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Save</property>
<property name="action_name">app.on_data_save</property>
<property name="image">save_image</property>
<property name="always_show_image">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkButton" id="backup_tool_button">
<property name="label" translatable="yes"> </property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Backups</property>
<property name="action_name">app.on_backup_tool_show</property>
<property name="image">backups_image</property>
<property name="always_show_image">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButtonBox" id="toolbar_extra_box">
<property name="can_focus">False</property>
<property name="opacity">0.99000000022351742</property>
<property name="resize_mode">immediate</property>
<property name="layout_style">expand</property>
<child>
<object class="GtkToggleButton" id="search_tool_button">
<property name="label" translatable="yes">Search</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Search</property>
<property name="action_name">win.search</property>
<property name="image">find_image</property>
<property name="always_show_image">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkToggleButton" id="filter_tool_button">
<property name="label" translatable="yes">Filter</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Filter</property>
<property name="action_name">win.filter</property>
<property name="image">filter_image</property>
<property name="always_show_image">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>
</child>
<child>
<object class="GtkButton" id="lock_tool_button">
<property name="label" translatable="yes">Lock</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Parent lock</property>
<property name="action_name">app.on_locked</property>
<property name="image">lock_image</property>
<property name="always_show_image">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">5</property>
</packing>
</child>
<child>
<object class="GtkButton" id="hide_tool_button">
<property name="label" translatable="yes">Hide</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Hide/Skip</property>
<property name="action_name">app.on_hide</property>
<property name="image">hide_image</property>
<property name="always_show_image">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">5</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkButtonBox" id="toolbar_tools_box">
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="layout_style">expand</property>
<child>
<object class="GtkButton" id="sat_editor_tool_button">
<property name="label" translatable="yes">Satellites</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Satellites editor</property>
<property name="action_name">app.on_satellite_editor_show</property>
<property name="image">sat_editor_image</property>
<property name="always_show_image">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="picons_manager_tool_button">
<property name="label" translatable="yes">Picons</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Picons manager</property>
<property name="action_name">app.on_picons_manager_show</property>
<property name="image">picons_image</property>
<property name="always_show_image">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkMenuButton" id="iptv_tools_menu_button">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">IPTV tools</property>
<property name="popup">toolbar_iptv_menu</property>
<child>
<object class="GtkBox" id="iptv_tools_menu_button_box">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="spacing">2</property>
<child>
<object class="GtkImage" id="iptv_tools_menu_button_image">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="icon_name">network-wired-symbolic</property>
<property name="icon_size">1</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="iptv_tools_menu_button_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">IPTV</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">5</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
<property name="tooltip_text" translatable="yes">Profile</property>
<property name="active">0</property>
<property name="focus_on_click">False</property>
<signal name="changed" handler="on_profile_changed" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
@@ -1311,11 +1028,279 @@ Author: Dmitriy Yefremov
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButtonBox" id="toolbar_main_box">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="layout_style">expand</property>
<child>
<object class="GtkButton" id="open_tool_button">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Open</property>
<property name="action_name">app.on_data_open</property>
<property name="image">open_image</property>
<property name="always_show_image">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="download_tool_button">
<property name="label" translatable="yes"> </property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">FTP-transfer</property>
<property name="action_name">app.on_download</property>
<property name="image">ftp_image</property>
<property name="always_show_image">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButton" id="telnet_tool_button">
<property name="label" translatable="yes"> </property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Telnet</property>
<property name="action_name">app.on_telnet_client_show</property>
<property name="image">telnet_image</property>
<property name="always_show_image">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkButton" id="save_tool_button">
<property name="label" translatable="yes"> </property>
<property name="can_focus">False</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Save</property>
<property name="action_name">app.on_data_save</property>
<property name="image">save_image</property>
<property name="always_show_image">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkButton" id="backup_tool_button">
<property name="label" translatable="yes"> </property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Backups</property>
<property name="action_name">app.on_backup_tool_show</property>
<property name="image">backups_image</property>
<property name="always_show_image">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButtonBox" id="toolbar_extra_box">
<property name="can_focus">False</property>
<property name="opacity">0.99000000022351742</property>
<property name="resize_mode">immediate</property>
<property name="layout_style">expand</property>
<child>
<object class="GtkToggleButton" id="search_tool_button">
<property name="label" translatable="yes">Search</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Search</property>
<property name="action_name">win.search</property>
<property name="image">find_image</property>
<property name="always_show_image">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkToggleButton" id="filter_tool_button">
<property name="label" translatable="yes">Filter</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Filter</property>
<property name="action_name">win.filter</property>
<property name="image">filter_image</property>
<property name="always_show_image">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>
</child>
<child>
<object class="GtkButton" id="lock_tool_button">
<property name="label" translatable="yes">Lock</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Parent lock</property>
<property name="action_name">app.on_locked</property>
<property name="image">lock_image</property>
<property name="always_show_image">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">5</property>
</packing>
</child>
<child>
<object class="GtkButton" id="hide_tool_button">
<property name="label" translatable="yes">Hide</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Hide/Skip</property>
<property name="action_name">app.on_hide</property>
<property name="image">hide_image</property>
<property name="always_show_image">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">5</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkButtonBox" id="toolbar_tools_box">
<property name="can_focus">False</property>
<property name="layout_style">expand</property>
<child>
<object class="GtkButton" id="sat_editor_tool_button">
<property name="label" translatable="yes">Satellites</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Satellites editor</property>
<property name="action_name">app.on_satellite_editor_show</property>
<property name="image">sat_editor_image</property>
<property name="always_show_image">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="picons_manager_tool_button">
<property name="label" translatable="yes">Picons</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Picons manager</property>
<property name="action_name">app.on_picons_manager_show</property>
<property name="image">picons_image</property>
<property name="always_show_image">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkMenuButton" id="iptv_tools_menu_button">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">IPTV tools</property>
<property name="popup">toolbar_iptv_menu</property>
<child>
<object class="GtkBox" id="iptv_tools_menu_button_box">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="spacing">2</property>
<child>
<object class="GtkImage" id="iptv_tools_menu_button_image">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="icon_name">network-wired-symbolic</property>
<property name="icon_size">1</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="iptv_tools_menu_button_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">IPTV</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">5</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkButtonBox" id="toolbar_extra_tools_box">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="valign">center</property>
<property name="margin_right">10</property>
<property name="resize_mode">immediate</property>
<property name="homogeneous">True</property>
@@ -1323,7 +1308,7 @@ Author: Dmitriy Yefremov
<child>
<object class="GtkButton" id="add_bouquet_tool_button">
<property name="sensitive">False</property>
<property name="can_focus">False</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Create bouquet</property>
<property name="always_show_image">True</property>
@@ -1390,13 +1375,13 @@ Author: Dmitriy Yefremov
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">1</property>
<property name="position">4</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>