Commit dea80c18 authored by Michael Murtaugh's avatar Michael Murtaugh

basic ffmpeg info extraction, cli

parents
*.pyc
build/
*~
\ No newline at end of file
#!/usr/bin/env python
from __future__ import print_function
from ffmpeg import get_info, extract_frame
from timecode import timecode_tosecs
from argparse import ArgumentParser
import json, re, os
if __name__ == "__main__":
ap = ArgumentParser("")
ap.add_argument("input", nargs="*")
ap.add_argument("--frame", default=False, action="store_true", help="extract frame / image")
ap.add_argument("--framepath", default="{base}.jpg")
ap.add_argument("--frametime", default="00:00:05", help="time to take frame")
args = ap.parse_args()
if args.frame:
framepath = args.framepath
framepath = re.sub(r"{(\w+)}", r"{0[\1]}", framepath)
print ("framepath", framepath)
try:
frametime = float(args.frametime)
except ValueError:
frametime = timecode_tosecs(args.frametime)
r = []
for n in args.input:
d = get_info(n)
r.append(d)
if args.frame:
base = os.path.splitext(n)[0]
fp = framepath.format({'base': base})
print ("extracting frame to", fp)
usetime = frametime
# ensure frametime is valid
if 'duration' in d and usetime > d['duration']-1:
usetime = d['duration']-1
extract_frame(n, fp, usetime)
if len(r) == 1:
print (json.dumps(r[0], indent=2))
else:
print (json.dumps(r, indent=2))
#!/usr/bin/env python
from __future__ import print_function
from argparse import ArgumentParser
import sys, re, os, json, subprocess
from timecode import timecode_tosecs
def get_duration (text):
m = re.search(r"Duration: *(\d\d:\d\d:\d\d\.\d+)", text, flags=re.I)
if m:
return timecode_tosecs(m.group(1))
def get_size (text):
m = re.search(r"Stream #.+?Video.+?(\d\d+)x(\d\d+)", text, flags=re.I)
if m:
return int(m.group(1)), int(m.group(2))
def get_metadata (text):
ret = {}
for line in text.splitlines():
if ':' in line:
(name, value) = line.split(':', 1)
if not name.endswith("http") and (name.upper() == name):
ret[name.strip().lower().decode('utf-8', 'replace')] = value.strip().decode('utf-8', 'replace')
return ret
def extract_frame(url, path, t=5.0):
return subprocess.call(["ffmpeg", "-i", url, "-y", "-vframes", "1", "-ss", str(t), path])
def get_info(url):
data = {}
try:
o = subprocess.check_output(["ffmpeg", "-i", url], stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
o = e.output
d = get_duration(o)
if d:
data['duration'] = d
size = get_size(o)
if size:
data['size'] = size
data['width'] = size[0]
data['height'] = size[1]
md = get_metadata(o)
if md:
data['metadata'] = md
return data
if __name__ == "__main__":
ap = ArgumentParser("")
ap.add_argument("input", nargs="*")
ap.add_argument("--frame", default=False, action="store_true", help="extract frame / image")
ap.add_argument("--framepath", default="{base}.jpg")
ap.add_argument("--frametime", default="00:00:05", help="time to take frame")
args = ap.parse_args()
if args.frame:
framepath = args.framepath
framepath = re.sub(r"{(\w+)}", r"{0[\1]}", framepath)
print ("framepath", framepath)
try:
frametime = float(args.frametime)
except ValueError:
frametime = timecode_tosecs(args.frametime)
r = []
for n in args.input:
d = get_info(n)
r.append(d)
if args.frame:
base = os.path.splitext(n)[0]
fp = framepath.format({'base': base})
print ("extracting frame to", fp)
usetime = frametime
# ensure frametime is valid
if 'duration' in d and usetime > d-1:
usetime = d-1
extract_frame(n, fp, usetime)
if len(r) == 1:
print (json.dumps(r[0], indent=2))
else:
print (json.dumps(r, indent=2))
from distutils.core import setup
setup(
name='ffmpeg',
version='0.1.0',
author='Active Archives Contributors',
scripts=['bin/ffinfo'],
py_modules=['ffmpeg']
)
# This file is part of Active Archives.
# Copyright 2006-2016 the Active Archives contributors (see AUTHORS)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# Also add information on how to contact you by electronic and paper mail.
import math
import re
# timecode_pat = re.compile(r"(\d+):(\d+):(\d+)(?:[.,](\d+))?")
timecode_pat = re.compile(r"(?:(\d+):)?(\d+):(\d+)(?:[.,](\d+))?")
def timecode_fromsecs(rawsecs, fract=True, alwaysfract=False, fractdelim=',', \
alwayshours=False):
# returns a string in HH:MM:SS[.xxx] notation
# if fract is True, uses .xxx if either necessary (non-zero)
# OR alwaysfract is True
hours = math.floor(rawsecs / 3600)
rawsecs -= hours * 3600
mins = math.floor(rawsecs / 60)
rawsecs -= mins * 60
if fract:
secs = math.floor(rawsecs)
rawsecs -= secs
if (rawsecs > 0 or alwaysfract):
fract = "%.03f" % rawsecs
if hours or alwayshours:
return "%02d:%02d:%02d%s%s" % (hours, mins, secs, fractdelim, \
fract[2:])
else:
return "%02d:%02d%s%s" % (mins, secs, fractdelim, fract[2:])
else:
if hours or alwayshours:
return "%02d:%02d:%02d" % (hours, mins, secs)
else:
return "%02d:%02d" % (mins, secs)
else:
secs = round(rawsecs)
if hours or alwayshours:
return "%02d:%02d:%02d" % (hours, mins, secs)
else:
return "%02d:%02d" % (mins, secs)
def timecode_tosecs(tcstr):
r = timecode_pat.search(tcstr)
if r:
ret = 0
if r.group(1):
ret += 3600 * int(r.group(1))
ret += 60 * int(r.group(2))
ret += int(r.group(3))
if (r.group(4)):
ret = float(str(ret) + "." + r.group(4))
return ret
else:
return None
def parse2secs(val):
try:
return float(val)
except ValueError:
return timecode_tosecs(val)
## to accept None
# except TypeError:
# return
if __name__ == "__main__":
def t(x):
# with fraction
s = timecode_fromsecs(x, True, False)
print x, "=>", s, "=>", timecode_tosecs(s)
# without fraction
s = timecode_fromsecs(x, False)
print x, "=>", s, "=>", timecode_tosecs(s)
t(0)
t(59.666666666666666)
t(60)
t(60.0)
t(1235 / 3.0)
t(10000.5)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment