1# Copyright 2019 The GNOME Music developers 2# 3# GNOME Music is free software; you can redistribute it and/or modify 4# it under the terms of the GNU General Public License as published by 5# the Free Software Foundation; either version 2 of the License, or 6# (at your option) any later version. 7# 8# GNOME Music is distributed in the hope that it will be useful, 9# but WITHOUT ANY WARRANTY; without even the implied warranty of 10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11# GNU General Public License for more details. 12# 13# You should have received a copy of the GNU General Public License along 14# with GNOME Music; if not, write to the Free Software Foundation, Inc., 15# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 16# 17# The GNOME Music authors hereby grant permission for non-GPL compatible 18# GStreamer plugins to be used and distributed together with GStreamer 19# and GNOME Music. This permission is above and beyond the permissions 20# granted by the GPL license by which GNOME Music is covered. If you 21# modify this code, you may extend this exception to your version of the 22# code, but you are not obligated to do so. If you do not wish to do so, 23# delete this exception statement from your version. 24 25import gi 26gi.require_versions({"Gfm": "0.1", "Grl": "0.3"}) 27from gi.repository import Gfm, Gio, Grl, GObject 28 29import gnomemusic.utils as utils 30 31from gnomemusic.albumart import AlbumArt 32 33 34class CoreAlbum(GObject.GObject): 35 """Exposes a Grl.Media with relevant data as properties 36 """ 37 38 artist = GObject.Property(type=str) 39 composer = GObject.Property(type=str, default=None) 40 duration = GObject.Property(type=int, default=0) 41 media = GObject.Property(type=Grl.Media) 42 title = GObject.Property(type=str) 43 url = GObject.Property(type=str) 44 year = GObject.Property(type=str, default="----") 45 46 def __init__(self, application, media): 47 """Initiate the CoreAlbum object 48 49 :param Application application: The application object 50 :param Grl.Media media: A media object 51 """ 52 super().__init__() 53 54 self._application = application 55 self._coregrilo = application.props.coregrilo 56 self._model = None 57 self._selected = False 58 self._thumbnail = None 59 60 self.update(media) 61 62 def update(self, media): 63 """Update the CoreAlbum object with new info 64 65 :param Grl.Media media: A media object 66 """ 67 self.props.media = media 68 self.props.artist = utils.get_artist_name(media) 69 self.props.composer = media.get_composer() 70 self.props.title = utils.get_media_title(media) 71 self.props.url = media.get_url() 72 self.props.year = utils.get_media_year(media) 73 74 def _get_album_model(self): 75 disc_model = Gio.ListStore() 76 disc_model_sort = Gfm.SortListModel.new(disc_model) 77 78 def _disc_order_sort(disc_a, disc_b): 79 return disc_a.props.disc_nr - disc_b.props.disc_nr 80 81 disc_model_sort.set_sort_func( 82 utils.wrap_list_store_sort_func(_disc_order_sort)) 83 84 self._coregrilo.get_album_discs(self.props.media, disc_model) 85 86 return disc_model_sort 87 88 @GObject.Property( 89 type=Gfm.SortListModel, default=None, 90 flags=GObject.ParamFlags.READABLE) 91 def model(self): 92 if self._model is None: 93 self._model = self._get_album_model() 94 self._model.connect("items-changed", self._on_list_items_changed) 95 self._model.items_changed(0, 0, self._model.get_n_items()) 96 97 return self._model 98 99 def _on_list_items_changed(self, model, position, removed, added): 100 with self.freeze_notify(): 101 for coredisc in model: 102 coredisc.props.selected = self.props.selected 103 104 if added > 0: 105 for i in range(added): 106 coredisc = model[position + i] 107 coredisc.connect( 108 "notify::duration", self._on_duration_changed) 109 110 def _on_duration_changed(self, coredisc, duration): 111 duration = 0 112 113 for coredisc in self.props.model: 114 duration += coredisc.props.duration 115 116 self.props.duration = duration 117 118 @GObject.Property(type=bool, default=False) 119 def selected(self): 120 return self._selected 121 122 @selected.setter # type: ignore 123 def selected(self, value): 124 if value == self._selected: 125 return 126 127 self._selected = value 128 129 # The model is loaded on-demand, so the first time the model is 130 # returned it can still be empty. This is problem for returning 131 # a selection. Trigger loading of the model here if a selection 132 # is requested, it will trigger the filled model update as 133 # well. 134 self.props.model.items_changed(0, 0, 0) 135 136 @GObject.Property(type=str, default=None) 137 def thumbnail(self): 138 """Album art thumbnail retrieval 139 140 :return: The album art uri or "generic" 141 :rtype: string 142 """ 143 if self._thumbnail is None: 144 self._thumbnail = "generic" 145 AlbumArt(self._application, self) 146 147 return self._thumbnail 148 149 @thumbnail.setter # type: ignore 150 def thumbnail(self, value): 151 """Album art thumbnail setter 152 153 :param string value: uri or "generic" 154 """ 155 self._thumbnail = value 156