diff --git a/src/tivomirror/.cvsignore b/src/tivomirror/.cvsignore index 294d3d6..2ddccab 100644 --- a/src/tivomirror/.cvsignore +++ b/src/tivomirror/.cvsignore @@ -1,3 +1,4 @@ tivo toc-formatted.xml toc.xml +*.pyc diff --git a/src/tivomirror/tivomirror b/src/tivomirror/tivomirror index 0c72475..5d8ca9b 100755 --- a/src/tivomirror/tivomirror +++ b/src/tivomirror/tivomirror @@ -1,6 +1,6 @@ #!/usr/local/bin/python -# $Schlepperbande: src/tivomirror/tivomirror,v 1.41 2011/01/13 17:26:33 stb Exp $ +# $Schlepperbande: src/tivomirror/tivomirror,v 1.42 2011/07/04 21:10:43 stb Exp $ # # Stefans Script, um die Sendungen vom Tivo runterzuladen und in MPEG4 # zu transkodieren. @@ -19,6 +19,7 @@ import sys import time import urllib2 import xml.dom.minidom +import tivomp4 host = "tivo.lassitu.de" mak = "7194378159" @@ -87,6 +88,28 @@ class TivoException(Exception): def __str__(self): return repr(self.value) +class TivoItem: + def __init__(self, i): + self.title = getTagText(i, "Title") + self.episode = getTagText(i, "EpisodeTitle") + self.date = getTagText(i, "CaptureDate") + self.date = datetime.datetime.utcfromtimestamp(int(self.date, 16)) + self.datestr = self.date.strftime("%Y%m%d-%H%M") + self.url = getTagText(i, "Url") + self.inprogress = getTagText(i, "InProgress") + self.available = getTagText(i, "Available") + self.sourcesize = int(getTagText(i, "SourceSize")) + self.highdef = getTagText(i, "HighDefinition") + self.ar = 43 + if arset.has_key(self.title): + self.ar = arset[self.title] + elif self.highdef == "Yes": + self.ar = "hd" + if self.episode == "": + self.episode = self.datestr + def __str__(self): + return repr(self.title) + def gettoc(): url = "https://" + host + "/TiVoConnect?Command=QueryContainer&Container=%2FNowPlaying&Recurse=Yes" pwmgr.add_password(None, url, "tivo", mak) @@ -106,7 +129,7 @@ def getText(nodelist): def getTagText(element, tagname): try: - return getText(i.getElementsByTagName(tagname)[0].childNodes) + return getText(element.getElementsByTagName(tagname)[0].childNodes) except IndexError: return "" @@ -142,53 +165,8 @@ def waitForProcs(pids): raise TivoException("error executing processes") -def transcode(file, src, passno, ar): - print "--- transcoding pass %d" % passno - try: - os.remove(tmpmp4) - except OSError: - pass - transcode_opts = [ "ffmpeg" ]; - # transcode_opts.extend(["-t", "60"]) # testing only: only 60 seconds - #transcode_opts.extend(["-aspect", "4:3"]) - transcode_opts.extend(["-i", src]) - - if passno == 1: - transcode_opts.extend(["-an"]) - else: - transcode_opts.extend(["-acodec", "libfaac", "-ab", "96kb"]) - transcode_opts.extend(["-pass", "%d" % passno]) - transcode_opts.extend(["-vcodec", "libx264"]) - if passno == 1: - transcode_opts.extend(["-vpre", "fastfirstpass"]) - else: - transcode_opts.extend(["-vpre", "hq"]) - transcode_opts.extend(["-threads", "0", "-b", "900k", "-bt", "900k"]) - if ar == 43: - transcode_opts.extend(["-croptop", "4", "-cropbottom", "4", "-cropleft", "6", "-cropright", "6"]) - #transcode_opts.extend(["-s", "480x352"]) - transcode_opts.extend(["-s", "624x352"]) - #transcode_opts.extend(["-aspect", "4:3"]) - elif ar == 149: - transcode_opts.extend(["-croptop", "34", "-cropbottom", "34", "-cropleft", "6", "-cropright", "6"]) - transcode_opts.extend(["-s", "546x352"]) - transcode_opts.extend(["-aspect", "14:9"]) - else: - transcode_opts.extend(["-croptop", "60", "-cropbottom", "60", "-cropleft", "6", "-cropright", "6"]) - transcode_opts.extend(["-s", "624x352"]) - transcode_opts.extend(["-aspect", "16:9"]) - transcode_opts.extend(["-y", "-deinterlace"]) - if passno == 1: - transcode_opts.extend(["-f", "mp4", "/dev/null"]) - else: - transcode_opts.append(tmpmp4) - - print " %s" % " ".join(transcode_opts) - subprocess.check_call(transcode_opts) - - def download(file, url, mak, target): - print "--- dowloading \"%s\"" % (url) + print "--- downloading \"%s\"" % (url) start = time.time() p_curl = subprocess.Popen(["curl", "--anyauth", "--fail", \ "--insecure", "--cookie", "tivo/.cookies.txt", \ @@ -204,10 +182,10 @@ def download(file, url, mak, target): os.remove(target) except OSError: pass - raise e + raise size = os.path.getsize(target) if size < 1024: - print "error transcoding file: too small" + print "error downloadig file: too small" os.remove(target) raise TivoException("downloaded file is too small") elapsed = time.time() - start @@ -216,20 +194,22 @@ def download(file, url, mak, target): size/1e9, elapsed/3600, int(elapsed / 60) % 60, throughput/1e3) -def download_decode(file, url, mak, ar): +def download_decode(item, mak): #target = tmpmp2 - target = "%s.mpg" % file + target = "%s.mpg" % item.file try: - os.makedirs(dir) + os.makedirs(item.dir) except OSError: pass - #if os.path.exists(target): - # print " reusing existing download file" - #else: - download(file, url, mak, target) - return - transcode(file, target, 1, ar) - transcode(file, target, 2, ar) + if os.path.exists(target): + print " reusing existing download file" + else: + try: + download(item.file, item.url, mak, target) + except Exception, e: + os.remove(target) + raise + tivomp4.transcode(target, tmpmp4, item.ar) print "--- copying to \"%s\"" % file shutil.copy(tmpmp4, "%s.mp4" % file) os.remove(tmpmp2) @@ -241,75 +221,65 @@ def savetoc(toc): fd.write(toc) fd.close() +def main(): + curdir = os.getcwd() + os.chdir(tmp) + avail = getAvail(targetdir) + if avail < minfree: + print "%s: %.1fG available, at least %.1fG needed, stopping" % \ + (targetdir, avail / gig, minfree / gig) + sys.exit(1) -curdir = os.getcwd() -os.chdir(tmp) -avail = getAvail(targetdir) -if avail < minfree: - print "%s: %.1fG available, at least %.1fG needed, stopping" % \ - (targetdir, avail / gig, minfree / gig) - sys.exit(1) + downloaddb = anydbm.open(os.path.expanduser("~") + "/.tivo-downloads", "c") + print "*** Getting listing" + toc = gettoc() + savetoc(toc) + dom = xml.dom.minidom.parseString(toc) + cookiejar.save() -downloaddb = anydbm.open(os.path.expanduser("~") + "/.tivo-downloads", "c") -print "*** Getting listing" -toc = gettoc() -savetoc(toc) -dom = xml.dom.minidom.parseString(toc) -cookiejar.save() + items = dom.getElementsByTagName("Item") + print "*** %d shows listed" % (items.length) + for i in items: + item = TivoItem(i) + item.name = "%s - %s" % (item.title, item.episode) + #dir = "%s/tivo/%s" % (curdir, re.sub("[:/]", "-", item.title)) + item.dir = "%s/%s" % (targetdir, re.sub("[:/]", "-", item.title)) + item.file = "%s/%s" % (item.dir, re.sub("[:/]", "-", item.name)) + item.name = item.name.encode("utf-8"); + item.dir = item.dir.encode("utf-8"); + item.file = item.file.encode("utf-8"); -items = dom.getElementsByTagName("Item") -print "*** %d shows listed" % (items.length) -for i in items: - title = getTagText(i, "Title") - episode = getTagText(i, "EpisodeTitle") - date = getTagText(i, "CaptureDate") - date = datetime.datetime.utcfromtimestamp(int(date, 16)) - datestr = date.strftime("%Y%m%d-%H%M") - url = getTagText(i, "Url") - inprogress = getTagText(i, "InProgress") - available = getTagText(i, "Available") - sourcesize = int(getTagText(i, "SourceSize")) - if episode == "": - episode = datestr - name = "%s - %s" % (title, episode) - #dir = "%s/tivo/%s" % (curdir, re.sub("[:/]", "-", title)) - dir = "%s/%s" % (targetdir, re.sub("[:/]", "-", title)) - file = "%s/%s" % (dir, re.sub("[:/]", "-", name)) - name = name.encode("utf-8"); - dir = dir.encode("utf-8"); - file = file.encode("utf-8"); - - ar = 43 - if arset.has_key(title): - ar = arset[title] + if item.inprogress == "Yes": + print "*** skipping \"%s\": is currently being recorded" % item.name + continue + if item.available == "No": + print "*** skipping \"%s\": is not available" % item.name + continue + if downloaddb.has_key(item.name): + #print "*** skipping \"%s\": already downloaded" % item.name + continue + if excludes.has_key(item.title) or excludes.has_key(item.episode) or excludes.has_key(item.name): + #print "*** skipping \"%s\": excluded" % name + continue - if inprogress == "Yes": - print "*** skipping \"%s\": is currently being recorded" % name - continue - if available == "No": - print "*** skipping \"%s\": is not available" % name - continue - if downloaddb.has_key(name): - #print "*** skipping \"%s\": already downloaded" % name - continue - if excludes.has_key(title) or excludes.has_key(episode) or excludes.has_key(name): - #print "*** skipping \"%s\": excluded" % name - continue - print "*** downloading \"%s\": %.3fGB" % (name, sourcesize / 1e9) - try: - download_decode(file, url, mak, ar) + print "*** downloading \"%s\": %.3fGB" % (item.name, item.sourcesize / 1e9) try: - os.utime(file, [date, date]) - except Exception, e: - print "Urgh:", e - downloaddb[name] = datestr - if getattr(downloaddb, "sync", None) and callable(downloaddb.sync): - downloaddb.sync() - # stop after the first successful download since the tivo hangs easily - print "Stopping after one successful transfer" - break - except TivoException, e: - print "Error processing \"%s\": %s" % (name, e) + download_decode(item, mak) + try: + os.utime(item.file, [item.date, item.date]) + except Exception, e: + print "Urgh:", e + downloaddb[item.name] = item.datestr + if getattr(downloaddb, "sync", None) and callable(downloaddb.sync): + downloaddb.sync() + # stop after the first successful download since the tivo hangs easily + print "Stopping after one successful transfer" + break + except TivoException, e: + print "Error processing \"%s\": %s" % (item.name, e) -print "*** Completed" -downloaddb.close() + print "*** Completed" + downloaddb.close() + +if __name__ == "__main__": + main() diff --git a/src/tivomirror/tivomp4.py b/src/tivomirror/tivomp4.py new file mode 100755 index 0000000..307741d --- /dev/null +++ b/src/tivomirror/tivomp4.py @@ -0,0 +1,68 @@ +#!/usr/local/bin/python + +import getopt +import shutil +import subprocess +import sys + + +def transcode(src, tgt, fmt): + transcode_opts = [ "ffmpeg" ]; + #transcode_opts.extend(["-t", "60"]) # testing only: only 60 seconds + transcode_opts.extend(["-loglevel", "error"]) + transcode_opts.extend(["-i", src]) + + #transcode_opts.extend(["-acodec", "libfaac"]) + transcode_opts.extend(["-ac", "2", "-ab", "128k"]) + transcode_opts.extend(["-vcodec", "libx264"]) + transcode_opts.extend(["-tune", "film", "-profile", "main"]) + if fmt == "hd": + transcode_opts.extend(["-s", "1280x720"]) + transcode_opts.extend(["-threads", "0", "-b", "4000k"]) + else: + transcode_opts.extend(["-threads", "0", "-b", "900k", "-bt", "900k"]) + if str(fmt) == "43": + pass + elif str(fmt) == "149": + transcode_opts.extend(["-vf", "crop=640:412"]) + transcode_opts.extend(["-aspect", "14:9"]) + else: + transcode_opts.extend(["-vf", "crop=640:360"]) + transcode_opts.extend(["-aspect", "16:9"]) + transcode_opts.extend(["-y", "-deinterlace", "-vsync", "1"]) + transcode_opts.append(tgt) + + print " %s" % " ".join(transcode_opts) + subprocess.check_call(transcode_opts) + + +class Usage(Exception): + def __init__(self, msg): + self.msg = msg; + def __str__(self): + return repr(self.msg) + + +def main(argv=None): + if argv is None: + argv = sys.argv + try: + try: + opts, args = getopt.getopt(argv[1:], "h", ["help"]) + except getopt.error, msg: + raise Usage(msg) + for (o, v) in opts: + if o == "-h": + print >>sys.stderr, "help text" + return 0 + if len(args) != 3: + raise Usage("wrong number of arguments") + transcode(args[1], args[2], args[0]) + except Usage, err: + print >>sys.stderr, err.msg + print >>sys.stderr, "tivomp4 format source destination" + print >>sys.stderr, "for help use --help" + return 64 + +if __name__ == "__main__": + sys.exit(main())