585 changes: 585 additions & 0 deletions mythtv/bindings/python/ttvdbv4/locales.py

Large diffs are not rendered by default.

765 changes: 765 additions & 0 deletions mythtv/bindings/python/ttvdbv4/myth4ttvdbv4.py

Large diffs are not rendered by default.

545 changes: 545 additions & 0 deletions mythtv/bindings/python/ttvdbv4/ttvdbv4_api.py

Large diffs are not rendered by default.

66 changes: 66 additions & 0 deletions mythtv/bindings/python/ttvdbv4/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# -*- coding: UTF-8 -*-

from datetime import datetime
import sys


if sys.version_info[0] == 2:
from HTMLParser import HTMLParser
from StringIO import StringIO


class MLStripper(HTMLParser):
def __init__(self):
self.reset()
self.text = StringIO()

def handle_data(self, d):
self.text.write(d)

def get_data(self):
return self.text.getvalue()

else:
from io import StringIO
from html.parser import HTMLParser

class MLStripper(HTMLParser):
def __init__(self):
super().__init__()
self.reset()
self.strict = False
self.convert_charrefs= True
self.text = StringIO()

def handle_data(self, d):
self.text.write(d)

def get_data(self):
return self.text.getvalue()


def strip_tags(html):
if html is not None and html != "":
s = MLStripper()
s.feed(html)
return s.get_data()
else:
return ""


def convert_date(tstring):
if tstring is None or tstring == '':
return None
try:
return datetime.strptime(tstring, '%Y-%m-%d').date()
except(TypeError, ValueError):
return None


def convert_time(tstring):
if tstring is None or tstring == '':
return None
try:
return datetime.strptime(tstring, '%Y-%m-%d')
except(TypeError, ValueError):
return None
49 changes: 49 additions & 0 deletions mythtv/programs/scripts/metadata/Television/ttvdb4.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# -*- coding: UTF-8 -*-

# MythTV's television grabber 'ttvdb4.py':
# User modifiable configuration
# Hints:
# Option is separated from the value by a colon (:) or equal sign (=).
# Whitespace around the separator is ignored when the file is parsed.

[ConfigVersion]
TTVDBv4ConfigVersion = 1

[Authorization]
# Put your own key in if you have one:
# TTVDBv4Key =
# TTVDBv4Pin =

[Languages]
# Search order, preferred languages in ISO-639 Codes
# keys must be sortable in ascending order
Lang1 = eng
# Lang2 = deu
# Lang3 = fra
# Lang4 = it

[Preferences]
# Turn off fetching cast if query takes too long:
ShowPeople = 1

[Thresholds]
# Each entry is a floating point value of kind 'name similarity',
# with a maximum value of '1.00' considered as exact match.

# NameThreshold considers an exact match on:
NameThreshold = 0.99

# Start output if name similarity is bigger as:
ThresholdStart = 0.50

# Decrease of similarity which leads to stop the search:
ThresholdDecrease = 0.1

[TitleConversions]
# Used to convert titles for searching TTVDB
# MythTV Title = Original Title
# Marvel's Agents of S H I E L D = Marvel's Agents of S.H.I.E.L.D.

[InetrefConversions]
# Assign title to inetref for better search results
# Eleventh Hour = 83066
274 changes: 274 additions & 0 deletions mythtv/programs/scripts/metadata/Television/ttvdb4.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,274 @@
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-

import argparse
import sys
import os
import shutil
import shlex
from pprint import pprint
from configparser import ConfigParser


__title__ = "TheTVDatabaseV4"
__author__ = "Roland Ernst"
__version__ = "0.1.0"


def print_etree(etostr):
"""lxml.etree.tostring is a bytes object in python3, and a str in python2.
"""
if sys.version_info[0] == 2:
sys.stdout.write(etostr)
else:
sys.stdout.write(etostr.decode("utf-8"))


def _parse_config(config):
""" Parse the config read by ConfigParser."""
d = {}
for section in config.sections():
d[section] = {}
for k, v in config[section].items():
d[section][k] = v
return d


