-
Notifications
You must be signed in to change notification settings - Fork 88
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #66 from sintc0nery/hotfix/deprecated_method
I replaced the deprecated method readPlist
- Loading branch information
Showing
12 changed files
with
447 additions
and
143 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
import logging | ||
import plistlib | ||
from six import PY2 | ||
from six.moves.urllib import parse as urlparse | ||
import time | ||
|
||
from libpytunes.Song import Song | ||
from libpytunes.Playlist import Playlist | ||
|
||
|
||
logger = logging.getLogger(__name__) | ||
|
||
try: | ||
import xspf | ||
xspfAvailable = True | ||
except ImportError: | ||
xspfAvailable = False | ||
pass | ||
|
||
|
||
class Library: | ||
def __init__(self, itunesxml, musicPathXML=None, musicPathSystem=None, filesOnly=False): | ||
# musicPathXML and musicPathSystem will do path conversion | ||
# when xml is being processed on different OS then iTunes | ||
self.musicPathXML = musicPathXML | ||
self.musicPathSystem = musicPathSystem | ||
self.filesOnly = filesOnly | ||
with open(itunesxml, 'rb') as f: | ||
self.il = plistlib.load(f) | ||
self.songs = {} | ||
self.getSongs() | ||
|
||
def getSongs(self): | ||
format = "%Y-%m-%d %H:%M:%S" | ||
for trackid, attributes in self.il['Tracks'].items(): | ||
s = Song() | ||
|
||
s.name = attributes.get('Name') | ||
|
||
# Support classical music naming (Work+Movement Number+Movement Name) since iTunes 12.5 | ||
s.work = attributes.get('Work') | ||
s.movement_number = attributes.get('Movement Number') | ||
s.movement_count = attributes.get('Movement Count') | ||
s.movement_name = attributes.get('Movement Name') | ||
|
||
s.track_id = int(attributes.get('Track ID')) if attributes.get('Track ID') else None | ||
s.artist = attributes.get('Artist') | ||
s.album_artist = attributes.get('Album Artist') | ||
s.composer = attributes.get('Composer') | ||
s.album = attributes.get('Album') | ||
s.genre = attributes.get('Genre') | ||
s.kind = attributes.get('Kind') | ||
s.size = int(attributes.get('Size')) if attributes.get('Size') else None | ||
s.total_time = attributes.get('Total Time') | ||
s.track_number = attributes.get('Track Number') | ||
s.track_count = int(attributes.get('Track Count')) if attributes.get('Track Count') else None | ||
s.disc_number = int(attributes.get('Disc Number')) if attributes.get('Disc Number') else None | ||
s.disc_count = int(attributes.get('Disc Count')) if attributes.get('Disc Count') else None | ||
s.year = int(attributes.get('Year')) if attributes.get('Year') else None | ||
s.date_modified = time.strptime(str(attributes.get('Date Modified')), format) if attributes.get('Date Modified') else None | ||
s.date_added = time.strptime(str(attributes.get('Date Added')), format) if attributes.get('Date Added') else None | ||
s.bit_rate = int(attributes.get('Bit Rate')) if attributes.get('Bit Rate') else None | ||
s.sample_rate = int(attributes.get('Sample Rate')) if attributes.get('Sample Rate') else None | ||
s.comments = attributes.get("Comments") | ||
s.rating = int(attributes.get('Rating')) if attributes.get('Rating') else None | ||
s.rating_computed = 'Rating Computed' in attributes | ||
s.play_count = int(attributes.get('Play Count')) if attributes.get('Play Count') else None | ||
s.album_rating = attributes.get('Album Rating') | ||
s.album_rating_computed = 'Album Rating Computed' in attributes | ||
s.persistent_id = attributes.get('Persistent ID') | ||
|
||
if attributes.get('Location'): | ||
s.location_escaped = attributes.get('Location') | ||
s.location = s.location_escaped | ||
s.location = urlparse.unquote(urlparse.urlparse(attributes.get('Location')).path[1:]) | ||
s.location = s.location.decode('utf-8') if PY2 else s.location # fixes bug #19 | ||
if (self.musicPathXML is not None and self.musicPathSystem is not None): | ||
s.location = s.location.replace(self.musicPathXML, self.musicPathSystem) | ||
|
||
s.compilation = 'Compilation' in attributes | ||
s.lastplayed = time.strptime(str(attributes.get('Play Date UTC')), format) if attributes.get('Play Date UTC') else None | ||
s.skip_count = int(attributes.get('Skip Count')) if attributes.get('Skip Count') else None | ||
s.skip_date = time.strptime(str(attributes.get('Skip Date')), format) if attributes.get('Skip Date') else None | ||
s.length = int(attributes.get('Total Time')) if attributes.get('Total Time') else None | ||
s.track_type = attributes.get('Track Type') | ||
s.grouping = attributes.get('Grouping') | ||
s.podcast = 'Podcast' in attributes | ||
s.movie = 'Movie' in attributes | ||
s.has_video = 'Has Video' in attributes | ||
s.loved = 'Loved' in attributes | ||
s.album_loved = 'Album Loved' in attributes | ||
s.playlist_only = 'Playlist Only' in attributes | ||
s.apple_music = 'Apple Music' in attributes | ||
s.protected = 'Protected' in attributes | ||
|
||
self.songs[int(trackid)] = s | ||
|
||
def getPlaylistNames(self, ignoreList=[ | ||
"Library", "Music", "Movies", "TV Shows", "Purchased", "iTunes DJ", "Podcasts" | ||
]): | ||
|
||
playlists = [] | ||
for playlist in self.il['Playlists']: | ||
if playlist['Name'] not in ignoreList: | ||
playlists.append(playlist['Name']) | ||
return playlists | ||
|
||
def getPlaylist(self, playlistName): | ||
for playlist in self.il['Playlists']: | ||
if playlist['Name'] == playlistName: | ||
# id playlist_id track_num url title album artist length uniqueid | ||
p = Playlist(playlistName) | ||
p.playlist_id = playlist['Playlist ID'] | ||
p.is_folder = playlist.get('Folder', False) | ||
p.playlist_persistent_id = playlist.get('Playlist Persistent ID') | ||
p.parent_persistent_id = playlist.get('Parent Persistent ID') | ||
p.distinguished_kind = playlist.get('Distinguished Kind') | ||
p.is_genius_playlist = True if playlist.get('Genius Track ID') else False | ||
p.is_smart_playlist = True if playlist.get('Smart Info') and not playlist.get('Folder', False) else False | ||
tracknum = 1 | ||
# Make sure playlist was not empty | ||
if 'Playlist Items' in playlist: | ||
for track in playlist['Playlist Items']: | ||
id = int(track['Track ID']) | ||
t = self.songs[id] | ||
t.playlist_order = tracknum | ||
tracknum += 1 | ||
p.tracks.append(t) | ||
return p | ||
|
||
def getPlaylistxspf(self, playlistName): | ||
global xspfAvailable | ||
if (xspfAvailable): | ||
x = xspf.Xspf() | ||
for playlist in self.il['Playlists']: | ||
if playlist['Name'] == playlistName: | ||
x.title = playlistName | ||
x.info = "" | ||
for track in playlist['Playlist Items']: | ||
id = int(track['Track ID']) | ||
x.add_track(title=self.songs[id].name, creator="", location=self.songs[id].location) | ||
return x.toXml() | ||
else: | ||
logger.warning("xspf library missing, go to https://github.com/alastair/xspf to install.") | ||
return None |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
from six import iteritems | ||
|
||
class Playlist: | ||
is_folder = False | ||
playlist_persistent_id = None | ||
parent_persistent_id = None | ||
distinguished_kind = None | ||
playlist_id = None | ||
|
||
def __init__(self, playListName=None): | ||
self.name = playListName | ||
self.tracks = [] | ||
|
||
def __iter__(self): | ||
for attr, value in iteritems(self.__dict__): | ||
yield attr, value | ||
|
||
def ToDict(self): | ||
return {key: value for (key, value) in self} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
from six import iteritems | ||
|
||
|
||
class Song: | ||
""" | ||
Song Attributes: | ||
name (String) | ||
track_id (Integer) | ||
artist (String) | ||
album_artist (String) | ||
composer = None (String) | ||
album = None (String) | ||
genre = None (String) | ||
kind = None (String) | ||
size = None (Integer) | ||
total_time = None (Integer) | ||
track_number = None (Integer) | ||
track_count = None (Integer) | ||
disc_number = None (Integer) | ||
disc_count = None (Integer) | ||
year = None (Integer) | ||
date_modified = None (Time) | ||
date_added = None (Time) | ||
bit_rate = None (Integer) | ||
sample_rate = None (Integer) | ||
comments = None (String) | ||
rating = None (Integer) | ||
rating_computed = False (Boolean) | ||
album_rating = None (Integer) | ||
play_count = None (Integer) | ||
location = None (String) | ||
location_escaped = None (String) | ||
compilation = False (Boolean) | ||
grouping = None (String) | ||
lastplayed = None (Time) | ||
skip_count = None (Integer) | ||
skip_date = None (Time) | ||
length = None (Integer) | ||
persistent_id = None (String) | ||
album_rating_computed = False (Boolean) | ||
work = None (String) | ||
movement_name = None (String) | ||
movement_number = None (Integer) | ||
movement_count = None (Integer) | ||
playlist_only = None (Bool) | ||
apple_music = None (Bool) | ||
protected = None (Bool) | ||
""" | ||
name = None | ||
track_id = None | ||
artist = None | ||
album_artist = None | ||
composer = None | ||
album = None | ||
genre = None | ||
kind = None | ||
size = None | ||
total_time = None | ||
track_number = None | ||
track_count = None | ||
disc_number = None | ||
disc_count = None | ||
year = None | ||
date_modified = None | ||
date_added = None | ||
bit_rate = None | ||
sample_rate = None | ||
comments = None | ||
rating = None | ||
rating_computed = None | ||
album_rating = None | ||
play_count = None | ||
skip_count = None | ||
skip_date = None | ||
location = None | ||
location_escaped = None | ||
compilation = None | ||
grouping = None | ||
lastplayed = None | ||
length = None | ||
persistent_id = None | ||
album_rating_computed = None | ||
work = None | ||
movement_name = None | ||
movement_number = None | ||
movement_count = None | ||
playlist_only = None | ||
apple_music = None | ||
protected = None | ||
|
||
def __iter__(self): | ||
for attr, value in iteritems(self.__dict__): | ||
yield attr, value | ||
|
||
def ToDict(self): | ||
return {key: value for (key, value) in self} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
from libpytunes.Library import Library | ||
from libpytunes.Song import Song | ||
from libpytunes.Playlist import Playlist |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import unittest | ||
from libpytunes import Library | ||
import os | ||
|
||
class TestLibrary(unittest.TestCase): | ||
|
||
def setUp(self): | ||
self.it_library = Library(os.path.join(os.path.dirname(__file__), "Test Library.xml")) | ||
|
||
def test_songs(self): | ||
|
||
for id, song in self.it_library.songs.items(): | ||
assert(hasattr(song, 'name') == True) | ||
|
||
def test_playlists(self): | ||
|
||
playlists = self.it_library.getPlaylistNames() | ||
|
||
for song in self.it_library.getPlaylist(playlists[0]).tracks: | ||
assert(hasattr(song, 'track_number')) | ||
assert(hasattr(song, 'artist')) | ||
assert(hasattr(song, 'name')) | ||
|
||
if __name__ == '__main__': | ||
unittest.main() |
Binary file not shown.
Oops, something went wrong.