--- /dev/null
+tvh-json.py
+=================================
+(c) 2017 Tvheadend Foundation CIC
+
+The json import / export tool written in the python language.
+
+
+Background
+----------
+
+TVHeadend uses internal database called idnode. This database uses
+unique identifier (UUID) for each entry. The entries may be linked
+(thus some nodes may refer to another). The each entry belongs
+to a class which defines the meta data (type, description).
+
+
+Environment variables
+---------------------
+
+Name | Description
+-----------------------------------------------------------
+TVH_URL_API | URL like http://localhost:9981/api
+TVH_USER | username for HTTP API
+TVH_PASS | password for HTTP API
+
+
+Command 'get'
+-------------
+
+This is basic command which allows the elementary operation through
+the HTTP API. Basically, almost all other commands uses this internally.
+
+Arguments:
+* path
+* json string (arguments)
+
+
+Command 'pathlist'
+------------------
+
+Returns all path names.
+
+
+Command 'classes'
+-----------------
+
+Returns all class names.
+
+
+Command 'import'
+----------------
+
+Imports the json data and updates the internal structure in TVHeadend's
+database. The structure must be identical to data printed with the 'export'
+command.
+
+Arguments:
+* uuid
+* filename (optional - otherwise standard input is used)
+
+
+Command 'export'
+----------------
+
+Exports the json data from TVHeadend's database.
+
+Arguments:
+* uuid
+
+
+Command 'exportcls'
+-------------------
+
+Exports the json data from TVHeadend's database for all idnodes which
+belongs to the specified class.
+
+Arguments:
+* class
+
+
+Examples:
+---------
+
+`TVH_USER=admin TVH_PASS=admin ./tvh-json.py exportcls dvb_mux_dvbs`
+`TVH_USER=admin TVH_PASS=admin ./tvh-json.py export 6dbdb849bd8eae9c1fecf684f773baca > a.json`
+`TVH_USER=admin TVH_PASS=admin ./tvh-json.py import 6dbdb849bd8eae9c1fecf684f773baca < a.json`
#! /usr/bin/env python
#
-# TVH bintray tool, compatible with both python2 and python3
+# TVH json import/export tool, compatible with both python2 and python3
#
import os
try:
# Python 3
import urllib.request as urllib
- from urllib.parse import urlencode
+ from urllib.parse import urlencode, quote
except ImportError:
# Python 2
import urllib2 as urllib
- from urllib import urlencode
+ from urllib import urlencode, quote
-def env(key):
+def env(key, deflt):
if key in os.environ: return os.environ[key]
- return None
+ return deflt
DEBUG=False
-TVH_API=env('TVH_API_URL') or 'http://localhost:9981/api'
-TVH_USER=env('TVH_USER')
-TVH_PASS=env('TVH_PASS')
-
-PACKAGE_DESC='Tvheadend is a TV streaming server and recorder for Linux, FreeBSD and Android'
+TVH_API=env('TVH_API_URL', 'http://localhost:9981/api')
+TVH_USER=env('TVH_USER', None)
+TVH_PASS=env('TVH_PASS', None)
class Response(object):
def __init__(self, response):
if binary:
content_type = 'application/binary'
else:
- data = data and urlencode(data) or None
+ data = data and urlencode(data).encode('utf-8') or None
opener = self.opener()
path = self._path
if path[0] != '/': path = '/' + path
def post(self, data):
return self._push(data, method='POST')
-def do_get(*args):
+def do_get0(*args):
if len(args) < 1: error(1, 'get [path] [json_query]')
path = args[0]
- query = len(args) > 1 and json.loads(args[1]) or None
+ query = None
+ if len(args) > 1:
+ query = args[1]
+ if type(query) != type({}):
+ query = json.loads(query.decode('utf-8'))
if query:
for q in query:
r = query[q]
resp = TVHeadend(path).post(query)
if resp.code != 200 and resp.code != 201:
error(10, 'HTTP ERROR "%s" %s %s', resp.url, resp.code, resp.reason)
- if type(resp.body) == type({}) or type(resp.body) == type([]):
- print(json.dumps(resp.body, indent=4, separators=(',', ': ')))
+ return resp.body
+
+def do_get(*args):
+ body = do_get0(*args)
+ if type(body) == type({}) or type(body) == type([]):
+ print(json.dumps(body, indent=4, separators=(',', ': ')))
+ else:
+ print(body)
+
+def do_export(*args):
+ if len(args) < 1: error(1, 'get [uuid]')
+ body = do_get0('raw/export', {'uuid':args[0]})
+ if type(body) != type({}):
+ error(11, 'Unknown data')
+ if 'entries' in body:
+ body = body['entries']
+ if len(body) == 1:
+ body = body[0]
+ if not 'uuid' in body:
+ body['uuid'] = args[0].strip()
+ print(json.dumps(body, indent=4, separators=(',', ': ')))
+
+def do_exportcls(*args):
+ if len(args) < 1: error(1, 'get [class]')
+ body = do_get0('raw/export', {'class':args[0]})
+ if type(body) != type({}) and type(body) != type([]):
+ error(11, 'Unknown data')
+ if 'entries' in body:
+ body = body['entries']
+ if len(body) == 1:
+ body = body[0]
+ print(json.dumps(body, indent=4, separators=(',', ': ')))
+
+def do_import(*args):
+ if len(args) < 1:
+ jdata = sys.stdin.read()
else:
- print(resp.body)
+ fp = open(args[0])
+ jdata = fp.read()
+ fp.close()
+ jdata = json.loads(jdata.decode('utf-8'))
+ body = do_get0('raw/import', {'node':jdata})
+ if body and type(body) != type({}):
+ error(11, 'Unknown data / response')
+
+def do_paths(*args):
+ do_get('pathlist')
+
+def do_classes(*args):
+ do_get('classes')
+
+def do_unknown(*args):
+ r = 'Please, specify a valid command:\n'
+ for n in globals():
+ if n.startswith('do_') and n != 'do_unknown':
+ r += ' ' + n[3:] + '\n'
+ error(1, r[:-1])
def main(argv):
global DEBUG
if not TVH_USER or not TVH_PASS:
error(2, 'No credentals')
- if argv[1] == '--debug':
+ if len(argv) > 1 and argv[1] == '--debug':
DEBUG=1
argv.pop(0)
cmd = 'do_' + (len(argv) > 1 and argv[1] or 'unknown')