def buildVersion():
from lxml import etree
version = etree.XML(u'<grabber></grabber>')
etree.SubElement(version, "name").text = __title__
etree.SubElement(version, "author").text = __author__
etree.SubElement(version, "thumbnail").text = 'ttvdb.png'
etree.SubElement(version, "command").text = 'ttvdb4.py'
etree.SubElement(version, "type").text = 'television'
etree.SubElement(version, "description").text = \
'Search and downloads metadata from TheTVDB.com (API v4)'
etree.SubElement(version, "version").text = __version__
print_etree(etree.tostring(version, encoding='UTF-8', pretty_print=True,
xml_declaration=True))
sys.exit(0)


def performSelfTest(args):
err = 0
try:
import lxml
except:
err = 1
print("Failed to import python lxml library.")
try:
import requests
import requests_cache
except:
err = 1
print("Failed to import python-requests or python-request-cache library.")
try:
import MythTV
except:
err = 1
print("Failed to import MythTV bindings. Check your `configure` output "
"to make sure installation was not disabled due to external dependencies.")
try:
from MythTV.ttvdbv4.myth4ttvdbv4 import Myth4TTVDBv4
from MythTV.ttvdbv4 import ttvdbv4_api as ttvdb
if args.debug:
print("TheTVDBv4 Script Version: ", __version__)
print("TheTVDBv4-API version: ", ttvdb.MYTHTV_TTVDBV4_API_VERSION)
print("TheTVDBv4-API file location: ", ttvdb.__file__)
except:
err = 1
print("Failed to import Py TTVDB4 library. This should have been included "
"with the python MythTV bindings.")
try:
config = ConfigParser()
# preserve capital letters:
config.optionxform = str
config.read("./ttvdb4.ini", 'UTF-8')
config_dict = _parse_config(config)
config_version = config_dict['ConfigVersion']['TTVDBv4ConfigVersion']
if args.debug:
print("Config version of 'ttvdb4.ini': ", config_version)
except:
err = 1
print("Failed to read the ini file 'ttvdb4.ini'. Check your installation "
"if such a file exists alongside this grabber script.")
if not err:
print("Everything appears in order.")
sys.exit(err)


def main():
"""
Main executor for MythTV's ttvdb v4 grabber.
"""
description = '''A python script to retrieve metadata for TV-Shows.'''

parser = argparse.ArgumentParser(description=description)

parser.add_argument('-v', '--version', action="store_true",
dest="version", help="Display version and author")

parser.add_argument('-t', '--test', action="store_true", default=False,
dest="test", help="Perform self-test for dependencies.")

parser.add_argument('-l', "--language", metavar="LANGUAGE", default=u'en',
dest="language", help="Specify language for filtering.")

parser.add_argument('-a', "--area", metavar="COUNTRY", default=None,
dest="country", help="Specify country for custom data.")

group = parser.add_mutually_exclusive_group()

group.add_argument('-M', "--list", nargs=1,
dest="tvtitle", help="Get TV Shows matching 'tvtitle'.")

group.add_argument('-D', "--data", metavar=("INETREF","SEASON","EPISODE"),
nargs=3, type=int, dest="tvdata",
help="Get TV-Show data for 'inetref', 'season' and 'episode.")

group.add_argument('-C', "--collection", nargs=1, type=int, dest="collectionref",
help="Get Collection data for 'collectionref'.")

group.add_argument('-N', "--numbers", metavar=("ARG0","ARG1"), nargs=2, type=str,
dest="tvnumbers",
help="Get Season and Episode numbers: "
"'ARG0' can be ['series-title', 'inetref'] and "
"'ARG1': ['episode-title', 'iso-date-time'].")

parser.add_argument('--configure', nargs='?', type=str, default='ttvdb4.ini',
dest="inifile", help="Use local configuration file, defaults to "
"'~/.mythtv/'ttvdb4.ini'.")

parser.add_argument('--debug', action="store_true", default=False, dest="debug",
help="Disable caching and enable raw data output.")

parser.add_argument('--jsondebug', action="store_true", default=False, dest="jsondebug",
help="Enable raw json data output.")

