Created
August 5, 2019 11:36
-
-
Save indivisible/13f043f6596c5a30b42ea815b7c6c91e to your computer and use it in GitHub Desktop.
Shuffle plugin with (mostly) preserving previous / next order for Quod Libet
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import random | |
from quodlibet import _ | |
from quodlibet.plugins.playorder import ShufflePlugin | |
from quodlibet.qltk import Icons | |
from quodlibet.util import print_d | |
class OrderRememberedHack(ShufflePlugin): | |
PLUGIN_ID = "order_remembered_hack" | |
PLUGIN_NAME = _("Order remembered random") | |
PLUGIN_ICON = Icons.MEDIA_SKIP_FORWARD | |
PLUGIN_DESC = _("Preserve the play order of songs on forward / backward skip.") | |
def __init__(self): | |
super(OrderRememberedHack, self).__init__() | |
self.no_repeat_window_size = 20 | |
self.no_repeat_tries = 5 | |
self.window_size = 100 | |
self._forward_window = [] | |
self._backward_window = [] | |
def _trim_windows(self): | |
if len(self._forward_window) > self.window_size: | |
self._forward_window = self._forward_window[-self.window_size:] | |
if len(self._backward_window) > self.window_size: | |
self._backward_window = self._backward_window[-self.window_size:] | |
def add_to_window(self, playlist, iter, window): | |
if not iter: | |
return | |
item = (iter, playlist.get_value(iter, 0)) | |
window.append(item) | |
self._trim_windows() | |
def find_in_playlist(self, playlist, audiofile): | |
pass | |
def pop_window(self, playlist, window): | |
if not window: | |
return None | |
iter, audiofile = window.pop() | |
try: | |
new_af = playlist.get_value(iter, 0) | |
if new_af == audiofile: | |
return iter | |
except TypeError: | |
# the old iter is invalid for some reason | |
pass | |
i = playlist.get_iter_first() | |
while i: | |
value = playlist.get_value(i, 0) | |
if value == audiofile: | |
print_d('Found new entry with same audiofile!') | |
return i | |
i = playlist.iter_next(i) | |
print_d("Couldn't find entry for {}".format(audiofile)) | |
return None | |
def next(self, playlist, iter): | |
self.add_to_window(playlist, iter, self._backward_window) | |
next_iter = self.pop_window(playlist, self._forward_window) | |
if next_iter: | |
return next_iter | |
songs = set(range(len(playlist))) | |
if not songs: | |
return None | |
# For best results we'd have to try to find all recently played songs | |
# in the current playlist. That is expensive, so we just try rerolling | |
# a few times | |
size = self.no_repeat_window_size | |
for __ in range(self.no_repeat_tries): | |
if len(songs) == 1: | |
break | |
played = set(i[1] for i in self._backward_window[-size:]) | |
next_iter = playlist.get_iter((random.choice(list(songs)),)) | |
next_af = playlist.get_value(next_iter, 0) | |
if next_af not in played or len(songs) == 1: | |
return next_iter | |
# give up after a few tries | |
return playlist.get_iter((random.choice(list(songs)),)) | |
def previous(self, playlist, iter): | |
self.add_to_window(playlist, iter, self._forward_window) | |
return self.pop_window(playlist, self._backward_window) | |
def set_explicit(self, playlist, iter): | |
if playlist.current_iter: | |
self.add_to_window( | |
playlist, | |
playlist.current_iter, | |
self._backward_window) | |
return iter |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment