1# Copyright (c) 2014-2021 Cedric Bellegarde <cedric.bellegarde@adishatz.org> 2# This program is free software: you can redistribute it and/or modify 3# it under the terms of the GNU General Public License as published by 4# the Free Software Foundation, either version 3 of the License, or 5# (at your option) any later version. 6# This program is distributed in the hope that it will be useful, 7# but WITHOUT ANY WARRANTY; without even the implied warranty of 8# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9# GNU General Public License for more details. 10# You should have received a copy of the GNU General Public License 11# along with this program. If not, see <http://www.gnu.org/licenses/>. 12 13from gi.repository import GLib 14 15from random import choice 16from gettext import gettext as _ 17 18from lollypop.logger import Logger 19from lollypop.objects_album import Album 20from lollypop.player_auto_similar import AutoSimilarPlayer 21from lollypop.player_auto_random import AutoRandomPlayer 22from lollypop.define import App, Repeat 23from lollypop.utils import emit_signal 24 25 26class AlbumsPlayer: 27 """ 28 Handle player albums 29 """ 30 31 def __init__(self): 32 """ 33 Init player 34 """ 35 # Albums in current playlist 36 self._albums = [] 37 38 def add_album(self, album): 39 """ 40 Add album to player 41 @param album as Album 42 """ 43 self.add_albums([album]) 44 45 def add_album_ids(self, album_ids): 46 """ 47 Add album ids to player 48 @param album_ids as [int] 49 """ 50 self.add_albums([Album(album_id) for album_id in album_ids]) 51 52 def add_albums(self, albums): 53 """ 54 Add albums to player 55 @param albums as [Album] 56 """ 57 if not albums: 58 App().notify.send(_("No album available")) 59 return 60 try: 61 for album in albums: 62 # Merge album if previous is same 63 if self._albums and self._albums[-1].id == album.id: 64 track_ids = self._albums[-1].track_ids 65 for track in album.tracks: 66 if track.id not in track_ids: 67 self._albums[-1].append_track(track) 68 emit_signal(self, "playback-updated", self._albums[-1]) 69 else: 70 self._albums.append(album) 71 emit_signal(self, "playback-added", album) 72 self.update_next_prev() 73 except Exception as e: 74 Logger.error("Player::add_albums(): %s" % e) 75 76 def remove_album(self, album): 77 """ 78 Remove album from albums 79 @param album as Album 80 """ 81 try: 82 if album not in self._albums: 83 return 84 self._albums.remove(album) 85 emit_signal(self, "playback-removed", album) 86 self.update_next_prev() 87 except Exception as e: 88 Logger.error("Player::remove_album(): %s" % e) 89 90 def remove_album_by_id(self, album_id): 91 """ 92 Remove all instances of album id 93 @param album_id as int 94 """ 95 self.remove_album_by_ids([album_id]) 96 97 def remove_album_by_ids(self, album_ids): 98 """ 99 Remove all instances of album ids 100 @param album_ids as [int] 101 """ 102 try: 103 for album_id in album_ids: 104 for album in self._albums: 105 if album.id == album_id: 106 self.remove_album(album) 107 emit_signal(self, "playback-removed", album) 108 self.update_next_prev() 109 except Exception as e: 110 Logger.error("Player::remove_album_by_ids(): %s" % e) 111 112 def remove_track_from_album(self, track, album): 113 """ 114 Remove track from album 115 @param track as Track 116 @param album as Album 117 """ 118 is_current_track = track == self.current_track 119 if is_current_track: 120 self.next() 121 if album.remove_track(track): 122 self.remove_album(album) 123 emit_signal(self, "playback-removed", album) 124 else: 125 emit_signal(self, "playback-updated", album) 126 if not is_current_track: 127 self.update_next_prev() 128 129 def play_album(self, album): 130 """ 131 Play album 132 @param album as Album 133 """ 134 self.play_album_for_albums(album, [album]) 135 136 def play_albums(self, albums): 137 """ 138 Play albums 139 @param album as [Album] 140 """ 141 if not albums: 142 App().notify.send(_("No album available")) 143 return 144 if App().settings.get_value("shuffle"): 145 album = choice(albums) 146 else: 147 album = albums[0] 148 self.play_album_for_albums(album, albums) 149 150 def play_track_for_albums(self, track, albums): 151 """ 152 Play track and set albums as current playlist 153 @param albums as [Album] 154 @param track as Track 155 """ 156 if self.is_party: 157 App().lookup_action("party").change_state(GLib.Variant("b", False)) 158 self._albums = albums 159 self.load(track) 160 emit_signal(self, "playback-setted", list(albums)) 161 162 def play_album_for_albums(self, album, albums): 163 """ 164 Play album and set albums as current playlist 165 @param album as Album 166 @param albums as [Album] 167 """ 168 if not albums: 169 App().notify.send(_("No album available")) 170 return 171 if self.is_party: 172 App().lookup_action("party").change_state(GLib.Variant("b", False)) 173 if App().settings.get_value("shuffle"): 174 self.__play_shuffle_tracks(album, albums) 175 else: 176 self.__play_albums(album, albums) 177 178 def set_albums(self, albums, signal=True): 179 """ 180 Set player albums 181 @param albums as [Album] 182 @param signal as bool 183 """ 184 if not albums: 185 App().notify.send(_("No album available")) 186 return 187 self._albums = albums 188 if signal: 189 emit_signal(self, "playback-setted", list(albums)) 190 self.update_next_prev() 191 192 def clear_albums(self): 193 """ 194 Clear all albums 195 """ 196 self._albums = [] 197 emit_signal(self, "playback-setted", []) 198 self.update_next_prev() 199 200 def skip_album(self): 201 """ 202 Skip current album 203 """ 204 try: 205 # In party or shuffle, just update next track 206 if self.is_party or App().settings.get_value("shuffle"): 207 self.set_next() 208 elif self._current_track.id is not None: 209 index = self._albums.index( 210 self._current_track.album) 211 if index + 1 >= len(self._albums): 212 repeat = App().settings.get_enum("repeat") 213 if repeat == Repeat.AUTO_SIMILAR: 214 next_album = AutoSimilarPlayer.next_album(self) 215 if next_album is not None: 216 self.add_album(next_album) 217 elif repeat == Repeat.AUTO_RANDOM: 218 next_album = AutoRandomPlayer.next_album(self) 219 if next_album is not None: 220 self.add_album(next_album) 221 elif repeat == Repeat.ALL: 222 next_album = self._albums[0] 223 else: 224 next_album = None 225 else: 226 next_album = self._albums[index + 1] 227 if next_album is None: 228 self.stop() 229 else: 230 self.load(next_album.tracks[0]) 231 except Exception as e: 232 Logger.error("Player::skip_album(): %s" % e) 233 234 def track_in_playback(self, track): 235 """ 236 True if track present in current playback 237 @param track as Track 238 @return Track/None 239 """ 240 for album in self._albums: 241 if album.id == track.album.id: 242 for _track in album.tracks: 243 if track.id == _track.id: 244 return _track 245 return None 246 247 def get_albums_for_id(self, album_id): 248 """ 249 Get albums for id 250 @param album_id as int 251 @return [Album] 252 """ 253 return [album for album in self._albums if album.id == album_id] 254 255 @property 256 def albums(self): 257 """ 258 Return albums 259 @return albums as [Album] 260 """ 261 return list(self._albums) 262 263 @property 264 def album_ids(self): 265 """ 266 Return albums ids 267 @return albums ids as [int] 268 """ 269 return [album.id for album in self._albums] 270 271####################### 272# PRIVATE # 273####################### 274 def __play_shuffle_tracks(self, album, albums): 275 """ 276 Start shuffle tracks playback. 277 @param album as Album 278 @param albums as [albums] 279 """ 280 if album is None: 281 album = choice(albums) 282 if album.tracks: 283 track = choice(album.tracks) 284 else: 285 track = None 286 self._albums = albums 287 emit_signal(self, "playback-setted", list(albums)) 288 if track is not None: 289 self.load(track) 290 else: 291 self.update_next_prev() 292 293 def __play_albums(self, album, albums): 294 """ 295 Start albums playback. 296 @param album as Album 297 @param albums as [albums] 298 """ 299 if album is None: 300 album = albums[0] 301 if album.tracks: 302 track = album.tracks[0] 303 else: 304 track = None 305 self._albums = albums 306 emit_signal(self, "playback-setted", list(albums)) 307 if track is not None: 308 self.load(track) 309 else: 310 self.update_next_prev() 311