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"
|
ituneslibrary/library.py
ADDED
|
@@ -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)
|
ituneslibrary/parser.py
ADDED
|
@@ -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,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.
|