diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6a2bf47 --- /dev/null +++ b/.gitignore @@ -0,0 +1,90 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# 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/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# dotenv +.env + +# virtualenv +.venv/ +venv/ +ENV/ + +# Spyder project settings +.spyderproject + +# Rope project settings +.ropeproject diff --git a/api/README.md b/api/README.md new file mode 100644 index 0000000..9802a74 --- /dev/null +++ b/api/README.md @@ -0,0 +1,20 @@ +# API Karu Media + +This is a api writen in python for Karu Media storage server + +# How to set it up? +First you need Python 3.4 or newer and latest uwsgi installed in your system + +For Debian you have to install those pacakges + + sudo apt-get install python3 python3-pip python3-venv uwsgi uwsgi-plugin-python3 python3-uwsgidecorators + +And then you can just run those commands + + python3 -m venv venv + source venv/bin/activate + pip install -r requirements.txt + +And you can run it like so + + uwsgi --ini uwsgi.ini:dev diff --git a/api/karumedia/__init__.py b/api/karumedia/__init__.py new file mode 100644 index 0000000..47a36a9 --- /dev/null +++ b/api/karumedia/__init__.py @@ -0,0 +1,33 @@ +import json +from pathlib import Path + +import falcon + +from .tools import JsonRequest, JsonResponse, error_handler +from .controllers import * + +class AboutResource(): + + def on_get(self, req, resp): + r = {"about": { + "name": "Karu Media API", + "version": "1", + "docs": "TODO" + } + } + r.update({"endpoints":[ + {"url":"/", + "description":"About this API"} + ]}) + + resp.body = json.dumps(r, indent=4) + + +app = application = falcon.API(request_type=JsonRequest, response_type=JsonResponse) +app.add_error_handler(ValueError, error_handler) +app.add_route("/", AboutResource()) + +path = Path("/home/arti/Videod") + +app.add_route("/movies", MoviesCollection(path)) +app.add_route("/movies/{movie}", MoviesResource(path)) diff --git a/api/karumedia/controllers.py b/api/karumedia/controllers.py new file mode 100644 index 0000000..113b6e2 --- /dev/null +++ b/api/karumedia/controllers.py @@ -0,0 +1,71 @@ +import re +import json +from falcon import HTTPInternalServerError, HTTP_201 +from .tools import TODOException + +movie_name_and_year = re.compile("(.*)\((.*)\)") + +class BaseResource(): + def __init__(self, path): + self.path = path + +class MoviesCollection(BaseResource): + + def on_get(self, req, resp): + movie_paths = [p for p in (self.path / 'Filmid').iterdir() if p.is_dir()] + movies = [] + for movie_path in movie_paths: + if not (movie_path / "metadata.json").exists(): + match = movie_name_and_year.match(movie_path.name) + if not match: + mobj = { + "title":movie_path.name, + "title_english":movie_path.name, + "title_long":movie_path.name, + "state":"ok" + } + else: + movie_name, movie_year = match.groups() + mobj = { + "title":movie_name, + "title_english":movie_name, + "title_long":movie_path.name, + "year":movie_year, + "state": "ok" + } + movies.append(mobj) + continue + with (movie_path / "metadata.json").open() as f: + metadata = json.loads(f.read()) + mobj = { + "title":metadata["title"], + "title_english":metadata["title"], + "title_long":movie_path.name, + "year":metadata["year"], + "runtime": int(metadata["runtime"]) // 100, + "imdb_code": metadata["imdb_id"], + "rating": metadata["rating"], + "summary": metadata["plot_outline"], + "synopsis": metadata["plot_outline"], + "mpa_rating": metadata["certification"], + "genres": metadata["genres"], + "state": "ok" + } + if metadata["plots"]: + mobj["description_full"] = metadata["plots"][0] + movies.append(mobj) + jobj = {"data":{ + "limit":len(movies), + "count":len(movies), + "movies":movies, + "page_number":1 + }, + "status": "ok", + "status_message": "query was successful" + } + resp.json = jobj + +class MoviesResource(BaseResource): + + def on_get(self, req, resp, movie): + resp.json = [{"path": self.path, "movie":movie}] diff --git a/api/karumedia/tools.py b/api/karumedia/tools.py new file mode 100644 index 0000000..224695d --- /dev/null +++ b/api/karumedia/tools.py @@ -0,0 +1,52 @@ + +import json + +from falcon import Request as FalconRequest +from falcon import Response as FalconResponse +from falcon.errors import HTTPBadRequest, HTTPMissingParam, HTTPError +import falcon.status_codes as status + + +class JsonRequest(FalconRequest): + + __slots__ = set(FalconRequest.__slots__ + ("_json", "_args")) + + + @property + def json(self): + if not hasattr(self, "_json"): + if not self.client_accepts_json: + raise falcon.HTTPUnsupportedMediaType( + 'This API only supports the JSON formated data') + try: + self._json = json.loads(self.stream.read().decode('utf8')) + except json.decoder.JSONDecodeError as err: + raise HTTPBadRequest("JSONDecodeError", str(err)) + return self._json + + +class JsonResponse(FalconResponse): + + __slots__ = set(FalconRequest.__slots__ + ("_json",)) + + + @property + def json(self): + return self._json + + @json.setter + def json(self, value): + self._json = value + self.body = json.dumps(value, indent=4) + +def error_handler(ex, req, resp, params): + raise HTTPBadRequest(type(ex).__name__, str(ex)) + +class TODOException(HTTPError): + + def __init__(self, **kwargs): + super(TODOException, self).__init__(status.HTTP_NOT_IMPLEMENTED, **kwargs) + + @property + def has_representation(self): + return False diff --git a/api/requirements.txt b/api/requirements.txt new file mode 100644 index 0000000..e32366e --- /dev/null +++ b/api/requirements.txt @@ -0,0 +1 @@ +falcon diff --git a/api/uwsgi.ini b/api/uwsgi.ini new file mode 100644 index 0000000..7211e4b --- /dev/null +++ b/api/uwsgi.ini @@ -0,0 +1,23 @@ +[uwsgi] +plugin=http +plugin=python3 +plugin=python +master=true +virtualenv=venv +processes=2 +need-app=true + +[dev] +ini=:uwsgi +http=:8080 +#static-map2=/=../web +#static-index=index.html +mount=/api/v1=wsgi.py +manage-script-name = true + + +[prod] +ini=:uwsgi +module=karumedia +chdir=/srv/http/karumedia +socket=/run/uwsgi/karumedia diff --git a/api/wsgi.py b/api/wsgi.py new file mode 100644 index 0000000..feeb003 --- /dev/null +++ b/api/wsgi.py @@ -0,0 +1 @@ +from karumedia import application