iTunesLibrary 1.2.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,15 @@
1
+ # vi: set syntax=python ts=4 sw=4 sts=4 et ff=unix ai si :
2
+
3
+ """
4
+
5
+ (c) Steve Scholnick <scholnicks@gmail.com>
6
+ MIT License
7
+
8
+ https://github.com/scholnicks/itunes-library
9
+ """
10
+
11
+ name = "itunes-library"
12
+ __author__ = "Steven Scholnick <scholnicks@gmail.com>"
13
+ __version__ = "1.2.1"
14
+ __copyright__ = "Copyright (c) 2013- Steven Scholnick"
15
+ __license__ = "MIT"
@@ -0,0 +1,184 @@
1
+ # vi: set syntax=python ts=4 sw=4 sts=4 et ff=unix ai si :
2
+
3
+ """
4
+ Library - Represents a complete iTunes Library
5
+
6
+ (c) Steve Scholnick <scholnicks@gmail.com>
7
+ MIT License
8
+
9
+ https://github.com/scholnicks/itunes-library
10
+ """
11
+
12
+ from abc import ABCMeta
13
+
14
+
15
+ def parse(pathToXMLFile, ignoreRemoteSongs=True):
16
+ """Main method for constructor a Library object"""
17
+ from . import parser
18
+
19
+ return parser.Parser().parse(pathToXMLFile, ignoreRemoteSongs)
20
+
21
+
22
+ class Library(object):
23
+ """Represents a complete iTunes Library"""
24
+
25
+ def __init__(self, ignoreRemoteSongs):
26
+ """Constructor"""
27
+ super(Library, self).__init__()
28
+ self.playlists = []
29
+ self.items = []
30
+ self.ignoreRemoteSongs = ignoreRemoteSongs
31
+
32
+ def addPlaylist(self, playlist):
33
+ """Adds a playlist"""
34
+ self.playlists.append(playlist)
35
+
36
+ def addItem(self, item):
37
+ """Adds an item"""
38
+ if self.ignoreRemoteSongs and item.remote:
39
+ return
40
+
41
+ self.items.append(item)
42
+
43
+ def getItemsById(self, trackId):
44
+ """Returns an item based on its Track Id or None"""
45
+ trackId = str(trackId) # all keys are strings, allow for integers to be passed in
46
+ return next((i for i in self.items if i.getItunesAttribute("Track ID") == trackId), None)
47
+
48
+ def getPlaylist(self, name):
49
+ """Returns a Playlist based on its name or None"""
50
+ playlist = self.getAllPlaylists(name)
51
+ return playlist[0] if playlist else None
52
+
53
+ def getAllPlaylists(self, name):
54
+ """Returns all playlists that match the name or an empty list"""
55
+ return [p for p in self.playlists if p.title == name]
56
+
57
+ def getItemsForArtist(self, name):
58
+ """Returns all items for an artist as a List"""
59
+ return [i for i in self.items if i.artist == name]
60
+
61
+ def __iter__(self):
62
+ """Allows for quick iteration through the items"""
63
+ return iter(self.items)
64
+
65
+ def __len__(self):
66
+ """returns the number of items stored in the library"""
67
+ return len(self.items)
68
+
69
+
70
+ class ItunesItem(object):
71
+ """Abstract Base Class for iTunes items stored in the library"""
72
+
73
+ __metaclass__ = ABCMeta
74
+
75
+ def __init__(self):
76
+ """Constructor"""
77
+ self.itunesAttributes = dict()
78
+
79
+ def setItunesAttribute(self, key, value):
80
+ """Sets an iTunes attribute"""
81
+ self.itunesAttributes[key] = value
82
+
83
+ def getItunesAttribute(self, key):
84
+ """Returns an iTunes attribute"""
85
+ return self.itunesAttributes.get(key, None)
86
+
87
+ def keys(self):
88
+ """Returns all of the possible iTunes attribute keys"""
89
+ return self.itunesAttributes.keys()
90
+
91
+ @property
92
+ def title(self):
93
+ """Returns the title"""
94
+ return self.getItunesAttribute("Name")
95
+
96
+ @property
97
+ def remote(self):
98
+ """returns if the song is remote"""
99
+ return self.getItunesAttribute("Track Type") == "Remote"
100
+
101
+
102
+ class PlayList(ItunesItem):
103
+ """an iTunes Playlist"""
104
+
105
+ def __init__(self):
106
+ """Constructor"""
107
+ super(PlayList, self).__init__()
108
+ self.items = []
109
+
110
+ def addItem(self, item):
111
+ """Adds an item"""
112
+ self.items.append(item)
113
+
114
+ def is_master(self):
115
+ """returns whether this playlist is the "master" playlist (i.e. the root "Library" playlist)"""
116
+ return self.itunesAttributes.get("Master", False)
117
+
118
+ def is_folder(self):
119
+ """returns whether this playlist is actually a folder of playlists"""
120
+ return self.itunesAttributes.get("Folder", False)
121
+
122
+ def is_distinguished(self):
123
+ """returns whether this playlist is a "distinguished" playlist
124
+ (e.g. "Purchases", "Podcasts", etc.)"""
125
+ disinguished_kind = self.itunesAttributes.get("Distinguished Kind", None)
126
+ return disinguished_kind is not None
127
+
128
+ def is_smart(self):
129
+ """returns whether this playlist is a "smart" (i.e. automatic) playlist"""
130
+ if self.is_folder():
131
+ return False
132
+
133
+ smart_attributes = self.itunesAttributes.get("Smart Criteria", None)
134
+ return smart_attributes is not None
135
+
136
+ def is_regular(self):
137
+ """returns whether this playlist is a "regular" playlist
138
+ (i.e. neither a folder, nor a smart playlist, nor a special one, etc.)"""
139
+ return not (self.is_master() or self.is_folder() or self.is_smart() or self.is_distinguished())
140
+
141
+ def __str__(self):
142
+ """Returns a nice string representation"""
143
+ return "{}: {}".format(self.title.encode("utf-8"), len(self.items))
144
+
145
+ def __repr__(self):
146
+ """Returns a possible internal representation of this object"""
147
+ return str(self.__dict__)
148
+
149
+ def __iter__(self):
150
+ """Allows for quick iteration through the items"""
151
+ return iter(self.items)
152
+
153
+ def __len__(self):
154
+ """returns the number of items stored in this playlist"""
155
+ return len(self.items)
156
+
157
+
158
+ class Item(ItunesItem):
159
+ """an item stored in the iTunes Playlist"""
160
+
161
+ def __init__(self):
162
+ """Constructor"""
163
+ super(Item, self).__init__()
164
+
165
+ @property
166
+ def artist(self):
167
+ """Returns the title"""
168
+ return self.getItunesAttribute("Artist")
169
+
170
+ @property
171
+ def album(self):
172
+ """Returns the album name"""
173
+ return self.getItunesAttribute("Album")
174
+
175
+ def __repr__(self):
176
+ """Returns a possible internal representation of this object"""
177
+ return str(self.__dict__)
178
+
179
+ def __str__(self):
180
+ """Returns a nice string representation"""
181
+ artist = self.artist if self.artist else ""
182
+ album = self.album if self.album else ""
183
+ title = self.title if self.title else ""
184
+ return "{} {} {}".format(artist, album, title)
@@ -0,0 +1,144 @@
1
+ # vi: set syntax=python ts=4 sw=4 sts=4 et ff=unix ai si :
2
+
3
+ """
4
+ Parser - Parses the iTunes XML file
5
+
6
+ (c) Steve Scholnick <scholnicks@gmail.com>
7
+ MIT License
8
+
9
+ See http://search.cpan.org/~dinomite/Mac-iTunes-Library-1.0/lib/Mac/iTunes/Library/XML.pm for Notes on the ridiculous format for the
10
+ iTunes Library XML file
11
+
12
+ Thanks to https://github.com/dinomite for deciphering the iTunes Library XML format
13
+
14
+ https://github.com/scholnicks/itunes-library
15
+ """
16
+
17
+ import xml.sax
18
+ from . import library
19
+
20
+ DICT_TYPE = "dict"
21
+ ARRAY_TYPE = "array"
22
+ STRING_TYPE = "string"
23
+ INTEGER_TYPE = "integer"
24
+ ITEM_ATTRIBUTES = (INTEGER_TYPE, STRING_TYPE, "date", "data")
25
+
26
+
27
+ class Parser(xml.sax.ContentHandler):
28
+ """Parses the iTunes XML file"""
29
+
30
+ def __init__(self):
31
+ """Constructor"""
32
+ xml.sax.ContentHandler.__init__(self)
33
+ self.stack = []
34
+ self.inPlayLists = False
35
+ self.inTracks = False
36
+ self.inMusicFolder = False
37
+ self.inMajorVersion = False
38
+ self.inMinorVersion = False
39
+ self.inApplicationVersion = False
40
+ self.curKey = ""
41
+ self.bufferString = ""
42
+ self.item = None
43
+ self.lib = None
44
+
45
+ def parse(self, pathToXMLFile, ignoreRemoteSongs):
46
+ """parses the XML file passed in"""
47
+ self.lib = library.Library(ignoreRemoteSongs)
48
+ xml.sax.parse(open(pathToXMLFile, "r", encoding="utf8"), self)
49
+ return self.lib
50
+
51
+ def startElement(self, name, attrs):
52
+ """callback method for SAX parsing"""
53
+ self.stack.append(name)
54
+
55
+ if len(self.stack) == 1:
56
+ self.lib.version = attrs.getValue("version")
57
+ elif len(self.stack) == 4:
58
+ if name == DICT_TYPE:
59
+ if self.inPlayLists:
60
+ self.item = library.PlayList()
61
+ else:
62
+ self.item = library.Item()
63
+
64
+ def endElement(self, name):
65
+ """callback method for SAX parsing"""
66
+ depth = len(self.stack)
67
+ self.stack.pop()
68
+
69
+ if depth == 3:
70
+ if name == DICT_TYPE:
71
+ self.inTracks = False
72
+ elif name == ARRAY_TYPE:
73
+ self.inPlayLists = False
74
+
75
+ if self.inMusicFolder and name == STRING_TYPE:
76
+ self.lib.musicFolder = self.bufferString
77
+ self.inMusicFolder = False
78
+ self.curKey = ""
79
+ self.bufferString = ""
80
+ elif depth == 4:
81
+ if self.item:
82
+ if self.inPlayLists:
83
+ self.lib.addPlaylist(self.item)
84
+ else:
85
+ self.lib.addItem(self.item)
86
+
87
+ if name == DICT_TYPE:
88
+ self.item = None
89
+ elif depth == 5:
90
+ if name in ITEM_ATTRIBUTES:
91
+ self.item.setItunesAttribute(self.curKey, self.bufferString)
92
+ self.curKey = ""
93
+ self.bufferString = ""
94
+ elif name == "true":
95
+ self.item.setItunesAttribute(self.curKey, True)
96
+ self.curKey = ""
97
+ elif name == "false":
98
+ self.item.setItunesAttribute(self.curKey, False)
99
+ self.curKey = ""
100
+ elif depth == 7:
101
+ if name == INTEGER_TYPE:
102
+ track = self.lib.getItemsById(self.bufferString)
103
+ if track:
104
+ self.item.addItem(track)
105
+
106
+ self.curKey = ""
107
+ self.bufferString = ""
108
+
109
+ def characters(self, content):
110
+ """callback method for SAX parsing"""
111
+ if len(self.stack) == 3:
112
+ if self.stack[-1] == "key":
113
+ if content == "Application Version":
114
+ self.inApplicationVersion = True
115
+ elif content == "Major Version":
116
+ self.inMajorVersion = True
117
+ elif content == "Minor Version":
118
+ self.inMinorVersion = True
119
+ elif content == "Tracks":
120
+ self.inTracks = True
121
+ elif content == "Playlists":
122
+ self.inPlayLists = True
123
+ elif self.stack[-1] in ("integer", "string", "true", "false"):
124
+ if self.inApplicationVersion:
125
+ self.lib.applicationVersion = content
126
+ self.inApplicationVersion = False
127
+ elif self.inMajorVersion:
128
+ self.lib.majorVersion = content
129
+ self.inMajorVersion = False
130
+ elif self.inMinorVersion:
131
+ self.lib.minorVersion = content
132
+ self.inMinorVersion = False
133
+ elif self.inMusicFolder:
134
+ self.bufferString += content if content else ""
135
+ elif len(self.stack) == 5:
136
+ if self.stack[-1] == "key":
137
+ self.curKey += content if content else ""
138
+ elif self.stack[-1] in ITEM_ATTRIBUTES:
139
+ self.bufferString += content if content else ""
140
+ elif len(self.stack) == 7:
141
+ if self.stack[-1] == "key":
142
+ self.curKey += content
143
+ elif self.stack[-1] in ("integer", "string", "date"):
144
+ self.bufferString += content if content else ""
@@ -0,0 +1,71 @@
1
+ Metadata-Version: 2.4
2
+ Name: iTunesLibrary
3
+ Version: 1.2.2
4
+ Summary: itunesLibrary represents an iTunes Library.
5
+ License-Expression: MIT
6
+ License-File: LICENSE
7
+ Keywords: iTunes,Apple,Music,Library,XML
8
+ Author: Steve Scholnick
9
+ Author-email: scholnicks@gmail.com
10
+ Requires-Python: >=3.13
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Topic :: Utilities
16
+ Requires-Dist: docopt-ng (>=0.9.0,<0.10.0)
17
+ Project-URL: Homepage, https://pypi.org/project/iTunesLibrary/
18
+ Project-URL: Repository, https://github.com/scholnicks/itunes-library/
19
+ Description-Content-Type: text/markdown
20
+
21
+ # itunesLibrary
22
+ ==============
23
+
24
+ itunesLibrary represents an iTunes Library. It allows the caller to retrieve items, playlists, etc.
25
+
26
+ itunesLibrary is a port of Drew Stephen's excellent Perl module, https://github.com/dinomite/Mac-iTunes-Library. The Perl
27
+ library will be **not** re-created verbatim.
28
+
29
+ Installation :
30
+ ```bash
31
+ pip install itunesLibrary
32
+ ```
33
+
34
+ ## Example Code
35
+
36
+ ```python
37
+ import os
38
+ from itunesLibrary import library
39
+
40
+ path = os.path.join(os.getenv("HOME"),"Music/iTunes/iTunes Music Library.xml")
41
+
42
+ # must first parse...
43
+ lib = library.parse(path)
44
+
45
+ print(len(lib)) # number of items stored
46
+
47
+ for playlist in lib.playlists:
48
+ for item in playlist.items:
49
+ print(item) # perform function on each item in the playlist
50
+
51
+ # get a single playlist
52
+ playlist = lib.getPlaylist("Gray")
53
+
54
+ # check the playlist type
55
+ assert(not playlist.is_smart())
56
+ assert(not playlist.is_folder())
57
+
58
+ # get a list of all of the David Bowie songs
59
+ bowie_items = lib.getItemsForArtist("David Bowie")
60
+
61
+ # get a single song
62
+ single_song = lib.getItemsById("16116")
63
+
64
+ # get the iTunes application version
65
+ print(lib.applicationVersion)
66
+ ```
67
+
68
+ ## Contributors
69
+ * scholnicks@gmail.com
70
+ * software@froissart.eu
71
+
@@ -0,0 +1,7 @@
1
+ ituneslibrary/__init__.py,sha256=NhxwsZP22MI5Y1tBnhPDqisUePgP6p9jFDf8mWfxbgI,351
2
+ ituneslibrary/library.py,sha256=VHg0kAM4BrjL5sb9cJmMuT8v3exO1YP24QMU4FRWMq0,5692
3
+ ituneslibrary/parser.py,sha256=A2HSIhtDmb3wMI4IjkjqzvNrgMIBb7Jd42zOyfcjsZg,5154
4
+ ituneslibrary-1.2.2.dist-info/METADATA,sha256=tlANokjkqYy8a5WWtf9O-6NOyFqQMkgo19AHConQt8s,1919
5
+ ituneslibrary-1.2.2.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
6
+ ituneslibrary-1.2.2.dist-info/licenses/LICENSE,sha256=wT_mfbxynx42y5DZ0-Kuf_pie3PaQPeK5nXUL8_V1WQ,1073
7
+ ituneslibrary-1.2.2.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: poetry-core 2.2.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021- Steve Scholnick
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.