Skip to content

Commit

Permalink
Merge tvdb_ui and tvdb_exceptions into tvdb_api
Browse files Browse the repository at this point in the history
Modules remain for compatability for now
  • Loading branch information
dbr committed Aug 5, 2017
1 parent 8e07a8d commit 9166b9b
Show file tree
Hide file tree
Showing 3 changed files with 198 additions and 189 deletions.
192 changes: 187 additions & 5 deletions tvdb_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,6 @@
import logging
import datetime

from tvdb_ui import BaseUI, ConsoleUI
from tvdb_exceptions import (
tvdb_error, tvdb_shownotfound,
tvdb_seasonnotfound, tvdb_episodenotfound, tvdb_attributenotfound
)

"""Simple-to-use Python interface to The TVDB's API (thetvdb.com)
Expand All @@ -37,9 +32,11 @@
IS_PY2 = sys.version_info[0] == 2

if IS_PY2:
user_input = raw_input
from urllib import quote as url_quote
else:
from urllib.parse import quote as url_quote
user_input = input


if IS_PY2:
Expand All @@ -56,6 +53,191 @@ def log():
return logging.getLogger("tvdb_api")


## Exceptions

class tvdb_exception(Exception):
"""Any exception generated by tvdb_api
"""
pass

class tvdb_error(tvdb_exception):
"""An error with thetvdb.com (Cannot connect, for example)
"""
pass

class tvdb_userabort(tvdb_exception):
"""User aborted the interactive selection (via
the q command, ^c etc)
"""
pass

class tvdb_shownotfound(tvdb_exception):
"""Show cannot be found on thetvdb.com (non-existant show)
"""
pass

class tvdb_seasonnotfound(tvdb_exception):
"""Season cannot be found on thetvdb.com
"""
pass

class tvdb_episodenotfound(tvdb_exception):
"""Episode cannot be found on thetvdb.com
"""
pass

class tvdb_attributenotfound(tvdb_exception):
"""Raised if an episode does not have the requested
attribute (such as a episode name)
"""
pass


## UI

class BaseUI(object):
"""Base user interface for Tvdb show selection.
Selects first show.
A UI is a callback. A class, it's __init__ function takes two arguments:
- config, which is the Tvdb config dict, setup in tvdb_api.py
- log, which is Tvdb's logger instance (which uses the logging module). You can
call log.info() 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 thetvdb.com). 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'>
"""
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]
else:
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)"
else:
extra = ""

lid_map = dict((v, k) for (k, v) in self.config['langabbv_to_id'].items())

output = "%s -> %s [%s] # http://thetvdb.com/?tab=series&id=%s&lid=%s%s" % (
i_show,
cshow['seriesName'],
lid_map[cshow['lid']],
str(cshow['id']),
cshow['lid'],
extra
)
if IS_PY2:
print(output.encode("UTF-8", "ignore"))
else:
print(output)

def selectSeries(self, allSeries):
self._displaySeries(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
try:
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))
try:
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)
else:
log().debug('Unknown keypress %s' % (ans))
else:
log().debug('Trying to return ID: %d' % (selected_id))
try:
return allSeries[selected_id]
except IndexError:
log().debug('Invalid show number entered!')
print("Invalid number (%s) selected!")
self._displaySeries(allSeries)


## Main API

class ShowContainer(dict):
"""Simple dict that holds a series of Show instances
"""
Expand Down
43 changes: 8 additions & 35 deletions tvdb_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,42 +11,15 @@
__author__ = "dbr/Ben"
__version__ = "1.10"

import logging

__all__ = ["tvdb_error", "tvdb_userabort", "tvdb_shownotfound",
"tvdb_seasonnotfound", "tvdb_episodenotfound", "tvdb_attributenotfound"]

class tvdb_exception(Exception):
"""Any exception generated by tvdb_api
"""
pass

class tvdb_error(tvdb_exception):
"""An error with thetvdb.com (Cannot connect, for example)
"""
pass

class tvdb_userabort(tvdb_exception):
"""User aborted the interactive selection (via
the q command, ^c etc)
"""
pass

class tvdb_shownotfound(tvdb_exception):
"""Show cannot be found on thetvdb.com (non-existant show)
"""
pass

class tvdb_seasonnotfound(tvdb_exception):
"""Season cannot be found on thetvdb.com
"""
pass

class tvdb_episodenotfound(tvdb_exception):
"""Episode cannot be found on thetvdb.com
"""
pass
logging.getLogger(__name__).warning(
"tvdb_exceptions module is deprecated - use classes directly from tvdb_api instead")

class tvdb_attributenotfound(tvdb_exception):
"""Raised if an episode does not have the requested
attribute (such as a episode name)
"""
pass
from tvdb_api import (
tvdb_error, tvdb_userabort, tvdb_shownotfound,
tvdb_seasonnotfound, tvdb_episodenotfound, tvdb_attributenotfound
)
Loading

0 comments on commit 9166b9b

Please sign in to comment.