从 FFProbe STDERR 打印字符串时输出错误

Mangled output when printing strings from FFProbe STDERR

我正在尝试制作一个简单的函数来环绕 FFProbe,并且可以正确检索大部分数据。

问题是当使用 Windows 命令提示符和 Git Bash for Windows 将字符串实际打印到命令行时,输出看起来很乱顺序。

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

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 函数。无论我如何构建要打印的字符串、连接、逗号分隔参数等等,都没有关系。

我首先确定了歌曲的长度,其次是歌曲名称,但两者之间没有 space。由于某种原因,破折号挂在了尽头。根据之前代码中的打印语句,格式应该是 Title - 000 ({title} - {length}) 但输出看起来更像 000Title -。为什么?

我通过 .

中接受的答案解决了这个问题

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

  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)

这一切都非常有帮助,但是我使用了这些建议中的 none 个。

我不知道 FFPROBE 提供 JSON 到 STDOUT 的输出,但它确实提供了。执行此操作的代码如下。

#! /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 命令来说已经足够好了。它不是用来证明白痴的,它是在假设程序员不会破坏任何东西的情况下编写的,带有一些漏洞。