-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathplaytime.py
130 lines (106 loc) · 4.27 KB
/
playtime.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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# -*- coding: utf-8 -*-
import argparse
import os
import plistlib
import sys
import operator
from unidecode import unidecode
from tabulate import tabulate
ALIAS_DICT = {
'MF Doom': ['Doom','MF Doom','Danger Doom','King Geedorah','Madvillain','Viktor Vaughn'],
}
XML_PATH = 'path/to/MusicBee/iTunes Music Library.xml'
def time_to_human(seconds):
"""
Convert time in seconds to human readable format.
:returns: a string of the form "DD days, HH hours, MM minites and
SS seconds".
"""
assert seconds >= 0 #number of seconds should be nonnegative
dd = int(seconds) // 86400 # days
hh = (int(seconds) // 3600) % 24 # hours
mm = (int(seconds) // 60) % 60 # minutes
ss = seconds - (int(seconds) // 60) * 60 # seconds
text = ""
if dd:
text += "%d days, " % dd
if hh:
text += "%d hours, " % hh
if mm:
text += "%d min, " % mm
if ss:
text += "%d sec" % ss
return text
def get_alias(name):
"""
Return any known alias for a given name, otherwise return the given name.
:param name: String of name to check for aliases.
:returns: Alias string if found, else the name given.
"""
for alias,aka_list in ALIAS_DICT.items():
if name in aka_list:
return alias
return name
def itunes_total_time(library_plist=None, tag_key="Album Artist", optional_key=None):
"""
Prints the time spent on each track grouped by tag_key.
:param library_plist: path to the iTunes Music Library plist file.
:param tag_key: Metadata tag to group by.
:param optional_key: Add a tag to the grouped tag for readability.
:returns: List of tuples ordered by time where the tag_key is the first item and a dict with time, time_human, avg_time_human and count is stored.
"""
with open(library_plist, "rb") as fp:
tree = plistlib.load(fp) # the XML tree
if 'Tracks' not in tree:
return 0
tag_dict = {}
for track in tree['Tracks'].values():
try:
if tag_key not in track:
print("missing " + tag_key + " tag",unidecode(track['Artist']),unidecode(track['Name']))
if 'audio' in track['Kind'] and 'Total Time' in track and 'Play Count' in track:
curr_key = track[tag_key]
curr_key = get_alias(curr_key)
if optional_key:
curr_key += " (" + get_alias(track[optional_key]) + ")"
if curr_key not in tag_dict:
tag_dict[curr_key] = {'time':0, 'count':0}
tag_dict[curr_key]['time'] += track['Total Time'] * track['Play Count'] / 1000
tag_dict[curr_key]['count'] += track['Play Count']
else:
#print("skipping",unidecode(track['Artist']),unidecode(track['Name']))
pass
except KeyError:
pass
# Add a total
total_count = sum(x['count'] for x in tag_dict.values())
total_time = sum(x ['time'] for x in tag_dict.values())
tag_dict['Total Time'] = {'time': total_time, 'count': total_count}
#Print each time
sorted_times = sorted(tag_dict.items(), key=lambda kv: kv[1]['time'])
for curr_key, curr_dict in sorted_times:
time_human = time_to_human(curr_dict['time'])
curr_dict['time_human'] = time_human
curr_dict['avg_time_human'] = time_to_human(curr_dict['time']/curr_dict['count'])
return sorted_times
def print_sorted(sorted_times):
table_rows = []
for curr_key, curr_dict in sorted_times:
table_rows.append([unidecode(curr_key), curr_dict['time_human'], curr_dict['count'], curr_dict['avg_time_human']])
print(tabulate(table_rows, headers=['Name', 'Time', 'Play Count', 'Avg Song Time']))
def main(file_path=XML_PATH):
"""
Prints the time listened by Album then by Album Artist.
:param file_path: String of library xml.
"""
try:
print("------\nALBUM\n------")
print_sorted(itunes_total_time(file_path, tag_key="Album", optional_key="Album Artist"))
print("------\nARTIST\n------")
print_sorted(itunes_total_time(file_path))
except FileNotFoundError as err:
print(err, file=sys.stderr)
exit(1)
input("continue")
if __name__ == '__main__':
main()