Skip to content

Commit ad22c44

Browse files
Fixes for python2
1 parent a4de270 commit ad22c44

File tree

8 files changed

+119
-86
lines changed

8 files changed

+119
-86
lines changed

.github/workflows/addon-check.yml

Lines changed: 17 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,27 @@
11
name: Kodi
22
on:
3-
- pull_request
4-
- push
3+
# Run action when pushed to master, or for commits in a pull request.
4+
push:
5+
branches:
6+
- master
7+
pull_request:
8+
branches:
9+
- master
510
jobs:
6-
tests:
11+
kodi-addon-checker:
712
name: Addon checker
813
runs-on: ubuntu-latest
914
strategy:
1015
fail-fast: false
1116
matrix:
12-
kodi-branch: [leia, matrix]
17+
kodi-version: [ leia, matrix ]
1318
steps:
14-
- uses: actions/checkout@v2
15-
with:
16-
path: ${{ github.repository }}
17-
- name: Set up Python 3.8
18-
uses: actions/setup-python@v1
19-
with:
20-
python-version: 3.8
21-
- name: Install dependencies
22-
run: |
23-
sudo apt-get install xmlstarlet
24-
python -m pip install --upgrade pip
25-
# FIXME: Requires changes from xbmc/addon-check#217
26-
#pip install kodi-addon-checker
27-
pip install git+git://github.com/xbmc/addon-check.git@master
28-
- name: Remove unwanted files
29-
run: awk '/export-ignore/ { print $1 }' .gitattributes | xargs rm -rf --
30-
working-directory: ${{ github.repository }}
31-
- name: Rewrite addon.xml for Matrix
32-
run: |
33-
xmlstarlet ed -L -u '/addon/requires/import[@addon="xbmc.python"]/@version' -v "3.0.0" addon.xml
34-
version=$(xmlstarlet sel -t -v 'string(/addon/@version)' addon.xml)
35-
xmlstarlet ed -L -u '/addon/@version' -v "${version}+matrix.99" addon.xml
36-
working-directory: ${{ github.repository }}
37-
if: matrix.kodi-branch == 'matrix'
19+
- name: Check out ${{ github.sha }} from repository ${{ github.repository }}
20+
uses: actions/checkout@v2
21+
3822
- name: Run kodi-addon-checker
39-
run: kodi-addon-checker --branch=${{ matrix.kodi-branch }} ${{ github.repository }}/
23+
uses: xbmc/action-kodi-addon-checker@v1.2
24+
with:
25+
kodi-version: ${{ matrix.kodi-version }}
26+
rewrite-for-matrix: true
27+
addon-id: ${{ github.event.repository.name }}

resources/lib/modules/iptvsimple.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ def check(cls):
5151
try:
5252
addon = kodiutils.get_addon(IPTV_SIMPLE_ID)
5353
except Exception as exc: # pylint: disable=broad-except
54+
_LOGGER.exception(exc)
5455
return True # It might be restarting
5556

5657
# Validate IPTV Simple configuration

resources/lib/modules/sources/__init__.py

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818
class Sources:
1919
"""Helper class for Source updating"""
2020

21+
def __init__(self):
22+
""" Initialise object """
23+
2124
@classmethod
2225
def refresh(cls, show_progress=False):
2326
"""Update channels and EPG data"""
@@ -88,9 +91,12 @@ def refresh(cls, show_progress=False):
8891
progress.close()
8992

9093

91-
class Source:
94+
class Source(object): # pylint: disable=useless-object-inheritance
9295
""" Base class for a Source """
9396

97+
def __init__(self):
98+
""" Initialise object """
99+
94100
@staticmethod
95101
def detect_sources():
96102
""" Detect available sources. """
@@ -117,31 +123,30 @@ def _load_url(self, url):
117123
""" Load the specified URL. """
118124
response = requests.get(url)
119125
response.raise_for_status()
120-
data = response.content
121126

122127
if url.lower().endswith('.gz'):
123-
return self._decompress_gz(data)
128+
return self._decompress_gz(response.content)
124129
if url.lower().endswith('.bz2'):
125-
return self._decompress_bz2(data)
130+
return self._decompress_bz2(response.content)
126131

