forked from albertz/music-player
-
Notifications
You must be signed in to change notification settings - Fork 0
/
RandomFileQueue.py
109 lines (90 loc) · 2.93 KB
/
RandomFileQueue.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# MusicPlayer, https://github.com/albertz/music-player
# Copyright (c) 2012, Albert Zeyer, www.az2000.de
# All rights reserved.
# This code is under the 2-clause BSD license, see License.txt in the root directory of this project.
# loosely inspired from https://github.com/albertz/PictureSlider/blob/master/PictureSlider/FileQueue.cpp
import os, random
from os import access, R_OK
C_nonloaded_dirs_expectedFac = 0.5
C_nonloaded_dirs_expectedMin = 100
rndInt = random.randint
class RandomFileQueue:
def __init__(self, rootdir, fileexts):
self.rootdir = rootdir
self.fileexts = fileexts
def hasCorrectFileext(f):
ext = os.path.splitext(f)[1]
ext = ext.lower()
for allowedExt in self.fileexts:
if ext == "." + allowedExt.lower():
return True
return False
class Dir:
owner = self
isLoaded = False
base = None
def __init__(self):
self.files = []
self.loadedDirs = []
self.nonloadedDirs = []
def load(self):
self.isLoaded = True
# Note: If we could use the C readdir() more directly, that would be much faster because it already provides the stat info (wether it is a file or dir), so we don't need to do a separate call for isfile/isdir.
try:
listeddir = os.listdir(self.base)
except:
# it might fail because of permission errors or whatever
listeddir = []
for f in listeddir:
if f.startswith("."): continue
if os.path.isfile(self.base + "/" + f):
if hasCorrectFileext(f) and access(self.base + "/" + f, R_OK):
self.files += [f]
elif os.path.isdir(self.base + "/" + f):
subdir = Dir()
subdir.base = self.base + "/" + f
self.nonloadedDirs += [subdir]
def expectedFilesCount(self):
c = 0
c += len(self.files)
for d in self.loadedDirs:
c += d.expectedFilesCount()
c += len(self.nonloadedDirs) * \
max(int(C_nonloaded_dirs_expectedFac * c), C_nonloaded_dirs_expectedMin)
return c
def randomGet(self):
if not self.isLoaded: self.load()
while True:
rmax = self.expectedFilesCount()
if rmax == 0: return None
r = rndInt(0, rmax - 1)
if r < len(self.files):
return self.base + "/" + self.files[r]
r -= len(self.files)
for d in self.loadedDirs:
c = d.expectedFilesCount()
if r < c:
f = d.randomGet()
if f: return f
r = None
break
r -= c
if r is None: continue
assert len(self.nonloadedDirs) > 0
r = rndInt(0, len(self.nonloadedDirs) - 1)
d = self.nonloadedDirs[r]
self.nonloadedDirs = self.nonloadedDirs[:r] + self.nonloadedDirs[r+1:]
d.load()
self.loadedDirs += [d]
self.root = Dir()
self.root.base = rootdir
def getNextFile(self):
return self.root.randomGet()
def test():
q = RandomFileQueue(rootdir = os.path.expanduser("~/Music"), fileexts=["mp3","ogg","flac"])
i = 0
while i < 100:
i += 1
print q.getNextFile()
if __name__ == '__main__':
test()