Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Filter keys containing None values from dictionaries returned from the server #366

Merged
merged 5 commits into from
Aug 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 4 additions & 1 deletion .config/generate_xml.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import xml.etree.ElementTree as ET
import yaml
import sys
import os
from datetime import datetime

import yaml


def indent(elem, level=0):
'''
Nicely formats output xml with newlines and spaces
Expand All @@ -23,6 +25,7 @@ def indent(elem, level=0):
if level and (not elem.tail or not elem.tail.strip()):
elem.tail = i


try:
py_version = sys.argv[1]
except IndexError:
Expand Down
66 changes: 63 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,12 +1,72 @@
*.pyo
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/

# Translations
*.mo
*.pot



__local__/
machine_guid
/resources/media/Thumbs.db
Thumbs.db

.idea/
.DS_Store
.vscode/
pyinstrument/
pyinstrument_cext.so

# Now managed by templates
addon.xml

*.log
4 changes: 2 additions & 2 deletions context.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@

#################################################################################################

from entrypoint import Context # noqa: F402
from helper import LazyLogger # noqa: F402
from entrypoint import Context # noqa: E402
from helper import LazyLogger # noqa: E402

#################################################################################################

Expand Down
4 changes: 2 additions & 2 deletions context_play.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@

#################################################################################################

from entrypoint import Context # noqa: F402
from helper import LazyLogger # noqa: F402
from entrypoint import Context # noqa: E402
from helper import LazyLogger # noqa: E402

#################################################################################################

Expand Down
4 changes: 2 additions & 2 deletions default.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@

#################################################################################################

from entrypoint import Events # noqa: F402
from helper import LazyLogger # noqa: F402
from entrypoint import Events # noqa: E402
from helper import LazyLogger # noqa: E402

#################################################################################################

Expand Down
1 change: 1 addition & 0 deletions jellyfin_kodi/downloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ def get_library_items(library_id, item_type):

return _get(url, params)


def get_albums_by_artist(artist_id, basic=False):

params = {
Expand Down
4 changes: 2 additions & 2 deletions jellyfin_kodi/full_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -435,13 +435,13 @@ def music(self, library, dialog):
obj.artist(artist)

# Get all albums for each artist
artist_albums = [ album for album in albums if artist_name in album.get('Artists') ]
artist_albums = [album for album in albums if artist_name in album.get('Artists')]
for album in artist_albums:
# Add album to database
obj.album(album)
album_id = album.get('Id')
# Get all songs in each album
album_songs = [ song for song in songs if album_id == song.get('AlbumId') ]
album_songs = [song for song in songs if album_id == song.get('AlbumId')]
for song in album_songs:
dialog.update(percent,
message="%s/%s/%s" % (artist_name, album['Name'][:7], song['Name'][:7]))
Expand Down
2 changes: 1 addition & 1 deletion jellyfin_kodi/jellyfin/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,7 @@ def validate_authentication_token(self, server):
if response.status_code == 200:
return response.json()
else:
return { 'Status_Code': response.status_code }
return {'Status_Code': response.status_code}

def get_public_info(self, server_address):
response = self.send_request(server_address, "system/info/public")
Expand Down
2 changes: 1 addition & 1 deletion jellyfin_kodi/jellyfin/connection_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ def process_found_servers(self, found_servers):
}

servers.append(info)

return servers

# TODO: Make IPv6 compatable
Expand Down
2 changes: 1 addition & 1 deletion jellyfin_kodi/jellyfin/credentials.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ def add_update_server(self, servers, server):
existing['ConnectServerId'] = server['ConnectServerId']

return existing

servers.append(server)
return server

Expand Down
5 changes: 3 additions & 2 deletions jellyfin_kodi/jellyfin/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from helper.utils import JsonDebugPrinter
from helper import LazyLogger
from helper.exceptions import HTTPException
from jellyfin.utils import clean_none_dict_values

#################################################################################################

Expand Down Expand Up @@ -56,7 +57,7 @@ def _replace_user_info(self, string):
else:
LOG.debug("Server address not set")

if '{UserId}'in string:
if '{UserId}' in string:
if self.config.data.get('auth.user_id', None):
string = string.replace("{UserId}", self.config.data['auth.user_id'])
else:
Expand Down Expand Up @@ -161,7 +162,7 @@ def request(self, data, session=None):
LOG.debug("---<[ http ][%s ms]", elapsed)
LOG.debug(JsonDebugPrinter(response))

return response
return clean_none_dict_values(response)
except ValueError:
return

Expand Down
43 changes: 43 additions & 0 deletions jellyfin_kodi/jellyfin/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from six import string_types
from six.moves import collections_abc


def clean_none_dict_values(obj):
"""
Recursively remove keys with a value of None
"""
if not isinstance(obj, collections_abc.Iterable) or isinstance(obj, string_types):
return obj

queue = [obj]

while queue:
item = queue.pop()

if isinstance(item, collections_abc.Mapping):
mutable = isinstance(item, collections_abc.MutableMapping)
remove = []

for key, value in item.items():
if value is None and mutable:
remove.append(key)

elif isinstance(value, string_types):
continue

elif isinstance(value, collections_abc.Iterable):
queue.append(value)

if mutable:
# Remove keys with None value
for key in remove:
item.pop(key)

elif isinstance(item, collections_abc.Iterable):
for value in item:
if value is None or isinstance(value, string_types):
continue
elif isinstance(value, collections_abc.Iterable):
queue.append(value)

return obj
4 changes: 2 additions & 2 deletions jellyfin_kodi/library.py
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@ def fast_sync(self):
include = []
filters = ["tvshows", "boxsets", "musicvideos", "music", "movies"]
sync = get_sync()
whitelist = [ x.replace('Mixed:', "") for x in sync['Whitelist'] ]
whitelist = [x.replace('Mixed:', "") for x in sync['Whitelist']]
LOG.info("--[ retrieve changes ] %s", last_sync)

# Get the item type of each synced library and build list of types to request
Expand All @@ -395,7 +395,7 @@ def fast_sync(self):

try:
# Get list of updates from server for synced library types and populate work queues
result = self.server.jellyfin.get_sync_queue(last_sync, ",".join([ x for x in query_filter ]))
result = self.server.jellyfin.get_sync_queue(last_sync, ",".join([x for x in query_filter]))

if result is None:
return True
Expand Down
4 changes: 2 additions & 2 deletions jellyfin_kodi/objects/movies.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,13 +182,13 @@ def get_path_filename(self, obj):
if validate_dvd_dir(obj['Path'] + obj['Filename']):
obj['Path'] = obj['Path'] + obj['Filename'] + '/VIDEO_TS/'
obj['Filename'] = 'VIDEO_TS.IFO'
LOG.debug("DVD directry %s",obj['Path'])
LOG.debug("DVD directry %s", obj['Path'])

'''check bluray directries and point it to ./BDMV/index.bdmv'''
if validate_bluray_dir(obj['Path'] + obj['Filename']):
obj['Path'] = obj['Path'] + obj['Filename'] + '/BDMV/'
obj['Filename'] = 'index.bdmv'
LOG.debug("Bluray directry %s",obj['Path'])
LOG.debug("Bluray directry %s", obj['Path'])

else:
obj['Path'] = "plugin://plugin.video.jellyfin/%s/" % obj['LibraryId']
Expand Down
4 changes: 2 additions & 2 deletions jellyfin_kodi/player.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,9 +160,9 @@ def set_item(self, file, item):

def set_audio_subs(self, audio=None, subtitle=None):
if audio:
audio=int(audio)
audio = int(audio)
if subtitle:
subtitle=int(subtitle)
subtitle = int(subtitle)

''' Only for after playback started
'''
Expand Down
13 changes: 13 additions & 0 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
setuptools >= 44.1.1 # Old setuptools causes script.module.addon.signals to fail installing
six >= 1.13
python-dateutil >= 2.8.1
requests >= 2.22
futures >= 2.2; python_version < '3.0'
git+https://github.com/oddstr13/Kodistubs@python3 # Kodistubs >= 18
git+https://github.com/romanvm/kodi.six
git+https://github.com/ruuk/script.module.addon.signals

pytest >= 4.6.11
coverage >= 5.2
flake8 >= 3.8
flake8-import-order >= 0.18
6 changes: 3 additions & 3 deletions service.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@

#################################################################################################

from entrypoint import Service # noqa: F402
from helper.utils import settings # noqa: F402
from helper import LazyLogger # noqa: F402
from entrypoint import Service # noqa: E402
from helper.utils import settings # noqa: E402
from helper import LazyLogger # noqa: E402

#################################################################################################

Expand Down
Empty file added tests/__init__.py
Empty file.
51 changes: 51 additions & 0 deletions tests/test_clean_none_dict_values.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import sys

import pytest

sys.path.insert(0, 'jellyfin_kodi')

from jellyfin.utils import clean_none_dict_values # noqa: E402


@pytest.mark.parametrize("obj,expected", [
(None, None),
([None, 1, 2, 3, None, 4], [None, 1, 2, 3, None, 4]),
({'foo': None, 'bar': 123}, {'bar': 123}),
({
'dict': {
'empty': None,
'string': "Hello, Woorld!",
},
'number': 123,
'list': [
None,
123,
"foo",
{
'empty': None,
'number': 123,
'string': "foo",
'list': [],
'dict': {},
}
]
}, {
'dict': {
'string': "Hello, Woorld!",
},
'number': 123,
'list': [
None,
123,
"foo",
{
'number': 123,
'string': "foo",
'list': [],
'dict': {},
}
]
}),
])
def test_clean_none_dict_values(obj, expected):
assert clean_none_dict_values(obj) == expected
16 changes: 16 additions & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,19 @@ extend-ignore =
I202
per-file-ignores =
*/__init__.py: F401

[pytest]
minversion = 4.6
testpaths =
tests

[coverage:run]
source =
jellyfin_kodi
context.py
context_play.py
default.py
service.py
.config/generate_xml.py
omit = tests/*
command_line = -m pytest