Skip to content

Commit

Permalink
Merge pull request #2 from bneijt/issue/1
Browse files Browse the repository at this point in the history
Upgrade python to fix #1
  • Loading branch information
bneijt authored Jan 1, 2022
2 parents f4a62b2 + 66c09be commit 9bdbb93
Show file tree
Hide file tree
Showing 12 changed files with 472 additions and 101 deletions.
38 changes: 38 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
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
- uses: actions/upload-artifact@v2
with:
name: package
path: build/plugin_video_ipfs.zip
retention-days: 3
if-no-files-found: error
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,7 @@
/*.egg-info
/.pytest_cache
__pycache__
/venv
/.venv
/dist
/upload_build.sh
/.vscode
18 changes: 3 additions & 15 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -23,9 +10,10 @@ 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
cp -r src/*.py $(OUTPUT_PATH)
cp -r src/ipfs/*.py $(OUTPUT_PATH)/ipfs
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)
Expand Down
41 changes: 19 additions & 22 deletions addon.xml
Original file line number Diff line number Diff line change
@@ -1,23 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<addon id="plugin.video.ipfs"
version="0.0.5"
name="IPFS"
provider-name="ipfs.video">
<requires>
<import addon="xbmc.python" version="2.25.0"/>
<import addon="script.module.requests" version="2.22.0"/>
</requires>
<extension point="xbmc.python.pluginsource" library="main.py">
<provides>video</provides>
</extension>
<extension point="xbmc.addon.metadata">
<summary lang="en">IPFS video viewing plugin</summary>
<description lang="en_GB">A plugin allowing access to IPFS video media.</description>
<website>https://ipfs.video/</website>
<assets>
<icon>icon.png</icon>
<fanart>fanart.jpg</fanart>
<screenshot>resources/screenshot-01.jpg</screenshot>
</assets>
</extension>
</addon>
<addon id="plugin.video.ipfs" version="0.0.6" name="IPFS" provider-name="ipfs.video">
<requires>
<import addon="xbmc.python" version="3.0.0" />
<import addon="script.module.requests" version="2.22.0" />
</requires>
<extension point="xbmc.python.pluginsource" library="main.py">
<provides>video</provides>
</extension>
<extension point="xbmc.addon.metadata">
<summary lang="en">IPFS video viewing plugin</summary>
<description lang="en_GB">A plugin allowing access to IPFS video media.</description>
<website>https://ipfs.video/</website>
<assets>
<icon>icon.png</icon>
<fanart>fanart.jpg</fanart>
<screenshot>resources/screenshot-01.jpg</screenshot>
</assets>
</extension>
</addon>
25 changes: 16 additions & 9 deletions src/ipfs/__init__.py → ipfs_video_kodi/ipfs/__init__.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,43 @@
import random

import requests
import random


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):
assert len(gateway) > 0
self._gateway = gateway
self._cache = {}

def get(self, path, params):
url = self._gateway + '/api/v0/dag/get'
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"""
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})
r.raise_for_status()

entries = list(filter(lambda link: len(link['Name']) > 0 and '/' in link['Cid'], r.json()["links"]))
entries = self.get_links("/api/v0/dag/get", params={"arg": hash})
self._cache[hash] = entries
return entries

Expand Down
44 changes: 23 additions & 21 deletions src/main.py → ipfs_video_kodi/main.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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):
"""
Expand All @@ -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):
"""
Expand All @@ -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["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'))
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)
Expand Down Expand Up @@ -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:])
Loading

0 comments on commit 9bdbb93

Please sign in to comment.