【问题标题】:Mangled output when printing strings from FFProbe STDERR从 FFProbe STDERR 打印字符串时输出错误
【发布时间】:2018-07-18 05:37:20
【问题描述】:

我正在尝试做一个简单的函数来包裹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 函数打印到控制台时,输出(可以有效地从正则表达式模式中检索字符串,但是输出的格式很奇怪)才会出现混乱。我如何构建要打印的字符串、连接、逗号分隔的参数等都无关紧要。

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

【问题讨论】:

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


    【解决方案1】:

    我通过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)

    这一切都非常有帮助,但是我没有使用这些建议。

    我不知道 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 命令来说已经足够了。它不是为了证明白痴,它是在假设程序员不会破坏任何东西的情况下编写的。

    【讨论】:

      猜你喜欢
      • 2017-09-18
      • 1970-01-01
      • 1970-01-01
      • 2022-01-22
      • 2013-12-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多