Commit d0227ed4 authored by Cédric Bellegarde's avatar Cédric Bellegarde

Finally remove radio support. Fix #2416

parent c1492324
Pipeline #178592 canceled with stage
in 3 minutes and 6 seconds
......@@ -28,7 +28,7 @@ from urllib.parse import urlparse
from lollypop.utils import init_proxy_from_gnome, emit_signal
from lollypop.application_actions import ApplicationActions
from lollypop.utils_file import is_audio, is_pls, install_youtube_dl
from lollypop.define import Type, LOLLYPOP_DATA_PATH, ScanType, StorageType
from lollypop.define import LOLLYPOP_DATA_PATH, ScanType, StorageType
from lollypop.database import Database
from lollypop.player import Player
from lollypop.inhibitor import Inhibitor
......@@ -46,8 +46,6 @@ from lollypop.notification import NotificationManager
from lollypop.playlists import Playlists
from lollypop.objects_track import Track
from lollypop.objects_album import Album
from lollypop.objects_radio import Radio
from lollypop.radios import Radios
from lollypop.helper_task import TaskHelper
from lollypop.helper_art import ArtHelper
from lollypop.collection_scanner import CollectionScanner
......@@ -175,7 +173,6 @@ class Application(Gtk.Application, ApplicationActions):
self.artists = ArtistsDatabase(self.db)
self.genres = GenresDatabase(self.db)
self.tracks = TracksDatabase(self.db)
self.radios = Radios()
self.player = Player()
self.inhibitor = Inhibitor()
self.scanner = CollectionScanner()
......@@ -365,13 +362,6 @@ class Application(Gtk.Application, ApplicationActions):
open(LOLLYPOP_DATA_PATH + "/player.bin", "wb"))
dump(self.player.queue,
open(LOLLYPOP_DATA_PATH + "/queue.bin", "wb"))
# Save current playlist
if isinstance(self.player.current_track, Radio):
playlist_ids = [Type.RADIOS]
else:
playlist_ids = []
dump(playlist_ids,
open(LOLLYPOP_DATA_PATH + "/playlist_ids.bin", "wb"))
if self.player.current_track.id is not None:
position = self.player.position
else:
......@@ -397,7 +387,6 @@ class Application(Gtk.Application, ApplicationActions):
SqlCursor.remove(self.db)
self.cache.clean(True)
from lollypop.radios import Radios
with SqlCursor(self.db) as sql:
sql.isolation_level = None
sql.execute("VACUUM")
......@@ -406,10 +395,6 @@ class Application(Gtk.Application, ApplicationActions):
sql.isolation_level = None
sql.execute("VACUUM")
sql.isolation_level = ""
with SqlCursor(Radios()) as sql:
sql.isolation_level = None
sql.execute("VACUUM")
sql.isolation_level = ""
except Exception as e:
Logger.error("Application::__vacuum(): %s" % e)
......@@ -538,13 +523,7 @@ class Application(Gtk.Application, ApplicationActions):
for uri in audio_uris:
parsed = urlparse(uri)
if parsed.scheme in ["http", "https"]:
from lollypop.objects_radio import Radio
radio = Radio(Type.RADIOS)
radio.set_name(uri)
radio.set_uri(uri)
self.player.load(radio)
break
# Lollypop does not support radio playlists
pass
else:
to_scan_uris.append(uri)
self.scanner.update(ScanType.EXTERNAL, to_scan_uris)
......
......@@ -17,7 +17,6 @@ from hashlib import md5
from lollypop.art_base import BaseArt
from lollypop.art_album import AlbumArt
from lollypop.art_artist import ArtistArt
from lollypop.art_radio import RadioArt
from lollypop.art_downloader import DownloaderArt
from lollypop.logger import Logger
from lollypop.define import CACHE_PATH, ALBUMS_WEB_PATH, ALBUMS_PATH
......@@ -27,7 +26,7 @@ from lollypop.utils import emit_signal
from lollypop.utils_file import create_dir, remove_oldest
class Art(BaseArt, AlbumArt, ArtistArt, RadioArt, DownloaderArt):
class Art(BaseArt, AlbumArt, ArtistArt, DownloaderArt):
"""
Global artwork manager
"""
......@@ -39,7 +38,6 @@ class Art(BaseArt, AlbumArt, ArtistArt, RadioArt, DownloaderArt):
BaseArt.__init__(self)
AlbumArt.__init__(self)
ArtistArt.__init__(self)
RadioArt.__init__(self)
DownloaderArt.__init__(self)
# Move old store
# FIXME: Remove this later
......
......@@ -28,7 +28,6 @@ class BaseArt(GObject.GObject):
"album-artwork-changed": (GObject.SignalFlags.RUN_FIRST, None, (int,)),
"artist-artwork-changed": (GObject.SignalFlags.RUN_FIRST,
None, (str,)),
"radio-artwork-changed": (GObject.SignalFlags.RUN_FIRST, None, (str,)),
"uri-artwork-found": (GObject.SignalFlags.RUN_FIRST, None,
(GObject.TYPE_PYOBJECT,)),
}
......
# Copyright (c) 2014-2020 Cedric Bellegarde <cedric.bellegarde@adishatz.org>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from gi.repository import GLib, GdkPixbuf, Gio
import re
from lollypop.helper_task import TaskHelper
from lollypop.logger import Logger
from lollypop.define import ArtBehaviour, CACHE_PATH
from lollypop.utils import escape, emit_signal
from lollypop.utils_file import create_dir
class RadioArt:
"""
Manage radio artwork
"""
_RADIOS_PATH = GLib.get_user_data_dir() + "/lollypop/radios"
def __init__(self):
"""
Init radio art
Should be inherited by a BaseArt
"""
create_dir(self._RADIOS_PATH)
def get_radio_cache_path(self, name, width, height):
"""
get cover cache path for radio
@param name as str
@param width as int
@param height as int
@return cover path as string or None if no cover
"""
filename = ""
try:
filename = self.__get_radio_cache_name(name)
cache_path_png = "%s/%s_%s_%s.png" % (CACHE_PATH,
filename,
width,
height)
f = Gio.File.new_for_path(cache_path_png)
if f.query_exists():
return cache_path_png
else:
self.get_radio_artwork(name, width, height, 1)
if f.query_exists():
return cache_path_png
except Exception as e:
Logger.error("RadioArt::get_radio_cache_path(): %s, %s" %
(e, ascii(filename)))
return None
def get_radio_artwork(self, name, width, height, scale_factor,
behaviour=ArtBehaviour.CACHE |
ArtBehaviour.CROP_SQUARE):
"""
Return a cairo surface for radio name
@param name as string
@param width as int
@param height as int
@param scale_factor as int
@param behaviour as ArtBehaviour
@return GdkPixbuf.Pixbuf
"""
width *= scale_factor
height *= scale_factor
filename = self.__get_radio_cache_name(name)
cache_path_png = "%s/%s_%s_%s.png" % (CACHE_PATH, filename,
width, height)
pixbuf = None
try:
# Look in cache
f = Gio.File.new_for_path(cache_path_png)
if not behaviour & ArtBehaviour.NO_CACHE and f.query_exists():
pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(cache_path_png,
width,
height)
else:
filepath = self.__get_radio_art_path(name)
f = Gio.File.new_for_path(filepath)
if f.query_exists():
pixbuf = GdkPixbuf.Pixbuf.new_from_file(filepath)
pixbuf = self.load_behaviour(pixbuf, cache_path_png,
width, height, behaviour)
except Exception as e:
Logger.error("RadioArt::get_radio_artwork(): %s" % e)
return pixbuf
def cache_radio_uri(self, uri, name):
"""
Copy uri to cache at size
@param uri as str
@param name as str
"""
helper = TaskHelper()
helper.load_uri_content(uri,
None,
self.__on_uri_content,
name)
def rename_radio(self, old_name, new_name):
"""
Rename artwork
@param old_name as str
@param new_name as str
"""
old = self.__get_radio_art_path(old_name)
new = self.__get_radio_art_path(new_name)
try:
src = Gio.File.new_for_path(old)
dst = Gio.File.new_for_path(new)
if src.query_exists():
src.move(dst, Gio.FileCopyFlags.OVERWRITE, None, None)
except Exception as e:
Logger.error("RadioArt::rename_radio(): %s" % e)
def add_radio_artwork(self, name, data):
"""
Add radio artwork to store
@param name as str
@param data as bytes
@thread safe
"""
self.uncache_radio_artwork(name)
filepath = "%s/%s.png" % (self._RADIOS_PATH, escape(name))
if data is None:
f = Gio.File.new_for_path(filepath)
fstream = f.replace(None, False,
Gio.FileCreateFlags.REPLACE_DESTINATION, None)
fstream.close()
else:
bytes = GLib.Bytes.new(data)
stream = Gio.MemoryInputStream.new_from_bytes(bytes)
pixbuf = GdkPixbuf.Pixbuf.new_from_stream(stream, None)
stream.close()
pixbuf.savev(filepath, "png", [None], [None])
emit_signal(self, "radio-artwork-changed", name)
def uncache_radio_artwork(self, name):
"""
Remove radio artwork from cache
@param name as string
"""
cache_name = self.__get_radio_cache_name(name)
try:
f = Gio.File.new_for_path(CACHE_PATH)
infos = f.enumerate_children(
"standard::name",
Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS,
None)
for info in infos:
f = infos.get_child(info)
basename = f.get_basename()
if re.search(r"%s_.*\.png" % re.escape(cache_name), basename):
f.delete()
infos.close(None)
except Exception as e:
Logger.error("RadioArt::clean_radio_cache(): %s, %s" %
(e, cache_name))
#######################
# PRIVATE #
#######################
def __get_radio_art_path(self, name):
"""
Get radio artwork path
@param name as str
@return filepath as str
"""
return "%s/%s.png" % (self._RADIOS_PATH, escape(name))
def __get_radio_cache_name(self, name):
"""
Get a uniq string for radio
@param album_id as int
@param sql as sqlite cursor
"""
return "@@" + escape(name) + "@@radio@@"
def __on_uri_content(self, uri, status, content, name):
"""
Save image
@param uri as str
@param status as bool
@param content as bytes # The image
@param name as str
"""
if status:
cache_path_png = self.__get_radio_art_path(name)
bytes = GLib.Bytes.new(content)
stream = Gio.MemoryInputStream.new_from_bytes(bytes)
pixbuf = GdkPixbuf.Pixbuf.new_from_stream(stream, None)
stream.close()
pixbuf.savev(cache_path_png, "png", [None], [None])
del pixbuf
emit_signal(self, "radio-artwork-changed", name)
......@@ -10,7 +10,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from gi.repository import Gtk, GLib
from gi.repository import Gtk, GLib, Pango
from lollypop.logger import Logger
from lollypop.selectionlist import SelectionList
......@@ -18,7 +18,7 @@ from lollypop.define import App, Type, SelectionListMask, StorageType, ViewType
from lollypop.shown import ShownLists
from lollypop.helper_gestures import GesturesHelper
from lollypop.view import View
from lollypop.utils import emit_signal, get_default_storage_type
from lollypop.utils import emit_signal, get_default_storage_type, get_icon_name
class NoneView(View):
......@@ -237,7 +237,34 @@ class ListsContainer:
elif selected_id == Type.WEB:
view = self._get_view_albums([selected_id], [], StorageType.SAVED)
elif selected_id == Type.RADIOS:
view = self._get_view_radios()
message = """Radio support has been removed, sorry for that.
For a better radio player, use Shortwave:
"""
view = View(StorageType.ALL, ViewType.DEFAULT)
image = Gtk.Image.new_from_icon_name(get_icon_name(Type.RADIOS),
Gtk.IconSize.INVALID)
image.set_pixel_size(256)
image.set_property("expand", True)
style = image.get_style_context()
style.add_class("dim-label")
label = Gtk.Label()
style = label.get_style_context()
style.add_class("dim-label")
style.add_class("text-xx-large")
label.set_markup("%s" % GLib.markup_escape_text(message))
label.set_line_wrap_mode(Pango.WrapMode.WORD)
label.set_line_wrap(True)
button = Gtk.LinkButton.new(
"https://flathub.org/apps/details/de.haeckerfelix.Shortwave")
grid = Gtk.Grid()
grid.set_valign(Gtk.Align.CENTER)
grid.set_row_spacing(20)
grid.set_orientation(Gtk.Orientation.VERTICAL)
grid.add(image)
grid.add(label)
grid.add(button)
view.add_widget(grid)
view.show_all()
elif selected_id == Type.YEARS:
view = self._get_view_albums_decades(storage_type)
elif selected_id == Type.GENRES:
......
......@@ -116,8 +116,6 @@ class ViewsContainer:
view = self._get_view_albums_years(data, storage_type)
elif item_ids[0] == Type.PLAYLISTS:
view = self._get_view_playlists(data)
elif item_ids[0] == Type.RADIOS:
view = self._get_view_radios()
elif item_ids[0] == Type.EQUALIZER:
from lollypop.view_equalizer import EqualizerView
view = EqualizerView()
......@@ -362,17 +360,6 @@ class ViewsContainer:
view.populate()
return view
def _get_view_radios(self):
"""
Get radios view
@return RadiosView
"""
view_type = ViewType.SCROLLED
from lollypop.view_radios import RadiosView
view = RadiosView(view_type)
view.populate()
return view
def _get_view_info(self):
"""
Get view for information
......
# Copyright (c) 2014-2020 Cedric Bellegarde <cedric.bellegarde@adishatz.org>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from lollypop.define import App
from lollypop.helper_signals import SignalsHelper, signals_map
class ViewControllerType:
RADIO = "radio"
ALBUM = "album"
class ViewController(SignalsHelper):
"""
Update view for registered signals
Should be herited by a Gtk.Widget
"""
@signals_map
def __init__(self, controller_type):
"""
Init controller
@param controller_type as ViewControllerType
"""
return [
(App().player, "current-changed", "_on_current_changed"),
(App().player, "duration-changed", "_on_duration_changed"),
(App().art, "%s-artwork-changed" % controller_type,
"_on_artwork_changed")
]
#######################
# PROTECTED #
#######################
def _on_current_changed(self, player):
pass
def _on_artwork_changed(self, artwork, *args):
pass
def _on_duration_changed(self, player, track_id):
pass
#######################
# PRIVATE #
#######################
......@@ -19,7 +19,6 @@ from gettext import gettext as _
from lollypop.sqlcursor import SqlCursor
from lollypop.utils import translate_artist_name
from lollypop.database_history import History
from lollypop.radios import Radios
from lollypop.define import App, Type, StorageType, LOLLYPOP_DATA_PATH
from lollypop.logger import Logger
from lollypop.helper_task import TaskHelper
......@@ -140,7 +139,6 @@ class DatabaseAlbumsUpgrade(DatabaseUpgrade):
18: self.__upgrade_18,
19: self.__upgrade_19,
20: self.__upgrade_20,
21: self.__upgrade_21,
22: self.__upgrade_22,
23: self.__upgrade_23,
24: "ALTER TABLE albums ADD album_id TEXT",
......@@ -482,14 +480,6 @@ class DatabaseAlbumsUpgrade(DatabaseUpgrade):
persistent FROM backup")
sql.execute("DROP TABLE backup")
def __upgrade_21(self, db):
"""
Add rate to radios
"""
with SqlCursor(Radios()) as sql:
sql.execute("ALTER TABLE radios ADD rate\
INT NOT NULL DEFAULT -1")
def __upgrade_22(self, db):
"""
Remove Charts/Web entries
......
......@@ -20,7 +20,6 @@ from lollypop.widgets_player_progress import ProgressPlayerWidget
from lollypop.widgets_player_buttons import ButtonsPlayerWidget
from lollypop.widgets_player_artwork import ArtworkPlayerWidget
from lollypop.widgets_player_label import LabelPlayerWidget
from lollypop.objects_radio import Radio
from lollypop.container import Container
from lollypop.window_adaptive import AdaptiveWindow
from lollypop.logger import Logger
......@@ -285,9 +284,6 @@ class FullScreen(Gtk.Window, AdaptiveWindow, SignalsHelper):
Update progress bar visibility
"""
if App().player.current_track.id is not None:
if isinstance(App().player.current_track, Radio):
self.__progress_widget.hide()
else:
self.__progress_widget.show()
else:
self.__progress_widget.hide()
......@@ -309,18 +305,7 @@ class FullScreen(Gtk.Window, AdaptiveWindow, SignalsHelper):
ArtBehaviour.DARKER)
# We don't want this for background, stored for album cover
behaviour &= ~ArtBehaviour.ROUNDED
if isinstance(App().player.current_track, Radio):
if self.__background_id == App().player.current_track.name:
return
App().art_helper.set_radio_artwork(
App().player.current_track.name,
allocation.width,
allocation.height,
self.get_scale_factor(),
behaviour | ArtBehaviour.BLUR_MAX,
self.__on_artwork,
False)
elif not album_artwork and\
if not album_artwork and\
App().settings.get_value("artist-artwork"):
if App().player.current_track.album.artists:
artist = App().player.current_track.album.artists[0]
......
......@@ -73,31 +73,6 @@ class ArtHelper(GObject.Object):
callback,
*args))
def set_radio_artwork(self, radio, width, height, scale_factor,
effect, callback, *args):
"""
Set artwork for album id
@param radio as str
@param width as int
@param height as int
@param scale_factor as int
@param effect as ArtBehaviour
@param callback as function
"""
App().task_helper.run(App().art.get_radio_artwork,
radio,
width,
height,
scale_factor,
effect,
callback=(self._on_get_artwork_pixbuf,
width,
height,
scale_factor,
effect,
callback,
*args))
def set_artist_artwork(self, name, width, height, scale_factor,
effect, callback, *args):
"""
......
......@@ -173,12 +173,6 @@ class LyricsHelper:
@return str
"""
# Update lyrics
title = ""
if isinstance(track, Radio):
split = " ".join(track.artists).split(" - ")
if len(split) > 1:
title = split[1]
else:
title = track.name
if escape:
return GLib.uri_escape_string(title, None, False)
......
# Copyright (c) 2014-2020 Cedric Bellegarde <cedric.bellegarde@adishatz.org>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from gi.repository import Gtk, Gio, GObject
from gettext import gettext as _
from lollypop.widgets_rating import RatingWidget
from lollypop.define import App, ArtSize, ArtBehaviour, MARGIN, MARGIN_SMALL
from lollypop.define import ViewType
from lollypop.widgets_artwork_radio import RadioArtworkSearchWidget
from lollypop.art import Art
from lollypop.utils import emit_signal
from lollypop.objects_radio import Radio
class RadioMenu(Gtk.Grid):
"""
Popover with radio logos from the web
"""
__gsignals__ = {
"hidden": (GObject.SignalFlags.RUN_FIRST, None, (bool,)),
}
def __init__(self, radio, view_type):
"""
Init Popover
@param radio as Radio
@param view_type as ViewType
@param header as bool