Skip to content

Commit

Permalink
Merge pull request #366 from oddstr13/pr-json-filter-1
Browse files Browse the repository at this point in the history
Filter keys containing None values from dictionaries returned from the server
  • Loading branch information
mcarlton00 authored Aug 21, 2020
2 parents 8b1b648 + ec92a1a commit 4e2c8a0
Show file tree
Hide file tree
Showing 20 changed files with 214 additions and 26 deletions.
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 @@ -191,13 +191,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

0 comments on commit 4e2c8a0

Please sign in to comment.