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 
11 import argparse
12 import sys
13 import os
14 import shutil
15 import shlex
16 from pprint import pprint
17 from configparser import ConfigParser
18 
19 
20 __title__ = "TheTVDatabaseV4"
21 __author__ = "Roland Ernst"
22 __version__ = "0.5.1"
23 
24 
25 def 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 
31 def _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 
57 def performSelfTest(args):
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 
107 def 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:
162  buildVersion()
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 
283 if __name__ == "__main__":
284  main()
ttvdb4.performSelfTest
def performSelfTest(args)
Definition: ttvdb4.py:57
ttvdb4.buildVersion
def buildVersion()
Definition: ttvdb4.py:41
ttvdb4.print_etree
def print_etree(etostr)
Definition: ttvdb4.py:25
print
static void print(const QList< uint > &raw_minimas, const QList< uint > &raw_maximas, const QList< float > &minimas, const QList< float > &maximas)
Definition: vbi608extractor.cpp:29
ttvdb4.main
def main()
Definition: ttvdb4.py:107
ttvdb4._parse_config
def _parse_config(config)
Definition: ttvdb4.py:31