From 34211dc59d4542029e0a877c223c3ba5a085df1e Mon Sep 17 00:00:00 2001 From: Bram Neijt Date: Fri, 31 Dec 2021 18:16:36 +0100 Subject: [PATCH 01/11] Upgrade python --- .gitignore | 3 +- Makefile | 17 +- addon.xml | 4 +- {src => ipfs_video_kodi}/ipfs/__init__.py | 7 +- {src => ipfs_video_kodi}/main.py | 0 poetry.lock | 339 ++++++++++++++++++++++ pyproject.toml | 18 ++ requirements.txt | 4 - setup.py | 11 - test/test_ipfs.py | 26 +- 10 files changed, 375 insertions(+), 54 deletions(-) rename {src => ipfs_video_kodi}/ipfs/__init__.py (87%) rename {src => ipfs_video_kodi}/main.py (100%) create mode 100644 poetry.lock create mode 100644 pyproject.toml delete mode 100644 requirements.txt delete mode 100644 setup.py diff --git a/.gitignore b/.gitignore index 3c6c035..dc23692 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ /*.egg-info /.pytest_cache __pycache__ -/venv +/.venv +/dist diff --git a/Makefile b/Makefile index 6067b43..0073824 100644 --- a/Makefile +++ b/Makefile @@ -2,19 +2,6 @@ SOURCES=$(shell find . -name '*.py') OUTPUT_PATH=build/plugin.video.ipfs -venv: - ( \ - virtualenv --python=python3.7 venv - source venv/bin/activate; \ - pip install -r requirements.txt; \ - ) - -test: install venv $(SOURCES) - venv/bin/py.test - -install: - venv/bin/python setup.py develop - clean: rm -rf build @@ -24,8 +11,8 @@ build/plugin_video_ipfs.zip: build build: $(SOURCES) fanart.jpg icon.png addon.xml resources/settings.xml mkdir -p $(OUTPUT_PATH)/ipfs - cp -r src/*.py $(OUTPUT_PATH) - cp -r src/ipfs/*.py $(OUTPUT_PATH)/ipfs + find ipfs_video_kodi -name '__pycache__' -exec rm -rf {} \; + cp -r ipfs_video_kodi/* $(OUTPUT_PATH) cp -r resources $(OUTPUT_PATH) cp icon.png $(OUTPUT_PATH) cp addon.xml $(OUTPUT_PATH) diff --git a/addon.xml b/addon.xml index b7e1290..8717bd3 100644 --- a/addon.xml +++ b/addon.xml @@ -1,10 +1,10 @@ - + diff --git a/src/ipfs/__init__.py b/ipfs_video_kodi/ipfs/__init__.py similarity index 87% rename from src/ipfs/__init__.py rename to ipfs_video_kodi/ipfs/__init__.py index 2570e03..72c91f0 100644 --- a/src/ipfs/__init__.py +++ b/ipfs_video_kodi/ipfs/__init__.py @@ -13,7 +13,7 @@ def __init__(self, gateway): def get(self, path, params): url = self._gateway + '/api/v0/dag/get' - r = requests.get(url, params=params, timeout=20) + r = requests.post(url, params=params, timeout=20) r.raise_for_status() return r @@ -28,9 +28,8 @@ def list(self, hash): return self._cache[hash] r = self.get('/api/v0/dag/get', params={"arg": hash}) - r.raise_for_status() - - entries = list(filter(lambda link: len(link['Name']) > 0 and '/' in link['Cid'], r.json()["links"])) + print(r.json()) + entries = list(filter(lambda link: len(link['Name']) > 0 and '/' in link['Hash'], r.json()["Links"])) self._cache[hash] = entries return entries diff --git a/src/main.py b/ipfs_video_kodi/main.py similarity index 100% rename from src/main.py rename to ipfs_video_kodi/main.py diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..1c8d876 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,339 @@ +[[package]] +name = "atomicwrites" +version = "1.4.0" +description = "Atomic file writes." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "attrs" +version = "21.4.0" +description = "Classes Without Boilerplate" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.extras] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] +docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] +tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"] + +[[package]] +name = "black" +version = "21.12b0" +description = "The uncompromising code formatter." +category = "dev" +optional = false +python-versions = ">=3.6.2" + +[package.dependencies] +click = ">=7.1.2" +mypy-extensions = ">=0.4.3" +pathspec = ">=0.9.0,<1" +platformdirs = ">=2" +tomli = ">=0.2.6,<2.0.0" +typing-extensions = ">=3.10.0.0" + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +python2 = ["typed-ast (>=1.4.3)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "certifi" +version = "2021.10.8" +description = "Python package for providing Mozilla's CA Bundle." +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "charset-normalizer" +version = "2.0.9" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +category = "main" +optional = false +python-versions = ">=3.5.0" + +[package.extras] +unicode_backport = ["unicodedata2"] + +[[package]] +name = "click" +version = "8.0.3" +description = "Composable command line interface toolkit" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.4" +description = "Cross-platform colored terminal text." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "idna" +version = "3.3" +description = "Internationalized Domain Names in Applications (IDNA)" +category = "main" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "iniconfig" +version = "1.1.1" +description = "iniconfig: brain-dead simple config-ini parsing" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "mypy-extensions" +version = "0.4.3" +description = "Experimental type system extensions for programs checked with the mypy typechecker." +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "packaging" +version = "21.3" +description = "Core utilities for Python packages" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" + +[[package]] +name = "pathspec" +version = "0.9.0" +description = "Utility library for gitignore style pattern matching of file paths." +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" + +[[package]] +name = "platformdirs" +version = "2.4.1" +description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.extras] +docs = ["Sphinx (>=4)", "furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)"] +test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] + +[[package]] +name = "pluggy" +version = "1.0.0" +description = "plugin and hook calling mechanisms for python" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "py" +version = "1.11.0" +description = "library with cross-python path, ini-parsing, io, code, log facilities" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "pyparsing" +version = "3.0.6" +description = "Python parsing module" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + +[[package]] +name = "pytest" +version = "6.2.5" +description = "pytest: simple powerful testing with Python" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} +attrs = ">=19.2.0" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +py = ">=1.8.2" +toml = "*" + +[package.extras] +testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] + +[[package]] +name = "requests" +version = "2.26.0" +description = "Python HTTP for Humans." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""} +idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""} +urllib3 = ">=1.21.1,<1.27" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] +use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] + +[[package]] +name = "toml" +version = "0.10.2" +description = "Python Library for Tom's Obvious, Minimal Language" +category = "dev" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" + +[[package]] +name = "tomli" +version = "1.2.3" +description = "A lil' TOML parser" +category = "dev" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "typing-extensions" +version = "4.0.1" +description = "Backported and Experimental Type Hints for Python 3.6+" +category = "dev" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "urllib3" +version = "1.26.7" +description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" + +[package.extras] +brotli = ["brotlipy (>=0.6.0)"] +secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + +[metadata] +lock-version = "1.1" +python-versions = "~3.8" +content-hash = "5efc61e5e11dbf61fcf9c5621f676c29778d9251713d95a85c05ade4f852e261" + +[metadata.files] +atomicwrites = [ + {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, + {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, +] +attrs = [ + {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, + {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, +] +black = [ + {file = "black-21.12b0-py3-none-any.whl", hash = "sha256:a615e69ae185e08fdd73e4715e260e2479c861b5740057fde6e8b4e3b7dd589f"}, + {file = "black-21.12b0.tar.gz", hash = "sha256:77b80f693a569e2e527958459634f18df9b0ba2625ba4e0c2d5da5be42e6f2b3"}, +] +certifi = [ + {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"}, + {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"}, +] +charset-normalizer = [ + {file = "charset-normalizer-2.0.9.tar.gz", hash = "sha256:b0b883e8e874edfdece9c28f314e3dd5badf067342e42fb162203335ae61aa2c"}, + {file = "charset_normalizer-2.0.9-py3-none-any.whl", hash = "sha256:1eecaa09422db5be9e29d7fc65664e6c33bd06f9ced7838578ba40d58bdf3721"}, +] +click = [ + {file = "click-8.0.3-py3-none-any.whl", hash = "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3"}, + {file = "click-8.0.3.tar.gz", hash = "sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b"}, +] +colorama = [ + {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, + {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, +] +idna = [ + {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, + {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, +] +iniconfig = [ + {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, + {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, +] +mypy-extensions = [ + {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, + {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, +] +packaging = [ + {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, + {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, +] +pathspec = [ + {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"}, + {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, +] +platformdirs = [ + {file = "platformdirs-2.4.1-py3-none-any.whl", hash = "sha256:1d7385c7db91728b83efd0ca99a5afb296cab9d0ed8313a45ed8ba17967ecfca"}, + {file = "platformdirs-2.4.1.tar.gz", hash = "sha256:440633ddfebcc36264232365d7840a970e75e1018d15b4327d11f91909045fda"}, +] +pluggy = [ + {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, + {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, +] +py = [ + {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, + {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, +] +pyparsing = [ + {file = "pyparsing-3.0.6-py3-none-any.whl", hash = "sha256:04ff808a5b90911829c55c4e26f75fa5ca8a2f5f36aa3a51f68e27033341d3e4"}, + {file = "pyparsing-3.0.6.tar.gz", hash = "sha256:d9bdec0013ef1eb5a84ab39a3b3868911598afa494f5faa038647101504e2b81"}, +] +pytest = [ + {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"}, + {file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"}, +] +requests = [ + {file = "requests-2.26.0-py2.py3-none-any.whl", hash = "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24"}, + {file = "requests-2.26.0.tar.gz", hash = "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"}, +] +toml = [ + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, +] +tomli = [ + {file = "tomli-1.2.3-py3-none-any.whl", hash = "sha256:e3069e4be3ead9668e21cb9b074cd948f7b3113fd9c8bba083f48247aab8b11c"}, + {file = "tomli-1.2.3.tar.gz", hash = "sha256:05b6166bff487dc068d322585c7ea4ef78deed501cc124060e0f238e89a9231f"}, +] +typing-extensions = [ + {file = "typing_extensions-4.0.1-py3-none-any.whl", hash = "sha256:7f001e5ac290a0c0401508864c7ec868be4e701886d5b573a9528ed3973d9d3b"}, + {file = "typing_extensions-4.0.1.tar.gz", hash = "sha256:4ca091dea149f945ec56afb48dae714f21e8692ef22a395223bcd328961b6a0e"}, +] +urllib3 = [ + {file = "urllib3-1.26.7-py2.py3-none-any.whl", hash = "sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844"}, + {file = "urllib3-1.26.7.tar.gz", hash = "sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece"}, +] diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..1262938 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,18 @@ +[tool.poetry] +name = "ipfs-video-kodi" +version = "0.0.6" +description = "Kodi plugin to view IPFS video files" +authors = ["Bram Neijt "] +license = "GPLv3" + +[tool.poetry.dependencies] +python = "~3.8" +requests = "^2.26.0" + +[tool.poetry.dev-dependencies] +pytest = "^6.2.5" +black = {version = "^21.12b0", allow-prereleases = true} + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 9a55a62..0000000 --- a/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ - -requests - -pytest diff --git a/setup.py b/setup.py deleted file mode 100644 index 5818e32..0000000 --- a/setup.py +++ /dev/null @@ -1,11 +0,0 @@ -from setuptools import setup - -setup(name='ipfs-video-kodi', - version='0.1', - description='The funniest joke in the world', - url='', - author='Flying Circus', - author_email='flyingcircus@example.com', - license='MIT', - packages=["src"], - zip_safe=False) diff --git a/test/test_ipfs.py b/test/test_ipfs.py index b8ac98d..41b94f5 100644 --- a/test/test_ipfs.py +++ b/test/test_ipfs.py @@ -1,22 +1,14 @@ # -*- coding: utf-8 -*- import unittest -import src.ipfs as ipfs +import ipfs_video_kodi.ipfs as ipfs -test_gateway = ipfs.via("http://51.15.122.1") +test_gateway = ipfs.via("http://127.0.0.1:5001") -class TestIpfsMethods(unittest.TestCase): - - def test_list_file_should_be_empty(self): - a = test_gateway.list("QmTNdv6MBhCjcGY5tpabi7aCeLZL65tmDzW37J9ZrFbZfL") - self.assertEqual(a, []) - - def test_list_directory_should_work(self): - a = test_gateway.list("QmVZV84e6nSwfA8LppiS4KXKiAhpbqqGYzofHtecQjd9js") - self.assertEqual(len(a), 1) - self.assertEqual(a[0]['Name'], "pexel") - - - -if __name__ == '__main__': - unittest.main() +def test_list_directory_should_work(): + a = test_gateway.list("Qme4QjkyZQuFtN2SDhELfXVshMyAEec53jaFQ8kR4maLeV") + assert len(a) == 1 + assert ( + a[0]["Name"] + == "Alan Kay at OOPSLA 1997 - The computer revolution hasnt happened yet.webm" + ) From 85768bda1427a7f8ae9285d708ea0030b07b54c4 Mon Sep 17 00:00:00 2001 From: Bram Neijt Date: Fri, 31 Dec 2021 21:43:31 +0100 Subject: [PATCH 02/11] Use source from Python package --- Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 0073824..8e5a0d5 100644 --- a/Makefile +++ b/Makefile @@ -10,9 +10,11 @@ build/plugin_video_ipfs.zip: build cd build && zip -r plugin_video_ipfs.zip plugin.video.ipfs build: $(SOURCES) fanart.jpg icon.png addon.xml resources/settings.xml + poetry build mkdir -p $(OUTPUT_PATH)/ipfs find ipfs_video_kodi -name '__pycache__' -exec rm -rf {} \; - cp -r ipfs_video_kodi/* $(OUTPUT_PATH) + tar -xzf dist/ipfs-video-kodi-*.tar.gz -C dist --wildcards '*/ipfs_video_kodi' + cp -r dist/*/ipfs_video_kodi/* $(OUTPUT_PATH) cp -r resources $(OUTPUT_PATH) cp icon.png $(OUTPUT_PATH) cp addon.xml $(OUTPUT_PATH) From 60965bf244a10d5f5a65e66fea4b14116c327d63 Mon Sep 17 00:00:00 2001 From: Bram Neijt Date: Sat, 1 Jan 2022 01:50:38 +0100 Subject: [PATCH 03/11] Change default setting to point to Caminandes --- resources/settings.xml | 2 +- test/test_ipfs.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/resources/settings.xml b/resources/settings.xml index ea805d4..9565bec 100644 --- a/resources/settings.xml +++ b/resources/settings.xml @@ -1,7 +1,7 @@ - + diff --git a/test/test_ipfs.py b/test/test_ipfs.py index 41b94f5..bda06d2 100644 --- a/test/test_ipfs.py +++ b/test/test_ipfs.py @@ -2,7 +2,7 @@ import unittest import ipfs_video_kodi.ipfs as ipfs -test_gateway = ipfs.via("http://127.0.0.1:5001") +test_gateway = ipfs.via("https://ipfs.io") def test_list_directory_should_work(): @@ -12,3 +12,5 @@ def test_list_directory_should_work(): a[0]["Name"] == "Alan Kay at OOPSLA 1997 - The computer revolution hasnt happened yet.webm" ) + b = test_gateway.list("QmYHDhsgUgdKSAimguGC92MzQ8VNFHZw3yp6kAHwiXCFLm") + assert len(b) == 3 From 2b4070854f5b28a38bf09ed55ef0bf9528b67389 Mon Sep 17 00:00:00 2001 From: Bram Neijt Date: Sat, 1 Jan 2022 08:24:20 +0100 Subject: [PATCH 04/11] Revert method change --- ipfs_video_kodi/ipfs/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipfs_video_kodi/ipfs/__init__.py b/ipfs_video_kodi/ipfs/__init__.py index 72c91f0..a341a9d 100644 --- a/ipfs_video_kodi/ipfs/__init__.py +++ b/ipfs_video_kodi/ipfs/__init__.py @@ -13,7 +13,7 @@ def __init__(self, gateway): def get(self, path, params): url = self._gateway + '/api/v0/dag/get' - r = requests.post(url, params=params, timeout=20) + r = requests.get(url, params=params, timeout=20) r.raise_for_status() return r From fecacbe45cd6073b8d1c62e777884448520ec404 Mon Sep 17 00:00:00 2001 From: Bram Neijt Date: Sat, 1 Jan 2022 08:24:46 +0100 Subject: [PATCH 05/11] Remove cache removal --- Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/Makefile b/Makefile index 8e5a0d5..9dd64b9 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,6 @@ build/plugin_video_ipfs.zip: build build: $(SOURCES) fanart.jpg icon.png addon.xml resources/settings.xml poetry build mkdir -p $(OUTPUT_PATH)/ipfs - find ipfs_video_kodi -name '__pycache__' -exec rm -rf {} \; tar -xzf dist/ipfs-video-kodi-*.tar.gz -C dist --wildcards '*/ipfs_video_kodi' cp -r dist/*/ipfs_video_kodi/* $(OUTPUT_PATH) cp -r resources $(OUTPUT_PATH) From 1dc135badc335de7c41b3ad830c4c48d3e6dd991 Mon Sep 17 00:00:00 2001 From: Bram Neijt Date: Sat, 1 Jan 2022 08:27:44 +0100 Subject: [PATCH 06/11] Indentation --- addon.xml | 41 ++++++++++++++--------------- ipfs_video_kodi/ipfs/__init__.py | 19 +++++++++----- ipfs_video_kodi/main.py | 44 +++++++++++++++++--------------- 3 files changed, 55 insertions(+), 49 deletions(-) diff --git a/addon.xml b/addon.xml index 8717bd3..90779e9 100644 --- a/addon.xml +++ b/addon.xml @@ -1,23 +1,20 @@ - - - - - - - video - - - IPFS video viewing plugin - A plugin allowing access to IPFS video media. - https://ipfs.video/ - - icon.png - fanart.jpg - resources/screenshot-01.jpg - - - + + + + + + + video + + + IPFS video viewing plugin + A plugin allowing access to IPFS video media. + https://ipfs.video/ + + icon.png + fanart.jpg + resources/screenshot-01.jpg + + + \ No newline at end of file diff --git a/ipfs_video_kodi/ipfs/__init__.py b/ipfs_video_kodi/ipfs/__init__.py index a341a9d..39db049 100644 --- a/ipfs_video_kodi/ipfs/__init__.py +++ b/ipfs_video_kodi/ipfs/__init__.py @@ -1,10 +1,12 @@ +import random import requests -import random + def via(gateway): return IPFS(gateway) + class IPFS: def __init__(self, gateway): assert len(gateway) > 0 @@ -12,7 +14,7 @@ def __init__(self, gateway): self._cache = {} def get(self, path, params): - url = self._gateway + '/api/v0/dag/get' + url = self._gateway + "/api/v0/dag/get" r = requests.get(url, params=params, timeout=20) r.raise_for_status() return r @@ -22,14 +24,19 @@ def list(self, hash): assert type(hash) == str if hash in self._cache: if len(self._cache) > 50: - #Drop 10 keys + # Drop 10 keys for k in random.sample(self._cache.keys(), 10): del self._cache[k] return self._cache[hash] - r = self.get('/api/v0/dag/get', params={"arg": hash}) - print(r.json()) - entries = list(filter(lambda link: len(link['Name']) > 0 and '/' in link['Hash'], r.json()["Links"])) + r = self.get("/api/v0/dag/get", params={"arg": hash}) + print(r.json()) + entries = list( + filter( + lambda link: len(link["Name"]) > 0 and "/" in link["Hash"], + r.json()["Links"], + ) + ) self._cache[hash] = entries return entries diff --git a/ipfs_video_kodi/main.py b/ipfs_video_kodi/main.py index 3f58dbc..df69b69 100644 --- a/ipfs_video_kodi/main.py +++ b/ipfs_video_kodi/main.py @@ -1,11 +1,12 @@ # -*- coding: utf-8 -*- import sys + import xbmcgui import xbmcplugin try: - #Python 3 - from urllib.parse import urlencode, parse_qsl + # Python 3 + from urllib.parse import parse_qsl, urlencode except ImportError: from urllib import urlencode from urlparse import parse_qsl @@ -16,8 +17,9 @@ _url = sys.argv[0] # Get the plugin handle as an integer number. _handle = int(sys.argv[1]) -_rootCid = xbmcplugin.getSetting(_handle, 'rootCid') -_ipfs = ipfs.via(xbmcplugin.getSetting(_handle, 'ipfsGateway')) +_rootCid = xbmcplugin.getSetting(_handle, "rootCid") +_ipfs = ipfs.via(xbmcplugin.getSetting(_handle, "ipfsGateway")) + def self_url(**kwargs): """ @@ -28,7 +30,8 @@ def self_url(**kwargs): :return: plugin call URL :rtype: str """ - return '{0}?{1}'.format(_url, urlencode(kwargs)) + return "{0}?{1}".format(_url, urlencode(kwargs)) + def list_node(cid): """ @@ -42,24 +45,23 @@ def list_node(cid): xbmcplugin.setPluginCategory(_handle, cid) # Set plugin content. It allows Kodi to select appropriate views # for this type of content. - xbmcplugin.setContent(_handle, 'videos') + xbmcplugin.setContent(_handle, "videos") # Get the list of videos in the category. links = _ipfs.list(cid) for link in links: - is_folder = len(_ipfs.list(link['Cid']['/'])) > 0 + is_folder = len(_ipfs.list(link["Cid"]["/"])) > 0 - list_item = xbmcgui.ListItem(label=link['Name']) + list_item = xbmcgui.ListItem(label=link["Name"]) # Set additional info for the list item. # 'mediatype' is needed for skin to display info for this ListItem correctly. - list_item.setInfo('video', {'title': link['Name'], - 'mediatype': 'video'}) + list_item.setInfo("video", {"title": link["Name"], "mediatype": "video"}) # TODO set thumbnails # list_item.setArt({'thumb': video['thumb'], 'icon': video['thumb'], 'fanart': video['thumb']}) - list_item.setProperty('IsPlayable', ('false' if is_folder else 'true')) + list_item.setProperty("IsPlayable", ("false" if is_folder else "true")) - url = self_url(action=('list' if is_folder else 'play'), cid=link['Cid']['/']) + url = self_url(action=("list" if is_folder else "play"), cid=link["Cid"]["/"]) # Add our item to the Kodi virtual folder listing. xbmcplugin.addDirectoryItem(_handle, url, list_item, is_folder) # Add a sort method for the virtual folder items (alphabetically, ignore articles) @@ -88,21 +90,21 @@ def router(paramstring): """ params = dict(parse_qsl(paramstring)) - #Default action + # Default action if not params: - params['action'] = 'list' - params['cid'] = _rootCid + params["action"] = "list" + params["cid"] = _rootCid # Check the parameters passed to the plugin - if params['action'] == 'list': - list_node(params['cid']) - elif params['action'] == 'play': - play_node(params['cid']) + if params["action"] == "list": + list_node(params["cid"]) + elif params["action"] == "play": + play_node(params["cid"]) else: - raise ValueError('Invalid paramstring: {0}!'.format(paramstring)) + raise ValueError("Invalid paramstring: {0}!".format(paramstring)) -if __name__ == '__main__': +if __name__ == "__main__": # Call the router function and pass the plugin call parameters to it. # We use string slicing to trim the leading '?' from the plugin call paramstring router(sys.argv[2][1:]) From 61d7d6cfda1039f13046e32ab4682358badcc58b Mon Sep 17 00:00:00 2001 From: Bram Neijt Date: Sat, 1 Jan 2022 08:28:38 +0100 Subject: [PATCH 07/11] Ignore local dev script --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index dc23692..8624bc8 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ __pycache__ /.venv /dist +/upload_build.sh \ No newline at end of file From 6c8e1b294911fa82b91febb0f89ed3cbad4f58f6 Mon Sep 17 00:00:00 2001 From: Bram Neijt Date: Sat, 1 Jan 2022 08:32:38 +0100 Subject: [PATCH 08/11] Add CI --- .github/workflows/main.yml | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 .github/workflows/main.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..c94f140 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,32 @@ +name: main + +on: + push: + branches: [main] + pull_request: + +jobs: + main: + env: + POETRY_VIRTUALENVS_IN_PROJECT: true + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + with: + python-version: 3.8 + - run: pip install poetry==1.1.7 + - name: cache venv + uses: actions/cache@v2 + with: + path: .venv + key: venv-${{ runner.os }}-${{ hashFiles('**/poetry.lock') }} + - run: poetry install + - name: static code analysis + run: | + poetry run black --check + - run: poetry run pytest + - name: build package + run: | + make clean + make package From 7b3787f9ba0559e11761e98ea7576fc066d420b8 Mon Sep 17 00:00:00 2001 From: Bram Neijt Date: Sat, 1 Jan 2022 08:39:20 +0100 Subject: [PATCH 09/11] Add artifact exposure --- .github/workflows/main.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c94f140..7024b93 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -30,3 +30,8 @@ jobs: run: | make clean make package + - uses: actions/upload-artifact@v2 + with: + name: package + path: build/plugin_video_ipfs.zip + retention-days: 3 \ No newline at end of file From 85a9360d0509bd647f81aedbbd50b407ec54ccb0 Mon Sep 17 00:00:00 2001 From: Bram Neijt Date: Sat, 1 Jan 2022 09:42:44 +0100 Subject: [PATCH 10/11] Error if package is not there --- .github/workflows/main.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7024b93..9d76b83 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -34,4 +34,5 @@ jobs: with: name: package path: build/plugin_video_ipfs.zip - retention-days: 3 \ No newline at end of file + retention-days: 3 + if-no-files-found: error From 66c09be4142eb8e1a0b2bb6372baf1303e117141 Mon Sep 17 00:00:00 2001 From: Bram Neijt Date: Sat, 1 Jan 2022 10:08:04 +0100 Subject: [PATCH 11/11] Fix Cid -> Hash change --- .gitignore | 3 ++- ipfs_video_kodi/ipfs/__init__.py | 21 +++++++++++---------- ipfs_video_kodi/main.py | 8 ++++---- test/test_ipfs.py | 2 +- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index 8624bc8..1261a23 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ __pycache__ /.venv /dist -/upload_build.sh \ No newline at end of file +/upload_build.sh +/.vscode \ No newline at end of file diff --git a/ipfs_video_kodi/ipfs/__init__.py b/ipfs_video_kodi/ipfs/__init__.py index 39db049..0ba6257 100644 --- a/ipfs_video_kodi/ipfs/__init__.py +++ b/ipfs_video_kodi/ipfs/__init__.py @@ -6,6 +6,8 @@ def via(gateway): return IPFS(gateway) +def lower_keys(dictList): + return [{k.lower(): v for k, v in entry.items()} for entry in dictList] class IPFS: def __init__(self, gateway): @@ -13,11 +15,17 @@ def __init__(self, gateway): self._gateway = gateway self._cache = {} - def get(self, path, params): + def get_links(self, path, params): url = self._gateway + "/api/v0/dag/get" r = requests.get(url, params=params, timeout=20) r.raise_for_status() - return r + rjson = r.json() + return lower_keys( + filter( + lambda link: len(link["Name"]) > 0 and "/" in link["Hash"], + rjson["Links"], + ) + ) def list(self, hash): """Get the directory content of the given hash""" @@ -29,14 +37,7 @@ def list(self, hash): del self._cache[k] return self._cache[hash] - r = self.get("/api/v0/dag/get", params={"arg": hash}) - print(r.json()) - entries = list( - filter( - lambda link: len(link["Name"]) > 0 and "/" in link["Hash"], - r.json()["Links"], - ) - ) + entries = self.get_links("/api/v0/dag/get", params={"arg": hash}) self._cache[hash] = entries return entries diff --git a/ipfs_video_kodi/main.py b/ipfs_video_kodi/main.py index df69b69..87ae7c0 100644 --- a/ipfs_video_kodi/main.py +++ b/ipfs_video_kodi/main.py @@ -50,18 +50,18 @@ def list_node(cid): links = _ipfs.list(cid) for link in links: - is_folder = len(_ipfs.list(link["Cid"]["/"])) > 0 + is_folder = len(_ipfs.list(link["hash"]["/"])) > 0 - list_item = xbmcgui.ListItem(label=link["Name"]) + list_item = xbmcgui.ListItem(label=link["name"]) # Set additional info for the list item. # 'mediatype' is needed for skin to display info for this ListItem correctly. - list_item.setInfo("video", {"title": link["Name"], "mediatype": "video"}) + list_item.setInfo("video", {"title": link["name"], "mediatype": "video"}) # TODO set thumbnails # list_item.setArt({'thumb': video['thumb'], 'icon': video['thumb'], 'fanart': video['thumb']}) list_item.setProperty("IsPlayable", ("false" if is_folder else "true")) - url = self_url(action=("list" if is_folder else "play"), cid=link["Cid"]["/"]) + url = self_url(action=("list" if is_folder else "play"), cid=link["hash"]["/"]) # Add our item to the Kodi virtual folder listing. xbmcplugin.addDirectoryItem(_handle, url, list_item, is_folder) # Add a sort method for the virtual folder items (alphabetically, ignore articles) diff --git a/test/test_ipfs.py b/test/test_ipfs.py index bda06d2..49797c7 100644 --- a/test/test_ipfs.py +++ b/test/test_ipfs.py @@ -9,7 +9,7 @@ def test_list_directory_should_work(): a = test_gateway.list("Qme4QjkyZQuFtN2SDhELfXVshMyAEec53jaFQ8kR4maLeV") assert len(a) == 1 assert ( - a[0]["Name"] + a[0]["name"] == "Alan Kay at OOPSLA 1997 - The computer revolution hasnt happened yet.webm" ) b = test_gateway.list("QmYHDhsgUgdKSAimguGC92MzQ8VNFHZw3yp6kAHwiXCFLm")