221d5cf Nov 8, 2014
168 lines (136 sloc) 5.57 KB
#!/usr/bin/env python
#license:unlicense (
"""Contains included user interfaces for Tvdb show selection.
A UI is a callback. A class, it's __init__ function takes two arguments:
- config, which is the Tvdb config dict, setup in
- log, which is Tvdb's logger instance (which uses the logging module). You can
call log.warning() etc
It must have a method "selectSeries", this is passed a list of dicts, each dict
contains the the keys "name" (human readable show name), and "sid" (the shows
ID as on For example:
[{'name': u'Lost', 'sid': u'73739'},
{'name': u'Lost Universe', 'sid': u'73181'}]
The "selectSeries" method must return the appropriate dict, or it can raise
tvdb_userabort (if the selection is aborted), tvdb_shownotfound (if the show
cannot be found).
A simple example callback, which returns a random series:
>>> import random
>>> from tvdb_ui import BaseUI
>>> class RandomUI(BaseUI):
... def selectSeries(self, allSeries):
... import random
... return random.choice(allSeries)
Then to use it..
>>> from tvdb_api import Tvdb
>>> t = Tvdb(custom_ui = RandomUI)
>>> random_matching_series = t['Lost']
>>> type(random_matching_series)
<class 'tvdb_api.Show'>
__author__ = "dbr/Ben"
__version__ = "1.10"
import sys
import logging
import warnings
from tvdb_exceptions import tvdb_userabort
IS_PY2 = sys.version_info[0] == 2
if IS_PY2:
user_input = raw_input
user_input = input
def log():
return logging.getLogger(__name__)
class BaseUI:
"""Default non-interactive UI, which auto-selects first results
def __init__(self, config, log = None):
self.config = config
if log is not None:
warnings.warn("the UI's log parameter is deprecated, instead use\n"
"use import logging; logging.getLogger('ui').info('blah')\n"
"The self.log attribute will be removed in the next version")
self.log = logging.getLogger(__name__)
def selectSeries(self, allSeries):
return allSeries[0]
class ConsoleUI(BaseUI):
"""Interactively allows the user to select a show from a console based UI
def _displaySeries(self, allSeries, limit = 6):
"""Helper function, lists series with corresponding ID
if limit is not None:
toshow = allSeries[:limit]
toshow = allSeries
print("TVDB Search Results:")
for i, cshow in enumerate(toshow):
i_show = i + 1 # Start at more human readable number 1 (not 0)
log().debug('Showing allSeries[%s], series %s)' % (i_show, allSeries[i]['seriesname']))
if i == 0:
extra = " (default)"
extra = ""
output = "%s -> %s [%s] #" % (
if IS_PY2:
print(output.encode("UTF-8", "ignore"))
def selectSeries(self, allSeries):
if len(allSeries) == 1:
# Single result, return it!
print("Automatically selecting only result")
return allSeries[0]
if self.config['select_first'] is True:
print("Automatically returning first search result")
return allSeries[0]
while True: # return breaks this loop
print("Enter choice (first number, return for default, 'all', ? for help):")
ans = user_input()
except KeyboardInterrupt:
raise tvdb_userabort("User aborted (^c keyboard interupt)")
except EOFError:
raise tvdb_userabort("User aborted (EOF received)")
log().debug('Got choice of: %s' % (ans))
selected_id = int(ans) - 1 # The human entered 1 as first result, not zero
except ValueError: # Input was not number
if len(ans.strip()) == 0:
# Default option
log().debug('Default option, returning first series')
return allSeries[0]
if ans == "q":
log().debug('Got quit command (q)')
raise tvdb_userabort("User aborted ('q' quit command)")
elif ans == "?":
print("## Help")
print("# Enter the number that corresponds to the correct show.")
print("# a - display all results")
print("# all - display all results")
print("# ? - this help")
print("# q - abort tvnamer")
print("# Press return with no input to select first result")
elif ans.lower() in ["a", "all"]:
self._displaySeries(allSeries, limit = None)
log().debug('Unknown keypress %s' % (ans))
log().debug('Trying to return ID: %d' % (selected_id))
return allSeries[selected_id]
except IndexError:
log().debug('Invalid show number entered!')
print("Invalid number (%s) selected!")