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
|
tivo
|
||||||
toc-formatted.xml
|
toc-formatted.xml
|
||||||
toc.xml
|
toc.xml
|
||||||
|
*.pyc
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#!/usr/local/bin/python
|
#!/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
|
# Stefans Script, um die Sendungen vom Tivo runterzuladen und in MPEG4
|
||||||
# zu transkodieren.
|
# zu transkodieren.
|
||||||
|
@ -19,6 +19,7 @@ import sys
|
||||||
import time
|
import time
|
||||||
import urllib2
|
import urllib2
|
||||||
import xml.dom.minidom
|
import xml.dom.minidom
|
||||||
|
import tivomp4
|
||||||
|
|
||||||
host = "tivo.lassitu.de"
|
host = "tivo.lassitu.de"
|
||||||
mak = "7194378159"
|
mak = "7194378159"
|
||||||
|
@ -87,6 +88,28 @@ class TivoException(Exception):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return repr(self.value)
|
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():
|
def gettoc():
|
||||||
url = "https://" + host + "/TiVoConnect?Command=QueryContainer&Container=%2FNowPlaying&Recurse=Yes"
|
url = "https://" + host + "/TiVoConnect?Command=QueryContainer&Container=%2FNowPlaying&Recurse=Yes"
|
||||||
pwmgr.add_password(None, url, "tivo", mak)
|
pwmgr.add_password(None, url, "tivo", mak)
|
||||||
|
@ -106,7 +129,7 @@ def getText(nodelist):
|
||||||
|
|
||||||
def getTagText(element, tagname):
|
def getTagText(element, tagname):
|
||||||
try:
|
try:
|
||||||
return getText(i.getElementsByTagName(tagname)[0].childNodes)
|
return getText(element.getElementsByTagName(tagname)[0].childNodes)
|
||||||
except IndexError:
|
except IndexError:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
@ -142,53 +165,8 @@ def waitForProcs(pids):
|
||||||
raise TivoException("error executing processes")
|
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):
|
def download(file, url, mak, target):
|
||||||
print "--- dowloading \"%s\"" % (url)
|
print "--- downloading \"%s\"" % (url)
|
||||||
start = time.time()
|
start = time.time()
|
||||||
p_curl = subprocess.Popen(["curl", "--anyauth", "--fail", \
|
p_curl = subprocess.Popen(["curl", "--anyauth", "--fail", \
|
||||||
"--insecure", "--cookie", "tivo/.cookies.txt", \
|
"--insecure", "--cookie", "tivo/.cookies.txt", \
|
||||||
|
@ -204,10 +182,10 @@ def download(file, url, mak, target):
|
||||||
os.remove(target)
|
os.remove(target)
|
||||||
except OSError:
|
except OSError:
|
||||||
pass
|
pass
|
||||||
raise e
|
raise
|
||||||
size = os.path.getsize(target)
|
size = os.path.getsize(target)
|
||||||
if size < 1024:
|
if size < 1024:
|
||||||
print "error transcoding file: too small"
|
print "error downloadig file: too small"
|
||||||
os.remove(target)
|
os.remove(target)
|
||||||
raise TivoException("downloaded file is too small")
|
raise TivoException("downloaded file is too small")
|
||||||
elapsed = time.time() - start
|
elapsed = time.time() - start
|
||||||
|
@ -216,20 +194,22 @@ def download(file, url, mak, target):
|
||||||
size/1e9, elapsed/3600, int(elapsed / 60) % 60, throughput/1e3)
|
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 = tmpmp2
|
||||||
target = "%s.mpg" % file
|
target = "%s.mpg" % item.file
|
||||||
try:
|
try:
|
||||||
os.makedirs(dir)
|
os.makedirs(item.dir)
|
||||||
except OSError:
|
except OSError:
|
||||||
pass
|
pass
|
||||||
#if os.path.exists(target):
|
if os.path.exists(target):
|
||||||
# print " reusing existing download file"
|
print " reusing existing download file"
|
||||||
#else:
|
else:
|
||||||
download(file, url, mak, target)
|
try:
|
||||||
return
|
download(item.file, item.url, mak, target)
|
||||||
transcode(file, target, 1, ar)
|
except Exception, e:
|
||||||
transcode(file, target, 2, ar)
|
os.remove(target)
|
||||||
|
raise
|
||||||
|
tivomp4.transcode(target, tmpmp4, item.ar)
|
||||||
print "--- copying to \"%s\"" % file
|
print "--- copying to \"%s\"" % file
|
||||||
shutil.copy(tmpmp4, "%s.mp4" % file)
|
shutil.copy(tmpmp4, "%s.mp4" % file)
|
||||||
os.remove(tmpmp2)
|
os.remove(tmpmp2)
|
||||||
|
@ -241,75 +221,65 @@ def savetoc(toc):
|
||||||
fd.write(toc)
|
fd.write(toc)
|
||||||
fd.close()
|
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()
|
downloaddb = anydbm.open(os.path.expanduser("~") + "/.tivo-downloads", "c")
|
||||||
os.chdir(tmp)
|
print "*** Getting listing"
|
||||||
avail = getAvail(targetdir)
|
toc = gettoc()
|
||||||
if avail < minfree:
|
savetoc(toc)
|
||||||
print "%s: %.1fG available, at least %.1fG needed, stopping" % \
|
dom = xml.dom.minidom.parseString(toc)
|
||||||
(targetdir, avail / gig, minfree / gig)
|
cookiejar.save()
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
downloaddb = anydbm.open(os.path.expanduser("~") + "/.tivo-downloads", "c")
|
items = dom.getElementsByTagName("Item")
|
||||||
print "*** Getting listing"
|
print "*** %d shows listed" % (items.length)
|
||||||
toc = gettoc()
|
for i in items:
|
||||||
savetoc(toc)
|
item = TivoItem(i)
|
||||||
dom = xml.dom.minidom.parseString(toc)
|
item.name = "%s - %s" % (item.title, item.episode)
|
||||||
cookiejar.save()
|
#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")
|
if item.inprogress == "Yes":
|
||||||
print "*** %d shows listed" % (items.length)
|
print "*** skipping \"%s\": is currently being recorded" % item.name
|
||||||
for i in items:
|
continue
|
||||||
title = getTagText(i, "Title")
|
if item.available == "No":
|
||||||
episode = getTagText(i, "EpisodeTitle")
|
print "*** skipping \"%s\": is not available" % item.name
|
||||||
date = getTagText(i, "CaptureDate")
|
continue
|
||||||
date = datetime.datetime.utcfromtimestamp(int(date, 16))
|
if downloaddb.has_key(item.name):
|
||||||
datestr = date.strftime("%Y%m%d-%H%M")
|
#print "*** skipping \"%s\": already downloaded" % item.name
|
||||||
url = getTagText(i, "Url")
|
continue
|
||||||
inprogress = getTagText(i, "InProgress")
|
if excludes.has_key(item.title) or excludes.has_key(item.episode) or excludes.has_key(item.name):
|
||||||
available = getTagText(i, "Available")
|
#print "*** skipping \"%s\": excluded" % name
|
||||||
sourcesize = int(getTagText(i, "SourceSize"))
|
continue
|
||||||
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
|
print "*** downloading \"%s\": %.3fGB" % (item.name, item.sourcesize / 1e9)
|
||||||
if arset.has_key(title):
|
|
||||||
ar = arset[title]
|
|
||||||
|
|
||||||
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)
|
|
||||||
try:
|
try:
|
||||||
os.utime(file, [date, date])
|
download_decode(item, mak)
|
||||||
except Exception, e:
|
try:
|
||||||
print "Urgh:", e
|
os.utime(item.file, [item.date, item.date])
|
||||||
downloaddb[name] = datestr
|
except Exception, e:
|
||||||
if getattr(downloaddb, "sync", None) and callable(downloaddb.sync):
|
print "Urgh:", e
|
||||||
downloaddb.sync()
|
downloaddb[item.name] = item.datestr
|
||||||
# stop after the first successful download since the tivo hangs easily
|
if getattr(downloaddb, "sync", None) and callable(downloaddb.sync):
|
||||||
print "Stopping after one successful transfer"
|
downloaddb.sync()
|
||||||
break
|
# stop after the first successful download since the tivo hangs easily
|
||||||
except TivoException, e:
|
print "Stopping after one successful transfer"
|
||||||
print "Error processing \"%s\": %s" % (name, e)
|
break
|
||||||
|
except TivoException, e:
|
||||||
|
print "Error processing \"%s\": %s" % (item.name, e)
|
||||||
|
|
||||||
print "*** Completed"
|
print "*** Completed"
|
||||||
downloaddb.close()
|
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