diff --git a/tivodb b/tivodb.py similarity index 100% rename from tivodb rename to tivodb.py diff --git a/tivomirror b/tivomirror.py similarity index 79% rename from tivomirror rename to tivomirror.py index 7d5a051..c469a22 100755 --- a/tivomirror +++ b/tivomirror.py @@ -1,4 +1,5 @@ #!/usr/local/bin/python +# -*- coding: utf8 -*- # Stefans Script, um die Sendungen vom Tivo runterzuladen und in MPEG4 # zu transkodieren. @@ -18,6 +19,7 @@ import functools import logging import logging.handlers import os +import pytz import re import requests import signal @@ -48,46 +50,51 @@ headers.update( } ) -arset = dict() +class IncludeShow: + includes = dict() + + def __init__(self, title, short=None): + self.short = short + self.title = title + self.timestamp = False + self.includes[title] = self -includes = dict() -includes['Anthony Bourdain Parts Unknown'] = 1 -includes['Better Call Saul'] = 1 -includes['Brooklyn Nine-Nine'] = 1 -includes['Bull'] = 1 -includes['College Football'] = 1 -includes['Conan'] = 1 -includes["Dirk Gently's Holistic Detective Agency"] = 1 -includes['The Expanse'] = 1 -includes['Family Guy'] = 1 -includes['Full Frontal With Samantha Bee'] = 1 -includes['Hot in Cleveland'] = 1 -includes["How It's Made"] = 1 -includes["How Do They Do It?"] = 1 -includes["How We Got to Now With Steven Johnson"] = 1 -includes['Inside Amy Schumer'] = 1 -includes['Join or Die With Craig Ferguson'] = 1 -includes['Last Week Tonight With John Oliver'] = 1 -includes['Louie'] = 1 -includes['Mad Men'] = 1 -includes['Modern Family'] = 1 -includes['MythBusters'] = 1 -includes['MythBusters: The Search'] = 1 -includes['NCIS'] = 1 -includes['NCIS: New Orleans'] = 1 -#includes['NFL Football'] = 1 -includes['Person of Interest'] = 1 -includes['Saturday Night Live'] = 1 -includes['Sesame Street'] = 1 -includes["Somebody's Gotta Do It With Mike Rowe"] = 1 -includes['StarTalk'] = 1 -includes['The Big Bang Theory'] = 1 -includes['The Daily Show With Trevor Noah'] = 1 -includes['The Late Show With Stephen Colbert'] = 1 -#includes['The Late Late Show With James Corden'] = 1 -includes['The Muppets'] = 1 -includes['The X-Files'] = 1 -#includes['The Tonight Show Starring Jimmy Fallon'] = 1 +IncludeShow('Splash and Bubbles') +IncludeShow('Angie Tribeca') +IncludeShow('Anthony Bourdain Parts Unknown') +IncludeShow('Better Call Saul') +IncludeShow('Brooklyn Nine-Nine') +IncludeShow('Bull') +IncludeShow('College Football') +IncludeShow('Conan') +IncludeShow("Dirk Gently's Holistic Detective Agency", short='Dirk Gently') +IncludeShow('The Expanse') +IncludeShow('Family Guy') +IncludeShow('Full Frontal With Samantha Bee', short='Full Frontal') +IncludeShow("How It's Made") +IncludeShow("How Do They Do It?") +IncludeShow("How We Got to Now With Steven Johnson") +IncludeShow('Inside Amy Schumer') +IncludeShow('Join or Die With Craig Ferguson') +IncludeShow('Last Week Tonight With John Oliver', short='John Oliver') +IncludeShow('Louie') +IncludeShow('Modern Family') +IncludeShow('MythBusters') +IncludeShow('NCIS') +IncludeShow('NCIS: New Orleans') +#IncludeShow('NFL Football') +IncludeShow('Person of Interest') +IncludeShow('Saturday Night Live', short='SNL') +IncludeShow('Sesame Street') +IncludeShow("Somebody's Gotta Do It With Mike Rowe") +IncludeShow('StarTalk') +IncludeShow('The Big Bang Theory') +IncludeShow('The Daily Show With Trevor Noah', short='Daily Show') +IncludeShow('The Late Show With Stephen Colbert', short='Colbert') +#IncludeShow('The Late Late Show With James Corden') +IncludeShow('The Muppets') +IncludeShow('The X-Files') +#IncludeShow('The Tonight Show Starring Jimmy Fallon') @@ -113,6 +120,17 @@ session.auth = requests.auth.HTTPDigestAuth("tivo", mak) session.keep_alive = False session.proxies = proxies + +def roundTime(dt=None, roundTo=60): + """ + http://stackoverflow.com/questions/3463930/how-to-round-the-minute-of-a-datetime-object-python + """ + if dt == None : dt = datetime.datetime.now() + seconds = (dt.replace(tzinfo=None) - dt.min).seconds + rounding = (seconds+roundTo/2) // roundTo * roundTo + return dt + datetime.timedelta(0,rounding-seconds,-dt.microsecond) + + class TimeoutError(Exception): pass @@ -169,21 +187,18 @@ class TivoItem: self.episodeNumber = getTagText(i, "EpisodeNumber") self.description = trimDescription(getTagText(i, "Description")) d = getTagText(i, "CaptureDate") - self.date = datetime.datetime.utcfromtimestamp(int(d, 16)) - self.time = int(d, base=0) + self.date = datetime.datetime.fromtimestamp(int(d, 16), pytz.utc) + est = pytz.timezone('US/Eastern') + eastern = roundTime(self.date, 15*60).astimezone(est) self.datestr = self.date.strftime("%Y%m%d-%H%M") + self.shortdate = eastern.strftime("%m%d-%H%M") self.url = getTagText(i, "Url") self.url = self.url + "&Format=video/x-tivo-mpeg-ts" self.inprogress = getTagText(i, "InProgress") self.available = getTagText(i, "Available") self.sourcesize = int(getTagText(i, "SourceSize")) self.highdef = getTagText(i, "HighDefinition") - self.ar = 43 self.unique = True - if arset.has_key(self.title): - self.ar = arset[self.title] - elif self.highdef == "Yes": - self.ar = "hd" if ignoreepisodetitle: self.episode = self.datestr if self.episode == "": @@ -211,6 +226,22 @@ class TivoItem: self.name = self.name.encode("utf-8"); self.dir = self.dir.encode("utf-8"); self.file = self.file.encode("utf-8"); + def getPath(self, options): + title = self.title + if options.short: + title = options.short + if self.episodeNumber and self.episodeNumber != u'0': + en = int(self.episodeNumber) + if en >= 100: + name = "%s S%02dE%02d %s" % (title, en / 100, en % 100, self.episode) + else: + name = "%s E%s %s" % (title, self.episodeNumber, self.episode) + elif self.unique: + name = "%s - %s" % (title, self.episode) + else: + name = "%s - %s %s" % (title, self.shortdate, self.episode) + path = "%s/%s" % (self.dir, re.sub("[:/]", "-", name)) + return path.encode("utf-8"); def __str__(self): return repr(self.title) @@ -351,6 +382,7 @@ def download_item(item, mak, target): url = item.url #url = re.sub("tivo.lassitu.de:80", "wavehh.lassitu.de:30080", url) logger.info("--- downloading \"%s\"" % (url)) + logger.info(" {}".format(target)) start = time.time() r = session.get(url, stream=True, verify=False, proxies=proxies, headers=headers) r.raise_for_status() @@ -419,8 +451,8 @@ def download_item(item, mak, target): raise TivoException("downloaded file is too small") -def download_decode(item, mak): - target = "%s.mpg" % item.file +def download_decode(item, options, mak): + target = "%s.mpg" % item.getPath(options) try: os.makedirs(item.dir) except OSError: @@ -440,11 +472,11 @@ def download_decode(item, mak): logger.error("Problem setting timestamp: %" % (e)) -def download_one(item, downloaddb): +def download_one(item, downloaddb, options): global logger, mak logger.info("*** downloading \"%s\": %.3fGB" % (item.name, item.sourcesize / 1e9)) try: - download_decode(item, mak) + download_decode(item, options, mak) downloaddb[item.name] = item.datestr if getattr(downloaddb, "sync", None) and callable(downloaddb.sync): downloaddb.sync() @@ -461,13 +493,10 @@ def wantitem(item, downloaddb): return "not available" if downloaddb.has_key(item.name): return "already downloaded" - #if excludes.has_key(item.title) or excludes.has_key(item.episode) or excludes.has_key(item.name): - # return "excluded" - if includes.has_key(item.title) or includes.has_key(item.episode) or includes.has_key(item.name): - pass - else: - return "not included" - return "" + for i in (item.title, item.episode, item.name): + if IncludeShow.includes.has_key(i): + return IncludeShow.includes[i] + return "not included" def mirror(toc, downloaddb, one=False): @@ -480,11 +509,11 @@ def mirror(toc, downloaddb, one=False): items = toc.getItems() logger.info("*** %d shows listed" % (len(items))) for item in items: - reason = wantitem(item, downloaddb) - if reason != "": - logger.debug("*** skipping \"%s\": %s" % (item.name, reason)) + options = wantitem(item, downloaddb) + if isinstance(options, basestring): + logger.debug("*** skipping \"%s\": %s" % (item.name, options)) else: - download_one(item, downloaddb) + download_one(item, downloaddb, options) if one: break @@ -506,9 +535,9 @@ def printtoc(toc, downloaddb): shows[item.title].append(item) for title in sorted(shows): for item in sorted(shows[title], key=lambda i: i.name): - reason = wantitem(item, downloaddb) - if (reason != ""): - print "%-7.7s: %s" % (reason, item.name) + options = wantitem(item, downloaddb) + if isinstance(options, basestring): + print "%-7.7s: %s" % (options, item.name) continue print "*** downloading %s (%.3fGB)" % (item.name, item.sourcesize / 1e9)