parser.add_argument('--doctest', action="store_true", default=False,
dest="doctest", help="Run doctests.")

args = parser.parse_args()

if args.version:
buildVersion()

if args.test:
performSelfTest(args)

# assemble arguments
cmd_args = vars(args)
if args.debug:
print("cmd_args: ", cmd_args)

import requests
confdir = os.environ.get('MYTHCONFDIR', '')
if (not confdir) or (confdir == '/'):
confdir = os.environ.get('HOME', '')
if (not confdir) or (confdir == '/'):
print("Unable to find MythTV directory for grabber initialization.")
sys.exit(1)
confdir = os.path.join(confdir, '.mythtv')

if not args.debug and not args.doctest:
cachedir = os.path.join(confdir, 'cache')
if not os.path.exists(cachedir):
os.makedirs(cachedir)
if sys.version_info[0] == 2:
cache_name = os.path.join(cachedir, 'py2ttvdb4')
else:
cache_name = os.path.join(cachedir, 'py3ttvdb4')
import requests_cache
requests_cache.install_cache(cache_name, backend='sqlite', expire_after=3600)

# Add config from config file to cmd_args:
config_dict = {}
# read global config
try:
global_config = ConfigParser()
# preserve capital letters:
global_config.optionxform = str
global_config.read("./ttvdb4.ini", 'UTF-8')
config_dict = _parse_config(global_config)
if args.debug:
print("Global Config File parsed successfully.")
except KeyError:
if args.debug:
print("Parsing Global Config File failed.")
# read local config, which overrides the global one
if args.inifile:
local_config_file = os.path.join(confdir, 'ttvdb4.ini')
if os.path.isfile(local_config_file):
try:
local_config = ConfigParser()
# preserve capital letters:
local_config.optionxform = str
local_config.read(local_config_file, 'UTF-8')
for section in local_config.sections():
for k,v in local_config[section].items():
config_dict[section][k] = v
if args.debug:
print("Local Config File parsed successfully.")
except KeyError:
if args.debug:
print("Parsing Local Config File failed.")
else:
# create local config with values from global config
shutil.copy("./ttvdb4.ini", confdir)
if args.debug:
print("Local config file 'ttvdbv4.ini' created.")
cmd_args["config"] = config_dict
if args.debug:
print("Using this configuration:")
pprint(cmd_args["config"])

if args.doctest:
import doctest
try:
with open("ttvdb4_doctests") as f:
if sys.version_info[0] == 2:
dtests = b"".join(f.readlines()).decode('utf-8')
else:
dtests = "".join(f.readlines())
main.__doc__ += dtests
except IOError:
pass
# perhaps try optionflags=doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE
return doctest.testmod(verbose=args.debug, optionflags=doctest.ELLIPSIS)

# finally, grab the damn metadata
try:
from MythTV.ttvdbv4.myth4ttvdbv4 import Myth4TTVDBv4
from lxml import etree
mttvdb = Myth4TTVDBv4(**cmd_args)
if args.tvdata:
# option -D inetref season episode
mttvdb.buildSingle()
elif args.collectionref:
# option -C inetref
mttvdb.buildCollection()
elif args.tvtitle:
# option -M title
mttvdb.buildList()
elif args.tvnumbers:
# option -N title subtitle
# option -N inetref subtitle
mttvdb.buildNumbers()
else:
sys.stdout.write('ERROR: This script must be called with one of '
'[-t, -v, -C, -D, -M, -N] switches.')
sys.exit(1)

print_etree(etree.tostring(mttvdb.tree, encoding='UTF-8', pretty_print=True,
xml_declaration=True))
except:
if args.debug:
raise
sys.stdout.write('ERROR: ' + str(sys.exc_info()[0]) + ' : '
+ str(sys.exc_info()[1]) + '\n')
sys.exit(1)


if __name__ == "__main__":
main()
563 changes: 563 additions & 0 deletions mythtv/programs/scripts/metadata/Television/ttvdb4_doctests

Large diffs are not rendered by default.