-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlibrary.py
More file actions
159 lines (141 loc) · 6.18 KB
/
library.py
File metadata and controls
159 lines (141 loc) · 6.18 KB
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
import os
import random
import re
import stat
_numRe = re.compile(r'^\s*[0-9]+\s*$')
_songRe = re.compile(r"^\s*(.+?)\s+-\s+(.+?)\.[^\.]+$", re.S)
_exts = {e:None for e in ['aac','aiff','alac','au','flac','m4a','m4b','mp3','oga','ogg','opus','ra','rm','wav','webm','wma']}
class Group:
def __init__(self, name, songs):
self.name = name
self.songs = songs
def __str__(self): return self.name
class Song:
def __init__(self, path, artist, title):
self.path = path
self.artist = artist
self.title = title
def __str__(self): return self.artist + ' - ' + self.title
class Library:
def __init__(self, root):
self.root = root
self.groups = None
def __str__(self): return self.root
def getPath(self, song): return os.path.join(self.root, song.path)
def scan(self):
self.groups = {}
for name in os.listdir(self.root):
s = os.stat(os.path.join(self.root, name))
if stat.S_ISDIR(s.st_mode):
self.groups[name] = self._scanGroup(name)
def _scanGroup(self, name):
return Group(name, Library._scanSongs(self.root, name, []))
@staticmethod
def _scanSongs(root, rel, songs):
dir = os.path.join(root, rel)
for name in os.listdir(dir):
s = os.stat(os.path.join(dir, name))
if stat.S_ISDIR(s.st_mode):
Library._scanSongs(root, rel+'/'+name, songs)
else:
(base, ext) = os.path.splitext(name)
if ext[1:].lower() in _exts:
m = _songRe.fullmatch(name)
if not m:
(artist, title) = ('Unknown', base)
else:
(artist, title) = m.groups()
if _numRe.search(artist): # if 'artist' is a number, get it from the directory
slash = rel.find('/') # strip the group name off the front
if slash >= 0: (artist, title) = (os.path.basename(rel[slash+1:]), artist + ' - ' + title)
else: artist = 'Unknown'
songs.append(Song(rel+'/'+name, artist, title))
return songs
class Playlist:
def __init__(self, playlistFile, library):
self.songs = []
self.paths = {}
self.index = 0
if playlistFile:
if library:
try:
with open(playlistFile) as file:
songsByPath = {}
for g in library.groups.values():
for s in g.songs: songsByPath[s.path] = s
while True:
line = file.readline()
if not line: break
song = songsByPath.get(line[0:-1]) # strip \n
if song:
self.paths[song.path] = len(self.songs)
self.songs.append(song)
except FileNotFoundError: pass
self.index = max(0, min(self.index, len(self.songs)-1))
self.playlistFile = playlistFile
def add(self, songs, moveTo=False):
if type(songs) == Song: songs = (songs,)
elif type(songs) == Group: songs = songs.songs
firstSong = None
for song in songs:
if firstSong is None: firstSong = song
if song.path not in self.paths:
if moveTo: self.index = len(self.songs)
self.paths[song.path] = len(self.songs)
self.songs.append(song)
elif moveTo: self.index = self.paths[song.path]
moveTo = False
return firstSong
def clear(self):
self.songs.clear()
self.paths.clear()
self.index = 0
def remove(self, songs):
if type(songs) == Song: songs = (songs,)
elif type(songs) == Group: songs = songs.songs
if len(songs) == 1:
index = self.paths.get(songs[0].path)
if index >= 0:
self.paths.pop(songs[0].path)
self.songs.remove(self.songs[index])
if index == self.index and index == len(self.songs): self.index -= 1
for p,i in self.paths.items():
if i > index: self.paths[p] -= 1
else:
for song in songs:
index = self.paths.get(song.path)
if index >= 0:
self.paths.pop(song.path)
if index > self.index or index == len(self.songs): self.index -= 1
L = [self.songs[i] for i in self.paths.values()]
self.paths = {L[i].path:i for i in range(len(L))}
self.songs.clear() # avoid changing the self.songs reference, since others may have a reference to it
self.songs.extend(L)
if not self._validIndex(): self.index = 0
def contains(self, song): return song.path in self.paths
def count(self): return len(self.songs)
def getCurrent(self): return self.songs[self.index] if self._validIndex() else None
def isempty(self): return len(self.songs) == 0
def save(self, playlistFile=None):
if playlistFile is None: playlistFile = self.playlistFile
with open(playlistFile, 'w') as file:
for s in self.songs: file.write(s.path + "\n")
def select(self, item):
newIndex = item
if isinstance(item, Song): newIndex = self.paths.get(song.path)
elif isinstance(item, str): newIndex = self.paths.get(item)
if newIndex is None or newIndex < 0 or newIndex >= len(self.songs): return False
self.index = newIndex
return True
def shuffle(self, changeSong=False):
i = 0
while i < len(self.songs):
j = random.randint(i, len(self.songs)-1)
if i != j:
(a, b) = (self.songs[i], self.songs[j])
(self.songs[i], self.songs[j], self.paths[a.path], self.paths[b.path]) = (b, a, j, i)
if not changeSong:
if self.index == i: self.index = j
elif self.index == j: self.index = i
i += 1
def _validIndex(self): return self.index >= 0 and self.index < len(self.songs)