127-
return data
132+
return response.text
128133

129134
def _load_file(self, filename):
130135
""" Load the specified file. """
131-
with open(filename, 'r') as fdesc:
136+
with open(filename, 'rb') as fdesc:
132137
data = fdesc.read()
133138

134139
if filename.lower().endswith('.gz'):
135140
return self._decompress_gz(data)
136141
if filename.lower().endswith('.bz2'):
137142
return self._decompress_bz2(data)
138143

139-
return data
144+
return data.decode()
140145

141146
@staticmethod
142147
def _extract_m3u(data):
143148
""" Extract the m3u content """
144-
return data.replace('#EXTM3U\n', '')
149+
return data.replace('#EXTM3U', '').strip()
145150

146151
@staticmethod
147152
def _extract_xmltv(data):
@@ -151,8 +156,14 @@ def _extract_xmltv(data):
151156
@staticmethod
152157
def _decompress_gz(data):
153158
""" Decompress gzip data. """
154-
from gzip import decompress
155-
return decompress(data).decode()
159+
try: # Python 3
160+
from gzip import decompress
161+
return decompress(data).decode()
162+
except ImportError: # Python 2
163+
from gzip import GzipFile
164+
from StringIO import StringIO
165+
with GzipFile(fileobj=StringIO(data)) as fdesc:
166+
return fdesc.read().decode()
156167

157168
@staticmethod
158169
def _decompress_bz2(data):

resources/lib/modules/sources/addon.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ class AddonSource(Source):
3535
EPG_VERSION = 1
3636

3737
def __init__(self, addon_id, enabled=False, channels_uri=None, epg_uri=None):
38+
""" Initialise object """
39+
super(AddonSource, self).__init__()
3840
self.addon_id = addon_id
3941
self.enabled = enabled
4042
self.channels_uri = channels_uri

resources/lib/modules/sources/custom.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ class CustomSource(Source):
2323
TYPE_FILE = 2
2424

2525
def __init__(self, uuid, name, enabled, playlist_uri=None, playlist_type=TYPE_NONE, epg_uri=None, epg_type=TYPE_NONE):
26+
""" Initialise object """
27+
super(CustomSource, self).__init__()
2628
self.uuid = uuid
2729
self.name = name
2830
self.enabled = enabled
@@ -106,28 +108,33 @@ def get_epg(self):
106108

107109
def save(self):
108110
""" Save this source. """
111+
output_path = kodiutils.addon_profile()
109112
try:
110-
with open(os.path.join(kodiutils.addon_profile(), CustomSource.SOURCES_FILE), 'r') as fdesc:
113+
if not os.path.exists(output_path):
114+
os.mkdir(output_path)
115+
116+
with open(os.path.join(output_path, CustomSource.SOURCES_FILE), 'r') as fdesc:
111117
sources = json.loads(fdesc.read())
112118
except (IOError, TypeError, ValueError):
113119
sources = {}
114120

115121
# Update the element with my uuid
116122
sources[self.uuid] = self.__dict__
117123

118-
with open(os.path.join(kodiutils.addon_profile(), CustomSource.SOURCES_FILE), 'w') as fdesc:
124+
with open(os.path.join(output_path, CustomSource.SOURCES_FILE), 'w') as fdesc:
119125
json.dump(sources, fdesc)
120126

121127
def delete(self):
122128
""" Delete this source. """
129+
output_path = kodiutils.addon_profile()
123130
try:
124-
with open(os.path.join(kodiutils.addon_profile(), CustomSource.SOURCES_FILE), 'r') as fdesc:
131+
with open(os.path.join(output_path, CustomSource.SOURCES_FILE), 'r') as fdesc:
125132
sources = json.loads(fdesc.read())
126133
except (IOError, TypeError, ValueError):
127134
sources = {}
128135

129136
# Remove the element with my uuid
130137
sources.pop(self.uuid)
131138

132-
with open(os.path.join(kodiutils.addon_profile(), CustomSource.SOURCES_FILE), 'w') as fdesc:
139+
with open(os.path.join(output_path, CustomSource.SOURCES_FILE), 'w') as fdesc:
133140
json.dump(sources, fdesc)

