MythTV master
ttvdb4.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2# -*- coding: UTF-8 -*-
3
4# ----------------------------------------------------
5# Purpose: MythTV Python Bindings for TheTVDB v4 API
6# Copyright: (c) 2021 Roland Ernst
7# License: GPL v2 or later, see LICENSE for details
8# ----------------------------------------------------
9
10
11import argparse
12import sys
13import os
14import shutil
15import shlex
16from pprint import pprint
17from configparser import ConfigParser
18
19
20__title__ = "TheTVDatabaseV4"
21__author__ = "Roland Ernst"
22__version__ = "0.5.1"
23
24
25def print_etree(etostr):
26 """lxml.etree.tostring is a bytes object in python3, and a str in python2.
27 """
28 sys.stdout.write(etostr.decode("utf-8"))
29
30
31def _parse_config(config):
32 """ Parse the config read by ConfigParser."""
33 d = {}
34 for section in config.sections():
35 d[section] = {}
36 for k, v in config[section].items():
37 d[section][k] = v
38 return d
39
40
42 from lxml import etree
43 version = etree.XML(u'<grabber></grabber>')
44 etree.SubElement(version, "name").text = __title__
45 etree.SubElement(version, "author").text = __author__
46 etree.SubElement(version, "thumbnail").text = 'ttvdb.png'
47 etree.SubElement(version, "command").text = 'ttvdb4.py'
48 etree.SubElement(version, "type").text = 'television'
49 etree.SubElement(version, "description").text = \
50 'Search and downloads metadata from TheTVDB.com (API v4)'
51 etree.SubElement(version, "version").text = __version__
52 print_etree(etree.tostring(version, encoding='UTF-8', pretty_print=True,
53 xml_declaration=True))
54 sys.exit(0)
55
56
58 err = 0
59 try:
60 import lxml
61 except:
62 err = 1
63 print("Failed to import python lxml library.")
64 try:
65 import requests
66 import requests_cache
67 except:
68 err = 1
69 print("Failed to import python-requests or python-request-cache library.")
70 try:
71 import MythTV
72 except:
73 err = 1
74 print("Failed to import MythTV bindings. Check your `configure` output "
75 "to make sure installation was not disabled due to external dependencies.")
76 try:
77 from MythTV.ttvdbv4.myth4ttvdbv4 import Myth4TTVDBv4
78 from MythTV.ttvdbv4 import ttvdbv4_api as ttvdb
79 if args.debug:
80 print("TheTVDBv4 Script Version: ", __version__)
81 print("TheTVDBv4-API version: ", ttvdb.MYTHTV_TTVDBV4_API_VERSION)
82 print("TheTVDBv4-API file location: ", ttvdb.__file__)
83 except:
84 err = 1
85 print("Failed to import Py TTVDB4 library. This should have been included "
86 "with the python MythTV bindings.")
87 try:
88 inipath = os.path.abspath(os.path.dirname(sys.argv[0]))
89 inifile = os.path.join(inipath, "ttvdb4.ini")
90 config = ConfigParser()
91 # preserve capital letters:
92 config.optionxform = str
93 config.read(inifile, 'UTF-8')
94 config_dict = _parse_config(config)
95 config_version = config_dict['ConfigVersion']['TTVDBv4ConfigVersion']
96 if args.debug:
97 print("Config version of 'ttvdb4.ini': ", config_version)
98 except:
99 err = 1
100 print("Failed to read the ini file 'ttvdb4.ini'. Check your installation "
101 "if such a file exists alongside this grabber script.")
102 if not err:
103 print("Everything appears in order.")
104 sys.exit(err)
105
106
107def main():
108 """
109 Main executor for MythTV's ttvdb v4 grabber.
110 """
111 description = '''A python script to retrieve metadata for TV-Shows.'''
112
113 parser = argparse.ArgumentParser(description=description)
114
115 parser.add_argument('-v', '--version', action="store_true",
116 dest="version", help="Display version and author")
117
118 parser.add_argument('-t', '--test', action="store_true", default=False,
119 dest="test", help="Perform self-test for dependencies.")
120
121 parser.add_argument('-l', "--language", metavar="LANGUAGE", default=u'en',
122 dest="language", help="Specify language for filtering.")
123
124 parser.add_argument('-a', "--area", metavar="COUNTRY", default=None,
125 dest="country", help="Specify country for custom data.")
126
127 group = parser.add_mutually_exclusive_group()
128
129 group.add_argument('-M', "--list", nargs=1,
130 dest="tvtitle", help="Get TV Shows matching 'tvtitle'.")
131
132 group.add_argument('-D', "--data", metavar=("INETREF","SEASON","EPISODE"),
133 nargs=3, type=int, dest="tvdata",
134 help="Get TV-Show data for 'inetref', 'season' and 'episode.")
135
136 group.add_argument('-C', "--collection", nargs=1, type=int, dest="collectionref",
137 help="Get Collection data for 'collectionref'.")
138
139 group.add_argument('-N', "--numbers", metavar=("ARG0","ARG1"), nargs=2, type=str,
140 dest="tvnumbers",
141 help="Get Season and Episode numbers: "
142 "'ARG0' can be ['series-title', 'inetref'] and "
143 "'ARG1': ['episode-title', 'iso-date-time'].")
144
145 parser.add_argument('--configure', nargs='?', type=str, default='ttvdb4.ini',
146 dest="inifile", help="Use local configuration file, defaults to "
147 "'~/.mythtv/'ttvdb4.ini'.")
148
149 parser.add_argument('--debug', action="store_true", default=False, dest="debug",
150 help="Disable caching and enable raw data output.")
151
152 parser.add_argument('--jsondebug', action="store_true", default=False, dest="jsondebug",
153 help="Enable raw json data output.")
154
155 parser.add_argument('--doctest', action="store_true", default=False, dest="doctest",
156 help="Run doctests. You need to change to the folder where ttvdb4.py "
157 "is installed.")
158
159 args = parser.parse_args()
160
161 if args.version:
163
164 if args.test:
165 performSelfTest(args)
166
167 # assemble arguments
168 cmd_args = vars(args)
169 if args.debug:
170 print("0000: Init: cmd_args: ", cmd_args)
171
172 # read the ini files
173 import requests
174 confdir = os.environ.get('MYTHCONFDIR', '')
175 if (not confdir) or (confdir == '/'):
176 confdir = os.environ.get('HOME', '')
177 if (not confdir) or (confdir == '/'):
178 print("Unable to find MythTV directory for grabber initialization.")
179 sys.exit(1)
180 confdir = os.path.join(confdir, '.mythtv')
181
182 cachedir = os.path.join(confdir, 'cache')
183 if not os.path.exists(cachedir):
184 os.makedirs(cachedir)
185
186 if not args.debug and not args.doctest:
187 cache_name = os.path.join(cachedir, 'py3ttvdb4')
188 import requests_cache
189 requests_cache.install_cache(cache_name, backend='sqlite', expire_after=3600)
190
191 # Add config from config file to cmd_args:
192 config_dict = {}
193 # read global config
194 inipath = os.path.abspath(os.path.dirname(sys.argv[0]))
195 inifile = os.path.join(inipath, "ttvdb4.ini")
196 try:
197 global_config = ConfigParser()
198 # preserve capital letters:
199 global_config.optionxform = str
200 global_config.read(inifile, 'UTF-8')
201 config_dict = _parse_config(global_config)
202 if args.debug:
203 print("0000: Init: Global Config File parsed successfully.")
204 except KeyError:
205 if args.debug:
206 print("0000: Init: Parsing Global Config File failed.")
207 # read local config, which overrides the global one
208 if args.inifile:
209 local_config_file = os.path.join(confdir, args.inifile)
210 if os.path.isfile(local_config_file):
211 try:
212 local_config = ConfigParser()
213 # preserve capital letters:
214 local_config.optionxform = str
215 local_config.read(local_config_file, 'UTF-8')
216 for section in local_config.sections():
217 for k,v in local_config[section].items():
218 config_dict[section][k] = v
219 if args.debug:
220 print("0000: Init: Local Config File '%s' parsed successfully."
221 % local_config_file)
222 except KeyError:
223 if args.debug:
224 print("0000: Init: Parsing Local Config File failed.")
225 else:
226 # create local config with values from global config
227 shutil.copy(inifile, local_config_file)
228 if args.debug:
229 print("0000: Init: Local config file '%s' created." % local_config_file)
230
231 # storage for authentication bearer token
232 config_dict['auth_file'] = os.path.join(cachedir, "py3ttvdb4_bearer.pickle")
233
234 cmd_args["config"] = config_dict
235 if args.debug:
236 print("0000: Init: Using this configuration:")
237 pprint(cmd_args["config"])
238
239 if args.doctest:
240 import doctest
241 try:
242 with open("ttvdb4_doctests") as f:
243 dtests = "".join(f.readlines())
244 main.__doc__ += dtests
245 except IOError:
246 pass
247 # perhaps try optionflags=doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE
248 return doctest.testmod(verbose=args.debug, optionflags=doctest.ELLIPSIS)
249
250 # finally, grab the damn metadata
251 try:
252 from MythTV.ttvdbv4.myth4ttvdbv4 import Myth4TTVDBv4
253 from lxml import etree
254 mttvdb = Myth4TTVDBv4(**cmd_args)
255 if args.tvdata:
256 # option -D inetref season episode
257 mttvdb.buildSingle()
258 elif args.collectionref:
259 # option -C inetref
260 mttvdb.buildCollection()
261 elif args.tvtitle:
262 # option -M title
263 mttvdb.buildList()
264 elif args.tvnumbers:
265 # option -N title subtitle
266 # option -N inetref subtitle
267 mttvdb.buildNumbers()
268 else:
269 sys.stdout.write('ERROR: This script must be called with one of '
270 '[-t, -v, -C, -D, -M, -N] switches.')
271 sys.exit(1)
272
273 print_etree(etree.tostring(mttvdb.tree, encoding='UTF-8', pretty_print=True,
274 xml_declaration=True))
275 except:
276 if args.debug:
277 raise
278 sys.stdout.write('ERROR: ' + str(sys.exc_info()[0]) + ' : '
279 + str(sys.exc_info()[1]) + '\n')
280 sys.exit(1)
281
282
283if __name__ == "__main__":
284 main()
def main()
Definition: ttvdb4.py:107
def buildVersion()
Definition: ttvdb4.py:41
def _parse_config(config)
Definition: ttvdb4.py:31
def print_etree(etostr)
Definition: ttvdb4.py:25
def performSelfTest(args)
Definition: ttvdb4.py:57
static void print(const QList< uint > &raw_minimas, const QList< uint > &raw_maximas, const QList< float > &minimas, const QList< float > &maximas)