--- /dev/null
+#! /usr/bin/env python
+# Retrieve details for a series from tvdb.
+#
+# Required options:
+# --tvdb-key XX
+# Option important options:
+# --tvdb-languages - a csv of 2-character languages to use such as en,nl
+#
+# TV information and images are provided by TheTVDB.com, but we are
+# not endorsed or certified by TheTVDB.com or its affiliates
+#
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+import os,sys
+import json
+import logging
+import requests
+
+
+
+def get_capabilities():
+ return {
+ "name": "tv_meta_tvdb",
+ "version": "0.1",
+ "description": "Grab movie details from TVDB.",
+ "supports_tv": True,
+ "supports_movie": False,
+ "required_config" : {
+ "tvdb-key" : "string"
+ }
+ }
+
+
+
+class Tvdb(object):
+ """Basic tvdb wrapper.
+
+The class assumes you pass a key (from registering at tvdb.com).
+Exceptions are thrown to indicate data could not be retrieved.
+"""
+ def __init__(self, args):
+ logging.info(args)
+ for arg in (["key"]):
+ if args is None or arg not in args or args[arg] is None or args[arg] == "":
+ logging.critical("Need a tvdb-" + arg)
+ raise RuntimeError("Need a tvdb-" + arg);
+
+ self.languages = "en"
+ # 2 character language code. At time of writing, valid languages on the server are:
+ # ['da', 'fi', 'nl', 'de', 'it', 'es', 'fr', 'pl', 'hu', 'el', 'tr', 'ru', 'he', 'ja', 'pt', 'zh', 'cs', 'sl', 'hr', 'ko', 'en', 'sv', 'no']
+ # We accept a csv of languages.
+ #
+ # In general, it seems best to include "en" otherwise you only
+ # get fanart that is specifically tagged for your language (there
+ # does not seem to be a "all languages" option on the images).
+ if 'languages' in args and args["languages"] is not None:
+ self.languages = args["languages"]
+
+ self.auth = None
+ self.session = requests.Session()
+ self.session.headers = self._get_headers()
+ self.timeout = 10 # Timeout in seconds
+ self.apikey = args["key"]
+ self.base_url = "https://api.thetvdb.com/"
+ self.auth = self._get_auth()
+
+ def _get_headers(self):
+ headers = { 'Content-Type': 'application/json',
+ 'Accept-Language': self.languages }
+ if self.auth:
+ headers['Authorization'] = 'Bearer ' + self.auth
+ return headers
+
+ def _get_auth(self):
+ r = self.session.post(
+ self.base_url + 'login',
+ timeout = self.timeout,
+ data=json.dumps({'apikey' : self.apikey}))
+ token = r.json()['token']
+ self.session.headers.update({'Authorization' : 'Bearer ' + token})
+ return token
+
+ def get_tvdbid(self, title):
+ """Return episode tvdb id"""
+ r=self.session.get(
+ self.base_url + 'search/series',
+ timeout = self.timeout,
+ params={'name': title})
+ return r.json()['data'][0]['id']
+
+ def _get_art(self, title = None, tvdbid = None, artworkType = 'fanart'):
+ if tvdbid is None:
+ tvdbid = self.get_tvdbid(title)
+
+ logging.debug("%s type %s" % (tvdbid, type(tvdbid)))
+ url = self.base_url + 'series/' + str(tvdbid) + '/images/query'
+ logging.debug("Searching %s with id %s and keytype %s" % (url, tvdbid, artworkType))
+ r=self.session.get(
+ url,
+ timeout = self.timeout,
+ params={'keyType': artworkType})
+ r.raise_for_status()
+ filename = r.json()['data'][0]['fileName']
+ if not filename.startswith("http"):
+ filename = "https://thetvdb.com/banners/" + filename
+ return filename
+
+ def get_fanart(self, title = None, tvdbid = None):
+ return self._get_art(title = title, tvdbid = tvdbid, artworkType = 'fanart')
+
+ def get_poster(self, title = None, tvdbid = None):
+ return self._get_art(title = title, tvdbid = tvdbid, artworkType = 'poster')
+
+
+class Tv_meta_tvdb(object):
+
+ def __init__(self, args):
+ self.tvdb = Tvdb(args)
+
+ def fetch_details(self, args):
+ logging.debug("Fetching with details %s " % args);
+ title = args["title"]
+ year = args["year"]
+
+ if title is None:
+ logging.critical("Need a title");
+ raise RuntimeError("Need a title");
+
+ tvdbid = None
+ query = title
+
+ # try with "title (year)".
+ if year is not None:
+ query = query + " (%s)" % year
+ try:
+ tvdbid = self.tvdb.get_tvdbid(query)
+ except:
+ pass
+
+ if tvdbid is None:
+ try:
+ tvdbid = self.tvdb.get_tvdbid(title)
+ except:
+ logging.error("Could not find any matching episode");
+ raise LookupError("Could not find match for " + title);
+
+ poster = fanart = None
+ # We don't want failure to process one image to affect the other.
+ try:
+ poster = self.tvdb.get_poster(tvdbid = tvdbid)
+ except Exception:
+ pass
+
+ try:
+ fanart = self.tvdb.get_fanart(tvdbid = tvdbid)
+ except:
+ pass
+
+ logging.debug("poster=%s fanart=%s title=%s year=%s" % (poster, fanart, title, year))
+ return {"poster": poster, "fanart": fanart}
+
+if __name__ == '__main__':
+ def process(argv):
+ from optparse import OptionParser
+ optp = OptionParser()
+ optp.add_option('--tvdb-key', default=None,
+ help='Specify authorization key.')
+ optp.add_option('--tvdb-languages', default=None,
+ help='Specify tvdb language codes separated by commas, such as "en,sv".')
+ optp.add_option('--title', default=None,
+ help='Title to search for.')
+ optp.add_option('--year', default=None, type="int",
+ help='Year to search for.')
+ optp.add_option('--capabilities', default=None, action="store_true",
+ help='Display program capabilities (for PVR grabber)')
+ optp.add_option('--debug', default=None, action="store_true",
+ help='Enable debug.')
+ (opts, args) = optp.parse_args(argv)
+ if (opts.debug):
+ logging.root.setLevel(logging.DEBUG)
+
+ if opts.capabilities:
+ # Output a program-parseable format.
+ print(json.dumps(get_capabilities()))
+ return 0
+
+ if opts.title is None or opts.tvdb_key is None:
+ print("Need a title to search for and a tvdb-key.")
+ sys.exit(1)
+
+ grabber = Tv_meta_tvdb({
+ "key" : opts.tvdb_key,
+ "languages": opts.tvdb_languages,
+ })
+ print(json.dumps(grabber.fetch_details({
+ "title": opts.title,
+ "year" : opts.year,
+ })))
+
+ try:
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s:%(levelname)s:%(module)s:%(message)s')
+ sys.exit(process(sys.argv))
+ except KeyboardInterrupt: pass
+ except (RuntimeError,LookupError) as err:
+ logging.info("Got exception: " + str(err))
+ sys.exit(1)