@ -29,12 +29,12 @@ class UgoiraPP(PostProcessor):
def __init__ ( self , job , options ) :
def __init__ ( self , job , options ) :
PostProcessor . __init__ ( self , job )
PostProcessor . __init__ ( self , job )
self . extension = options . get ( " extension " ) or " webm "
self . args = options . get ( " ffmpeg-args " ) or ( )
self . args = options . get ( " ffmpeg-args " ) or ( )
self . twopass = options . get ( " ffmpeg-twopass " , False )
self . twopass = options . get ( " ffmpeg-twopass " , False )
self . output = options . get ( " ffmpeg-output " , " error " )
self . output = options . get ( " ffmpeg-output " , " error " )
self . delete = not options . get ( " keep-files " , False )
self . delete = not options . get ( " keep-files " , False )
self . repeat = options . get ( " repeat-last-frame " , True )
self . repeat = options . get ( " repeat-last-frame " , True )
self . metadata = options . get ( " metadata " , True )
self . mtime = options . get ( " mtime " , True )
self . mtime = options . get ( " mtime " , True )
self . skip = options . get ( " skip " , True )
self . skip = options . get ( " skip " , True )
self . uniform = self . _convert_zip = self . _convert_files = False
self . uniform = self . _convert_zip = self . _convert_files = False
@ -45,24 +45,31 @@ class UgoiraPP(PostProcessor):
mkvmerge = options . get ( " mkvmerge-location " )
mkvmerge = options . get ( " mkvmerge-location " )
self . mkvmerge = util . expand_path ( mkvmerge ) if mkvmerge else " mkvmerge "
self . mkvmerge = util . expand_path ( mkvmerge ) if mkvmerge else " mkvmerge "
demuxer = options . get ( " ffmpeg-demuxer " )
ext = options . get ( " extension " )
if demuxer is None or demuxer == " auto " :
mode = options . get ( " mode " ) or options . get ( " ffmpeg-demuxer " )
if self . extension in ( " webm " , " mkv " ) and (
if mode is None or mode == " auto " :
if ext in ( None , " webm " , " mkv " ) and (
mkvmerge or shutil . which ( " mkvmerge " ) ) :
mkvmerge or shutil . which ( " mkvmerge " ) ) :
demuxer = " mkvmerge "
mo de = " mkvmerge "
else :
else :
demuxer = " concat "
mo de = " concat "
if demuxer == " mkvmerge " :
if mo de == " mkvmerge " :
self . _process = self . _process_mkvmerge
self . _process = self . _process_mkvmerge
self . _finalize = self . _finalize_mkvmerge
self . _finalize = self . _finalize_mkvmerge
elif demuxer == " image2 " :
elif mo de == " image2 " :
self . _process = self . _process_image2
self . _process = self . _process_image2
self . _finalize = None
self . _finalize = None
elif mode == " archive " :
if ext is None :
ext = " zip "
self . _convert_impl = self . convert_to_archive
self . _tempdir = util . NullContext
else :
else :
self . _process = self . _process_concat
self . _process = self . _process_concat
self . _finalize = None
self . _finalize = None
self . log . debug ( " using %s demuxer " , demuxer )
self . extension = " webm " if ext is None else ext
self . log . debug ( " using %s demuxer " , mode )
rate = options . get ( " framerate " , " auto " )
rate = options . get ( " framerate " , " auto " )
if rate == " uniform " :
if rate == " uniform " :
@ -93,8 +100,8 @@ class UgoiraPP(PostProcessor):
job . register_hooks ( {
job . register_hooks ( {
" prepare " : self . prepare ,
" prepare " : self . prepare ,
" file " : self . convert_ zip,
" file " : self . convert_ from_ zip,
" after " : self . convert_f iles,
" after " : self . convert_f rom_f iles,
} , options )
} , options )
def prepare ( self , pathfmt ) :
def prepare ( self , pathfmt ) :
@ -117,7 +124,7 @@ class UgoiraPP(PostProcessor):
frame = self . _frames [ index ] . copy ( )
frame = self . _frames [ index ] . copy ( )
frame [ " index " ] = index
frame [ " index " ] = index
frame [ " path " ] = pathfmt . realpath
frame [ " path " ] = pathfmt . realpath
frame [ " ext " ] = pathfmt . kwdict[ " extension" ]
frame [ " ext " ] = pathfmt . extension
if not index :
if not index :
self . _files = [ frame ]
self . _files = [ frame ]
@ -126,31 +133,34 @@ class UgoiraPP(PostProcessor):
if len ( self . _files ) > = len ( self . _frames ) :
if len ( self . _files ) > = len ( self . _frames ) :
self . _convert_files = True
self . _convert_files = True
def convert_ zip( self , pathfmt ) :
def convert_ from_ zip( self , pathfmt ) :
if not self . _convert_zip :
if not self . _convert_zip :
return
return
self . _convert_zip = False
self . _convert_zip = False
self . _zip_source = True
with tempfile . TemporaryDirectory ( ) as tempdir :
with self . _tempdir ( ) as tempdir :
try :
if tempdir :
with zipfile . ZipFile ( pathfmt . temppath ) as zfile :
try :
zfile . extractall ( tempdir )
with zipfile . ZipFile ( pathfmt . temppath ) as zfile :
except FileNotFoundError :
zfile . extractall ( tempdir )
pathfmt . realpath = pathfmt . temppath
except FileNotFoundError :
return
pathfmt . realpath = pathfmt . temppath
return
if self . convert ( pathfmt , tempdir ) :
if self . convert ( pathfmt , tempdir ) :
if self . delete :
if self . delete :
pathfmt . delete = True
pathfmt . delete = True
el se :
el if pathfmt . extension != " zip " :
self . log . info ( pathfmt . filename )
self . log . info ( pathfmt . filename )
pathfmt . set_extension ( " zip " )
pathfmt . set_extension ( " zip " )
pathfmt . build_path ( )
pathfmt . build_path ( )
def convert_f iles( self , pathfmt ) :
def convert_f rom_f iles( self , pathfmt ) :
if not self . _convert_files :
if not self . _convert_files :
return
return
self . _convert_files = False
self . _convert_files = False
self . _zip_source = False
with tempfile . TemporaryDirectory ( ) as tempdir :
with tempfile . TemporaryDirectory ( ) as tempdir :
for frame in self . _files :
for frame in self . _files :
@ -159,13 +169,14 @@ class UgoiraPP(PostProcessor):
frame [ " file " ] = name = " {} . {} " . format (
frame [ " file " ] = name = " {} . {} " . format (
frame [ " file " ] . partition ( " . " ) [ 0 ] , frame [ " ext " ] )
frame [ " file " ] . partition ( " . " ) [ 0 ] , frame [ " ext " ] )
# move frame into tempdir
if tempdir :
try :
# move frame into tempdir
self . _copy_file ( frame [ " path " ] , tempdir + " / " + name )
try :
except OSError as exc :
self . _copy_file ( frame [ " path " ] , tempdir + " / " + name )
self . log . debug ( " Unable to copy frame %s ( %s : %s ) " ,
except OSError as exc :
name , exc . __class__ . __name__ , exc )
self . log . debug ( " Unable to copy frame %s ( %s : %s ) " ,
return
name , exc . __class__ . __name__ , exc )
return
pathfmt . kwdict [ " num " ] = 0
pathfmt . kwdict [ " num " ] = 0
self . _frames = self . _files
self . _frames = self . _files
@ -182,6 +193,9 @@ class UgoiraPP(PostProcessor):
if self . skip and pathfmt . exists ( ) :
if self . skip and pathfmt . exists ( ) :
return True
return True
return self . _convert_impl ( pathfmt , tempdir )
def convert_to_animation ( self , pathfmt , tempdir ) :
# process frames and collect command-line arguments
# process frames and collect command-line arguments
args = self . _process ( pathfmt , tempdir )
args = self . _process ( pathfmt , tempdir )
if self . args_pp :
if self . args_pp :
@ -222,6 +236,42 @@ class UgoiraPP(PostProcessor):
util . set_mtime ( pathfmt . realpath , mtime )
util . set_mtime ( pathfmt . realpath , mtime )
return True
return True
def convert_to_archive ( self , pathfmt , tempdir ) :
frames = self . _frames
if self . metadata :
if isinstance ( self . metadata , str ) :
metaname = self . metadata
else :
metaname = " animation.json "
framedata = util . json_dumps ( [
{ " file " : frame [ " file " ] , " delay " : frame [ " delay " ] }
for frame in frames
] ) . encode ( )
if self . _zip_source :
self . delete = False
if self . metadata :
with zipfile . ZipFile ( pathfmt . temppath , " a " ) as zfile :
with zfile . open ( metaname , " w " ) as fp :
fp . write ( framedata )
else :
with zipfile . ZipFile ( pathfmt . realpath , " w " ) as zfile :
for frame in frames :
zinfo = zipfile . ZipInfo . from_file (
frame [ " path " ] , frame [ " file " ] )
with open ( frame [ " path " ] , " rb " ) as src , \
zfile . open ( zinfo , " w " ) as dst :
shutil . copyfileobj ( src , dst , 1024 * 8 )
if self . metadata :
with zfile . open ( metaname , " w " ) as fp :
fp . write ( framedata )
return True
_convert_impl = convert_to_animation
_tempdir = tempfile . TemporaryDirectory
def _exec ( self , args ) :
def _exec ( self , args ) :
self . log . debug ( args )
self . log . debug ( args )
out = None if self . output else subprocess . DEVNULL
out = None if self . output else subprocess . DEVNULL