1# Copyright 2006 Joe Wreschnig 2# 2016-17 Nick Boultbee 3# 4# This program is free software; you can redistribute it and/or modify 5# it under the terms of the GNU General Public License as published by 6# the Free Software Foundation; either version 2 of the License, or 7# (at your option) any later version. 8 9from quodlibet import _, print_d 10 11 12class Order(object): 13 """Base class for all play orders 14 15 In all methods: 16 `playlist` is a GTK+ `ListStore` containing at least an `AudioFile` 17 as the first element in each row 18 (in the future there may be more elements per row). 19 20 `iter` is a `GtkTreeIter` for the song that just finished, if any. 21 If the song is not in the list, this iter will be `None`. 22 """ 23 24 name = "unknown_order" 25 """The name by which this order is known""" 26 27 display_name = _("Unknown") 28 """The (translated) display name""" 29 30 accelerated_name = _("_Unknown") 31 """The (translated) display name with (optional) accelerators""" 32 33 replaygain_profiles = ["track"] 34 """The ReplayGain mode(s) to use with this order. 35 Shuffled ones typically prefer track modes""" 36 37 priority = 100 38 """The priority relative to other orders of its type. 39 Larger numbers typically appear lower in lists.""" 40 41 def __init__(self): 42 """Must have a zero-arg constructor""" 43 pass 44 45 def next(self, playlist, iter): 46 """Not called directly, but the default implementation of 47 `next_explicit` and `next_implicit` both just call this. """ 48 raise NotImplementedError 49 50 def previous(self, playlist, iter): 51 """Not called directly, but the default implementation of 52 `previous_explicit` calls this. 53 Right now there is no such thing as `previous_implicit`.""" 54 raise NotImplementedError 55 56 def set(self, playlist, iter): 57 """Not called directly, but the default implementations of 58 `set_explicit` and `set_implicit` call this. """ 59 return iter 60 61 def next_explicit(self, playlist, iter): 62 """Not called directly, but the default implementations of 63 `set_explicit` and `set_implicit` call this.""" 64 return self.next(playlist, iter) 65 66 def next_implicit(self, playlist, iter): 67 """Called when a song ends passively, e.g. it plays through.""" 68 return self.next(playlist, iter) 69 70 def previous_explicit(self, playlist, iter): 71 """Called when the user presses a "Previous" button.""" 72 return self.previous(playlist, iter) 73 74 def set_explicit(self, playlist, iter): 75 """Called when the user manually selects a song (at `iter`). 76 If desired the play order can override that, or just 77 log it and return the iter again. 78 Note that playlist.current_iter is the current iter, if any. 79 80 If the play order returns `None`, 81 no action will be taken by the player. 82 """ 83 return self.set(playlist, iter) 84 85 def set_implicit(self, playlist, iter): 86 """Called when the song is set by a means other than the UI.""" 87 return self.set(playlist, iter) 88 89 def reset(self, playlist): 90 """Called when there is no song ready to prepare for a new order. 91 Implementations should reset the state of the current order, 92 e.g. forgetting history / clearing pre-cached orders.""" 93 pass 94 95 def __str__(self): 96 """By default there is no interesting state""" 97 return "<%s>" % self.display_name 98 99 100class OrderRemembered(Order): 101 """Shared class for all the shuffle modes that keep a memory 102 of their previously played songs.""" 103 104 def __init__(self): 105 super(OrderRemembered, self).__init__() 106 self._played = [] 107 108 def next(self, playlist, iter): 109 if iter is not None: 110 self._played.append(playlist.get_path(iter).get_indices()[0]) 111 112 def previous(self, playlist, iter): 113 try: 114 path = self._played.pop() 115 except IndexError: 116 return None 117 else: 118 return playlist.get_iter(path) 119 120 def set(self, playlist, iter): 121 if iter is not None: 122 self._played.append(playlist.get_path(iter).get_indices()[0]) 123 return iter 124 125 def reset(self, playlist): 126 del(self._played[:]) 127 128 def remaining(self, playlist): 129 """Gets a map of all song indices to their song from the `playlist` 130 that haven't yet been played""" 131 all_indices = set(range(len(playlist))) 132 played = set(self._played) 133 print_d("Played %d of %d song(s)" % (len(self._played), len(playlist))) 134 remaining = list(all_indices.difference(played)) 135 all_songs = playlist.get() 136 return {i: all_songs[i] for i in remaining} 137 138 139class OrderInOrder(Order): 140 """Keep to the order of the supplied playlist""" 141 name = "in_order" 142 display_name = _("In Order") 143 accelerated_name = _("_In Order") 144 replaygain_profiles = ["album", "track"] 145 priority = 0 146 147 def next(self, playlist, iter): 148 if iter is None: 149 return playlist.get_iter_first() 150 else: 151 return playlist.iter_next(iter) 152 153 def previous(self, playlist, iter): 154 if len(playlist) == 0: 155 return None 156 elif iter is None: 157 return playlist[(len(playlist) - 1,)].iter 158 else: 159 path = max(1, playlist.get_path(iter).get_indices()[0]) 160 try: 161 return playlist.get_iter((path - 1,)) 162 except ValueError: 163 return None 164