Skip to content

Commit

Permalink
Merge pull request #29 from fsadannn/develop
Browse files Browse the repository at this point in the history
Support for 10bits video, user aac as audio codec, keep all video metadata
  • Loading branch information
hiancdtrsnm authored Oct 11, 2020
2 parents dd72226 + c39fa18 commit 199a3d4
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 26 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@
*.w4a
*.aac
*.vscode
dist
26 changes: 14 additions & 12 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 9 additions & 7 deletions video_diet/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import os
import ffmpeg
import enlighten
from .utils import get_bitdepth
if sys.platform == 'win32':
import wexpect as expect
# patch windows consloe for scale correctly characters
Expand All @@ -21,6 +22,9 @@
COUNTER_FMT = u'{desc}{desc_pad}{count:.1f} {unit}{unit_pad}' + \
u'[{elapsed}, {rate:.2f}{unit_pad}{unit}/s]{fill}'

CONVERT_COMMAND_10Bits = 'ffmpeg -progress pipe:1 -i "{source}" -map 0 -map -v -map V -c:v libx265 -x265-params crf=26:profile=main10 -c:a aac -y "{dest}"'
CONVERT_COMMAND = 'ffmpeg -progress pipe:1 -i "{source}" -map 0 -map -v -map V -c:v libx265 -crf 26 -c:a aac -y "{dest}"'

def convert_file(source: str, dest: str):
stream = ffmpeg.input(source)
stream = ffmpeg.output(stream, dest, vcodec='libx265', crf='28')
Expand All @@ -29,14 +33,12 @@ def convert_file(source: str, dest: str):
def convert_video_progress_bar(source: str, dest: str, manager=None):
if manager is None:
manager = enlighten.get_manager()
stream = ffmpeg.input(source)
stream = ffmpeg.output(stream, dest, vcodec='libx265', crf='28')
args = ffmpeg.compile(stream, 'ffmpeg')
args.insert(1,'-progress pipe:1')
args = map(lambda x: '"'+x+'"' if '\\' in x or '/' in x else x,args)
args = list(args)
name = source.rsplit(os.path.sep,1)[-1]
proc = expect.spawn(' '.join(args), encoding='utf-8')
if get_bitdepth(source).is_10bit:
args = CONVERT_COMMAND_10Bits.format(source=source, dest=dest)
else:
args = CONVERT_COMMAND.format(source=source, dest=dest)
proc = expect.spawn(args, encoding='utf-8')
pbar = None
try:
proc.expect(pattern_duration)
Expand Down
6 changes: 3 additions & 3 deletions video_diet/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ def folder(path: Path = typer.Argument(
os.remove(str(new_path))

try:
#convert_video(str(video), str(new_path))
convert_video_progress_bar(str(video), str(new_path), manager)
os.remove(str(video))
if video.suffix == new_path.suffix:
Expand Down Expand Up @@ -124,6 +123,8 @@ def file(path: Path = typer.Argument(
dir_okay=False,
readable=True,
resolve_path=True
), force: bool = typer.Option(
default=False,
)):
"""
Convert a file
Expand All @@ -146,13 +147,12 @@ def file(path: Path = typer.Argument(
return


if get_codec(str(path)) == 'hevc':
if get_codec(str(path)) == 'hevc' and not force:
typer.secho('This video codec is already \'hevc\'', fg=GREEN)
return

try:
convert_video_progress_bar(str(path), str(conv_path))
#convert_video(str(path), str(conv_path))

except FileNotFoundError as error:
if error.filename == 'ffmpeg':
Expand Down
82 changes: 82 additions & 0 deletions video_diet/patch_ffprobe.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import os
import re
import pipes
import platform
import subprocess
from ffprobe.ffprobe import FFStream

class FFProbe:
"""
FFProbe wraps the ffprobe command and pulls the data into an object form::
metadata=FFProbe('multimedia-file.mov')
"""

def __init__(self, path_to_video):
self.path_to_video = path_to_video

try:
with open(os.devnull, 'w') as tempf:
subprocess.check_call(["ffprobe", "-h"], stdout=tempf, stderr=tempf)
except FileNotFoundError:
raise IOError('ffprobe not found.')

if os.path.isfile(self.path_to_video):
if platform.system() == 'Windows':
cmd = ["ffprobe", "-show_streams", self.path_to_video]
else:
cmd = ["ffprobe -show_streams " + pipes.quote(self.path_to_video)]

out = subprocess.getoutput(cmd)
out = out.split('\n')

stream = False
self.streams = []
self.video = []
self.audio = []
self.subtitle = []
self.attachment = []

self.metadata = {}
is_metadata = False
stream_metadata_met = False
chapter_metadata_met = False

for line in out:

if 'Metadata:' in line and not (stream_metadata_met or chapter_metadata_met):
is_metadata = True
elif 'Stream #' in line:
is_metadata = False
stream_metadata_met = True
elif 'Chapter #' in line:
is_metadata = False
chapter_metadata_met = True
elif is_metadata:
splits = line.split(',')
for s in splits:
m = re.search(r'(\w+)\s*:\s*(.*)$', s)
self.metadata[m.groups()[0]] = m.groups()[1].strip()

if '[STREAM]' in line:
stream = True
data_lines = []
elif '[/STREAM]' in line and stream:
stream = False
self.streams.append(FFStream(data_lines))
elif stream:
data_lines.append(line)

for stream in self.streams:
if stream.is_audio():
self.audio.append(stream)
elif stream.is_video():
self.video.append(stream)
elif stream.is_subtitle():
self.subtitle.append(stream)
elif stream.is_attachment():
self.attachment.append(stream)
else:
raise IOError('No such media file ' + self.path_to_video)

def __repr__(self):
return "<FFprobe: {metadata}, {video}, {audio}, {subtitle}, {attachment}>".format(**vars(self))
50 changes: 46 additions & 4 deletions video_diet/utils.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,39 @@
import re
from pathlib import Path
import filetype
import typer

from ffprobe import FFProbe
from .patch_ffprobe import FFProbe

px10bit = re.compile('10le$')
px12bit = re.compile('12le$')

class PixelFormat:
__slots__ = ('_px_format', '_is_10bit', '_is_12bit')
def __init__(self, px_format):
self._px_format = px_format
self._is_10bit = px10bit.search(px_format) is not None
self._is_12bit = px12bit.search(px_format) is not None

@property
def pixel_format(self):
return self._px_format

@property
def is_10bit(self):
return self._is_10bit

@property
def is_12bit(self):
return self._is_12bit

@property
def is_8bit(self):
return not(self._is_10bit or self._is_12bit)

def __str__(self):
return self._px_format



def get_codec(path: str):
Expand All @@ -11,11 +42,22 @@ def get_codec(path: str):
except:
return None

if 'video' in metadata.streams:
return metadata.video[0].codec_name
if len(metadata.video) != 0:
return metadata.video[0].codec()

return metadata.audio[0].codec()

def get_bitdepth(path: str):
try:
metadata = FFProbe(path)
except:
return None

return metadata.audio[0].codec_name
if len(metadata.video) != 0:
pixel_format = metadata.video[0].pixel_format()
return PixelFormat(pixel_format)

return None

def convertion_path(path: Path, audio: bool ):

Expand Down

0 comments on commit 199a3d4

Please sign in to comment.