diff --git a/README.md b/README.md
index 60a66588..54dd55c3 100644
--- a/README.md
+++ b/README.md
@@ -3,24 +3,25 @@
## Enigma2 channel and satellites list editor for GNU/Linux.
Experimental support of Neutrino-MP or others on the same basis (BPanther, etc).
Focused on the convenience of working in lists from the keyboard. The mouse is also fully supported (Drag and Drop etc)
-
### Keyboard shortcuts:
-**Ctrl + X, C, V, Up, Down, PageUp, PageDown, Home, End, S, T, E, L, H, Space; Insert, Delete, F2.**
-*Insert* - copies the selected channels from the main list to the bouquet or inserts (creates) a new bouquet.
-*Ctrl + X* - only in bouquet list. *Ctrl + C* - only in services list.
-Clipboard is "rubber". There is an accumulation before the insertion!
-*Ctrl + E* - edit.
-*Ctrl + R, F2* - rename.
-*Ctrl + S, T* in Satellites edit tool for create satellite or transponder.
-*Ctrl + L* - parental lock.
-*Ctrl + H* - hide/skip.
-*Space* - select/deselect.
-*Left/Right* - remove selection.
-
+**Ctrl + X, C, V, Up, Down, PageUp, PageDown, Home, End, S, T, E, L, H, Space; Insert, Delete, F2, Enter, P**
+**Insert** - copies the selected channels from the main list to the bouquet or inserts (creates) a new bouquet.
+**Ctrl + X** - only in bouquet list. **Ctrl + C** - only in services list.
+Clipboard is **"rubber"**. There is an accumulation before the insertion!
+**Ctrl + E** - edit.
+**Ctrl + R, F2** - rename.
+**Ctrl + S, T** in Satellites edit tool for create satellite or transponder.
+**Ctrl + L** - parental lock.
+**Ctrl + H** - hide/skip.
+**Space** - select/deselect.
+**Left/Right** - remove selection.
+**P** - enable/disable preview mode for IPTV in the bouquet list.
+**Enter** - Starting play IPTV or other stream in the bouquet list.
### Extra:
-Multiple selections in lists only with Space key (as in file managers).
-Ability to import IPTV into bouquet (Neutrino WEBTV) from m3u files.
-Tool for downloading picons from lyngsat.com.
+* Multiple selections in lists only with Space key (as in file managers).
+* Ability to import IPTV into bouquet (Neutrino WEBTV) from m3u files.
+* Tool for downloading picons from lyngsat.com.
+* Preview (playing) IPTV or other streams directly from the bouquet list(should be installed VLC).
### Minimum requirements:
Python >= 3.5.2 and GTK+ 3 with PyGObject bindings.
#### Note.
diff --git a/app/ui/main_app_window.py b/app/ui/main_app_window.py
index a8af7207..8fee851b 100644
--- a/app/ui/main_app_window.py
+++ b/app/ui/main_app_window.py
@@ -11,6 +11,7 @@ from app.eparser.ecommons import CAS, Flag
from app.eparser.enigma.bouquets import BqServiceType
from app.eparser.neutrino.bouquets import BqType
from app.properties import get_config, write_config, Profile
+from app.tools.media import Player
from .iptv import IptvDialog
from .search import SearchProvider
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, LOCKED_ICON, HIDE_ICON, IPTV_ICON, MOVE_KEYS
@@ -117,6 +118,7 @@ class MainAppWindow:
"on_services_add_new": self.on_services_add_new,
"on_iptv": self.on_iptv,
"on_fav_iptv_mode": self.on_fav_iptv_mode,
+ "on_drawing_area_realize": self.on_drawing_area_realize,
"on_new_bouquet": self.on_new_bouquet,
"on_bouquets_edit": self.on_bouquets_edit,
"on_create_bouquet_for_current_satellite": self.on_create_bouquet_for_current_satellite,
@@ -137,7 +139,10 @@ class MainAppWindow:
self._picons = {}
self._blacklist = set()
self._current_bq_name = None
+ # Player
self._iptv_preview_mode = False
+ self._player = None
+ self._is_played = False
builder = Gtk.Builder()
builder.set_translation_domain("demon-editor")
@@ -156,6 +161,8 @@ class MainAppWindow:
self._bouquets_model = builder.get_object("bouquets_tree_store")
self._status_bar = builder.get_object("status_bar")
self._player_frame = builder.get_object("player_frame")
+ self._drawing_area_xid = None
+ self._fav_iptv_mode_popup_item = builder.get_object("fav_iptv_mode_popup_item")
self._profile_label = builder.get_object("profile_label")
self._ip_label = builder.get_object("ip_label")
self._ip_label.set_text(self._options.get(self._profile).get("host"))
@@ -208,9 +215,13 @@ class MainAppWindow:
""" Function for force ctrl press event for view """
event.state |= Gdk.ModifierType.CONTROL_MASK
+ @run_idle
def on_quit(self, *args):
""" Called before app quit """
write_config(self._options) # storing current config
+ if self._player:
+ self._player.stop()
+ self._player.release()
Gtk.main_quit()
def on_resize(self, window):
@@ -768,6 +779,13 @@ class MainAppWindow:
self.on_service_edit(view)
elif key == Gdk.KEY_Left or key == Gdk.KEY_Right:
view.do_unselect_all(view)
+ elif (key == Gdk.KEY_P or key == Gdk.KEY_p) and model_name == self._FAV_LIST_NAME:
+ self._iptv_preview_mode = not self._iptv_preview_mode
+ self._fav_iptv_mode_popup_item.set_active(self._iptv_preview_mode)
+ self.on_fav_iptv_mode(self._fav_iptv_mode_popup_item)
+ elif (key == Gdk.KEY_Return or key == Gdk.KEY_KP_Enter) and model_name == self._FAV_LIST_NAME:
+ if self._iptv_preview_mode:
+ self.test_iptv()
def on_download(self, item):
show_download_dialog(transient=self._main_window,
@@ -902,11 +920,6 @@ class MainAppWindow:
if response != Gtk.ResponseType.CANCEL:
self.update_fav_num_column(self._fav_model)
- @run_idle
- def on_fav_iptv_mode(self, item):
- self._iptv_preview_mode = item.get_active()
- self._player_frame.set_visible(self._iptv_preview_mode)
-
def on_insert_marker(self, view):
""" Inserts marker into bouquet services list. """
insert_marker(view, self._bouquets, self.get_selected_bouquet(), self._services, self._main_window)
@@ -915,19 +928,48 @@ class MainAppWindow:
def on_edit_marker(self, view):
edit_marker(view, self._bouquets, self.get_selected_bouquet(), self._services, self._main_window)
- def on_fav_press(self, menu, event):
- event_type = event.get_event_type()
- if event_type == Gdk.EventType.BUTTON_PRESS and event.button == Gdk.BUTTON_SECONDARY:
- self.on_view_popup_menu(menu, event)
- elif self._iptv_preview_mode and event_type == Gdk.EventType.DOUBLE_BUTTON_PRESS:
- path, column = self._fav_view.get_cursor()
- if path:
- row = self._fav_model[path][:]
- if row[5] == BqServiceType.IPTV.value:
- self.test_iptv(row[7])
+ def on_fav_iptv_mode(self, item):
+ self._iptv_preview_mode = item.get_active()
+ self._player_frame.set_visible(self._iptv_preview_mode)
+ if not self._iptv_preview_mode:
+ self.on_player_stop(None)
- def test_iptv(self, fav_id):
- print(fav_id)
+ def on_fav_press(self, menu, event):
+ self.on_view_popup_menu(menu, event)
+
+ if self._iptv_preview_mode and event.get_event_type() == Gdk.EventType.DOUBLE_BUTTON_PRESS:
+ self.test_iptv()
+
+ def test_iptv(self):
+ path, column = self._fav_view.get_cursor()
+ if path:
+ row = self._fav_model[path][:]
+ if row[5] == BqServiceType.IPTV.value:
+ profile = Profile(self._profile)
+ data = row[7].split(":" if profile is Profile.ENIGMA_2 else "::")
+ url = data[-3 if profile is Profile.ENIGMA_2 else 0]
+ url = url.replace("%3a", ":") if profile is Profile.ENIGMA_2 else url
+ if not url:
+ return
+
+ self.on_player_stop(None)
+
+ if not self._player:
+ self._player = Player.get_vlc_instance().media_player_new()
+ self._player.set_xwindow(self._drawing_area_xid)
+
+ if self._player:
+ self._player.set_mrl(url)
+ self._is_played = True
+ self._player.play()
+
+ def on_player_stop(self, item):
+ if self._player:
+ self._player.stop()
+ self._is_played = False
+
+ def on_drawing_area_realize(self, widget):
+ self._drawing_area_xid = widget.get_window().get_xid()
def on_locate_in_services(self, view):
locate_in_services(view, self._services_view, self._main_window)
diff --git a/app/ui/main_window.glade b/app/ui/main_window.glade
index 99d60c9d..274fe885 100644
--- a/app/ui/main_window.glade
+++ b/app/ui/main_window.glade
@@ -2057,6 +2057,7 @@