Videos nach dem Download automatisch transkodieren
This commit is contained in:
parent
7011a68f47
commit
bc44a7f91d
3 changed files with 165 additions and 126 deletions
|
@ -1,3 +1,4 @@
|
|||
tivo
|
||||
toc-formatted.xml
|
||||
toc.xml
|
||||
*.pyc
|
||||
|
|
|
@ -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()
|
||||
|
|
68
src/tivomirror/tivomp4.py
Executable file
68
src/tivomirror/tivomp4.py
Executable file
|
@ -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())
|
Loading…
Reference in a new issue