Skip to content

Commit 36ca942

Browse files
committed
Moved functions from random.py into random plugin. Removed random.py
1 parent c2d1bc3 commit 36ca942

File tree

2 files changed

+97
-113
lines changed

2 files changed

+97
-113
lines changed

beets/random.py

Lines changed: 0 additions & 112 deletions
This file was deleted.

beetsplug/random.py

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,11 @@
1414

1515
"""Get a random song or album from the library."""
1616

17+
import random
18+
from itertools import groupby
19+
from operator import attrgetter
20+
1721
from beets.plugins import BeetsPlugin
18-
from beets.random import random_objs
1922
from beets.ui import Subcommand, print_
2023

2124

@@ -64,3 +67,96 @@ def random_func(lib, opts, args):
6467
class Random(BeetsPlugin):
6568
def commands(self):
6669
return [random_cmd]
70+
71+
72+
def _length(obj, album):
73+
"""Get the duration of an item or album."""
74+
if album:
75+
return sum(i.length for i in obj.items())
76+
else:
77+
return obj.length
78+
79+
80+
def _equal_chance_permutation(objs, field="albumartist", random_gen=None):
81+
"""Generate (lazily) a permutation of the objects where every group
82+
with equal values for `field` have an equal chance of appearing in
83+
any given position.
84+
"""
85+
rand = random_gen or random
86+
87+
# Group the objects by artist so we can sample from them.
88+
key = attrgetter(field)
89+
objs.sort(key=key)
90+
objs_by_artists = {}
91+
for artist, v in groupby(objs, key):
92+
objs_by_artists[artist] = list(v)
93+
94+
# While we still have artists with music to choose from, pick one
95+
# randomly and pick a track from that artist.
96+
while objs_by_artists:
97+
# Choose an artist and an object for that artist, removing
98+
# this choice from the pool.
99+
artist = rand.choice(list(objs_by_artists.keys()))
100+
objs_from_artist = objs_by_artists[artist]
101+
i = rand.randint(0, len(objs_from_artist) - 1)
102+
yield objs_from_artist.pop(i)
103+
104+
# Remove the artist if we've used up all of its objects.
105+
if not objs_from_artist:
106+
del objs_by_artists[artist]
107+
108+
109+
def _take(iter, num):
110+
"""Return a list containing the first `num` values in `iter` (or
111+
fewer, if the iterable ends early).
112+
"""
113+
out = []
114+
for val in iter:
115+
out.append(val)
116+
num -= 1
117+
if num <= 0:
118+
break
119+
return out
120+
121+
122+
def _take_time(iter, secs, album):
123+
"""Return a list containing the first values in `iter`, which should
124+
be Item or Album objects, that add up to the given amount of time in
125+
seconds.
126+
"""
127+
out = []
128+
total_time = 0.0
129+
for obj in iter:
130+
length = _length(obj, album)
131+
if total_time + length <= secs:
132+
out.append(obj)
133+
total_time += length
134+
return out
135+
136+
137+
def random_objs(
138+
objs, album, number=1, time=None, equal_chance=False, random_gen=None
139+
):
140+
"""Get a random subset of the provided `objs`.
141+
142+
If `number` is provided, produce that many matches. Otherwise, if
143+
`time` is provided, instead select a list whose total time is close
144+
to that number of minutes. If `equal_chance` is true, give each
145+
artist an equal chance of being included so that artists with more
146+
songs are not represented disproportionately.
147+
"""
148+
rand = random_gen or random
149+
150+
# Permute the objects either in a straightforward way or an
151+
# artist-balanced way.
152+
if equal_chance:
153+
perm = _equal_chance_permutation(objs)
154+
else:
155+
perm = objs
156+
rand.shuffle(perm) # N.B. This shuffles the original list.
157+
158+
# Select objects by time our count.
159+
if time:
160+
return _take_time(perm, time * 60, album)
161+
else:
162+
return _take(perm, number)

0 commit comments

Comments
 (0)