-
-
Notifications
You must be signed in to change notification settings - Fork 141
/
Copy pathffprobe.py
148 lines (119 loc) · 3.82 KB
/
ffprobe.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
"""
ffmpeg_streaming._ffprobe
~~~~~~~~~~~~
Probe the video
:copyright: (c) 2020 by Amin Yazdanpanah.
:website: https://www.aminyazdanpanah.com
:email: [email protected]
:license: MIT, see LICENSE for more details.
"""
import json
import logging
import subprocess
from ._media_property import Size, Bitrate
class Streams:
def __init__(self, streams):
self.streams = streams
def video(self, ignore_error=True):
"""
@TODO: add documentation
"""
return self._get_stream('video', ignore_error)
def audio(self, ignore_error=True):
"""
@TODO: add documentation
"""
return self._get_stream('audio', ignore_error)
def first_stream(self):
"""
@TODO: add documentation
"""
return self.streams[0]
def all(self):
"""
@TODO: add documentation
"""
return self.streams
def videos(self):
"""
@TODO: add documentation
"""
return self._get_streams('video')
def audios(self):
"""
@TODO: add documentation
"""
return self._get_streams('audio')
def _get_stream(self, media, ignore_error):
"""
@TODO: add documentation
"""
media_attr = next((stream for stream in self.streams if stream['codec_type'] == media), None)
if media_attr is None and not ignore_error:
raise ValueError('No {} stream found'.format(str(media)))
return media_attr if media_attr is not None else {}
def _get_streams(self, media):
"""
@TODO: add documentation
"""
for stream in self.streams:
if stream['codec_type'] == media:
yield stream
class FFProbe:
def __init__(self, filename, cmd='ffprobe'):
"""
@TODO: add documentation
"""
commands = [cmd, '-show_format', '-show_streams', '-of', 'json', filename]
logging.info("ffprobe running command: {}".format(" ".join(commands)))
process = subprocess.Popen(commands, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
self.out, err = process.communicate()
if process.returncode != 0:
logging.error(str(self.out) + str(err))
raise RuntimeError('ffprobe', self.out, err)
logging.info("ffprobe executed command successfully!")
def streams(self):
"""
@TODO: add documentation
"""
return Streams(json.loads(self.out.decode('utf-8'))['streams'])
def format(self):
"""
@TODO: add documentation
"""
return json.loads(self.out.decode('utf-8'))['format']
def all(self):
"""
@TODO: add documentation
"""
return json.loads(self.out.decode('utf-8'))
def save_as_json(self, path):
"""
@TODO: add documentation
"""
with open(path, 'w', encoding='utf-8') as probe:
probe.write(self.out.decode('utf-8'))
@property
def video_size(self) -> Size:
"""
@TODO: add documentation
"""
width = int(self.streams().video().get('width', 0))
height = int(self.streams().video().get('height', 0))
if width == 0 or height == 0:
raise RuntimeError('It could not determine the value of width/height')
return Size(width, height)
@property
def bitrate(self, _type: str = "k") -> Bitrate:
"""
@TODO: add documentation
"""
overall = int(self.format().get('bit_rate', 0))
video = int(self.streams().video().get('bit_rate', 0))
audio = int(self.streams().audio().get('bit_rate', 0))
if overall == 0:
raise RuntimeError('It could not determine the value of bitrate')
return Bitrate(video, audio, overall, type=_type)
__all__ = [
'FFProbe'
]