tests/data/custom_playlist.m3u.gz

156 Bytes
Binary file not shown.

tests/home/addons/plugin.video.example.raw/plugin.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,16 @@ def send(self):
4444
@via_socket
4545
def send_channels(): # pylint: disable=no-method-argument
4646
"""Return JSON-STREAMS formatted information to IPTV Manager"""
47-
with open(os.path.dirname(__file__) + '/resources/raw_playlist.m3u', 'r') as fdesc:
47+
with open(os.path.dirname(__file__) + '/resources/raw_playlist.m3u', 'rb') as fdesc:
4848
channels = fdesc.read()
49-
return channels
49+
return channels.decode()
5050

5151
@via_socket
5252
def send_epg(): # pylint: disable=no-method-argument
5353
"""Return JSON-EPG formatted information to IPTV Manager"""
54-
with open(os.path.dirname(__file__) + '/resources/raw_epg.xml', 'r') as fdesc:
54+
with open(os.path.dirname(__file__) + '/resources/raw_epg.xml', 'rb') as fdesc:
5555
epg = fdesc.read()
56-
return epg
56+
return epg.decode()
5757

5858

5959
if __name__ == "__main__":

tests/test_sources.py

Lines changed: 63 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,15 @@ def test_create(self):
5353
self.assertNotIn(key, [source.uuid for source in sources])
5454

5555
def test_fetch_none(self):
56-
source = CustomSource(uuid=str(uuid4()),
57-
name='Test Source',
58-
enabled=True,
59-
playlist_uri=None,
60-
playlist_type=CustomSource.TYPE_NONE,
61-
epg_uri=None,
62-
epg_type=CustomSource.TYPE_NONE,
63-
)
56+
source = CustomSource(
57+
uuid=str(uuid4()),
58+
name='Test Source',
59+
enabled=True,
60+
playlist_uri=None,
61+
playlist_type=CustomSource.TYPE_NONE,
62+
epg_uri=None,
63+
epg_type=CustomSource.TYPE_NONE,
64+
)
6465

6566
channels = source.get_channels()
6667
self.assertEqual(channels, '')
@@ -69,20 +70,30 @@ def test_fetch_none(self):
6970
self.assertEqual(epg, '')
7071

7172
def test_fetch_file(self):
72-
source = CustomSource(uuid=str(uuid4()),
73-
name='Test Source',
74-
enabled=True,
75-
playlist_uri=os.path.realpath('tests/data/custom_playlist.m3u'),
76-
playlist_type=CustomSource.TYPE_FILE,
77-
epg_uri=os.path.realpath('tests/data/custom_epg.xml'),
78-
epg_type=CustomSource.TYPE_FILE,
79-
)
73+
source = CustomSource(
74+
uuid=str(uuid4()),
75+
name='Test Source',
76+
enabled=True,
77+
playlist_uri=os.path.realpath('tests/data/custom_playlist.m3u'),
78+
playlist_type=CustomSource.TYPE_FILE,
79+
epg_uri=os.path.realpath('tests/data/custom_epg.xml'),
80+
epg_type=CustomSource.TYPE_FILE,
81+
)
82+
expected_channels = Source._extract_m3u(open('tests/data/custom_playlist.m3u', 'r').read())
83+
expected_epg = Source._extract_xmltv(open('tests/data/custom_epg.xml', 'r').read())
84+
85+
# Test channels
86+
channels = source.get_channels()
87+
self.assertEqual(channels.replace('\r\n', '\n'), expected_channels)
8088

89+
# Test channels (gzip)
90+
source.playlist_uri = os.path.realpath('tests/data/custom_playlist.m3u.gz')
8191
channels = source.get_channels()
82-
self.assertEqual(channels, Source._extract_m3u(open('tests/data/custom_playlist.m3u').read()))
92+
self.assertEqual(channels.replace('\r\n', '\n'), expected_channels)
8393

94+
# Test EPG
8495
epg = source.get_epg()
85-
self.assertEqual(epg, Source._extract_xmltv(open('tests/data/custom_epg.xml').read()))
96+
self.assertEqual(epg.replace('\r\n', '\n'), expected_epg)
8697

