Как я могу структурировать jq-фильтры для возврата информации о видео и аудио кодеках из ffprobe?

В интересах стандартизации моей видео-библиотеки я пытаюсь найти способ быстро создать список файлов, которые необходимо преобразовать. Посмотрев на этот вопрос и его ответ (и многое другое), я думаю, что у меня есть основы, но у меня возникли проблемы с разработкой раздела jq. Для справки, команда jq, с которой я начинаю, выглядит следующим образом:

jq -c '.format.filename as $path | .streams[]? | select(.codec_type=="video" and .codec_name!="h264") | .codec_name as $vcodec | {video: $vcodec, path: $path}'

и, для простоты, предположим, что это то, что подается в jq:

{ "streams": [ { "index": 0, "codec_name": "hevc", "codec_type": "video" }, { "index": 1, "codec_name": "aac", "codec_type": "audio" } ], "format": { "filename": "Video.mkv" } }

, который производит следующий вывод:

{"video":"hevc","path":"./Video.mkv"}

Это здорово, но я хочу пойти еще дальше - я также хотел бы включить кодек, используемый для любых аудиопотоков. Поэтому, учитывая тот же ввод, я хотел бы получить следующий результат:

{"video":"hevc","audio":"aac","path":"./Video.mkv"}

Как это сделать?

3
задан 6 July 2017 в 11:12

6 ответов

Чтобы выбрать типы аудио и видео кодеков и исключить видео h264:

$ jq '.format.filename as $path |
    [.streams[]? | select(.codec_type=="audio" 
                          or (.codec_type=="video" 
                              and .codec_name!="h264")) | 
     {(.codec_type): .codec_name, $path}] | 
    group_by(.path) | map(add) | .[]' input.json
$ jq --version      
jq-1.5-1-a5b5cbe  

Если команда оболочки становится сложной и занимает больше нескольких строк; Я переключусь на более подробный Python для управления сложностью:

result = dict(path=data['format']['filename'])
for stream in data['streams']:
    if (stream['codec_type'] == 'audio'
        or (stream['codec_type'] == 'video'
            and stream['codec_name'] != 'h264')):
        result[stream['codec_type']] = stream['codec_name'] # last value wins

data - это вход (data = json.loads(json_text)), а result - желаемый вывод (print(json.dumps(result))).

Для вашего конкретного случая должно быть относительно просто адаптировать приведенный выше код, если вы более знакомы с императивным программированием на Python, чем с более функциональным стилем в jq.

2
ответ дан 22 May 2018 в 20:53
  • 1
    Спасибо, это было очень полезно. Я немного расширил ваш ответ, изменив / добавив этот бит в конец: .[]? | select(.video!="h264") | {video: .video, audio: .audio, path: .path}. В любом случае, меня интересует только аудиокодек, если видеокодек не тот, кем я его хочу. Похоже, стоит потратить некоторое время на изучение питона. – Ziggy114 4 July 2017 в 19:05
  • 2
    Одна заключительная нота - все отлично работало при тестировании на jqplay.org, но возвращало синтаксическую ошибку для неожиданного $ при выполнении той же самой команды на моем компьютере. Оказывается, у меня была jq версия 1.4 («текущая» версия на Debian Jessie), обновление до версии 1.5 исправило эту проблему. – Ziggy114 4 July 2017 в 19:23
  • 3
    @ Ziggy114: я обновил ответ, чтобы исключить видео h264 и показать версию jq (я использую пакет jq для Ubuntu) – jfs 6 July 2017 в 10:28

Чтобы выбрать типы аудио и видео кодеков и исключить видео h264:

$ jq '.format.filename as $path | [.streams[]? | select(.codec_type=="audio" or (.codec_type=="video" and .codec_name!="h264")) | {(.codec_type): .codec_name, $path}] | group_by(.path) | map(add) | .[]' input.json $ jq --version jq-1.5-1-a5b5cbe

Если команда оболочки становится сложной и занимает больше нескольких строк; Я переключусь на более подробный Python для управления сложностью:

result = dict(path=data['format']['filename']) for stream in data['streams']: if (stream['codec_type'] == 'audio' or (stream['codec_type'] == 'video' and stream['codec_name'] != 'h264')): result[stream['codec_type']] = stream['codec_name'] # last value wins

data - это вход (data = json.loads(json_text)), а result - желаемый вывод (print(json.dumps(result))).

Для вашего конкретного случая должно быть относительно просто адаптировать приведенный выше код, если вы более знакомы с императивным программированием на Python, чем с более функциональным стилем в jq.

2
ответ дан 18 July 2018 в 10:45

Чтобы выбрать типы аудио и видео кодеков и исключить видео h264:

$ jq '.format.filename as $path | [.streams[]? | select(.codec_type=="audio" or (.codec_type=="video" and .codec_name!="h264")) | {(.codec_type): .codec_name, $path}] | group_by(.path) | map(add) | .[]' input.json $ jq --version jq-1.5-1-a5b5cbe

Если команда оболочки становится сложной и занимает больше нескольких строк; Я переключусь на более подробный Python для управления сложностью:

result = dict(path=data['format']['filename']) for stream in data['streams']: if (stream['codec_type'] == 'audio' or (stream['codec_type'] == 'video' and stream['codec_name'] != 'h264')): result[stream['codec_type']] = stream['codec_name'] # last value wins

data - это вход (data = json.loads(json_text)), а result - желаемый вывод (print(json.dumps(result))).

Для вашего конкретного случая должно быть относительно просто адаптировать приведенный выше код, если вы более знакомы с императивным программированием на Python, чем с более функциональным стилем в jq.

2
ответ дан 24 July 2018 в 19:38

Вот еще один подход.

$ jq -M '
  def getpath: {path: .format.filename} ;
  def getcodecs: [
        .streams[]?
      | {(.codec_type):.codec_name}
      | if . == {"video":"h264"} then empty else . end
    ] | add
  ;
  getpath + getcodecs
  ' input.json

Выход

{
  "path": "Video.mkv",
  "video": "hevc",
  "audio": "aac"
}
1
ответ дан 22 May 2018 в 20:53

Вот еще один подход.

$ jq -M ' def getpath: {path: .format.filename} ; def getcodecs: [ .streams[]? | {(.codec_type):.codec_name} | if . == {"video":"h264"} then empty else . end ] | add ; getpath + getcodecs ' input.json

Выход

{ "path": "Video.mkv", "video": "hevc", "audio": "aac" }
1
ответ дан 18 July 2018 в 10:45

Вот еще один подход.

$ jq -M ' def getpath: {path: .format.filename} ; def getcodecs: [ .streams[]? | {(.codec_type):.codec_name} | if . == {"video":"h264"} then empty else . end ] | add ; getpath + getcodecs ' input.json

Выход

{ "path": "Video.mkv", "video": "hevc", "audio": "aac" }
1
ответ дан 24 July 2018 в 19:38

Другие вопросы по тегам:

Похожие вопросы: