forked from Zylence/m3u-Playlist-Creation-Script
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathm3u.py
More file actions
138 lines (116 loc) · 5.35 KB
/
m3u.py
File metadata and controls
138 lines (116 loc) · 5.35 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
#For documentation refer to my Github: https://github.com/Zylence/m3u-Playlist-Creation-Script
import os
from optparse import OptionParser
import music_tag
def scanDirectories():
'''Scans 'startDirectory' and all subdirectories for all files listed in 'acceptedFormats' and
writes them to a dictionary mapping files to a list of paths.
'''
foundFiles = dict()
for root, dirs, files in os.walk(startDirectory):
if os.path.split(root)[1] not in excludeDirectories:
for file in files:
if os.path.splitext(file)[1] in acceptedFormats:
foundFiles.setdefault(file, []).append(root)
return foundFiles
def createPathsHelper(filename, path):
'''Returns a pair of an absolute or relative path according to the'absolutePaths' variable
and an absolute path used in case of adding #EXTINF.
'''
abs_path = os.path.join(path, filename)
return abs_path if absolutePaths else os.path.relpath(abs_path, start=startDirectory)
def createPathsExtHelper(filename, path):
'''Returns a pair of an absolute or relative path according to the'absolutePaths' variable
and an absolute path used in case of adding #EXTINF.
'''
abs_path = os.path.join(path, filename)
mt = music_tag.load_file(abs_path)
length = int(round(mt['#length'].value))
artist = mt['artist'].value
track = mt['tracktitle'].value
title = f'{artist} - {track}'
res_path = abs_path if absolutePaths else os.path.relpath(abs_path, start=startDirectory)
return res_path, title, length
def createPaths(files, helper):
'''Takes a dictionary of files mapping to paths. Returns a list of absolute or
relative paths with duplicates either removed or not.
'''
resPaths = []
for filename, paths in files.items(): # paths is a list
if removeDuplicates:
# discard all but the first path
resPaths.append(helper(filename, paths[0]))
else:
# keep all paths
for path in paths:
resPaths.append(helper(filename, path))
print(f'{len(resPaths)} files found in {startDirectory}.')
return resPaths
def writePlaylist(paths):
'''Writes all the 'paths' into a playlist file named 'fileName' using 'mode'
as method of writing.
'''
if sortItems:
paths.sort()
with open(fileName, mode, encoding=codec) as f:
for path in paths:
f.write(path + '\n')
f.close()
def writePlaylistExt(paths):
'''Writes all the 'paths' into a playlist file named 'fileName' using 'mode'
as method of writing.
also adds '#EXTINF:LENGTH, ARTIST - TRACK'
'''
if sortItems:
paths.sort(key=lambda e: e[1])
with open(fileName, mode, encoding=codec) as f:
f.write('#EXTM3U\n\n')
for path, title, length in paths:
f.write(f'#EXTINF:{length},{title}\n')
f.write(path)
f.write('\n\n')
f.close()
if __name__ == '__main__':
# args parsing
parser = OptionParser()
parser.add_option('-n', default='playlist.m3u',
help='the name of your playlist, use `.m3u` extension')
parser.add_option('-m', default='w',
help='mode used for writing, choose `a` to append, and `w` to overwrite the playlist file')
parser.add_option('-c', default='utf-8',
help='codec used for opening (writing) a file')
parser.add_option('-s', default=os.getcwd(),
help='the starting directory for the script to scan for music files, usually your music library')
parser.add_option('-e', default='',
help='string containing subdirectories separated by whitespaces, e.g.: `Celtic Classic` will '
'not be included in the playlist')
parser.add_option('-d', action='store_true', default=False,
help='boolean determining whether or not to exclude duplicate files from the playlist')
parser.add_option('-i', action='store_true', default=False,
help='boolean determining whether or not to include #EXTINF tag before each file')
parser.add_option('-a', action='store_true', default=False,
help='boolean determining whether to sort items alphabetically')
parser.add_option('-r', action='store_true', default=False,
help='boolean determining whether to use relative paths')
parser.add_option('-f', default='.mp3 .flac .wav .aac',
help='string containing file formats separated by whitespaces, e.g.: `.mp3 .flac`')
(options, args) = parser.parse_args()
# if you prefer hard coding - edit those assignments
fileName = options.n
mode = options.m
codec = options.c
startDirectory = options.s
excludeDirectories = options.e.split()
removeDuplicates = options.d
acceptedFormats = options.f.split()
absolutePaths = not options.r
appendExtInf = options.i
sortItems = options.a
# main script
foundFiles = scanDirectories()
if appendExtInf:
paths = createPaths(foundFiles, createPathsExtHelper)
writePlaylistExt(paths)
else:
paths = createPaths(foundFiles, createPathsHelper)
writePlaylist(paths)