python-didl-lite 1.3.2__tar.gz → 1.4.1__tar.gz
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.
- python_didl_lite-1.4.1/PKG-INFO +44 -0
- {python-didl-lite-1.3.2 → python_didl_lite-1.4.1}/README.rst +4 -4
- {python-didl-lite-1.3.2 → python_didl_lite-1.4.1}/didl_lite/didl_lite.py +40 -19
- python_didl_lite-1.4.1/python_didl_lite.egg-info/PKG-INFO +44 -0
- {python-didl-lite-1.3.2 → python_didl_lite-1.4.1}/python_didl_lite.egg-info/SOURCES.txt +2 -1
- {python-didl-lite-1.3.2 → python_didl_lite-1.4.1}/setup.py +7 -4
- python_didl_lite-1.4.1/tests/test_didl_lite.py +682 -0
- python-didl-lite-1.3.2/PKG-INFO +0 -41
- python-didl-lite-1.3.2/python_didl_lite.egg-info/PKG-INFO +0 -41
- {python-didl-lite-1.3.2 → python_didl_lite-1.4.1}/LICENSE.md +0 -0
- {python-didl-lite-1.3.2 → python_didl_lite-1.4.1}/didl_lite/__init__.py +0 -0
- {python-didl-lite-1.3.2 → python_didl_lite-1.4.1}/didl_lite/py.typed +0 -0
- {python-didl-lite-1.3.2 → python_didl_lite-1.4.1}/didl_lite/utils.py +0 -0
- {python-didl-lite-1.3.2 → python_didl_lite-1.4.1}/python_didl_lite.egg-info/dependency_links.txt +0 -0
- {python-didl-lite-1.3.2 → python_didl_lite-1.4.1}/python_didl_lite.egg-info/requires.txt +0 -0
- {python-didl-lite-1.3.2 → python_didl_lite-1.4.1}/python_didl_lite.egg-info/top_level.txt +0 -0
- {python-didl-lite-1.3.2 → python_didl_lite-1.4.1}/setup.cfg +0 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: python-didl-lite
|
|
3
|
+
Version: 1.4.1
|
|
4
|
+
Summary: DIDL-Lite (Digital Item Declaration Language) tools for Python
|
|
5
|
+
Home-page: https://github.com/StevenLooman/python-didl-lite
|
|
6
|
+
Author: Steven Looman
|
|
7
|
+
Author-email: steven.looman@gmail.com
|
|
8
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
11
|
+
Classifier: Programming Language :: Python
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Requires-Python: >=3.8
|
|
19
|
+
Description-Content-Type: text/x-rst
|
|
20
|
+
License-File: LICENSE.md
|
|
21
|
+
Requires-Dist: defusedxml>=0.6.0
|
|
22
|
+
|
|
23
|
+
DIDL-Lite (Digital Item Declaration Language) tools for Python
|
|
24
|
+
==============================================================
|
|
25
|
+
|
|
26
|
+
DIDL-Lite tools for Python to read and write DIDL-Lite-xml.
|
|
27
|
+
|
|
28
|
+
Usage
|
|
29
|
+
-----
|
|
30
|
+
|
|
31
|
+
See ``tests/`` for examples on how to use this.
|
|
32
|
+
|
|
33
|
+
Note that this package does **NOT** do any type checking. Coercion of data types from and to strings must be done by the user of this library.
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
Resources / documents
|
|
37
|
+
---------------------
|
|
38
|
+
|
|
39
|
+
DIDL-Lite resources and documents:
|
|
40
|
+
|
|
41
|
+
* `UPnP-av-ContentDirectory-v1-Service <http://upnp.org/specs/av/UPnP-av-ContentDirectory-v1-Service.pdf>`_
|
|
42
|
+
* `UPnP-av-ContentDirectory-v2-Service <http://upnp.org/specs/av/UPnP-av-ContentDirectory-v2-Service.pdf>`_
|
|
43
|
+
* `didl-lite-v2.xsd <http://www.upnp.org/schemas/av/didl-lite-v2.xsd>`_
|
|
44
|
+
* `mpeg21-didl <http://xml.coverpages.org/mpeg21-didl.html>`_
|
|
@@ -16,7 +16,7 @@ Resources / documents
|
|
|
16
16
|
|
|
17
17
|
DIDL-Lite resources and documents:
|
|
18
18
|
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
19
|
+
* `UPnP-av-ContentDirectory-v1-Service <http://upnp.org/specs/av/UPnP-av-ContentDirectory-v1-Service.pdf>`_
|
|
20
|
+
* `UPnP-av-ContentDirectory-v2-Service <http://upnp.org/specs/av/UPnP-av-ContentDirectory-v2-Service.pdf>`_
|
|
21
|
+
* `didl-lite-v2.xsd <http://www.upnp.org/schemas/av/didl-lite-v2.xsd>`_
|
|
22
|
+
* `mpeg21-didl <http://xml.coverpages.org/mpeg21-didl.html>`_
|
|
@@ -28,10 +28,10 @@ from .utils import (
|
|
|
28
28
|
to_camel_case,
|
|
29
29
|
)
|
|
30
30
|
|
|
31
|
-
TDO = TypeVar("TDO", bound="DidlObject")
|
|
32
|
-
TC = TypeVar("TC", bound="Container")
|
|
33
|
-
TD = TypeVar("TD", bound="Descriptor")
|
|
34
|
-
TR = TypeVar("TR", bound="Resource")
|
|
31
|
+
TDO = TypeVar("TDO", bound="DidlObject") # pylint: disable=invalid-name
|
|
32
|
+
TC = TypeVar("TC", bound="Container") # pylint: disable=invalid-name
|
|
33
|
+
TD = TypeVar("TD", bound="Descriptor") # pylint: disable=invalid-name
|
|
34
|
+
TR = TypeVar("TR", bound="Resource") # pylint: disable=invalid-name
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
class DidlLiteException(Exception):
|
|
@@ -39,6 +39,12 @@ class DidlLiteException(Exception):
|
|
|
39
39
|
|
|
40
40
|
|
|
41
41
|
# region: DidlObjects
|
|
42
|
+
|
|
43
|
+
# upnp_class to python type mapping
|
|
44
|
+
_upnp_class_map: Dict[str, Type["DidlObject"]] = {}
|
|
45
|
+
_upnp_class_map_lowercase: Dict[str, Type["DidlObject"]] = {}
|
|
46
|
+
|
|
47
|
+
|
|
42
48
|
class DidlObject:
|
|
43
49
|
"""DIDL Object."""
|
|
44
50
|
|
|
@@ -61,6 +67,15 @@ class DidlObject:
|
|
|
61
67
|
xml_el: Optional[ET.Element]
|
|
62
68
|
descriptors: Sequence["Descriptor"]
|
|
63
69
|
|
|
70
|
+
@classmethod
|
|
71
|
+
def __init_subclass__(cls: Type["DidlObject"], **kwargs: Any) -> None:
|
|
72
|
+
"""Create mapping of upnp_class to Python type for fast lookup."""
|
|
73
|
+
super().__init_subclass__(**kwargs)
|
|
74
|
+
assert cls.upnp_class not in _upnp_class_map
|
|
75
|
+
assert cls.upnp_class.lower() not in _upnp_class_map_lowercase
|
|
76
|
+
_upnp_class_map[cls.upnp_class] = cls
|
|
77
|
+
_upnp_class_map_lowercase[cls.upnp_class.lower()] = cls
|
|
78
|
+
|
|
64
79
|
def __init__(
|
|
65
80
|
self,
|
|
66
81
|
id: str = "",
|
|
@@ -85,9 +100,6 @@ class DidlObject:
|
|
|
85
100
|
self.xml_el = xml_el
|
|
86
101
|
self.descriptors = descriptors if descriptors else []
|
|
87
102
|
|
|
88
|
-
def _clean_property_names(self, properties: Dict[str, Any]) -> Dict[str, Any]:
|
|
89
|
-
"""Turn all property names into lower_camel_case."""
|
|
90
|
-
|
|
91
103
|
def _ensure_required_properties(
|
|
92
104
|
self, strict: bool, properties: Mapping[str, Any]
|
|
93
105
|
) -> None:
|
|
@@ -1082,9 +1094,17 @@ def from_xml_el(
|
|
|
1082
1094
|
# construct item
|
|
1083
1095
|
upnp_class = child_el.find("./upnp:class", NAMESPACES)
|
|
1084
1096
|
if upnp_class is None or not upnp_class.text:
|
|
1085
|
-
|
|
1086
|
-
|
|
1097
|
+
if strict:
|
|
1098
|
+
continue
|
|
1099
|
+
# WiiM Pro and possibly other Linkplay devices emit
|
|
1100
|
+
# upnp_class above the item element instead of inside it
|
|
1101
|
+
upnp_class = xml_el.find("./upnp:class", NAMESPACES)
|
|
1102
|
+
if upnp_class is None or not upnp_class.text:
|
|
1103
|
+
continue
|
|
1104
|
+
didl_object_type = type_by_upnp_class(upnp_class.text, strict)
|
|
1087
1105
|
if didl_object_type is None:
|
|
1106
|
+
if strict:
|
|
1107
|
+
raise DidlLiteException(f"upnp:class {upnp_class.text} is unknown")
|
|
1088
1108
|
continue
|
|
1089
1109
|
didl_object = didl_object_type.from_xml(child_el, strict)
|
|
1090
1110
|
didl_objects.append(didl_object)
|
|
@@ -1098,13 +1118,14 @@ def from_xml_el(
|
|
|
1098
1118
|
|
|
1099
1119
|
|
|
1100
1120
|
# upnp_class to python type mapping
|
|
1101
|
-
def type_by_upnp_class(
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1121
|
+
def type_by_upnp_class(
|
|
1122
|
+
upnp_class: str, strict: bool = True
|
|
1123
|
+
) -> Optional[Type[DidlObject]]:
|
|
1124
|
+
"""Get DidlObject-type by upnp_class.
|
|
1125
|
+
|
|
1126
|
+
When strict is False, the upnp_class lookup will be done ignoring string
|
|
1127
|
+
case.
|
|
1128
|
+
"""
|
|
1129
|
+
if strict:
|
|
1130
|
+
return _upnp_class_map.get(upnp_class)
|
|
1131
|
+
return _upnp_class_map_lowercase.get(upnp_class.lower())
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: python-didl-lite
|
|
3
|
+
Version: 1.4.1
|
|
4
|
+
Summary: DIDL-Lite (Digital Item Declaration Language) tools for Python
|
|
5
|
+
Home-page: https://github.com/StevenLooman/python-didl-lite
|
|
6
|
+
Author: Steven Looman
|
|
7
|
+
Author-email: steven.looman@gmail.com
|
|
8
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
11
|
+
Classifier: Programming Language :: Python
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Requires-Python: >=3.8
|
|
19
|
+
Description-Content-Type: text/x-rst
|
|
20
|
+
License-File: LICENSE.md
|
|
21
|
+
Requires-Dist: defusedxml>=0.6.0
|
|
22
|
+
|
|
23
|
+
DIDL-Lite (Digital Item Declaration Language) tools for Python
|
|
24
|
+
==============================================================
|
|
25
|
+
|
|
26
|
+
DIDL-Lite tools for Python to read and write DIDL-Lite-xml.
|
|
27
|
+
|
|
28
|
+
Usage
|
|
29
|
+
-----
|
|
30
|
+
|
|
31
|
+
See ``tests/`` for examples on how to use this.
|
|
32
|
+
|
|
33
|
+
Note that this package does **NOT** do any type checking. Coercion of data types from and to strings must be done by the user of this library.
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
Resources / documents
|
|
37
|
+
---------------------
|
|
38
|
+
|
|
39
|
+
DIDL-Lite resources and documents:
|
|
40
|
+
|
|
41
|
+
* `UPnP-av-ContentDirectory-v1-Service <http://upnp.org/specs/av/UPnP-av-ContentDirectory-v1-Service.pdf>`_
|
|
42
|
+
* `UPnP-av-ContentDirectory-v2-Service <http://upnp.org/specs/av/UPnP-av-ContentDirectory-v2-Service.pdf>`_
|
|
43
|
+
* `didl-lite-v2.xsd <http://www.upnp.org/schemas/av/didl-lite-v2.xsd>`_
|
|
44
|
+
* `mpeg21-didl <http://xml.coverpages.org/mpeg21-didl.html>`_
|
|
@@ -10,4 +10,5 @@ python_didl_lite.egg-info/PKG-INFO
|
|
|
10
10
|
python_didl_lite.egg-info/SOURCES.txt
|
|
11
11
|
python_didl_lite.egg-info/dependency_links.txt
|
|
12
12
|
python_didl_lite.egg-info/requires.txt
|
|
13
|
-
python_didl_lite.egg-info/top_level.txt
|
|
13
|
+
python_didl_lite.egg-info/top_level.txt
|
|
14
|
+
tests/test_didl_lite.py
|
|
@@ -17,13 +17,13 @@ INSTALL_REQUIRES = [
|
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
TEST_REQUIRES = [
|
|
20
|
-
"pytest~=
|
|
20
|
+
"pytest~=7.4.3",
|
|
21
21
|
]
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
setup(
|
|
25
25
|
name="python-didl-lite",
|
|
26
|
-
version="1.
|
|
26
|
+
version="1.4.1",
|
|
27
27
|
description=DESCRIPTION,
|
|
28
28
|
long_description=LONG_DESCRIPTION,
|
|
29
29
|
long_description_content_type="text/x-rst",
|
|
@@ -35,15 +35,18 @@ setup(
|
|
|
35
35
|
"Intended Audience :: Developers",
|
|
36
36
|
"License :: OSI Approved :: Apache Software License",
|
|
37
37
|
"Programming Language :: Python",
|
|
38
|
-
"Programming Language :: Python :: 3.6",
|
|
39
|
-
"Programming Language :: Python :: 3.7",
|
|
40
38
|
"Programming Language :: Python :: 3.8",
|
|
41
39
|
"Programming Language :: Python :: 3.9",
|
|
40
|
+
"Programming Language :: Python :: 3.10",
|
|
41
|
+
"Programming Language :: Python :: 3.11",
|
|
42
|
+
"Programming Language :: Python :: 3.12",
|
|
43
|
+
"Programming Language :: Python :: 3.13",
|
|
42
44
|
],
|
|
43
45
|
packages=["didl_lite"],
|
|
44
46
|
package_data={
|
|
45
47
|
"didl_lite": ["py.typed"],
|
|
46
48
|
},
|
|
49
|
+
python_requires=">=3.8",
|
|
47
50
|
install_requires=INSTALL_REQUIRES,
|
|
48
51
|
tests_require=TEST_REQUIRES,
|
|
49
52
|
)
|
|
@@ -0,0 +1,682 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""Unit tests for didl_lite."""
|
|
3
|
+
|
|
4
|
+
import pytest
|
|
5
|
+
from defusedxml import ElementTree as ET
|
|
6
|
+
|
|
7
|
+
from didl_lite import didl_lite
|
|
8
|
+
|
|
9
|
+
NAMESPACES = {
|
|
10
|
+
"didl_lite": "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/",
|
|
11
|
+
"dc": "http://purl.org/dc/elements/1.1/",
|
|
12
|
+
"upnp": "urn:schemas-upnp-org:metadata-1-0/upnp/",
|
|
13
|
+
"xsi": "http://www.w3.org/2001/XMLSchema-instance",
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class TestDidlLite:
|
|
18
|
+
"""Tests for didl_lite."""
|
|
19
|
+
|
|
20
|
+
# pylint: disable=too-many-public-methods
|
|
21
|
+
|
|
22
|
+
def test_item_from_xml(self) -> None:
|
|
23
|
+
"""Test item from XML."""
|
|
24
|
+
didl_string = """
|
|
25
|
+
<DIDL-Lite xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"
|
|
26
|
+
xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/"
|
|
27
|
+
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
28
|
+
xmlns:sec="http://www.sec.co.kr/">
|
|
29
|
+
<item id="0" parentID="0" restricted="1">
|
|
30
|
+
<dc:title>Audio Item Title</dc:title>
|
|
31
|
+
<upnp:class>object.item.audioItem</upnp:class>
|
|
32
|
+
<dc:language>English</dc:language>
|
|
33
|
+
<res protocolInfo="protocol_info">url</res>
|
|
34
|
+
<upnp:longDescription>Long description</upnp:longDescription>
|
|
35
|
+
</item>
|
|
36
|
+
</DIDL-Lite>"""
|
|
37
|
+
items = didl_lite.from_xml_string(didl_string)
|
|
38
|
+
assert len(items) == 1
|
|
39
|
+
|
|
40
|
+
item = items[0]
|
|
41
|
+
assert item.xml_el is not None
|
|
42
|
+
assert getattr(item, "title") == "Audio Item Title"
|
|
43
|
+
assert getattr(item, "upnp_class") == "object.item.audioItem"
|
|
44
|
+
assert getattr(item, "language") == "English"
|
|
45
|
+
assert getattr(item, "longDescription") == "Long description"
|
|
46
|
+
assert getattr(item, "long_description") == "Long description"
|
|
47
|
+
assert isinstance(item, didl_lite.AudioItem)
|
|
48
|
+
assert not hasattr(item, "non_existing")
|
|
49
|
+
|
|
50
|
+
resources = item.res
|
|
51
|
+
assert len(resources) == 1
|
|
52
|
+
resource = resources[0]
|
|
53
|
+
assert resource.xml_el is not None
|
|
54
|
+
assert resource.protocol_info == "protocol_info"
|
|
55
|
+
assert resource.uri == "url"
|
|
56
|
+
assert not hasattr(item, "non_existing")
|
|
57
|
+
assert item.res == item.resources
|
|
58
|
+
|
|
59
|
+
def test_item_bad_class(self) -> None:
|
|
60
|
+
"""Test item from XML that has a badly-cased upnp:class."""
|
|
61
|
+
didl_string = """
|
|
62
|
+
<DIDL-Lite xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"
|
|
63
|
+
xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/"
|
|
64
|
+
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
65
|
+
xmlns:sec="http://www.sec.co.kr/">
|
|
66
|
+
<item id="0" parentID="0" restricted="1">
|
|
67
|
+
<dc:title>Audio Item Title</dc:title>
|
|
68
|
+
<upnp:class>Object.Item.AudioItem</upnp:class>
|
|
69
|
+
<dc:language>English</dc:language>
|
|
70
|
+
<res protocolInfo="protocol_info">url</res>
|
|
71
|
+
<upnp:longDescription>Long description</upnp:longDescription>
|
|
72
|
+
</item>
|
|
73
|
+
</DIDL-Lite>"""
|
|
74
|
+
with pytest.raises(
|
|
75
|
+
didl_lite.DidlLiteException,
|
|
76
|
+
match="upnp:class Object.Item.AudioItem is unknown",
|
|
77
|
+
):
|
|
78
|
+
didl_lite.from_xml_string(didl_string)
|
|
79
|
+
|
|
80
|
+
items = didl_lite.from_xml_string(didl_string, strict=False)
|
|
81
|
+
assert len(items) == 1
|
|
82
|
+
|
|
83
|
+
item = items[0]
|
|
84
|
+
assert isinstance(item, didl_lite.AudioItem)
|
|
85
|
+
|
|
86
|
+
def test_item_from_xml_not_strict(self) -> None:
|
|
87
|
+
"""Test item from XML."""
|
|
88
|
+
didl_string = """
|
|
89
|
+
<DIDL-Lite xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"
|
|
90
|
+
xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/"
|
|
91
|
+
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
92
|
+
xmlns:sec="http://www.sec.co.kr/">
|
|
93
|
+
<item id="0" parentID="0" restricted="1">
|
|
94
|
+
<dc:title>Audio Item Title</dc:title>
|
|
95
|
+
<upnp:class>object.item.audioItem</upnp:class>
|
|
96
|
+
<dc:language>English</dc:language>
|
|
97
|
+
<res>url</res>
|
|
98
|
+
</item>
|
|
99
|
+
</DIDL-Lite>"""
|
|
100
|
+
items = didl_lite.from_xml_string(didl_string, strict=False)
|
|
101
|
+
assert len(items) == 1
|
|
102
|
+
|
|
103
|
+
item = items[0]
|
|
104
|
+
assert item.xml_el is not None
|
|
105
|
+
assert getattr(item, "title") == "Audio Item Title"
|
|
106
|
+
assert getattr(item, "upnp_class") == "object.item.audioItem"
|
|
107
|
+
assert getattr(item, "language") == "English"
|
|
108
|
+
assert isinstance(item, didl_lite.AudioItem)
|
|
109
|
+
assert not hasattr(item, "non_existing")
|
|
110
|
+
|
|
111
|
+
resources = item.res
|
|
112
|
+
assert len(resources) == 1
|
|
113
|
+
resource = resources[0]
|
|
114
|
+
assert resource.xml_el is not None
|
|
115
|
+
assert resource.protocol_info is None # This is now allowed with strict=False
|
|
116
|
+
assert resource.uri == "url"
|
|
117
|
+
assert not hasattr(item, "non_existing")
|
|
118
|
+
assert item.res == item.resources
|
|
119
|
+
|
|
120
|
+
def test_item_to_xml(self) -> None:
|
|
121
|
+
"""Test item to XML."""
|
|
122
|
+
resource = didl_lite.Resource("url", "protocol_info")
|
|
123
|
+
items = [
|
|
124
|
+
didl_lite.AudioItem(
|
|
125
|
+
id="0",
|
|
126
|
+
parent_id="0",
|
|
127
|
+
title="Audio Item Title",
|
|
128
|
+
restricted="1",
|
|
129
|
+
resources=[resource],
|
|
130
|
+
language="English",
|
|
131
|
+
longDescription="Long description",
|
|
132
|
+
),
|
|
133
|
+
]
|
|
134
|
+
didl_string = didl_lite.to_xml_string(*items).decode("utf-8")
|
|
135
|
+
|
|
136
|
+
assert 'xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"' in didl_string
|
|
137
|
+
assert 'xmlns:dc="http://purl.org/dc/elements/1.1/"' in didl_string
|
|
138
|
+
assert 'xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/"' in didl_string
|
|
139
|
+
assert 'xmlns:sec="http://www.sec.co.kr/"' in didl_string
|
|
140
|
+
assert 'xmlns:ns1="urn:schemas-upnp-org:metadata-1-0/upnp/"' not in didl_string
|
|
141
|
+
|
|
142
|
+
didl_el = ET.fromstring(didl_string)
|
|
143
|
+
|
|
144
|
+
item_el = didl_el.find("./didl_lite:item", NAMESPACES)
|
|
145
|
+
assert item_el is not None
|
|
146
|
+
assert item_el.attrib["id"] == "0"
|
|
147
|
+
assert item_el.attrib["parentID"] == "0"
|
|
148
|
+
assert item_el.attrib["restricted"] == "1"
|
|
149
|
+
|
|
150
|
+
title_el = item_el.find("./dc:title", NAMESPACES)
|
|
151
|
+
assert title_el is not None
|
|
152
|
+
assert title_el.text == "Audio Item Title"
|
|
153
|
+
|
|
154
|
+
class_el = item_el.find("./upnp:class", NAMESPACES)
|
|
155
|
+
assert class_el is not None
|
|
156
|
+
assert class_el.text == "object.item.audioItem"
|
|
157
|
+
|
|
158
|
+
language_el = item_el.find("./dc:language", NAMESPACES)
|
|
159
|
+
assert language_el is not None
|
|
160
|
+
assert language_el.text == "English"
|
|
161
|
+
|
|
162
|
+
long_description_el = item_el.find("./upnp:longDescription", NAMESPACES)
|
|
163
|
+
assert long_description_el is not None
|
|
164
|
+
assert long_description_el.text == "Long description"
|
|
165
|
+
|
|
166
|
+
res_el = item_el.find("./didl_lite:res", NAMESPACES)
|
|
167
|
+
assert res_el is not None
|
|
168
|
+
assert res_el.attrib["protocolInfo"] == "protocol_info"
|
|
169
|
+
assert res_el.text == "url"
|
|
170
|
+
|
|
171
|
+
def test_item_repr(self) -> None:
|
|
172
|
+
"""Test item's repr can convert back to an equivalent item."""
|
|
173
|
+
# pylint: disable=import-outside-toplevel
|
|
174
|
+
# repr method doesn't know how package was imported, so only uses class names
|
|
175
|
+
from didl_lite.didl_lite import AudioItem, Resource
|
|
176
|
+
|
|
177
|
+
item = AudioItem(
|
|
178
|
+
id="0",
|
|
179
|
+
parent_id="0",
|
|
180
|
+
title="Audio Item Title",
|
|
181
|
+
restricted="1",
|
|
182
|
+
res=[
|
|
183
|
+
Resource("url", "protocol_info"),
|
|
184
|
+
Resource("url2", "protocol_info2"),
|
|
185
|
+
],
|
|
186
|
+
language="English",
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
item_repr = repr(item)
|
|
190
|
+
item_remade = eval(item_repr) # pylint: disable=eval-used
|
|
191
|
+
assert ET.tostring(item.to_xml()) == ET.tostring(item_remade.to_xml())
|
|
192
|
+
|
|
193
|
+
def test_container_from_xml(self) -> None:
|
|
194
|
+
"""Test container from XML."""
|
|
195
|
+
didl_string = """
|
|
196
|
+
<DIDL-Lite xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"
|
|
197
|
+
xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/"
|
|
198
|
+
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
199
|
+
xmlns:sec="http://www.sec.co.kr/">
|
|
200
|
+
<container id="0" parentID="0" restricted="1">
|
|
201
|
+
<dc:title>Album Container Title</dc:title>
|
|
202
|
+
<upnp:class>object.container.album</upnp:class>
|
|
203
|
+
|
|
204
|
+
<item id="1" parentID="0" restricted="1">
|
|
205
|
+
<dc:title>Audio Item Title</dc:title>
|
|
206
|
+
<upnp:class>object.item.audioItem</upnp:class>
|
|
207
|
+
<dc:language>English</dc:language>
|
|
208
|
+
<res protocolInfo="protocol_info">url</res>
|
|
209
|
+
</item>
|
|
210
|
+
</container>
|
|
211
|
+
</DIDL-Lite>"""
|
|
212
|
+
items = didl_lite.from_xml_string(didl_string)
|
|
213
|
+
assert len(items) == 1
|
|
214
|
+
|
|
215
|
+
container = items[0]
|
|
216
|
+
assert container.xml_el is not None
|
|
217
|
+
assert isinstance(container, didl_lite.Container)
|
|
218
|
+
assert getattr(container, "title") == "Album Container Title"
|
|
219
|
+
assert getattr(container, "upnp_class") == "object.container.album"
|
|
220
|
+
|
|
221
|
+
item = container[0]
|
|
222
|
+
assert item.xml_el is not None
|
|
223
|
+
assert item.title == "Audio Item Title"
|
|
224
|
+
assert item.upnp_class == "object.item.audioItem"
|
|
225
|
+
assert item.language == "English"
|
|
226
|
+
|
|
227
|
+
resources = item.res
|
|
228
|
+
assert len(resources) == 1
|
|
229
|
+
resource = resources[0]
|
|
230
|
+
assert resource.xml_el is not None
|
|
231
|
+
assert resource.protocol_info == "protocol_info"
|
|
232
|
+
assert resource.uri == "url"
|
|
233
|
+
assert item.res == item.resources
|
|
234
|
+
|
|
235
|
+
def test_container_to_xml(self) -> None:
|
|
236
|
+
"""Test container to XML."""
|
|
237
|
+
container = didl_lite.Album(
|
|
238
|
+
id="0", parent_id="0", title="Audio Item Title", restricted="1"
|
|
239
|
+
)
|
|
240
|
+
resource = didl_lite.Resource("url", "protocol_info")
|
|
241
|
+
item = didl_lite.AudioItem(
|
|
242
|
+
id="0",
|
|
243
|
+
parent_id="0",
|
|
244
|
+
title="Audio Item Title",
|
|
245
|
+
restricted="1",
|
|
246
|
+
resources=[resource],
|
|
247
|
+
language="English",
|
|
248
|
+
)
|
|
249
|
+
container.append(item)
|
|
250
|
+
|
|
251
|
+
didl_string = didl_lite.to_xml_string(container).decode("utf-8")
|
|
252
|
+
didl_el = ET.fromstring(didl_string)
|
|
253
|
+
|
|
254
|
+
container_el = didl_el.find("./didl_lite:container", NAMESPACES)
|
|
255
|
+
assert container_el is not None
|
|
256
|
+
assert container_el.attrib["id"] == "0"
|
|
257
|
+
assert container_el.attrib["parentID"] == "0"
|
|
258
|
+
assert container_el.attrib["restricted"] == "1"
|
|
259
|
+
|
|
260
|
+
item_el = container_el.find("./didl_lite:item", NAMESPACES)
|
|
261
|
+
assert item_el is not None
|
|
262
|
+
assert item_el.attrib["id"] == "0"
|
|
263
|
+
assert item_el.attrib["parentID"] == "0"
|
|
264
|
+
assert item_el.attrib["restricted"] == "1"
|
|
265
|
+
|
|
266
|
+
title_el = item_el.find("./dc:title", NAMESPACES)
|
|
267
|
+
assert title_el is not None
|
|
268
|
+
assert title_el.text == "Audio Item Title"
|
|
269
|
+
|
|
270
|
+
class_el = item_el.find("./upnp:class", NAMESPACES)
|
|
271
|
+
assert class_el is not None
|
|
272
|
+
assert class_el.text == "object.item.audioItem"
|
|
273
|
+
|
|
274
|
+
language_el = item_el.find("./dc:language", NAMESPACES)
|
|
275
|
+
assert language_el is not None
|
|
276
|
+
assert language_el.text == "English"
|
|
277
|
+
|
|
278
|
+
res_el = item_el.find("./didl_lite:res", NAMESPACES)
|
|
279
|
+
assert res_el is not None
|
|
280
|
+
assert res_el.attrib["protocolInfo"] == "protocol_info"
|
|
281
|
+
assert res_el.text == "url"
|
|
282
|
+
|
|
283
|
+
def test_container_repr(self) -> None:
|
|
284
|
+
"""Test containers's repr can convert back to an equivalent container."""
|
|
285
|
+
# pylint: disable=import-outside-toplevel
|
|
286
|
+
from didl_lite.didl_lite import Album, AudioItem, Resource
|
|
287
|
+
|
|
288
|
+
container = Album(
|
|
289
|
+
id="0", parent_id="0", title="Audio Item Title", restricted="1"
|
|
290
|
+
)
|
|
291
|
+
resource = Resource("url", "protocol_info")
|
|
292
|
+
item = AudioItem(
|
|
293
|
+
id="0",
|
|
294
|
+
parent_id="0",
|
|
295
|
+
title="Audio Item Title",
|
|
296
|
+
restricted="1",
|
|
297
|
+
resources=[resource],
|
|
298
|
+
language="English",
|
|
299
|
+
)
|
|
300
|
+
container.append(item)
|
|
301
|
+
|
|
302
|
+
container_repr = repr(container)
|
|
303
|
+
container_remade = eval(container_repr) # pylint: disable=eval-used
|
|
304
|
+
assert ET.tostring(container.to_xml()) == ET.tostring(container_remade.to_xml())
|
|
305
|
+
|
|
306
|
+
def test_descriptor_from_xml_root(self) -> None:
|
|
307
|
+
"""Test root descriptor from XML."""
|
|
308
|
+
didl_string = """
|
|
309
|
+
<DIDL-Lite xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"
|
|
310
|
+
xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/"
|
|
311
|
+
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
312
|
+
xmlns:sec="http://www.sec.co.kr/">
|
|
313
|
+
<desc id="1" nameSpace="ns" type="type">Text</desc>
|
|
314
|
+
</DIDL-Lite>"""
|
|
315
|
+
|
|
316
|
+
items = didl_lite.from_xml_string(didl_string)
|
|
317
|
+
assert len(items) == 1
|
|
318
|
+
|
|
319
|
+
descriptor = items[0]
|
|
320
|
+
assert descriptor is not None
|
|
321
|
+
assert descriptor.xml_el is not None
|
|
322
|
+
assert getattr(descriptor, "id") == "1"
|
|
323
|
+
assert getattr(descriptor, "name_space") == "ns"
|
|
324
|
+
assert getattr(descriptor, "type") == "type"
|
|
325
|
+
assert getattr(descriptor, "text") == "Text"
|
|
326
|
+
|
|
327
|
+
def test_descriptor_from_xml_item(self) -> None:
|
|
328
|
+
"""Test item descriptor from XML."""
|
|
329
|
+
didl_string = """
|
|
330
|
+
<DIDL-Lite xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"
|
|
331
|
+
xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/"
|
|
332
|
+
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
333
|
+
xmlns:sec="http://www.sec.co.kr/">
|
|
334
|
+
<item id="1" parentID="0" restricted="1">
|
|
335
|
+
<dc:title>Audio Item Title</dc:title>
|
|
336
|
+
<upnp:class>object.item.audioItem</upnp:class>
|
|
337
|
+
<dc:language>English</dc:language>
|
|
338
|
+
<res protocolInfo="protocol_info">url</res>
|
|
339
|
+
<desc id="1" nameSpace="ns" type="type">Text</desc>
|
|
340
|
+
</item>
|
|
341
|
+
</DIDL-Lite>"""
|
|
342
|
+
|
|
343
|
+
items = didl_lite.from_xml_string(didl_string)
|
|
344
|
+
assert len(items) == 1
|
|
345
|
+
|
|
346
|
+
item = items[0]
|
|
347
|
+
assert item is not None
|
|
348
|
+
assert isinstance(item, didl_lite.AudioItem)
|
|
349
|
+
|
|
350
|
+
descriptor = item.descriptors[0]
|
|
351
|
+
assert descriptor is not None
|
|
352
|
+
assert descriptor.xml_el is not None
|
|
353
|
+
assert descriptor.id == "1"
|
|
354
|
+
assert descriptor.name_space == "ns"
|
|
355
|
+
assert descriptor.type == "type"
|
|
356
|
+
assert descriptor.text == "Text"
|
|
357
|
+
|
|
358
|
+
def test_descriptor_from_xml_container(self) -> None:
|
|
359
|
+
"""Test container descriptor from XML."""
|
|
360
|
+
didl_string = """
|
|
361
|
+
<DIDL-Lite xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"
|
|
362
|
+
xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/"
|
|
363
|
+
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
364
|
+
xmlns:sec="http://www.sec.co.kr/">
|
|
365
|
+
<container id="0" parentID="0" restricted="1">
|
|
366
|
+
<dc:title>Album Container Title</dc:title>
|
|
367
|
+
<upnp:class>object.container.album</upnp:class>
|
|
368
|
+
|
|
369
|
+
<desc id="1" nameSpace="ns" type="type">Text</desc>
|
|
370
|
+
</container>
|
|
371
|
+
</DIDL-Lite>"""
|
|
372
|
+
|
|
373
|
+
items = didl_lite.from_xml_string(didl_string)
|
|
374
|
+
assert len(items) == 1
|
|
375
|
+
|
|
376
|
+
container = items[0]
|
|
377
|
+
assert container is not None
|
|
378
|
+
assert container.xml_el is not None
|
|
379
|
+
assert isinstance(container, didl_lite.Container)
|
|
380
|
+
|
|
381
|
+
descriptor = container.descriptors[0]
|
|
382
|
+
assert descriptor is not None
|
|
383
|
+
assert descriptor.xml_el is not None
|
|
384
|
+
assert descriptor.id == "1"
|
|
385
|
+
assert descriptor.name_space == "ns"
|
|
386
|
+
assert descriptor.type == "type"
|
|
387
|
+
assert descriptor.text == "Text"
|
|
388
|
+
|
|
389
|
+
def test_descriptor_from_xml_container_item(self) -> None:
|
|
390
|
+
"""Test item descriptor in container from XML."""
|
|
391
|
+
didl_string = """
|
|
392
|
+
<DIDL-Lite xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"
|
|
393
|
+
xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/"
|
|
394
|
+
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
395
|
+
xmlns:sec="http://www.sec.co.kr/">
|
|
396
|
+
<container id="0" parentID="0" restricted="1">
|
|
397
|
+
<dc:title>Album Container Title</dc:title>
|
|
398
|
+
<upnp:class>object.container.album</upnp:class>
|
|
399
|
+
|
|
400
|
+
<item id="1" parentID="0" restricted="1">
|
|
401
|
+
<dc:title>Audio Item Title</dc:title>
|
|
402
|
+
<upnp:class>object.item.audioItem</upnp:class>
|
|
403
|
+
<dc:language>English</dc:language>
|
|
404
|
+
<res protocolInfo="protocol_info">url</res>
|
|
405
|
+
<desc id="1" nameSpace="ns" type="type">Text</desc>
|
|
406
|
+
</item>
|
|
407
|
+
</container>
|
|
408
|
+
</DIDL-Lite>"""
|
|
409
|
+
|
|
410
|
+
items = didl_lite.from_xml_string(didl_string)
|
|
411
|
+
assert len(items) == 1
|
|
412
|
+
|
|
413
|
+
container = items[0]
|
|
414
|
+
assert container is not None
|
|
415
|
+
assert isinstance(container, didl_lite.Container)
|
|
416
|
+
|
|
417
|
+
item = container[0]
|
|
418
|
+
assert item is not None
|
|
419
|
+
|
|
420
|
+
descriptor = item.descriptors[0]
|
|
421
|
+
assert descriptor is not None
|
|
422
|
+
assert descriptor.xml_el is not None
|
|
423
|
+
assert descriptor.id == "1"
|
|
424
|
+
assert descriptor.name_space == "ns"
|
|
425
|
+
assert descriptor.type == "type"
|
|
426
|
+
assert descriptor.text == "Text"
|
|
427
|
+
|
|
428
|
+
def test_descriptor_to_xml(self) -> None:
|
|
429
|
+
"""Test descriptor to XML."""
|
|
430
|
+
descriptor = didl_lite.Descriptor(
|
|
431
|
+
id="1", name_space="ns", type="type", text="Text"
|
|
432
|
+
)
|
|
433
|
+
item = didl_lite.AudioItem(
|
|
434
|
+
id="0",
|
|
435
|
+
parent_id="0",
|
|
436
|
+
title="Audio Item Title",
|
|
437
|
+
restricted="1",
|
|
438
|
+
language="English",
|
|
439
|
+
descriptors=[descriptor],
|
|
440
|
+
)
|
|
441
|
+
didl_string = didl_lite.to_xml_string(item).decode("utf-8")
|
|
442
|
+
didl_el = ET.fromstring(didl_string)
|
|
443
|
+
|
|
444
|
+
item_el = didl_el.find("./didl_lite:item", NAMESPACES)
|
|
445
|
+
assert item_el is not None
|
|
446
|
+
|
|
447
|
+
descriptor_el = item_el.find("./didl_lite:desc", NAMESPACES)
|
|
448
|
+
assert descriptor_el is not None
|
|
449
|
+
assert len(descriptor_el.attrib) == 3
|
|
450
|
+
assert descriptor_el.attrib["id"] == "1"
|
|
451
|
+
assert descriptor_el.attrib["nameSpace"] == "ns"
|
|
452
|
+
assert descriptor_el.attrib["type"] == "type"
|
|
453
|
+
assert descriptor_el.text == "Text"
|
|
454
|
+
|
|
455
|
+
descriptor = didl_lite.Descriptor(id="2", name_space="ns2")
|
|
456
|
+
descriptor_el = descriptor.to_xml()
|
|
457
|
+
assert descriptor_el is not None
|
|
458
|
+
assert len(descriptor_el.attrib) == 2
|
|
459
|
+
assert descriptor_el.attrib["id"] == "2"
|
|
460
|
+
assert descriptor_el.attrib["nameSpace"] == "ns2"
|
|
461
|
+
|
|
462
|
+
def test_descriptor_repr(self) -> None:
|
|
463
|
+
"""Test descriptor's repr can convert back to an equivalent descriptorb."""
|
|
464
|
+
# pylint: disable=import-outside-toplevel
|
|
465
|
+
from didl_lite.didl_lite import Descriptor
|
|
466
|
+
|
|
467
|
+
descriptor = Descriptor(id="1", name_space="ns", type="type", text="Text")
|
|
468
|
+
|
|
469
|
+
descriptor_repr = repr(descriptor)
|
|
470
|
+
descriptor_remade = eval(descriptor_repr) # pylint: disable=eval-used
|
|
471
|
+
assert ET.tostring(descriptor.to_xml()) == ET.tostring(
|
|
472
|
+
descriptor_remade.to_xml()
|
|
473
|
+
)
|
|
474
|
+
|
|
475
|
+
def test_item_order(self) -> None:
|
|
476
|
+
"""Test item ordering."""
|
|
477
|
+
didl_string = """
|
|
478
|
+
<DIDL-Lite xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"
|
|
479
|
+
xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/"
|
|
480
|
+
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
481
|
+
xmlns:sec="http://www.sec.co.kr/">
|
|
482
|
+
<item id="0" parentID="0" restricted="1">
|
|
483
|
+
<dc:title>Audio Item Title 1</dc:title>
|
|
484
|
+
<upnp:class>object.item.audioItem</upnp:class>
|
|
485
|
+
<dc:language>English</dc:language>
|
|
486
|
+
</item>
|
|
487
|
+
<container id="1" parentID="0" restricted="1">
|
|
488
|
+
<dc:title>Album Container Title</dc:title>
|
|
489
|
+
<upnp:class>object.container.album</upnp:class>
|
|
490
|
+
</container>
|
|
491
|
+
<item id="2" parentID="0" restricted="1">
|
|
492
|
+
<dc:title>Audio Item Title 1</dc:title>
|
|
493
|
+
<upnp:class>object.item.audioItem</upnp:class>
|
|
494
|
+
<dc:language>English</dc:language>
|
|
495
|
+
</item>
|
|
496
|
+
</DIDL-Lite>"""
|
|
497
|
+
|
|
498
|
+
items = didl_lite.from_xml_string(didl_string)
|
|
499
|
+
assert len(items) == 3
|
|
500
|
+
|
|
501
|
+
assert isinstance(items[0], didl_lite.AudioItem)
|
|
502
|
+
assert isinstance(items[1], didl_lite.Album)
|
|
503
|
+
assert isinstance(items[2], didl_lite.AudioItem)
|
|
504
|
+
|
|
505
|
+
def test_item_property_attribute_from_xml(self) -> None:
|
|
506
|
+
"""Test item property from XML attribute."""
|
|
507
|
+
didl_string = """
|
|
508
|
+
<DIDL-Lite xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"
|
|
509
|
+
xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/"
|
|
510
|
+
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
511
|
+
xmlns:sec="http://www.sec.co.kr/">
|
|
512
|
+
<item id="0" parentID="0" restricted="1">
|
|
513
|
+
<dc:title>Video Item Title</dc:title>
|
|
514
|
+
<upnp:class>object.item.videoItem</upnp:class>
|
|
515
|
+
<upnp:genre id="genreId">Action</upnp:genre>
|
|
516
|
+
</item>
|
|
517
|
+
</DIDL-Lite>"""
|
|
518
|
+
|
|
519
|
+
items = didl_lite.from_xml_string(didl_string)
|
|
520
|
+
assert len(items) == 1
|
|
521
|
+
|
|
522
|
+
item = items[0]
|
|
523
|
+
assert item is not None
|
|
524
|
+
assert getattr(item, "genre") == "Action"
|
|
525
|
+
assert getattr(item, "genre_id") == "genreId"
|
|
526
|
+
|
|
527
|
+
def test_item_property_attribute_to_xml(self) -> None:
|
|
528
|
+
"""Test item property to XML."""
|
|
529
|
+
item = didl_lite.VideoItem(
|
|
530
|
+
id="0",
|
|
531
|
+
parent_id="0",
|
|
532
|
+
title="Video Item Title",
|
|
533
|
+
restricted="1",
|
|
534
|
+
genre="Action",
|
|
535
|
+
genre_id="genreId",
|
|
536
|
+
)
|
|
537
|
+
didl_string = didl_lite.to_xml_string(item).decode("utf-8")
|
|
538
|
+
didl_el = ET.fromstring(didl_string)
|
|
539
|
+
|
|
540
|
+
item_el = didl_el.find("./didl_lite:item", NAMESPACES)
|
|
541
|
+
assert item_el is not None
|
|
542
|
+
|
|
543
|
+
genre_el = item_el.find("./upnp:genre", NAMESPACES)
|
|
544
|
+
assert genre_el is not None
|
|
545
|
+
assert genre_el.text == "Action"
|
|
546
|
+
assert genre_el.attrib["id"] == "genreId"
|
|
547
|
+
|
|
548
|
+
def test_item_missing_id(self) -> None:
|
|
549
|
+
"""Test item missing ID from XML."""
|
|
550
|
+
didl_string = """
|
|
551
|
+
<DIDL-Lite xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"
|
|
552
|
+
xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/"
|
|
553
|
+
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
554
|
+
xmlns:sec="http://www.sec.co.kr/">
|
|
555
|
+
<item restricted="1">
|
|
556
|
+
<dc:title>Video Item Title</dc:title>
|
|
557
|
+
<upnp:class>object.item</upnp:class>
|
|
558
|
+
</item>
|
|
559
|
+
</DIDL-Lite>"""
|
|
560
|
+
|
|
561
|
+
items = didl_lite.from_xml_string(didl_string)
|
|
562
|
+
assert len(items) == 1
|
|
563
|
+
|
|
564
|
+
def test_item_set_attributes(self) -> None:
|
|
565
|
+
"""Test item attribute from XML."""
|
|
566
|
+
didl_string = """
|
|
567
|
+
<DIDL-Lite xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"
|
|
568
|
+
xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/"
|
|
569
|
+
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
570
|
+
xmlns:sec="http://www.sec.co.kr/">
|
|
571
|
+
<item restricted="1">
|
|
572
|
+
<dc:title>Video Item Title</dc:title>
|
|
573
|
+
<upnp:class>object.item.videoItem</upnp:class>
|
|
574
|
+
</item>
|
|
575
|
+
</DIDL-Lite>"""
|
|
576
|
+
|
|
577
|
+
items = didl_lite.from_xml_string(didl_string)
|
|
578
|
+
assert len(items) == 1
|
|
579
|
+
|
|
580
|
+
item = items[0]
|
|
581
|
+
assert getattr(item, "title") == "Video Item Title"
|
|
582
|
+
assert hasattr(item, "rating")
|
|
583
|
+
assert getattr(item, "rating") is None
|
|
584
|
+
assert isinstance(item, didl_lite.VideoItem)
|
|
585
|
+
assert len(item.res) == 0
|
|
586
|
+
assert item.res == item.resources
|
|
587
|
+
|
|
588
|
+
def test_extra_properties(self) -> None:
|
|
589
|
+
"""Test extra item properties from XML."""
|
|
590
|
+
didl_string = """
|
|
591
|
+
<DIDL-Lite xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"
|
|
592
|
+
xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/"
|
|
593
|
+
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
594
|
+
xmlns:sec="http://www.sec.co.kr/">
|
|
595
|
+
<item restricted="1">
|
|
596
|
+
<dc:title>Video Item Title</dc:title>
|
|
597
|
+
<upnp:class>object.item.videoItem</upnp:class>
|
|
598
|
+
<upnp:albumArtURI>extra_property</upnp:albumArtURI>
|
|
599
|
+
</item>
|
|
600
|
+
</DIDL-Lite>"""
|
|
601
|
+
items = didl_lite.from_xml_string(didl_string)
|
|
602
|
+
assert len(items) == 1
|
|
603
|
+
|
|
604
|
+
item = items[0]
|
|
605
|
+
assert hasattr(item, "album_art_uri")
|
|
606
|
+
assert getattr(item, "album_art_uri") == "extra_property"
|
|
607
|
+
assert getattr(item, "albumArtURI") == "extra_property"
|
|
608
|
+
|
|
609
|
+
def test_default_properties_set(self) -> None:
|
|
610
|
+
"""Test defaults for item properties."""
|
|
611
|
+
item = didl_lite.VideoItem(
|
|
612
|
+
id="0", parent_id="0", title="Video Item Title", restricted="1"
|
|
613
|
+
)
|
|
614
|
+
assert hasattr(item, "genre_type") # property is set
|
|
615
|
+
|
|
616
|
+
def test_property_case(self) -> None:
|
|
617
|
+
"""Test item properties can be accessed using snake_case or camelCase."""
|
|
618
|
+
item = didl_lite.MusicTrack(
|
|
619
|
+
id="0",
|
|
620
|
+
parent_id="0",
|
|
621
|
+
title="Audio Item Title",
|
|
622
|
+
restricted="1",
|
|
623
|
+
language="English",
|
|
624
|
+
originalTrackNumber="1",
|
|
625
|
+
storage_medium="HDD",
|
|
626
|
+
)
|
|
627
|
+
assert hasattr(item, "original_track_number")
|
|
628
|
+
assert hasattr(item, "originalTrackNumber")
|
|
629
|
+
assert item.original_track_number is item.originalTrackNumber
|
|
630
|
+
assert item.original_track_number == "1"
|
|
631
|
+
|
|
632
|
+
assert hasattr(item, "storage_medium")
|
|
633
|
+
assert hasattr(item, "storageMedium")
|
|
634
|
+
assert item.storage_medium is item.storageMedium
|
|
635
|
+
assert item.storage_medium == "HDD"
|
|
636
|
+
|
|
637
|
+
assert hasattr(item, "long_description")
|
|
638
|
+
assert hasattr(item, "longDescription")
|
|
639
|
+
assert item.long_description is item.longDescription
|
|
640
|
+
assert item.long_description is None
|
|
641
|
+
|
|
642
|
+
assert not hasattr(item, "otherItem")
|
|
643
|
+
assert not hasattr(item, "other_item")
|
|
644
|
+
|
|
645
|
+
item.storageMedium = "CD"
|
|
646
|
+
assert item.storage_medium is item.storageMedium
|
|
647
|
+
assert item.storage_medium == "CD"
|
|
648
|
+
|
|
649
|
+
item.long_description = "Long description"
|
|
650
|
+
assert item.long_description is item.longDescription
|
|
651
|
+
assert item.long_description == "Long description"
|
|
652
|
+
|
|
653
|
+
item.otherItem = "otherItem"
|
|
654
|
+
assert hasattr(item, "otherItem")
|
|
655
|
+
assert not hasattr(item, "other_item")
|
|
656
|
+
assert item.otherItem == "otherItem"
|
|
657
|
+
|
|
658
|
+
def test_item_improper_class_nesting(self) -> None:
|
|
659
|
+
"""
|
|
660
|
+
Test item from XML that has upnp_class element above item.
|
|
661
|
+
|
|
662
|
+
Cater for WiiM Pro and possibly other Linkplay devices that
|
|
663
|
+
emit upnp_class above the item element instead of inside it
|
|
664
|
+
"""
|
|
665
|
+
didl_string = """
|
|
666
|
+
<DIDL-Lite xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
667
|
+
xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/"
|
|
668
|
+
xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/">
|
|
669
|
+
<upnp:class>object.item.audioItem.musicTrack</upnp:class>
|
|
670
|
+
<item id="0">
|
|
671
|
+
<res protocolInfo="protocol_info"></res>
|
|
672
|
+
<dc:title>Music Track Title</dc:title>
|
|
673
|
+
<dc:creator>Music Track Creator</dc:creator>
|
|
674
|
+
<upnp:artist>Artist</upnp:artist>
|
|
675
|
+
<upnp:album>Album</upnp:album>
|
|
676
|
+
</item>
|
|
677
|
+
</DIDL-Lite>"""
|
|
678
|
+
items = didl_lite.from_xml_string(didl_string, strict=False)
|
|
679
|
+
assert len(items) == 1
|
|
680
|
+
|
|
681
|
+
item = items[0]
|
|
682
|
+
assert isinstance(item, didl_lite.MusicTrack)
|
python-didl-lite-1.3.2/PKG-INFO
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: python-didl-lite
|
|
3
|
-
Version: 1.3.2
|
|
4
|
-
Summary: DIDL-Lite (Digital Item Declaration Language) tools for Python
|
|
5
|
-
Home-page: https://github.com/StevenLooman/python-didl-lite
|
|
6
|
-
Author: Steven Looman
|
|
7
|
-
Author-email: steven.looman@gmail.com
|
|
8
|
-
License: UNKNOWN
|
|
9
|
-
Description: DIDL-Lite (Digital Item Declaration Language) tools for Python
|
|
10
|
-
==============================================================
|
|
11
|
-
|
|
12
|
-
DIDL-Lite tools for Python to read and write DIDL-Lite-xml.
|
|
13
|
-
|
|
14
|
-
Usage
|
|
15
|
-
-----
|
|
16
|
-
|
|
17
|
-
See ``tests/`` for examples on how to use this.
|
|
18
|
-
|
|
19
|
-
Note that this package does **NOT** do any type checking. Coercion of data types from and to strings must be done by the user of this library.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
Resources / documents
|
|
23
|
-
---------------------
|
|
24
|
-
|
|
25
|
-
DIDL-Lite resources and documents:
|
|
26
|
-
|
|
27
|
-
* [UPnP-av-ContentDirectory-v1-Service](http://upnp.org/specs/av/UPnP-av-ContentDirectory-v1-Service.pdf)
|
|
28
|
-
* [UPnP-av-ContentDirectory-v2-Service](http://upnp.org/specs/av/UPnP-av-ContentDirectory-v2-Service.pdf)
|
|
29
|
-
* [didl-lite-v2.xsd](http://www.upnp.org/schemas/av/didl-lite-v2.xsd)
|
|
30
|
-
* [mpeg21-didl](http://xml.coverpages.org/mpeg21-didl.html)
|
|
31
|
-
|
|
32
|
-
Platform: UNKNOWN
|
|
33
|
-
Classifier: Development Status :: 5 - Production/Stable
|
|
34
|
-
Classifier: Intended Audience :: Developers
|
|
35
|
-
Classifier: License :: OSI Approved :: Apache Software License
|
|
36
|
-
Classifier: Programming Language :: Python
|
|
37
|
-
Classifier: Programming Language :: Python :: 3.6
|
|
38
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
39
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
40
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
41
|
-
Description-Content-Type: text/x-rst
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: python-didl-lite
|
|
3
|
-
Version: 1.3.2
|
|
4
|
-
Summary: DIDL-Lite (Digital Item Declaration Language) tools for Python
|
|
5
|
-
Home-page: https://github.com/StevenLooman/python-didl-lite
|
|
6
|
-
Author: Steven Looman
|
|
7
|
-
Author-email: steven.looman@gmail.com
|
|
8
|
-
License: UNKNOWN
|
|
9
|
-
Description: DIDL-Lite (Digital Item Declaration Language) tools for Python
|
|
10
|
-
==============================================================
|
|
11
|
-
|
|
12
|
-
DIDL-Lite tools for Python to read and write DIDL-Lite-xml.
|
|
13
|
-
|
|
14
|
-
Usage
|
|
15
|
-
-----
|
|
16
|
-
|
|
17
|
-
See ``tests/`` for examples on how to use this.
|
|
18
|
-
|
|
19
|
-
Note that this package does **NOT** do any type checking. Coercion of data types from and to strings must be done by the user of this library.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
Resources / documents
|
|
23
|
-
---------------------
|
|
24
|
-
|
|
25
|
-
DIDL-Lite resources and documents:
|
|
26
|
-
|
|
27
|
-
* [UPnP-av-ContentDirectory-v1-Service](http://upnp.org/specs/av/UPnP-av-ContentDirectory-v1-Service.pdf)
|
|
28
|
-
* [UPnP-av-ContentDirectory-v2-Service](http://upnp.org/specs/av/UPnP-av-ContentDirectory-v2-Service.pdf)
|
|
29
|
-
* [didl-lite-v2.xsd](http://www.upnp.org/schemas/av/didl-lite-v2.xsd)
|
|
30
|
-
* [mpeg21-didl](http://xml.coverpages.org/mpeg21-didl.html)
|
|
31
|
-
|
|
32
|
-
Platform: UNKNOWN
|
|
33
|
-
Classifier: Development Status :: 5 - Production/Stable
|
|
34
|
-
Classifier: Intended Audience :: Developers
|
|
35
|
-
Classifier: License :: OSI Approved :: Apache Software License
|
|
36
|
-
Classifier: Programming Language :: Python
|
|
37
|
-
Classifier: Programming Language :: Python :: 3.6
|
|
38
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
39
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
40
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
41
|
-
Description-Content-Type: text/x-rst
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python-didl-lite-1.3.2 → python_didl_lite-1.4.1}/python_didl_lite.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|