从FFProbe STDERR打印字符串时的Mangled输出

时间:2018-02-07 19:49:36

标签: python python-3.x ffmpeg subprocess ffprobe

我正在尝试创建一个简单的函数来包装FFProbe ,并且可以正确检索大部分数据。

问题是当使用Windows命令提示符和Git Bash for Windows实际打印字符串到命令行时,输出会出现损坏且无序。

某些歌曲(特别是文件Imagine Dragons - Hit Parade_ Best of the Dance Music Charts\80 - Beazz - Lime (Extended Mix).flac)缺少元数据。我不知道为什么,但下面的函数返回的字典是空的。

FFProbe将其结果输出到stderr,可以将其传送到subprocess.PIPE,进行解码和解析。我为解析位选择了正则表达式。

这是我的代码的精简版本,输出结果请看Github gist

#! /usr/bin/env python3
# -*- coding: utf-8 -*-

import os

from glob import glob
from re import findall, MULTILINE
from subprocess import Popen, PIPE


def glob_from(path, ext):
    """Return glob from a directory."""
    working_dir = os.getcwd()
    os.chdir(path)

    file_paths = glob("**/*." + ext)

    os.chdir(working_dir)

    return file_paths


def media_metadata(file_path):
    """Use FFPROBE to get information about a media file."""
    stderr = Popen(("ffprobe", file_path), shell=True, stderr=PIPE).communicate()[1].decode()

    metadata = {}

    for match in findall(r"(\w+)\s+:\s(.+)$", stderr, MULTILINE):
        metadata[match[0].lower()] = match[1]

    return metadata


if __name__ == "__main__":
    base = "C:/Users/spike/Music/Deezloader"

    for file in glob_from(base, "flac"):
        meta = media_metadata(os.path.join(base, file))
        title_length = meta.get("title", file) + " - " + meta.get("length", "000")

        print(title_length)

输出Gist 输出Raw

我不明白为什么输出(可以有效地从正则表达式模式中检索字符串,但是打印时输出格式奇怪)只有在使用python的print函数打印到控制台时才会出现混乱。无论我如何构建字符串以打印,连接,逗号分隔的参数,无论如何。

我最后以歌曲的长度结束,歌曲名称第二,但两者之间没有空格。由于某种原因,破折号悬而未决。基于之前代码中的print语句,格式应为Title - 000{title} - {length}),但输出看起来更像000Title -。为什么呢?

1 个答案:

答案 0 :(得分:0)

我通过related question中接受的答案解决了这个问题。

我忘记了每一行末尾的回程车。给出的解决方案如下:

  1. 在子流程调用中使用universal_newlines=True
    • stderr = Popen(("ffprobe", file_path), shell=True, stderr=PIPE, universal_newlines=True).communicate()[1]
  2. stderr剥离该行周围的空白。

    • *.communicate()[1].decode().rstrip()在最后删除所有空格。
    • *.communicate()[1].decode().strip()剥离周围的所有空间。
    • *.communicate()[1].decode()[:-2]删除最后两个字符。
  3. 以正则表达式模式吞咽\r

    • findall(r"(\w+)\s+:\s(.+)\r$", stderr, MULTILINE)
  4. 这一切都非常有用,但我没有使用这些建议。

    我不知道FFPROBE向STDOUT提供JSON输出,但确实如此。这样做的代码如下。

    #! /usr/bin/env python3
    # -*- coding: utf-8 -*-
    from json import loads
    from subprocess import check_output, DEVNULL, PIPE
    
    
    def arg_builder(args, kwargs, defaults={}):
        """Build arguments from `args` and `kwargs` in a shell-lexical manner."""
        for key, val in defaults.items():
            kwargs[key] = kwargs.get(key, val)
    
        args = list(args)
    
        for arg, val in kwargs.items():
            if isinstance(val, bool):
                if val:
                    args.append("-" + arg)
            else:
                args.extend(("-" + arg, val))
    
        return args
    
    
    def run_ffprobe(file_path, *args, **kwargs):
        """Use FFPROBE to get information about a media file."""
        return loads(check_output(("ffprobe", arg_builder(args, kwargs, defaults={"show_format": True}),
                                   "-of", "json", file_path), shell=True, stderr=DEVNULL))
    

    您可能也会使用arg_builder()这不是完美的,但它对于简单的shell命令来说已经足够了。它没有被证明是白痴证明,它是用一些漏洞写的,假设程序员不会破坏任何东西。