diff --git a/.gitignore b/.gitignore
index 7f7cccc..627a1bd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -58,3 +58,4 @@ docs/_build/
# PyBuilder
target/
+.vscode/
diff --git a/Pipfile b/Pipfile
new file mode 100644
index 0000000..bc88337
--- /dev/null
+++ b/Pipfile
@@ -0,0 +1,15 @@
+[[source]]
+url = "https://pypi.python.org/simple"
+verify_ssl = true
+name = "pypi"
+
+[packages]
+pygobject = "*"
+jupyterlab = "*"
+ipykernel = "*"
+
+[dev-packages]
+autopep8 = "*"
+
+[requires]
+python_version = "3.8"
diff --git a/src/gtk_mediathek_player/gst_pipeline.py b/src/gtk_mediathek_player/gst_pipeline.py
new file mode 100644
index 0000000..feeadd5
--- /dev/null
+++ b/src/gtk_mediathek_player/gst_pipeline.py
@@ -0,0 +1,21 @@
+import gi
+gi.require_version('Gst', '1.0')
+
+from gi.repository import Gst
+
+def init_gst() -> None:
+ if not init_gst.gst_initialized:
+ Gst.init(None)
+ Gst.init_check(None)
+ init_gst.gst_initialized = True
+
+init_gst.gst_initialized = False
+
+
+def create_pipeline():
+ init_gst()
+
+ pipeline = Gst.Pipeline()
+
+ return pipeline
+
diff --git a/src/gtk_mediathek_player/gst_widget.py b/src/gtk_mediathek_player/gst_widget.py
new file mode 100644
index 0000000..a3a53e2
--- /dev/null
+++ b/src/gtk_mediathek_player/gst_widget.py
@@ -0,0 +1,97 @@
+from gi.repository import Gtk, Gst
+
+from . import gst_pipeline
+
+
+class GstWidget(Gtk.Box):
+ def __init__(self):
+ Gtk.Box.__init__(self)
+
+ self._state = Gst.State.NULL
+
+ self.connect('realize', self._on_realize)
+
+ def _on_realize(self, widget):
+ self._build_sink_and_widget()
+ widget.pack_start(self._sinkwidget, True, True, 0)
+ self._create_player()
+ self._bus = self._player.get_bus()
+ self._bus.add_signal_watch()
+ self._bus.connect("message::state-changed", self.on_state_changed)
+ #self.load_from_uri("https://pdvideosdaserste-a.akamaihd.net/int/2020/05/20/412d8633-dfaf-438c-93d2-f81914d44945/960-1_679663.mp4")
+ self._sinkwidget.show()
+ #self.play()
+ #self._pipeline.set_state(Gst.State.PLAYING)
+
+ def _build_pipeline(self):
+ self._pipeline = Gst.Pipeline()
+
+ bin = Gst.parse_bin_from_description('videotestsrc', True)
+
+ factory = self._pipeline.get_factory()
+ gtksink = factory.make('gtksink')
+
+ self._pipeline.add(bin)
+
+ self._pipeline.add(gtksink)
+
+ bin.link(gtksink)
+
+ return gtksink
+
+ def _build_sink_and_widget(self) -> None:
+ gtkglsink = Gst.ElementFactory.make("gtkglsink")
+
+ # TODO: fallback if not graphic acceleration is available
+
+ sinkbin = Gst.ElementFactory.make("glsinkbin")
+ sinkbin.set_property("sink", gtkglsink)
+
+ self._sink = sinkbin
+
+ self._sinkwidget = gtkglsink.get_property("widget")
+
+ def _create_player(self) -> None:
+ self._player = Gst.ElementFactory.make("playbin", "player")
+ self._player.set_property("video-sink", self._sink)
+ self._state = Gst.State.NULL
+
+
+ def load_from_uri(self, url:str) -> None:
+ self.stop()
+ self._player.set_property("uri", url)
+
+ def play(self):
+ self._player.set_state(Gst.State.PLAYING)
+
+ def pause(self):
+ self._player.set_state(Gst.State.PAUSED)
+
+ def stop(self):
+ self._player.set_state(Gst.State.NULL)
+
+ def get_state(self):
+ return self._state
+
+ def get_duration(self):
+ return self._player.query_duration(Gst.Format.TIME)[1] / Gst.SECOND
+
+ def get_position(self):
+ return self._player.query_position(Gst.Format.TIME)[1] / Gst.SECOND
+
+ def set_position(self, position):
+ if self._state == Gst.State.PLAYING or self._state == Gst.State.PAUSED:
+ self._player.seek_simple(
+ Gst.Format.TIME,
+ Gst.SeekFlags.FLUSH | Gst.SeekFlags.KEY_UNIT,
+ position * Gst.SECOND
+ )
+
+
+ def on_state_changed(self, bus, msg):
+ old, new, pending = msg.parse_state_changed()
+ if not msg.src == self._player:
+ # not from the playbin, ignore
+ return
+
+ self._state = new
diff --git a/src/gtk_mediathek_player/main_window.py b/src/gtk_mediathek_player/main_window.py
new file mode 100644
index 0000000..13f0990
--- /dev/null
+++ b/src/gtk_mediathek_player/main_window.py
@@ -0,0 +1,110 @@
+import gi
+
+from gtk_mediathek_player.tools import new_button_with_icon, new_radio_button_with_icon
+
+gi.require_version("Gtk", "3.0")
+from gi.repository import Gtk, Gio
+
+from . import player_widget
+from . import search_widget
+from . import tools
+from . import mediathek_request as mr
+
+class MainApp(Gtk.Window):
+ def __init__(self):
+ Gtk.Window.__init__(self, title="Gtk Mediathek Player")
+
+ self.set_default_size(800, 600)
+
+ self._main_container = Gtk.Overlay()
+
+ self._main_stack = Gtk.Stack()
+
+ self._fullscreen = False
+
+ self._player_widget = self._create_player_widget()
+ self._search_widget = self._create_search_widget()
+
+ self._main_stack.add_named(self._player_widget, "player")
+ self._main_stack.add_named(self._search_widget, "search")
+
+ self._main_stack.show_all()
+
+ self.set_active_pane("search")
+
+
+ self._main_container.add(self._main_stack)
+
+ self._headerbar = self._create_headerbar(True)
+
+
+ #self._main_container.add_overlay(self._create_fullscreen_bar())
+
+ self.add(self._main_container)
+
+
+ def on_search(self, _ = None):
+ if self.get_active_pane() == "player":
+ self._player_widget.stop()
+ self.set_active_pane("search")
+
+ def _create_context_switch(self):
+
+ self._search_radio = new_button_with_icon("system-search")
+
+ self._search_radio.connect("clicked", self.on_search)
+
+ # more to come...
+
+
+
+ def _create_headerbar(self, main_bar = True):
+ headerbar = Gtk.HeaderBar()
+
+ fullscreen = tools.new_button_with_icon("view-fullscreen")
+
+ headerbar.pack_end(fullscreen)
+
+ if main_bar:
+
+
+ self._create_context_switch()
+
+ headerbar.pack_start(self._search_radio)
+
+
+
+ headerbar.set_show_close_button(True)
+ headerbar.props.title = "Main Window"
+ self.set_titlebar(headerbar)
+ return headerbar
+
+
+ def _create_fullscreen_bar(self):
+ self._revealer = Gtk.Revealer()
+ self._revealer.add(self._create_headerbar(False))
+ self.set_valign(Gtk.Align.START)
+ return self._revealer
+
+
+ def _create_player_widget(self):
+ return player_widget.PlayerWidget()
+
+ def _create_search_widget(self):
+ return search_widget.SearchWidget(self)
+
+ def run(self):
+ self.connect("destroy", Gtk.main_quit)
+ self.show_all()
+ Gtk.main()
+
+ def set_active_pane(self, name: str):
+ self._main_stack.set_visible_child_name(name)
+
+ def get_active_pane(self):
+ return self._main_stack.get_visible_child_name()
+
+ def start_player(self, uri: str):
+ if self.get_active_pane() != "player":
+ self.set_active_pane("player")
+ self._player_widget.play_from_uri(uri)
\ No newline at end of file
diff --git a/src/gtk_mediathek_player/mediathek_request.py b/src/gtk_mediathek_player/mediathek_request.py
new file mode 100644
index 0000000..e79ed74
--- /dev/null
+++ b/src/gtk_mediathek_player/mediathek_request.py
@@ -0,0 +1,185 @@
+import requests
+import json
+
+
+class MediathekViewWebAnswer(object):
+ def __init__(self, answer_json: str):
+
+ def get_field(field: str):
+ if field in answer_json:
+ return answer_json[field]
+ return None
+
+ self._raw = answer_json
+
+ self._channel = get_field('channel')
+ self._topic = get_field('topic')
+ self._title = get_field('title')
+ self._description = get_field('description')
+ self._timestamp = get_field('timestamp')
+ self._duration = get_field('duration')
+ self._size = get_field('size')
+ self._url_website = get_field('url_website')
+ self._url_subtitle = get_field('url_subtitle')
+ self._filmlisteTimestamp = get_field('filmlisteTimestamp')
+ self._id = get_field('id')
+ self._url_video = get_field('url_video')
+ self._url_video_low = get_field('url_video_low')
+ self._url_video_hd = get_field('url_video_hd')
+
+ def get_best_vid_url(self) -> str:
+ if self._url_video_hd is not None:
+ return self._url_video_hd
+
+ if self._url_video is not None:
+ return self._url_video
+
+ if self._url_video_low is not None:
+ return self._url_video_low
+
+ return None
+
+ def get_lowest_vid_url(self) -> str:
+ if self._url_video_low is not None:
+ return self._url_video_low
+
+ if self._url_video is not None:
+ return self._url_video
+
+ if self._url_video_hd is not None:
+ return self._url_video_hd
+
+ return None
+
+ def get_id(self):
+ return self._id
+
+ def get_site(self):
+ return self._url_website
+
+ def get_title(self):
+ return self._title
+
+ def get_channel(self):
+ return self._channel
+
+ def get_topic(self):
+ return self._topic
+
+ def get_description(self):
+ return self._description
+
+
+class MediathekViewWebRequest(object):
+ @staticmethod
+ def from_serialization(
+ d: dict,
+ request_url="https://mediathekviewweb.de/api/query"
+ ) -> 'MediathekViewWebRequest':
+
+ json_query = d['queries'][0]
+ sort_by = d['sortBy']
+ sort_order = d['sortOrder']
+ future = d['future']
+ offset = d['offset']
+ size = d['size']
+
+ query = json_query['query']
+ fields = json_query['fields']
+
+ search_channel = "channel" in fields
+ search_topic = "topic" in fields
+ search_title = "title" in fields
+
+ return MediathekViewWebRequest(
+ query=query,
+ search_title=search_title,
+ search_channel=search_channel,
+ search_topic=search_topic,
+ sort_by=sort_by,
+ sort_order=sort_order,
+ future=future,
+ size=size,
+ offset=offset,
+ request_url=request_url
+ )
+
+ @staticmethod
+ def from_json(
+ js: str,
+ request_url="https://mediathekviewweb.de/api/query"
+ ) -> 'MediathekViewWebRequest':
+ return MediathekViewWebRequest.from_serialization(
+ d=json.loads(js),
+ request_url=request_url
+ )
+
+ def __init__(self,
+ query: str,
+ search_title: bool = True,
+ search_channel: bool = False,
+ search_topic: bool = False,
+ sort_by: str = 'timestamp',
+ sort_order: str = 'desc',
+ future: bool = False,
+ size: int = 10,
+ offset: int = 0,
+ request_url="https://mediathekviewweb.de/api/query"):
+
+ assert search_title or search_channel or search_topic
+ assert sort_order == 'desc' or sort_order == 'asc'
+
+ self._query = query
+ self._fields = []
+ if search_title:
+ self._fields.append("title")
+ if search_channel:
+ self._fields.append("channel")
+ if search_topic:
+ self._fields.append("topic")
+
+ self._sort_by = sort_by
+ self._sort_order = sort_order
+ self._future = future
+ self._size = size
+ self._offset = offset
+ self._request_url = request_url
+
+ def serialize(self):
+ query = {
+ 'queries': [
+ {
+ 'fields': self._fields,
+ 'query': self._query
+ }
+ ],
+
+ 'sortBy': self._sort_by,
+ 'sortOrder': self._sort_order,
+ 'future': self._future,
+ 'offset': self._offset,
+ 'size': self._size
+ }
+ return query
+
+ def to_json(self):
+ return json.dumps(self.serialize())
+
+ def perform_request(self):
+ headers = {'Content-type': 'text/plain'}
+ req = requests.post(self._request_url,
+ data=self.to_json(), headers=headers)
+
+ if not req.ok:
+ return None
+
+ answer = json.loads(req.text)
+
+ if 'result' in answer:
+ result = answer['result']
+
+ if 'results' in result:
+ results = result['results']
+ return [MediathekViewWebAnswer(i) for i in results]
+
+ return None
diff --git a/src/gtk_mediathek_player/player_widget.py b/src/gtk_mediathek_player/player_widget.py
new file mode 100644
index 0000000..0e802f2
--- /dev/null
+++ b/src/gtk_mediathek_player/player_widget.py
@@ -0,0 +1,112 @@
+from . import tools
+from . import gst_widget
+from gi.repository import Gtk, GLib, Gst
+import gi
+
+gi.require_version("Gtk", "3.0")
+gi.require_version('Gst', '1.0')
+
+
+class PlayerWidget(Gtk.Overlay):
+ @staticmethod
+ def create_player_for_uri(uri:str):
+ widget = PlayerWidget()
+ widget.play_from_uri(uri)
+ return widget
+
+ def __init__(self) -> None:
+ Gtk.Overlay.__init__(self)
+ self._videoarea = gst_widget.GstWidget()
+
+ self._create_controls()
+
+ self.add(self._videoarea)
+
+ self.add_overlay(self._controls)
+
+ self._duration = None
+
+ self._is_update = False
+
+ GLib.timeout_add_seconds(1, self.update_controls)
+
+ self._slider.connect("value-changed", self._on_user_slider_change)
+
+ def update_controls(self):
+
+ self._is_update = True
+
+ state = self.get_state()
+
+ if state == Gst.State.PLAYING or state == Gst.State.PAUSED:
+ self._duration = self._videoarea.get_duration()
+
+ self._slider.set_range(0, self._duration)
+
+ if self._duration is not None:
+
+ self._slider.set_value(self._videoarea.get_position())
+
+ self._is_update = False
+
+ return True
+
+ def _on_user_slider_change(self, range):
+ if self._is_update:
+ return
+ value = self._slider.get_value()
+ self._videoarea.set_position(value)
+
+ def _create_controls(self) -> Gtk.Box:
+ self._slider = Gtk.Scale.new_with_range(orientation=Gtk.Orientation.HORIZONTAL,
+ min=0,
+ max=100,
+ step=1)
+
+ self._slider.props.draw_value = False
+
+ self._play_button = tools.new_button_with_icon('media-playback-start')
+ self._stop_button = tools.new_button_with_icon('media-playback-stop')
+
+ self._controls = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
+
+ self._controls.pack_start(self._play_button,
+ expand=False,
+ fill=False,
+ padding=0)
+
+ self._controls.pack_start(self._stop_button,
+ expand=False,
+ fill=False,
+ padding=0)
+
+ self._controls.pack_end(self._slider,
+ expand=True,
+ fill=True,
+ padding=0)
+
+ self._controls.set_valign(Gtk.Align.END)
+
+ self._play_button.connect("clicked", self.play)
+ self._stop_button.connect("clicked", self.pause)
+
+ def play_from_uri(self, uri: str):
+ self._videoarea.load_from_uri(uri)
+ self.play()
+
+ def play(self, _=None):
+ if self.get_state() != Gst.State.PLAYING:
+ self._videoarea.play()
+
+ def stop(self, _=None):
+ state = self.get_state()
+ if state == Gst.State.PLAYING or state == Gst.State.PAUSED:
+ self._videoarea.stop()
+ self._duration = None
+
+ def pause(self, _=None):
+ if self.get_state() == Gst.State.PLAYING:
+ self._videoarea.pause()
+
+ def get_state(self):
+ return self._videoarea.get_state()
diff --git a/src/gtk_mediathek_player/search_widget.py b/src/gtk_mediathek_player/search_widget.py
new file mode 100644
index 0000000..fdc1ef2
--- /dev/null
+++ b/src/gtk_mediathek_player/search_widget.py
@@ -0,0 +1,129 @@
+from gi.repository import Gtk, GLib
+import gi
+
+gi.require_version("Gtk", "3.0")
+
+from . import tools
+from . import mediathek_request as mr
+
+class SearchWidget(Gtk.Box):
+ def __init__(self, parent: 'main_window.MainApp') -> None:
+ Gtk.Box.__init__(self, orientation = Gtk.Orientation.VERTICAL)
+
+ self._search_bar = Gtk.SearchBar()
+ self._search_entry = Gtk.SearchEntry()
+ self._search_bar.add(self._search_entry)
+ self.pack_start(self._search_bar, False, False, 0)
+
+ self._search_bar.show_now()
+ self._search_bar.show_all()
+ self._search_bar.set_search_mode(True)
+
+ self._search_entry.connect("activate", self.on_search)
+
+ self._main_window = parent
+ self._current_search_cards = []
+ self._search_card_container = Gtk.Box(orientation = Gtk.Orientation.VERTICAL)
+
+ self.pack_end(self._search_card_container, False, True, 10)
+
+ def dialog_response(self, widget, response_id):
+ # if the button clicked gives response OK (-5)
+ if response_id == Gtk.ResponseType.OK:
+ pass
+ # if the messagedialog is destroyed (by pressing ESC)
+ elif response_id == Gtk.ResponseType.DELETE_EVENT:
+ pass
+ widget.destroy()
+
+ def display_warning(self, message):
+ messagedialog = Gtk.MessageDialog(parent=self,
+ flags=Gtk.DialogFlags.MODAL,
+ type=Gtk.MessageType.WARNING,
+ buttons=Gtk.ButtonsType.OK,
+ message_format=message)
+ # connect the response (of the button clicked) to the function
+ # dialog_response()
+ messagedialog.connect("response", self.dialog_response)
+ # show the messagedialog
+ messagedialog.show()
+
+ def create_result_card(self, answer:mr.MediathekViewWebAnswer) -> Gtk.Box:
+
+ url = answer.get_best_vid_url()
+ title = answer.get_title()
+ description = answer.get_description()
+
+ if len(description) > 50:
+ description = description[:50] + "..."
+
+ play_button = tools.new_button_with_icon("media-playback-start")
+
+ def on_click(_ = None):
+ if url is not None:
+ self._main_window.start_player(url)
+
+ play_button.connect("clicked", on_click)
+
+ title_label = Gtk.Label()
+ title_label.set_markup(f"{title}")
+ title_label.set_justify(Gtk.Justification.LEFT)
+ title_label.set_xalign(0)
+ description_label = Gtk.Label()
+ description_label.set_markup(description)
+ description_label.set_justify(Gtk.Justification.LEFT)
+ description_label.set_xalign(0)
+
+ text_box = Gtk.Box(orientation = Gtk.Orientation.VERTICAL)
+
+ text_box.pack_start(title_label, True, False, 0)
+ text_box.pack_start(description_label, True, False, 0)
+
+
+
+ card_content = Gtk.Box(orientation = Gtk.Orientation.HORIZONTAL)
+ card_content.pack_start(text_box, True, True, 0)
+ card_content.pack_start(play_button, False, False, 0)
+
+ card = Gtk.Box(orientation = Gtk.Orientation.VERTICAL)
+ card.pack_start(card_content, True, True, 0)
+ card.pack_end(Gtk.Separator(orientation = Gtk.Orientation.HORIZONTAL), False, False, 10)
+
+
+
+ return card
+
+ def clean_results(self):
+ for result in self._current_search_cards:
+ self._search_card_container.remove(result)
+
+ self._current_search_cards = []
+
+ def on_search(self, _) -> None:
+ search_text = self._search_entry.get_text()
+
+ req = mr.MediathekViewWebRequest(query=search_text)
+
+ answers = req.perform_request()
+
+ if answers is None:
+ self.display_warning("error while performing the search request")
+ return
+
+ if len(answers) == 0:
+ self.display_warning("no results found")
+ return
+
+ self.clean_results()
+ for answer in answers:
+ #print(answer)
+ card = self.create_result_card(answer)
+ self._current_search_cards.append(card)
+ self._search_card_container.pack_start(card, False, False, 0)
+
+ self.show_all()
+
+
+
+
+
diff --git a/src/gtk_mediathek_player/tools.py b/src/gtk_mediathek_player/tools.py
new file mode 100644
index 0000000..a7c3303
--- /dev/null
+++ b/src/gtk_mediathek_player/tools.py
@@ -0,0 +1,19 @@
+from gi.repository import Gtk
+
+
+def new_button_with_icon(freedesktop_icon: str) -> Gtk.Button:
+ button = Gtk.Button()
+ icon = Gtk.Image.new_from_icon_name(freedesktop_icon, Gtk.IconSize.BUTTON)
+ button.props.image = icon
+ return button
+
+
+def new_radio_button_with_icon(freedesktop_icon: str,
+ ref_widget=None) -> Gtk.RadioButton:
+ if ref_widget is None:
+ button = Gtk.RadioButton()
+ else:
+ button = Gtk.RadioButton.new_from_widget(ref_widget)
+ icon = Gtk.Image.new_from_icon_name(freedesktop_icon, Gtk.IconSize.LARGE_TOOLBAR)
+ button.props.image = icon
+ return button
diff --git a/testing/testing.ipynb b/testing/testing.ipynb
new file mode 100644
index 0000000..309eb3b
--- /dev/null
+++ b/testing/testing.ipynb
@@ -0,0 +1,232 @@
+{
+ "metadata": {
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.8.5-final"
+ },
+ "orig_nbformat": 2,
+ "kernelspec": {
+ "name": "Python 3.8.5 64-bit ('mediathek-viewer': pipenv)",
+ "display_name": "Python 3.8.5 64-bit ('mediathek-viewer': pipenv)",
+ "metadata": {
+ "interpreter": {
+ "hash": "263d32d413daecf6c7bf0192414d6289b163e9f1c6898c3837ddf25533151cb9"
+ }
+ }
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2,
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [
+ {
+ "output_type": "stream",
+ "name": "stdout",
+ "text": [
+ "/home/jonas/Dokumente/gitRepos/mediathek-viewer/src\n"
+ ]
+ }
+ ],
+ "source": [
+ "cd ../src"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import gi\n",
+ "from gtk_mediathek_player import main_window as mw\n",
+ "from gi.repository import Gtk, Gio, Gst\n",
+ "\n",
+ "gi.require_version('Gtk', '3.0')\n",
+ "gi.require_version('Gst', '1.0')\n",
+ "\n",
+ "Gst.init(None)\n",
+ "Gst.init_check(None)\n",
+ "\n",
+ "win = mw.MainApp()\n",
+ "#Gtk.Window.fullscreen(win)\n",
+ "win.run()\n",
+ "\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [
+ {
+ "output_type": "execute_result",
+ "data": {
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "execution_count": 3
+ }
+ ],
+ "source": [
+ "Gtk.Separator(orientation = Gtk.Orientation.HORIZONTAL)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "Gtk.Align."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "bar = Gtk.SearchBar()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "bar."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [
+ {
+ "output_type": "execute_result",
+ "data": {
+ "text/plain": [
+ "''"
+ ]
+ },
+ "metadata": {},
+ "execution_count": 7
+ }
+ ],
+ "source": [
+ "search_bar.get_text()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "gtkglsink = Gst.ElementFactory.make(\"gtkglsink\")\n",
+ "glsinkbin = Gst.ElementFactory.make(\"glsinkbin\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "glsinkbin.set_property(\"sink\", gtkglsink)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "metadata": {},
+ "outputs": [
+ {
+ "output_type": "execute_result",
+ "data": {
+ "text/plain": [
+ "__gi__.GstGtkGLSink"
+ ]
+ },
+ "metadata": {},
+ "execution_count": 13
+ }
+ ],
+ "source": [
+ "type(gtkglsink)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "metadata": {},
+ "outputs": [
+ {
+ "output_type": "execute_result",
+ "data": {
+ "text/plain": [
+ "<__gi__.GstGtkGLSink object at 0x7f2e5f1b1900 (GstGtkGLSink at 0x2686190)>"
+ ]
+ },
+ "metadata": {},
+ "execution_count": 14
+ }
+ ],
+ "source": [
+ "gtkglsink"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "player = Gst.ElementFactory.make(\"playbin\", \"player\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {},
+ "outputs": [
+ {
+ "output_type": "execute_result",
+ "data": {
+ "text/plain": [
+ "<__gi__.GstPlayBin object at 0x7f0b3559cfc0 (GstPlayBin at 0x37fe8f0)>"
+ ]
+ },
+ "metadata": {},
+ "execution_count": 8
+ }
+ ],
+ "source": []
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "Gst.State.NU"
+ ]
+ }
+ ]
+}
\ No newline at end of file