def __init__(self, radios_manager=None): """ Init Popover @param radios_manager as Radios """ Gtk.Popover.__init__(self) self._tunein = TuneIn() if radios_manager is not None: self._radios_manager = radios_manager else: self._radios_manager = Radios() self._current_url = None self._previous_urls = [] self._covers_to_download = [] self._stack = Gtk.Stack() self._stack.set_property('expand', True) self._stack.show() builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/TuneinPopover.ui') builder.connect_signals(self) widget = builder.get_object('widget') widget.attach(self._stack, 0, 2, 4, 1) self._back_btn = builder.get_object('back_btn') self._home_btn = builder.get_object('home_btn') self._label = builder.get_object('label') self._view = Gtk.FlowBox() self._view.set_selection_mode(Gtk.SelectionMode.NONE) self._view.set_max_children_per_line(100) self._view.set_property('row-spacing', 10) self._view.set_property('expand', True) self._view.show() self._spinner = builder.get_object('spinner') builder.get_object('viewport').add(self._view) builder.get_object('viewport').set_property('margin', 10) self._scrolled = builder.get_object('scrolled') self._stack.add_named(self._spinner, 'spinner') self._stack.add_named(builder.get_object('notfound'), 'notfound') self._stack.add_named(self._scrolled, 'scrolled') self.add(widget) size_setting = Lp().settings.get_value('window-size') if isinstance(size_setting[1], int): self.set_size_request(700, size_setting[1] * 0.7) else: self.set_size_request(700, 400)
def __init__(self, radios_manager=None): """ Init Popover @param radios_manager as Radios """ Gtk.Popover.__init__(self) self.__tunein = TuneIn() if radios_manager is not None: self.__radios_manager = radios_manager else: self.__radios_manager = Radios() self.__current_url = None self.__timeout_id = None self.__previous_urls = [] self.__covers_to_download = [] self.__stack = Gtk.Stack() self.__stack.set_property("expand", True) self.__stack.show() builder = Gtk.Builder() builder.add_from_resource("/org/gnome/Lollypop/TuneinPopover.ui") builder.connect_signals(self) widget = builder.get_object("widget") widget.attach(self.__stack, 0, 2, 5, 1) self.__back_btn = builder.get_object("back_btn") self.__home_btn = builder.get_object("home_btn") self.__label = builder.get_object("label") self.__view = Gtk.FlowBox() self.__view.set_selection_mode(Gtk.SelectionMode.NONE) self.__view.set_max_children_per_line(100) self.__view.set_property("row-spacing", 10) self.__view.set_property("expand", True) self.__view.show() self.__spinner = builder.get_object("spinner") builder.get_object("viewport").add(self.__view) builder.get_object("viewport").set_property("margin", 10) self.__scrolled = builder.get_object("scrolled") self.__stack.add_named(self.__spinner, "spinner") self.__stack.add_named(builder.get_object("notfound"), "notfound") self.__stack.add_named(self.__scrolled, "scrolled") self.add(widget) self.connect("map", self.__on_map) self.connect("unmap", self.__on_unmap)
def __init__(self, radio_manager): """ Init Popover @param radio manager as RadioManager """ Gtk.Popover.__init__(self) self._tunein = TuneIn() self._radio_manager = radio_manager self._current_url = None self._previous_urls = [] self._current_items = [] self._stack = Gtk.Stack() self._stack.set_property('expand', True) self._stack.show() builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/TuneinPopover.ui') builder.connect_signals(self) widget = builder.get_object('widget') widget.attach(self._stack, 0, 2, 4, 1) self._back_btn = builder.get_object('back_btn') self._home_btn = builder.get_object('home_btn') self._label = builder.get_object('label') self._view = Gtk.FlowBox() self._view.set_selection_mode(Gtk.SelectionMode.NONE) self._view.set_max_children_per_line(100) self._view.set_property('row-spacing', 10) self._view.set_property('expand', True) self._view.show() builder.get_object('viewport').add(self._view) builder.get_object('viewport').set_property('margin', 10) self._scrolled = builder.get_object('scrolled') self._spinner = builder.get_object('spinner') self._not_found = builder.get_object('notfound') self._stack.add(self._spinner) self._stack.add(self._not_found) self._stack.add(self._scrolled) self._stack.set_visible_child(self._spinner) self.add(widget)
def __init__(self, radios_manager=None): """ Init Popover @param radios_manager as Radios """ Gtk.Popover.__init__(self) self._tunein = TuneIn() if radios_manager is not None: self._radios_manager = radios_manager else: self._radios_manager = Radios() self._current_url = None self._timeout_id = None self._previous_urls = [] self._covers_to_download = [] self._stack = Gtk.Stack() self._stack.set_property('expand', True) self._stack.show() builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/TuneinPopover.ui') builder.connect_signals(self) widget = builder.get_object('widget') widget.attach(self._stack, 0, 2, 5, 1) self._back_btn = builder.get_object('back_btn') self._home_btn = builder.get_object('home_btn') self._label = builder.get_object('label') self._view = Gtk.FlowBox() self._view.set_selection_mode(Gtk.SelectionMode.NONE) self._view.set_max_children_per_line(100) self._view.set_property('row-spacing', 10) self._view.set_property('expand', True) self._view.show() self._spinner = builder.get_object('spinner') builder.get_object('viewport').add(self._view) builder.get_object('viewport').set_property('margin', 10) self._scrolled = builder.get_object('scrolled') self._stack.add_named(self._spinner, 'spinner') self._stack.add_named(builder.get_object('notfound'), 'notfound') self._stack.add_named(self._scrolled, 'scrolled') self.add(widget) size_setting = Lp().settings.get_value('window-size') if isinstance(size_setting[1], int): self.set_size_request(700, size_setting[1]*0.7) else: self.set_size_request(700, 400)
def __init__(self, radios_manager=None): """ Init Popover @param radios_manager as Radios """ Gtk.Popover.__init__(self) self._tunein = TuneIn() if radios_manager is not None: self._radios_manager = radios_manager else: self._radios_manager = Radios() self._current_url = None self._previous_urls = [] self._stack = Gtk.Stack() self._stack.set_property("expand", True) self._stack.show() builder = Gtk.Builder() builder.add_from_resource("/org/gnome/Lollypop/TuneinPopover.ui") builder.connect_signals(self) widget = builder.get_object("widget") widget.attach(self._stack, 0, 2, 4, 1) self._back_btn = builder.get_object("back_btn") self._home_btn = builder.get_object("home_btn") self._label = builder.get_object("label") self._view = Gtk.FlowBox() self._view.set_selection_mode(Gtk.SelectionMode.NONE) self._view.set_max_children_per_line(100) self._view.set_property("row-spacing", 10) self._view.set_property("expand", True) self._view.show() builder.get_object("viewport").add(self._view) builder.get_object("viewport").set_property("margin", 10) self._scrolled = builder.get_object("scrolled") self._stack.add_named(builder.get_object("spinner"), "spinner") self._stack.add_named(builder.get_object("notfound"), "notfound") self._stack.add_named(self._scrolled, "scrolled") self._stack.set_visible_child_name("spinner") self.add(widget) size_setting = Lp().settings.get_value("window-size") if isinstance(size_setting[1], int): self.set_size_request(700, size_setting[1] * 0.7) else: self.set_size_request(700, 400)
class TuneinPopover(Gtk.Popover): """ Popover showing tunin radios """ def __init__(self, radios_manager=None): """ Init Popover @param radios_manager as Radios """ Gtk.Popover.__init__(self) self.__tunein = TuneIn() if radios_manager is not None: self.__radios_manager = radios_manager else: self.__radios_manager = Radios() self.__current_url = None self.__timeout_id = None self.__previous_urls = [] self.__covers_to_download = [] self.__stack = Gtk.Stack() self.__stack.set_property("expand", True) self.__stack.show() builder = Gtk.Builder() builder.add_from_resource("/org/gnome/Lollypop/TuneinPopover.ui") builder.connect_signals(self) widget = builder.get_object("widget") widget.attach(self.__stack, 0, 2, 5, 1) self.__back_btn = builder.get_object("back_btn") self.__home_btn = builder.get_object("home_btn") self.__label = builder.get_object("label") self.__view = Gtk.FlowBox() self.__view.set_selection_mode(Gtk.SelectionMode.NONE) self.__view.set_max_children_per_line(100) self.__view.set_property("row-spacing", 10) self.__view.set_property("expand", True) self.__view.show() self.__spinner = builder.get_object("spinner") builder.get_object("viewport").add(self.__view) builder.get_object("viewport").set_property("margin", 10) self.__scrolled = builder.get_object("scrolled") self.__stack.add_named(self.__spinner, "spinner") self.__stack.add_named(builder.get_object("notfound"), "notfound") self.__stack.add_named(self.__scrolled, "scrolled") self.add(widget) self.connect("map", self.__on_map) self.connect("unmap", self.__on_unmap) def populate(self, url=None): """ Populate views @param url as string """ if url is None and self.__current_url is not None: return self.__spinner.start() self.__clear() self.__stack.set_visible_child_name("spinner") self.__current_url = url self.__back_btn.set_sensitive(False) self.__home_btn.set_sensitive(False) self.__label.set_text(_("Please wait…")) t = Thread(target=self.__populate, args=(url, )) t.daemon = True t.start() ####################### # PROTECTED # ####################### def _on_back_btn_clicked(self, btn): """ Go to previous URL @param btn as Gtk.Button """ url = None self.__current_url = None if self.__previous_urls: url = self.__previous_urls.pop() self.__stack.set_visible_child_name("spinner") self.__spinner.start() self.__clear() self.populate(url) def _on_home_btn_clicked(self, btn): """ Go to root URL @param btn as Gtk.Button """ self.__current_url = None self.__previous_urls = [] self.populate() def _on_search_changed(self, widget): """ Timeout filtering, call _really_do_filterting() after timeout @param widget as Gtk.TextEntry """ self.__current_url = None if self.__timeout_id is not None: GLib.source_remove(self.__timeout_id) self.__timeout_id = None text = widget.get_text() if text != "": self.__home_btn.set_sensitive(True) self.__timeout_id = GLib.timeout_add(1000, self.__on_search_timeout, text) else: self.__home_btn.set_sensitive(False) self.__timeout_id = GLib.timeout_add(1000, self._on_home_btn_clicked, None) ####################### # PRIVATE # ####################### def __show_not_found(self, message=""): """ Show not found message @param message as str """ # TODO Add a string self.__label.set_text(message) self.__stack.set_visible_child_name("notfound") self.__home_btn.set_sensitive(True) def __populate(self, url): """ Same as populate() @param url as string @thread safe """ try: if url is None: items = self.__tunein.get_items( "http://opml.radiotime.com/Browse.ashx?c=") else: items = self.__tunein.get_items(url) if self.__current_url == url: if items: self.__add_items(items, url) else: GLib.idle_add(self.__show_not_found) except: GLib.idle_add(self.__show_not_found, _("Can't connect to TuneIn…")) def __add_items(self, items, url): """ Add current items @param items as [TuneItem] @parma url as str @thread safe """ GLib.idle_add(self.__add_item, items, url) def __add_item(self, items, url): """ Add item @param items as [TuneItem] @param url as str """ if url != self.__current_url: return if not items: self.__home_btn.set_sensitive(self.__current_url is not None) t = Thread(target=self.__download_images, args=(url, )) t.daemon = True t.start() return item = items.pop(0) child = Gtk.Grid() child.set_column_spacing(5) child.set_property("halign", Gtk.Align.START) child.show() link = Gtk.LinkButton.new_with_label(item.URL, item.TEXT) # Hack link.get_children()[0].set_ellipsize(Pango.EllipsizeMode.END) link.connect("activate-link", self.__on_activate_link, item) link.show() if item.TYPE == "audio": link.set_tooltip_text(_("Play")) button = Gtk.Button.new_from_icon_name("list-add-symbolic", Gtk.IconSize.MENU) button.connect("clicked", self.__on_button_clicked, item) button.set_relief(Gtk.ReliefStyle.NONE) button.set_property("valign", Gtk.Align.CENTER) # Translators: radio context button.set_tooltip_text(_("Add")) button.show() child.add(button) image = Gtk.Image.new() image.set_property("width-request", ArtSize.MEDIUM) image.set_property("height-request", ArtSize.MEDIUM) image.show() child.add(image) self.__covers_to_download.append((item, image)) else: link.set_tooltip_text("") child.add(link) self.__view.add(child) # Remove spinner if exist if self.__stack.get_visible_child_name() == "spinner": self.__stack.set_visible_child_name("scrolled") self.__spinner.stop() self.__label.set_text("") if self.__current_url is not None: self.__back_btn.set_sensitive(True) GLib.idle_add(self.__add_items, items, url) def __download_images(self, url): """ Download and set image for TuneItem @param url as str @thread safe """ while self.__covers_to_download and url == self.__current_url: (item, image) = self.__covers_to_download.pop(0) try: f = Lio.File.new_for_uri(item.LOGO) (status, data, tag) = f.load_contents() if status: bytes = GLib.Bytes(data) stream = Gio.MemoryInputStream.new_from_bytes(bytes) bytes.unref() if stream is not None: GLib.idle_add(self.__set_image, image, stream) except Exception as e: GLib.idle_add(image.set_from_icon_name, "image-missing", Gtk.IconSize.LARGE_TOOLBAR) print("TuneinPopover::_download_images: %s" % e) def __set_image(self, image, stream): """ Set image with stream @param image as Gtk.Image @param stream as Gio.MemoryInputStream """ try: # Strange issue #969, stream is None # But there is a check in __download_images() pixbuf = GdkPixbuf.Pixbuf.new_from_stream_at_scale( stream, ArtSize.MEDIUM, ArtSize.MEDIUM, True, None) stream.close() surface = Gdk.cairo_surface_create_from_pixbuf(pixbuf, 0, None) image.set_from_surface(surface) except Exception as e: print("TuneinPopover::__set_image():", e) def __clear(self): """ Clear view """ for child in self.__view.get_children(): self.__view.remove(child) child.destroy() def __add_radio(self, item): """ Add selected radio @param item as TuneIn Item """ # Get cover art try: cache = Art._RADIOS_PATH s = Lio.File.new_for_uri(item.LOGO) d = Lio.File.new_for_path(cache + "/%s.png" % item.TEXT.replace("/", "-")) s.copy(d, Gio.FileCopyFlags.OVERWRITE, None, None) except Exception as e: print("TuneinPopover::_add_radio: %s" % e) url = item.URL # Tune in embbed uri in ashx files, so get content if possible try: f = Lio.File.new_for_uri(url) (status, data, tag) = f.load_contents() if status: url = data.decode("utf-8").split("\n")[0] except Exception as e: print("TuneinPopover::_add_radio: %s" % e) self.__radios_manager.add(item.TEXT.replace("/", "-"), url) def __on_map(self, widget): """ Resize and disable global shortcuts @param widget as Gtk.Widget """ # FIXME Not needed with GTK >= 3.18 Lp().window.enable_global_shortcuts(False) size = Lp().window.get_size() self.set_size_request(size[0] * 0.5, size[1] * 0.7) def __on_unmap(self, widget): """ Enable global shortcuts @param widget as Gtk.Widget """ # FIXME Not needed with GTK >= 3.18 Lp().window.enable_global_shortcuts(True) def __on_activate_link(self, link, item): """ Update header with new link @param link as Gtk.LinkButton @param item as TuneIn Item """ if item.TYPE == "link": self.__scrolled.get_vadjustment().set_value(0.0) if self.__current_url is not None: self.__previous_urls.append(self.__current_url) self.populate(item.URL) elif item.TYPE == "audio": if get_network_available(): # Cache for toolbar t = Thread(target=Lp().art.copy_uri_to_cache, args=(item.LOGO, item.TEXT, Lp().window.toolbar.artsize)) t.daemon = True t.start() # Cache for MPRIS t = Thread(target=Lp().art.copy_uri_to_cache, args=(item.LOGO, item.TEXT, ArtSize.BIG)) t.daemon = True t.start() # Cache for miniplayer t = Thread(target=Lp().art.copy_uri_to_cache, args=(item.LOGO, item.TEXT, WindowSize.SMALL)) t.daemon = True t.start() Lp().player.load_external(item.URL, item.TEXT) Lp().player.play_this_external(item.URL) return True def __on_button_clicked(self, button, item): """ Play the radio @param link as Gtk.Button @param item as TuneIn Item """ self.__timeout_id = None t = Thread(target=self.__add_radio, args=(item, )) t.daemon = True t.start() self.hide() def __on_search_timeout(self, string): """ Populate widget @param string as str """ self.__timeout_id = None url = "http://opml.radiotime.com/Search.ashx?query=%s" %\ GLib.uri_escape_string(string, "/", False) self.populate(url)
class TuneinPopover(Gtk.Popover): """ Popover showing tunin radios """ def __init__(self, radios_manager=None): """ Init Popover @param radios_manager as Radios """ Gtk.Popover.__init__(self) self._tunein = TuneIn() if radios_manager is not None: self._radios_manager = radios_manager else: self._radios_manager = Radios() self._current_url = None self._timeout_id = None self._previous_urls = [] self._covers_to_download = [] self._stack = Gtk.Stack() self._stack.set_property('expand', True) self._stack.show() builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/TuneinPopover.ui') builder.connect_signals(self) widget = builder.get_object('widget') widget.attach(self._stack, 0, 2, 5, 1) self._back_btn = builder.get_object('back_btn') self._home_btn = builder.get_object('home_btn') self._label = builder.get_object('label') self._view = Gtk.FlowBox() self._view.set_selection_mode(Gtk.SelectionMode.NONE) self._view.set_max_children_per_line(100) self._view.set_property('row-spacing', 10) self._view.set_property('expand', True) self._view.show() self._spinner = builder.get_object('spinner') builder.get_object('viewport').add(self._view) builder.get_object('viewport').set_property('margin', 10) self._scrolled = builder.get_object('scrolled') self._stack.add_named(self._spinner, 'spinner') self._stack.add_named(builder.get_object('notfound'), 'notfound') self._stack.add_named(self._scrolled, 'scrolled') self.add(widget) self.connect('map', self._on_map) self.connect('unmap', self._on_unmap) def populate(self, url=None): """ Populate views @param url as string """ if url is None and self._current_url is not None: return self._spinner.start() self._clear() self._stack.set_visible_child_name('spinner') self._current_url = url self._back_btn.set_sensitive(False) self._home_btn.set_sensitive(False) self._label.set_text(_("Please wait...")) t = Thread(target=self._populate, args=(url,)) t.daemon = True t.start() ####################### # PRIVATE # ####################### def _show_not_found(self, message=""): """ Show not found message @param message as str """ # TODO Add a string self._label.set_text(message) self._stack.set_visible_child_name('notfound') self._home_btn.set_sensitive(True) def _populate(self, url): """ Same as populate() @param url as string @thread safe """ try: if url is None: items = self._tunein.get_items( "http://opml.radiotime.com/Browse.ashx?c=") else: items = self._tunein.get_items(url) if self._current_url == url: if items: self._add_items(items, url) else: GLib.idle_add(self._show_not_found) except: GLib.idle_add(self._show_not_found, _("Can't connect to TuneIn...")) def _add_items(self, items, url): """ Add current items @param items as [TuneItem] @parma url as str @thread safe """ GLib.idle_add(self._add_item, items, url) def _add_item(self, items, url): """ Add item @param items as [TuneItem] @param url as str """ if url != self._current_url: return if not items: self._home_btn.set_sensitive(self._current_url is not None) t = Thread(target=self._download_images, args=(url,)) t.daemon = True t.start() return item = items.pop(0) child = Gtk.Grid() child.set_column_spacing(5) child.set_property('halign', Gtk.Align.START) child.show() link = Gtk.LinkButton.new_with_label(item.URL, item.TEXT) # Hack link.get_children()[0].set_ellipsize(Pango.EllipsizeMode.END) link.connect('activate-link', self._on_activate_link, item) link.show() if item.TYPE == "audio": link.set_tooltip_text(_("Play")) button = Gtk.Button.new_from_icon_name('list-add-symbolic', Gtk.IconSize.MENU) button.connect('clicked', self._on_button_clicked, item) button.set_relief(Gtk.ReliefStyle.NONE) button.set_property('valign', Gtk.Align.CENTER) button.set_tooltip_text(_("Add")) button.show() child.add(button) image = Gtk.Image.new() image.set_property('width-request', ArtSize.MEDIUM) image.set_property('height-request', ArtSize.MEDIUM) image.show() child.add(image) self._covers_to_download.append((item, image)) else: link.set_tooltip_text('') child.add(link) self._view.add(child) # Remove spinner if exist if self._stack.get_visible_child_name() == 'spinner': self._stack.set_visible_child_name('scrolled') self._spinner.stop() self._label.set_text("") if self._current_url is not None: self._back_btn.set_sensitive(True) GLib.idle_add(self._add_items, items, url) def _download_images(self, url): """ Download and set image for TuneItem @param url as str @thread safe """ while self._covers_to_download and url == self._current_url: (item, image) = self._covers_to_download.pop(0) try: f = Gio.File.new_for_uri(item.LOGO) (status, data, tag) = f.load_contents() if status: stream = Gio.MemoryInputStream.new_from_data(data, None) if stream is not None: GLib.idle_add(self._set_image, image, stream) except Exception as e: GLib.idle_add(image.set_from_icon_name, "image-missing", Gtk.IconSize.LARGE_TOOLBAR) print("TuneinPopover::_download_images: %s" % e) def _set_image(self, image, stream): """ Set image with stream @param image as Gtk.Image @param stream as Gio.MemoryInputStream """ pixbuf = GdkPixbuf.Pixbuf.new_from_stream_at_scale(stream, ArtSize.MEDIUM, ArtSize.MEDIUM, True, None) surface = Gdk.cairo_surface_create_from_pixbuf(pixbuf, 0, None) del pixbuf image.set_from_surface(surface) del surface def _clear(self): """ Clear view """ for child in self._view.get_children(): self._view.remove(child) child.destroy() def _add_radio(self, item): """ Add selected radio @param item as TuneIn Item """ # Get cover art try: cache = Art._RADIOS_PATH s = Gio.File.new_for_uri(item.LOGO) d = Gio.File.new_for_path(cache+"/%s.png" % item.TEXT.replace('/', '-')) s.copy(d, Gio.FileCopyFlags.OVERWRITE, None, None) except Exception as e: print("TuneinPopover::_add_radio: %s" % e) url = item.URL # Tune in embbed uri in ashx files, so get content if possible try: f = Gio.File.new_for_uri(url) (status, data, tag) = f.load_contents() if status: url = data.decode('utf-8').split('\n')[0] except Exception as e: print("TuneinPopover::_add_radio: %s" % e) self._radios_manager.add(item.TEXT.replace('/', '-'), url) def _on_map(self, widget): """ Resize and disable global shortcuts @param widget as Gtk.Widget """ # FIXME Not needed with GTK >= 3.18 Lp().window.enable_global_shorcuts(False) size = Lp().window.get_size() self.set_size_request(size[0]*0.5, size[1]*0.7) def _on_unmap(self, widget): """ Enable global shorcuts @param widget as Gtk.Widget """ # FIXME Not needed with GTK >= 3.18 Lp().window.enable_global_shorcuts(True) def _on_back_btn_clicked(self, btn): """ Go to previous URL @param btn as Gtk.Button """ url = None self._current_url = None if self._previous_urls: url = self._previous_urls.pop() self._stack.set_visible_child_name('spinner') self._spinner.start() self._clear() self.populate(url) def _on_home_btn_clicked(self, btn): """ Go to root URL @param btn as Gtk.Button """ self._current_url = None self._previous_urls = [] self.populate() def _on_activate_link(self, link, item): """ Update header with new link @param link as Gtk.LinkButton @param item as TuneIn Item """ if item.TYPE == "link": self._scrolled.get_vadjustment().set_value(0.0) if self._current_url is not None: self._previous_urls.append(self._current_url) self.populate(item.URL) elif item.TYPE == "audio": # Only toolbar will get this one, so only create small in cache if Gio.NetworkMonitor.get_default().get_network_available(): t = Thread(target=Lp().art.copy_uri_to_cache, args=(item.LOGO, item.TEXT, Lp().window.toolbar.artsize)) t.daemon = True t.start() t = Thread(target=Lp().art.copy_uri_to_cache, args=(item.LOGO, item.TEXT, ArtSize.BIG)) t.daemon = True t.start() Lp().player.load_external(item.URL, item.TEXT) Lp().player.play_this_external(item.URL) return True def _on_button_clicked(self, button, item): """ Play the radio @param link as Gtk.Button @param item as TuneIn Item """ self._timeout_id = None t = Thread(target=self._add_radio, args=(item,)) t.daemon = True t.start() self.hide() def _on_search_changed(self, widget): """ Timeout filtering, call _really_do_filterting() after timeout @param widget as Gtk.TextEntry """ self._current_url = None if self._timeout_id is not None: GLib.source_remove(self._timeout_id) self._timeout_id = None text = widget.get_text() if text != "": self._home_btn.set_sensitive(True) self._timeout_id = GLib.timeout_add(1000, self._on_search_timeout, text) else: self._home_btn.set_sensitive(False) self._timeout_id = GLib.timeout_add(1000, self._on_home_btn_clicked, None) def _on_search_timeout(self, string): """ Populate widget @param string as str """ self._timeout_id = None url = "http://opml.radiotime.com/Search.ashx?query=%s" % escape(string) self.populate(url)
class TuneinPopover(Gtk.Popover): """ Popover showing tunin radios """ def __init__(self, radios_manager=None): """ Init Popover @param radios_manager as Radios """ Gtk.Popover.__init__(self) self._tunein = TuneIn() if radios_manager is not None: self._radios_manager = radios_manager else: self._radios_manager = Radios() self._current_url = None self._previous_urls = [] self._stack = Gtk.Stack() self._stack.set_property('expand', True) self._stack.show() builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/TuneinPopover.ui') builder.connect_signals(self) widget = builder.get_object('widget') widget.attach(self._stack, 0, 2, 4, 1) self._back_btn = builder.get_object('back_btn') self._home_btn = builder.get_object('home_btn') self._label = builder.get_object('label') self._view = Gtk.FlowBox() self._view.set_selection_mode(Gtk.SelectionMode.NONE) self._view.set_max_children_per_line(100) self._view.set_property('row-spacing', 10) self._view.set_property('expand', True) self._view.show() builder.get_object('viewport').add(self._view) builder.get_object('viewport').set_property('margin', 10) self._scrolled = builder.get_object('scrolled') self._stack.add_named(builder.get_object('spinner'), 'spinner') self._stack.add_named(builder.get_object('notfound'), 'notfound') self._stack.add_named(self._scrolled, 'scrolled') self._stack.set_visible_child_name('spinner') self.add(widget) size_setting = Lp().settings.get_value('window-size') if isinstance(size_setting[1], int): self.set_size_request(700, size_setting[1] * 0.7) else: self.set_size_request(700, 400) def populate(self, url=None): """ Populate views @param url as string """ if not self._view.get_children(): self._current_url = url self._clear() self._back_btn.set_sensitive(False) self._home_btn.set_sensitive(False) self._label.set_text(_("Please wait...")) t = Thread(target=self._populate, args=(url, )) t.daemon = True t.start() ####################### # PRIVATE # ####################### def _show_not_found(self): """ Show not found message """ self._label.set_text(_("Can't connect to TuneIn...")) self._stack.set_visible_child_name('notfound') self._home_btn.set_sensitive(True) def _populate(self, url): """ Same as populate() @param url as string @thread safe """ if url is None: items = self._tunein.get_items() else: items = self._tunein.get_items(url) if items: self._add_items(items) else: GLib.idle_add(self._show_not_found) def _add_items(self, items): """ Add current items @param items as [TuneItem] @thread safe """ for item in items: GLib.idle_add(self._add_item, item) def _add_item(self, item): """ Add item @param item as TuneItem """ child = Gtk.Grid() child.set_property('halign', Gtk.Align.START) child.show() link = Gtk.LinkButton.new_with_label(item.URL, item.TEXT) link.connect('activate-link', self._on_activate_link, item) link.show() if item.TYPE == "audio": link.set_tooltip_text(_("Play")) button = Gtk.Button.new_from_icon_name('list-add-symbolic', Gtk.IconSize.MENU) button.connect('clicked', self._on_button_clicked, item) button.set_relief(Gtk.ReliefStyle.NONE) button.set_tooltip_text(_("Add")) button.show() child.add(button) else: link.set_tooltip_text('') child.add(link) self._view.add(child) # Remove spinner if exist if self._stack.get_visible_child_name() == 'spinner': self._stack.set_visible_child_name('scrolled') self._label.set_text(_("Browse themes and add a new radio")) if self._current_url is not None: self._back_btn.set_sensitive(True) self._home_btn.set_sensitive(True) def _clear(self): """ Clear view """ for child in self._view.get_children(): self._view.remove(child) child.destroy() def _add_radio(self, item): """ Add selected radio @param item as TuneIn Item """ # Get cover art try: cache = Art._RADIOS_PATH s = Gio.File.new_for_uri(item.LOGO) d = Gio.File.new_for_path(cache + "/%s.png" % item.TEXT.replace('/', '-')) s.copy(d, Gio.FileCopyFlags.OVERWRITE, None, None) except Exception as e: print("TuneinPopover::_add_radio: %s" % e) url = item.URL # Tune in embbed uri in ashx files, so get content if possible try: f = Gio.File.new_for_uri(url) (status, data, tag) = f.load_contents() if status: url = data.decode('utf-8').split('\n')[0] except Exception as e: print("TuneinPopover::_add_radio: %s" % e) self._radios_manager.add(item.TEXT.replace('/', '-'), url) def _on_back_btn_clicked(self, btn): """ Go to previous URL @param btn as Gtk.Button """ url = None self._current_url = None if self._previous_urls: url = self._previous_urls.pop() self._stack.set_visible_child_name('spinner') self._clear() self.populate(url) def _on_home_btn_clicked(self, btn): """ Go to root URL @param btn as Gtk.Button """ self._current_url = None self._previous_urls = [] self._stack.set_visible_child_name('spinner') self._clear() self.populate() def _on_activate_link(self, link, item): """ Update header with new link @param link as Gtk.LinkButton @param item as TuneIn Item """ if item.TYPE == "link": self._stack.set_visible_child_name('spinner') self._clear() self._scrolled.get_vadjustment().set_value(0.0) if self._current_url is not None: self._previous_urls.append(self._current_url) self.populate(item.URL) elif item.TYPE == "audio": # Only toolbar will get this one, so only create small in cache if Gio.NetworkMonitor.get_default().get_network_available(): t = Thread(target=Lp().art.copy_uri_to_cache, args=(item.LOGO, item.TEXT, ArtSize.SMALL)) t.daemon = True t.start() Lp().player.load_external(item.URL, item.TEXT) Lp().player.play_this_external(item.URL) return True def _on_button_clicked(self, button, item): """ Play the radio @param link as Gtk.Button @param item as TuneIn Item """ t = Thread(target=self._add_radio, args=(item, )) t.daemon = True t.start() self.hide()
class TuneinPopover(Gtk.Popover): """ Popover showing tunin radios """ def __init__(self, radio_manager): """ Init Popover @param radio manager as RadioManager """ Gtk.Popover.__init__(self) self._tunein = TuneIn() self._radio_manager = radio_manager self._current_url = None self._previous_urls = [] self._current_items = [] self._stack = Gtk.Stack() self._stack.set_property('expand', True) self._stack.show() builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/TuneinPopover.ui') builder.connect_signals(self) widget = builder.get_object('widget') widget.attach(self._stack, 0, 2, 4, 1) self._back_btn = builder.get_object('back_btn') self._home_btn = builder.get_object('home_btn') self._label = builder.get_object('label') self._view = Gtk.FlowBox() self._view.set_selection_mode(Gtk.SelectionMode.NONE) self._view.set_max_children_per_line(100) self._view.set_property('row-spacing', 10) self._view.set_property('expand', True) self._view.show() builder.get_object('viewport').add(self._view) builder.get_object('viewport').set_property('margin', 10) self._scrolled = builder.get_object('scrolled') self._spinner = builder.get_object('spinner') self._not_found = builder.get_object('notfound') self._stack.add(self._spinner) self._stack.add(self._not_found) self._stack.add(self._scrolled) self._stack.set_visible_child(self._spinner) self.add(widget) def populate(self, url=None): """ Populate views @param url as string """ if not self._view.get_children(): self._current_url = url self._clear() self._back_btn.set_sensitive(False) self._home_btn.set_sensitive(False) self._label.set_text(_("Please wait...")) start_new_thread(self._populate, (url,)) def do_show(self): """ Resize popover and set signals callback """ size_setting = Lp.settings.get_value('window-size') if isinstance(size_setting[1], int): self.set_size_request(700, size_setting[1]*0.7) else: self.set_size_request(700, 400) Gtk.Popover.do_show(self) ####################### # PRIVATE # ####################### def _show_not_found(self): """ Show not found message """ self._label.set_text(_("Can't connect to TuneIn...")) self._stack.set_visible_child(self._not_found) self._home_btn.set_sensitive(True) def _populate(self, url): """ Same as populate() @param url as string @thread safe """ if url is None: self._current_items = self._tunein.get_items() else: self._current_items = self._tunein.get_items(url) if self._current_items: self._add_items() else: GLib.idle_add(self._show_not_found) def _add_items(self): """ Add current items @thread safe """ for item in self._current_items: GLib.idle_add(self._add_item, item) def _add_item(self, item): """ Add item @param item as TuneItem """ child = Gtk.Grid() child.set_property('halign', Gtk.Align.START) child.show() if item.TYPE == "audio": button = Gtk.Button.new_from_icon_name('list-add-symbolic', Gtk.IconSize.MENU) button.connect('clicked', self._on_button_clicked, item) button.set_relief(Gtk.ReliefStyle.NONE) button.set_tooltip_text(_("Add")) button.show() child.add(button) link = Gtk.LinkButton.new_with_label(item.URL, item.TEXT) link.set_tooltip_text(_("Play")) link.connect('activate-link', self._on_activate_link, item) link.show() child.add(link) self._view.add(child) # Remove spinner if exist if self._spinner == self._stack.get_visible_child(): self._stack.set_visible_child(self._scrolled) self._label.set_text(_("Browse themes and add a new radio")) if self._current_url is not None: self._back_btn.set_sensitive(True) self._home_btn.set_sensitive(True) def _clear(self): """ Clear view """ for child in self._view.get_children(): self._view.remove(child) child.destroy() def _add_radio(self, item): """ Add selected radio @param item as TuneIn Item """ # Get cover art try: cache = Art._RADIOS_PATH s = Gio.File.new_for_uri(item.LOGO) d = Gio.File.new_for_path(cache+"/%s.png" %\ item.TEXT.replace('/', '-')) s.copy(d, Gio.FileCopyFlags.OVERWRITE, None, None) except Exception as e: print("TuneinPopover::_add_radio: %s" % e) url = item.URL # Tune in embbed uri in ashx files, so get content if possible try: f = Gio.File.new_for_uri(url) (status, data, tag) = f.load_contents() if status: url = data.decode('utf-8') except Exception as e: print("TuneinPopover::_add_radio: %s" % e) self._radio_manager.add(item.TEXT.replace('/', '-')) self._radio_manager.add_track(item.TEXT.replace('/', '-'), url) def _on_back_btn_clicked(self, btn): """ Go to previous URL @param btn as Gtk.Button """ url = None self._current_url = None if self._previous_urls: url = self._previous_urls.pop() self._stack.set_visible_child(self._spinner) self._clear() self.populate(url) def _on_home_btn_clicked(self, btn): """ Go to root URL @param btn as Gtk.Button """ self._current_url = None self._previous_urls = [] self._stack.set_visible_child(self._spinner) self._clear() self.populate() def _on_activate_link(self, link, item): """ Update header with new link @param link as Gtk.LinkButton @param item as TuneIn Item """ if item.TYPE == "link": self._stack.set_visible_child(self._spinner) self._clear() self._scrolled.get_vadjustment().set_value(0.0) if self._current_url is not None: self._previous_urls.append(self._current_url) self.populate(item.URL) elif item.TYPE == "audio": for i in self._current_items: Lp.player.load_external(i.URL, i.TEXT) Lp.player.play_this_external(item.URL) # Only toolbar will get this one, so only create small in cache if Gio.NetworkMonitor.get_default().get_network_available(): start_new_thread(Lp.art.copy_uri_to_cache, (item.LOGO, item.TEXT, ArtSize.SMALL)) return True def _on_button_clicked(self, button, item): """ Play the radio @param link as Gtk.Button @param item as TuneIn Item """ start_new_thread(self._add_radio, (item,)) self.hide()