8798
def test_fetch_url(self):
8899

@@ -99,55 +110,68 @@ def raise_for_status(self):
99110
def content(self):
100111
return self.data
101112

113+
@property
114+
def text(self):
115+
return self.data.decode()
116+
102117
if args[0].endswith('m3u'):
103-
data = open('tests/data/custom_playlist.m3u', 'r').read()
118+
data = open('tests/data/custom_playlist.m3u', 'rb').read()
104119
return MockResponse(data, 200)
105120

106121
if args[0].endswith('m3u.gz'):
107-
from gzip import compress
108122
data = open('tests/data/custom_playlist.m3u', 'rb').read()
109-
return MockResponse(compress(data), 200)
123+
try: # Python 3
124+
from gzip import compress
125+
return MockResponse(compress(data), 200)
126+
except ImportError: # Python 2
127+
from gzip import GzipFile
128+
from StringIO import StringIO
129+
buf = StringIO()
130+
with GzipFile(fileobj=buf, mode='wb') as f:
131+
f.write(data)
132+
return MockResponse(buf.getvalue(), 200)
110133

111134
if args[0].endswith('m3u.bz2'):
112135
from bz2 import compress
113136
data = open('tests/data/custom_playlist.m3u', 'rb').read()
114137
return MockResponse(compress(data), 200)
115138

116139
if args[0].endswith('xml'):
117-
data = open('tests/data/custom_epg.xml', 'r').read()
140+
data = open('tests/data/custom_epg.xml', 'rb').read()
118141
return MockResponse(data, 200)
119142

120143
return MockResponse(None, 404)
121144

122-
with patch('requests.get', side_effect=mocked_requests_get):
123-
source = CustomSource(uuid=str(uuid4()),
124-
name='Test Source',
125-
enabled=True,
126-
playlist_uri='https://example.com/playlist.m3u',
127-
playlist_type=CustomSource.TYPE_URL,
128-
epg_uri='https://example.com/xmltv.xml',
129-
epg_type=CustomSource.TYPE_URL,
130-
)
145+
source = CustomSource(
146+
uuid=str(uuid4()),
147+
name='Test Source',
148+
enabled=True,
149+
playlist_uri='https://example.com/playlist.m3u',
150+
playlist_type=CustomSource.TYPE_URL,
151+
epg_uri='https://example.com/xmltv.xml',
152+
epg_type=CustomSource.TYPE_URL,
153+
)
154+
expected_channels = Source._extract_m3u(open('tests/data/custom_playlist.m3u', 'r').read())
155+
expected_epg = Source._extract_xmltv(open('tests/data/custom_epg.xml', 'r').read())
131156

157+
with patch('requests.get', side_effect=mocked_requests_get):
132158
# Test channels
133159
channels = source.get_channels()
134-
self.assertEqual(channels, Source._extract_m3u(open('tests/data/custom_playlist.m3u').read()))
135-
136-
# Test EPG
137-
epg = source.get_epg()
138-
self.assertEqual(epg, Source._extract_xmltv(open('tests/data/custom_epg.xml').read()))
160+
self.assertEqual(channels.replace('\r\n', '\n'), expected_channels)
139161

140162
# Test channels (gzip)
141163
source.playlist_uri = 'https://example.com/playlist.m3u.gz'
142164
channels = source.get_channels()
143-
self.assertEqual(channels, Source._extract_m3u(open('tests/data/custom_playlist.m3u').read()))
165+
self.assertEqual(channels.replace('\r\n', '\n'), expected_channels)
144166

145167
# Test channels (bzip2)
146168
source.playlist_uri = 'https://example.com/playlist.m3u.bz2'
147169
channels = source.get_channels()
148-
self.assertEqual(channels, Source._extract_m3u(open('tests/data/custom_playlist.m3u').read()))
149-
170+
self.assertEqual(channels.replace('\r\n', '\n'), expected_channels)
150171

172+
# Test EPG
173+
epg = source.get_epg()
174+
self.assertEqual(epg.replace('\r\n', '\n'), expected_epg)
151175

152176

153177
if __name__ == '__main__':

0 commit comments

Comments
 (0)