From 5d90aca7a2302932b9246b29c7597fa5d938f8c3 Mon Sep 17 00:00:00 2001 From: Amin Date: Mon, 20 Apr 2020 21:44:58 +0430 Subject: [PATCH] update 0.1.1 renew almost all files --- README.md | 28 ++++++++-------- ffmpeg_streaming/_clouds.py | 15 +++++++++ ffmpeg_streaming/_command_builder.py | 11 ++++++- ffmpeg_streaming/_format.py | 21 ++++++++++++ ffmpeg_streaming/_hls_helper.py | 9 ++++++ ffmpeg_streaming/_input.py | 9 ++++++ ffmpeg_streaming/_media.py | 48 ++++++++++++++++++++++++++++ ffmpeg_streaming/_media_property.py | 36 +++++++++++++++++++++ ffmpeg_streaming/_media_streams.py | 24 ++++++++++++++ ffmpeg_streaming/_process.py | 15 +++++++++ ffmpeg_streaming/_reperesentation.py | 18 +++++++++++ ffmpeg_streaming/_utiles.py | 24 ++++++++++++++ ffmpeg_streaming/ffprobe.py | 21 ++++++++++++ 13 files changed, 263 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 70a10ec..041486c 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ import ffmpeg_streaming There are several ways to open a resource. #### 1. From a FFmpeg supported resource -You can pass a local path of video(or a supported resource) to the `open` method: +You can pass a local path of video(or a supported resource) to the `input` method: ```python video = ffmpeg_streaming.input('/var/media/video.mp4') ``` @@ -66,7 +66,7 @@ video = ffmpeg_streaming.input('https://www.aminyazdanpanah.com/?"PATH TO A VIDE ``` #### 2. From Clouds -You can open a file from a cloud by passing an array of cloud configuration to the `openFromCloud` method. +You can open a file from a cloud by passing an instance of a cloud configuration to the `input` method. ```python from ffmpeg_streaming import S3 @@ -77,10 +77,10 @@ video = ffmpeg_streaming.input(s3, bucket_name="bucket-name", key="video.mp4") Visit **[this page](https://video.aminyazdanpanah.com/python/start/clouds?r=open)** to see some examples of opening a file from **[Amazon S3](https://aws.amazon.com/s3)**, **[Google Cloud Storage](https://console.cloud.google.com/storage)**, **[Microsoft Azure Storage](https://azure.microsoft.com/en-us/features/storage-explorer/)**, and a custom cloud. #### 3. Capture Webcam or Screen (Live Streaming) -You can pass a name of the supported, connected capture device(i.e. name of webcam, camera, screen and etc) to the `capture` method to stream a live media over network. +You can pass a name of the supported, connected capture device(i.e. name of webcam, camera, screen and etc) to the `input` method to stream a live media over network. ```python -video = ffmpeg_streaming.input('CAMERA NAME OR SCREEN NAME', capture=True) +capture = ffmpeg_streaming.input('CAMERA NAME OR SCREEN NAME', capture=True) ``` To list the supported, connected capture devices, see **[FFmpeg Capture Webcam](https://trac.ffmpeg.org/wiki/Capture/Webcam)** and **[FFmpeg Capture Desktop](https://trac.ffmpeg.org/wiki/Capture/Desktop)**. @@ -134,7 +134,7 @@ _360p = Representation(Size(640, 360), Bitrate(276 * 1024, 128 * 1024)) _480p = Representation(Size(854, 480), Bitrate(750 * 1024, 192 * 1024)) _720p = Representation(Size(1280, 720), Bitrate(2048 * 1024, 320 * 1024)) -hls = video.hls(Formats.hevc()) +hls = video.hls(Formats.h264()) hls.representations(_360p, _480p, _720p) hls.output('/var/media/hls.m3u8') ``` @@ -174,7 +174,7 @@ See **[the example](https://video.aminyazdanpanah.com/python/start?r=enc-hls#hls ##### DRM However FFmpeg supports AES encryption for HLS packaging, which you can encrypt your content, it is not a full **[DRM](https://en.wikipedia.org/wiki/Digital_rights_management)** solution. If you want to use a full DRM solution, I recommend trying **[FairPlay Streaming](https://developer.apple.com/streaming/fps/)** solution which then securely exchange keys, and protect playback on devices. -**[Apple’s FairPlay](https://developer.apple.com/streaming/fps/)** is a recommended DRM system, but you can use other DRM systems such as **[Microsoft's PlayReady](https://www.microsoft.com/playready/overview/)** and **[Google’s Widevine](https://www.widevine.com/)**. +**Besides [Apple’s FairPlay](https://developer.apple.com/streaming/fps/)** DRM system, you can also use other DRM systems such as **[Microsoft's PlayReady](https://www.microsoft.com/playready/overview/)** and **[Google’s Widevine](https://www.widevine.com/)**. ### Transcoding You can get realtime information about the transcoding using the following code. @@ -208,8 +208,8 @@ dash.auto_generate_representations() dash.output('/var/media/dash.mpd') ``` -It can also be null. The default path to save files is the input path. -``` python +It can also be None. The default path to save files is the input path. +```python from ffmpeg_streaming import Formats hls = video.hls(Formats.h264()) @@ -217,10 +217,10 @@ hls.auto_generate_representations() hls.output() ``` -**NOTE:** If you open a file from a cloud and do not pass a path to save the file to your local machine, you will have to pass a local path to the `save` method. +**NOTE:** If you open a file from a cloud and do not pass a path to save the file to your local machine, you will have to pass a local path to the `output` method. #### 2. To Clouds -You can save your files to a cloud by passing an array of cloud configuration to the `save` method. +You can save your files to a cloud by passing an instance of a `CloudManager` to the `output` method. ```python from ffmpeg_streaming import S3, CloudManager @@ -244,9 +244,8 @@ Visit **[this page](https://video.aminyazdanpanah.com/python/start/clouds?r=save

#### 3. To a Server Instantly -You can pass a url(or a supported resource like `ftp`) to live method to upload all the segments files to the HTTP server(or other protocols) using the HTTP PUT method, and update the manifest files every refresh times. +You can pass a url(or a supported resource like `ftp`) to the `output` method to upload all the segments files to the HTTP server(or other protocols) using the HTTP PUT method, and update the manifest files every refresh times. -If you want to save stream files to your local machine, use the `save` method. ```python # DASH @@ -255,7 +254,7 @@ dash.output('http://YOUR-WEBSITE.COM/live-stream/out.mpd') # HLS hls.output('http://YOUR-WEBSITE.COM/live-stream/out.m3u8') ``` -**NOTE:** In the HLS format, you must upload the master playlist to the server manually. (Upload the `/var/www/stream/live-master-manifest.m3u8` file to the `http://YOUR-WEBSITE.COM`) +**NOTE:** In the HLS format, you must upload the master playlist to the server manually. See **[FFmpeg Protocols Documentation](https://ffmpeg.org/ffmpeg-protocols.html)** for more information. @@ -270,7 +269,7 @@ ffprobe = FFProbe('/var/media/video.mp4') See **[the example](https://video.aminyazdanpanah.com/python/start?r=metadata#metadata)** for more information. ### Conversion -You can convert your stream to a file or to another stream protocols. You should pass a manifest of the stream to the `open` method: +You can convert your stream to a file or to another stream protocols. You should pass a manifest of the stream to the `input` method: #### 1. HLS To DASH ```python @@ -300,7 +299,6 @@ video = ffmpeg_streaming.input('https://www.aminyazdanpanah.com/?PATH/TO/MANIFES stream = video.stream2file(Formats.h264()) stream.output('/var/media/new-video.mp4') - ``` ## Several Open Source Players diff --git a/ffmpeg_streaming/_clouds.py b/ffmpeg_streaming/_clouds.py index e88bdeb..572975b 100644 --- a/ffmpeg_streaming/_clouds.py +++ b/ffmpeg_streaming/_clouds.py @@ -19,6 +19,9 @@ class Clouds(abc.ABC): + """ + @TODO: add documentation + """ @abc.abstractmethod def upload_directory(self, directory: str, **options) -> None: pass @@ -30,6 +33,9 @@ def download(self, filename: str = None, **options) -> str: class S3(Clouds): def __init__(self, **options): + """ + @TODO: add documentation + """ try: import boto3 from botocore.exceptions import ClientError @@ -81,6 +87,9 @@ def download(self, filename=None, **options): class GCS(Clouds): def __init__(self, **options): + """ + @TODO: add documentation + """ try: from google.cloud import storage except ImportError as e: @@ -124,6 +133,9 @@ def download(self, filename=None, **options): class MAS(Clouds): def __init__(self, **options): + """ + @TODO: add documentation + """ try: from azure.storage.blob import BlockBlobService except ImportError as e: @@ -170,6 +182,9 @@ def download(self, filename=None, **options): class CloudManager: def __init__(self): + """ + @TODO: add documentation + """ self.clouds = [] def add(self, cloud: Clouds, **options): diff --git a/ffmpeg_streaming/_command_builder.py b/ffmpeg_streaming/_command_builder.py index dbcbf83..95fba32 100644 --- a/ffmpeg_streaming/_command_builder.py +++ b/ffmpeg_streaming/_command_builder.py @@ -77,7 +77,7 @@ def _get_hls_stream(hls, rep, dirname, name): 'b:a': rep.bitrate.audio, 'maxrate': rep.bitrate.max_rate, 'hls_segment_filename': dirname + "/" + name + "_" + str(rep.size.height) + "p_%04d." + _hls_seg_ext(hls), - 'hls_fmp4_init_filename': name + "_init.mp4", + 'hls_fmp4_init_filename': name + "_" + str(rep.size.height) + "p_init.mp4", 'strict': '-2' } args.update(hls.options) @@ -86,6 +86,9 @@ def _get_hls_stream(hls, rep, dirname, name): def _hls(hls): + """ + @TODO: add documentation + """ dirname, name = get_path_info(hls.output_) streams = [] for rep in hls.reps: @@ -95,10 +98,16 @@ def _hls(hls): def stream_args(media): + """ + @TODO: add documentation + """ return getattr(sys.modules[__name__], "_%s" % type(media).__name__.lower())(media) def command_builder(ffmpeg_bin: str, media): + """ + @TODO: add documentation + """ args = [ffmpeg_bin] + cnv_options_to_args(dict(media.media.input_opts)) + stream_args(media) return " ".join(clean_args(args)) diff --git a/ffmpeg_streaming/_format.py b/ffmpeg_streaming/_format.py index 7927a50..f8f600c 100644 --- a/ffmpeg_streaming/_format.py +++ b/ffmpeg_streaming/_format.py @@ -30,6 +30,9 @@ def _verify_codecs(codec, codecs): class Format(abc.ABC): + """ + @TODO: add documentation + """ def __init__(self, video: str, audio: str): self.video = video self.audio = audio @@ -40,6 +43,9 @@ def multiply(self) -> int: class H264(Format): + """ + @TODO: add documentation + """ def __init__(self, video: str = "libx264", audio: str = 'copy'): videos = ['libx264', 'h264', 'h264_afm'] audios = ['copy', 'aac', 'libvo_aacenc', 'libfaac', 'libmp3lame', 'libfdk_aac'] @@ -51,6 +57,9 @@ def multiply(self) -> int: class HEVC(Format): + """ + @TODO: add documentation + """ def __init__(self, video: str = "libx265", audio: str = 'copy'): videos = ['libx265', 'h265'] audios = ['copy', 'aac', 'libvo_aacenc', 'libfaac', 'libmp3lame', 'libfdk_aac'] @@ -62,6 +71,9 @@ def multiply(self) -> int: class VP9(Format): + """ + @TODO: add documentation + """ def __init__(self, video: str = "libvpx-vp9", audio: str = 'copy'): videos = ['libvpx', 'libvpx-vp9'] audios = ['copy', 'aac', 'libvo_aacenc', 'libfaac', 'libmp3lame', 'libfdk_aac'] @@ -75,14 +87,23 @@ def multiply(self) -> int: class Formats: @staticmethod def h264(video: str = "libx264", audio: str = 'copy') -> Format: + """ + @TODO: add documentation + """ return H264(video, audio) @staticmethod def hevc(video: str = "libx265", audio: str = 'copy') -> Format: + """ + @TODO: add documentation + """ return HEVC(video, audio) @staticmethod def vp9(video: str = "libvpx-vp9", audio: str = 'copy') -> Format: + """ + @TODO: add documentation + """ return VP9(video, audio) diff --git a/ffmpeg_streaming/_hls_helper.py b/ffmpeg_streaming/_hls_helper.py index 0538979..6128e04 100644 --- a/ffmpeg_streaming/_hls_helper.py +++ b/ffmpeg_streaming/_hls_helper.py @@ -19,6 +19,9 @@ class HLSKeyInfoFile: def __init__(self, key_info_file_path: str, path: str, url: str, period: int = 0, needle: str = '', length: int = 16): + """ + @TODO: add documentation + """ self.needle = needle self.period = period self.segments = [] @@ -71,6 +74,9 @@ def stream_info(rep) -> list: class HLSMasterPlaylist: def __init__(self, media): + """ + @TODO: add documentation + """ self.media = media self.path = media.output @@ -81,6 +87,9 @@ def generate(cls, media): playlist.write(cls(media)._content()) def _content(self) -> str: + """ + @TODO: add documentation + """ content = ['#EXTM3U'] + self._get_version() + self.media.options.get('description', []) for rep in self.media.reps: diff --git a/ffmpeg_streaming/_input.py b/ffmpeg_streaming/_input.py index 4bf757a..1a8d00d 100644 --- a/ffmpeg_streaming/_input.py +++ b/ffmpeg_streaming/_input.py @@ -17,6 +17,9 @@ class Capture(object): def __init__(self, video, options): + """ + @TODO: add documentation + """ self.options = options self.video = video @@ -58,6 +61,9 @@ def __iter__(self): def get_from_cloud(cloud: Clouds, options: dict): + """ + @TODO: add documentation + """ save_to = options.pop('save_to', None) return { 'i': cloud.download(save_to, **options), @@ -67,6 +73,9 @@ def get_from_cloud(cloud: Clouds, options: dict): class InputOption(object): def __init__(self, _input, **options): + """ + @TODO: add documentation + """ self.input_ = _input self.options = options diff --git a/ffmpeg_streaming/_media.py b/ffmpeg_streaming/_media.py index dd5525d..104aee6 100644 --- a/ffmpeg_streaming/_media.py +++ b/ffmpeg_streaming/_media.py @@ -29,6 +29,9 @@ class Save(abc.ABC): def __init__(self, media, _format: Format, **options): + """ + @TODO: add documentation + """ atexit.register(self.finish_up) self.output_ = '' @@ -40,6 +43,9 @@ def __init__(self, media, _format: Format, **options): self.output_temp = False def finish_up(self): + """ + @TODO: add documentation + """ if self.media.input_temp: os.remove(self.media.input) if self.output_temp: @@ -59,6 +65,9 @@ def method(*args, **kwargs): return method def output(self, output: str = None, clouds: CloudManager = None, monitor: callable = None, ffmpeg_bin: str = 'ffmpeg', **options): + """ + @TODO: add documentation + """ if output is None and clouds is None: self.output_ = self.media.input elif clouds is not None: @@ -77,12 +86,18 @@ def output(self, output: str = None, clouds: CloudManager = None, monitor: calla shutil.move(os.path.dirname(self.output_), os.path.dirname(output)) def _run(self, ffmpeg_bin, monitor: callable = None, **options): + """ + @TODO: add documentation + """ with Process(self, command_builder(ffmpeg_bin, self), monitor, **options) as process: self.pipe, err = process.run() class Streaming(Save, ABC): def __init__(self, media, _format: Format, **options): + """ + @TODO: add documentation + """ self.reps = list super(Streaming, self).__init__(media, _format, **options) @@ -90,6 +105,9 @@ def representations(self, *reps: Representation): self.reps = list(reps) def auto_generate_representations(self, heights=None, bitrate=None, ffprobe_bin='ffprobe'): + """ + @TODO: add documentation + """ probe = FFProbe(self.media.input, ffprobe_bin) self.reps = AutoRep(probe.video_size, probe.bitrate, self.format, heights, bitrate) @@ -106,9 +124,15 @@ class HLS(Streaming): KEY_INFO_FILE_PATH = None def set_up(self): + """ + @TODO: add documentation + """ HLSMasterPlaylist.generate(self) def encryption(self, path: str, url: str, key_rotation_period: int = 0, needle: str = ".ts' for writing", length: int = 16): + """ + @TODO: add documentation + """ with tempfile.NamedTemporaryFile(mode='w', suffix='_py_ff_vi_st.tmp', delete=False) as key_info: HLS.KEY_INFO_FILE_PATH = key_info.name @@ -119,14 +143,23 @@ def encryption(self, path: str, url: str, key_rotation_period: int = 0, needle: self.flags('periodic_rekey') def fragmented_mp4(self): + """ + @TODO: add documentation + """ self.options.update({'hls_segment_type': 'fmp4'}) def flags(self, *flags: str): + """ + @TODO: add documentation + """ hls_flags = self.options.pop('hls_flags', None) hls_flags = hls_flags + "+" + "+".join(list(flags)) if hls_flags is not None else "+".join(list(flags)) self.options.update({'hls_flags': hls_flags}) def finish_up(self): + """ + @TODO: add documentation + """ if HLS.KEY_INFO_FILE_PATH is not None: shutil.rmtree(HLS.KEY_INFO_FILE_PATH, ignore_errors=True) @@ -134,23 +167,38 @@ def finish_up(self): class Stream2File(Save): + """ + @TODO: add documentation + """ def set_up(self): pass class Media(object): def __init__(self, input_opts): + """ + @TODO: add documentation + """ options = dict(input_opts) self.input = options.get('i', None) self.input_temp = options.pop('is_tmp', False) self.input_opts = options def hls(self, _format: Format, **options): + """ + @TODO: add documentation + """ return HLS(self, _format, **options) def dash(self, _format: Format, **options): + """ + @TODO: add documentation + """ return DASH(self, _format, **options) def stream2file(self, _format: Format, **options): + """ + @TODO: add documentation + """ return Stream2File(self, _format, **options) diff --git a/ffmpeg_streaming/_media_property.py b/ffmpeg_streaming/_media_property.py index 0727d55..0cee90e 100644 --- a/ffmpeg_streaming/_media_property.py +++ b/ffmpeg_streaming/_media_property.py @@ -29,6 +29,9 @@ def cnv_bitrate(bitrate: int, _type: str) -> str: class Bitrate: def __init__(self, video: int = None, audio: int = None, overall: int = None, **kwargs): + """ + @TODO: add documentation + """ if video is None and overall is None: raise ValueError("You must at least specify value of the video or overall format") self.overall_ = overall @@ -38,17 +41,29 @@ def __init__(self, video: int = None, audio: int = None, overall: int = None, ** @property def overall(self): + """ + @TODO: add documentation + """ return cnv_bitrate(self.overall_, self.type) if self.overall_ is not None else None @property def video(self): + """ + @TODO: add documentation + """ return cnv_bitrate(self.video_, self.type) if self.video_ is not None else None @property def audio(self): + """ + @TODO: add documentation + """ return cnv_bitrate(self.audio_, self.type) if self.audio_ is not None else 'copy' def normalize_video(self, convert: bool = True): + """ + @TODO: add documentation + """ if self.video_ is not None and self.video_ != 0: val = self.video_ else: @@ -58,6 +73,9 @@ def normalize_video(self, convert: bool = True): @property def max_rate(self): + """ + @TODO: add documentation + """ return cnv_bitrate(int(self.normalize_video(False) * MAX_RATE_COEFFICIENT), self.type) @@ -77,6 +95,9 @@ def multiple_down(value, multiple): class Ratio: def __init__(self, width: int, height: int): + """ + @TODO: add documentation + """ self.width = width self.height = height @@ -84,6 +105,9 @@ def get_value(self) -> float: return self.width / self.height def calculate_width(self, height: int, multiple: int = 1) -> int: + """ + @TODO: add documentation + """ max_w = multiple_up(math.ceil(self.get_value() * height), multiple) min_w = multiple_down(math.floor(self.get_value() * height), multiple) @@ -93,6 +117,9 @@ def calculate_width(self, height: int, multiple: int = 1) -> int: return max_w if max_r < min_r else min_w def calculate_height(self, width: int, multiple: int = 1) -> int: + """ + @TODO: add documentation + """ max_h = multiple_up(math.ceil(width / self.get_value()), multiple) min_h = multiple_down(math.floor(width / self.get_value()), multiple) @@ -104,15 +131,24 @@ def calculate_height(self, width: int, multiple: int = 1) -> int: class Size: def __init__(self, width: int, height: int): + """ + @TODO: add documentation + """ self.width = width self.height = height @property def ratio(self) -> Ratio: + """ + @TODO: add documentation + """ return Ratio(self.width, self.height) @property def normalize(self) -> str: + """ + @TODO: add documentation + """ return str(self.width) + "x" + str(self.height) diff --git a/ffmpeg_streaming/_media_streams.py b/ffmpeg_streaming/_media_streams.py index 3e76bcb..899b156 100644 --- a/ffmpeg_streaming/_media_streams.py +++ b/ffmpeg_streaming/_media_streams.py @@ -17,30 +17,54 @@ def __init__(self, streams): self.streams = streams def video(self): + """ + @TODO: add documentation + """ return self._get_stream('video') def audio(self): + """ + @TODO: add documentation + """ return self._get_stream('audio') 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): + """ + @TODO: add documentation + """ media_attr = next((stream for stream in self.streams if stream['codec_type'] == media), None) if media_attr is None: raise ValueError('No ' + str(media) + ' stream found') return media_attr def _get_streams(self, media): + """ + @TODO: add documentation + """ for stream in self.streams: if stream['codec_type'] == media: yield stream diff --git a/ffmpeg_streaming/_process.py b/ffmpeg_streaming/_process.py index 587ca23..738bc92 100644 --- a/ffmpeg_streaming/_process.py +++ b/ffmpeg_streaming/_process.py @@ -21,6 +21,9 @@ def _p_open(commands, **options): + """ + @TODO: add documentation + """ logging.info("ffmpeg running command: {}".format(commands)) return subprocess.Popen(shlex.split(commands), **options) @@ -30,6 +33,9 @@ class Process(object): err = None def __init__(self, media, commands: str, monitor: callable = None, **options): + """ + @TODO: add documentation + """ self.is_monitor = False self.input = options.pop('input', None) self.timeout = options.pop('timeout', None) @@ -59,6 +65,9 @@ def __exit__(self, exc_type, exc_val, exc_tb): self.process.kill() def _monitor(self): + """ + @TODO: add documentation + """ duration = 1 time = 0 log = [] @@ -86,6 +95,9 @@ def _monitor(self): Process.out = log def _thread_mon(self): + """ + @TODO: add documentation + """ thread = threading.Thread(target=self._monitor) thread.start() @@ -98,6 +110,9 @@ def _thread_mon(self): raise RuntimeError(error) def run(self): + """ + @TODO: add documentation + """ if self.is_monitor: self._thread_mon() else: diff --git a/ffmpeg_streaming/_reperesentation.py b/ffmpeg_streaming/_reperesentation.py index b33c59c..9462039 100644 --- a/ffmpeg_streaming/_reperesentation.py +++ b/ffmpeg_streaming/_reperesentation.py @@ -18,11 +18,17 @@ class Representation: def __init__(self, size: Size, bitrate: Bitrate): + """ + @TODO: add documentation + """ self.size = size self.bitrate = bitrate def min_bitrate(bitrate: int) -> int: + """ + @TODO: add documentation + """ if bitrate < MINIMUM_BITRATE: return MINIMUM_BITRATE @@ -30,6 +36,9 @@ def min_bitrate(bitrate: int) -> int: def reduce_bitrate(bitrate: Bitrate, divide: int) -> Bitrate: + """ + @TODO: add documentation + """ if divide == 1: return bitrate @@ -48,6 +57,9 @@ def cal_bitrate(bitrate, org_bitrate: Bitrate, index: int) -> Bitrate: class AutoRep(object): def __init__(self, original_size: Size, original_bitrate: Bitrate, _format: Format, heights: list = None, bitrate: list = None): + """ + @TODO: add documentation + """ self.original_bitrate = original_bitrate self.original_size = original_size self._format = _format @@ -59,6 +71,9 @@ def __init__(self, original_size: Size, original_bitrate: Bitrate, _format: Form raise ValueError("The length of heights list must the same as length of bitrate") def __iter__(self): + """ + @TODO: add documentation + """ if not self.is_default: height = self.original_size.ratio.calculate_height(self.original_size.width, self._format.multiply()) self.heights = [height] + list(filter(lambda x: x < height, self.heights)) @@ -68,6 +83,9 @@ def __iter__(self): return self def __next__(self): + """ + @TODO: add documentation + """ if self.index < len(self.heights): height = self.heights[self.index] width = self.original_size.ratio.calculate_width(height, self._format.multiply()) diff --git a/ffmpeg_streaming/_utiles.py b/ffmpeg_streaming/_utiles.py index cc059e1..a668dfc 100644 --- a/ffmpeg_streaming/_utiles.py +++ b/ffmpeg_streaming/_utiles.py @@ -18,6 +18,9 @@ def get_path_info(path): + """ + @TODO: add documentation + """ dirname = os.path.dirname(path) name = str(os.path.basename(path).split('.')[0]) @@ -25,6 +28,9 @@ def get_path_info(path): def mkdir(dirname: str) -> None: + """ + @TODO: add documentation + """ try: os.makedirs(dirname) except OSError as exc: @@ -33,6 +39,9 @@ def mkdir(dirname: str) -> None: def clean_args(args: list) -> list: + """ + @TODO: add documentation + """ clean_args_ = [] for arg in args: if " " in arg: @@ -43,17 +52,26 @@ def clean_args(args: list) -> list: def convert_to_sec(time): + """ + @TODO: add documentation + """ h, m, s = time.split(":") return int(h) * 3600 + int(m) * 60 + int(s) def get_time(key, string): + """ + @TODO: add documentation + """ time = re.search('(?<=' + key + ')\w+:\w+:\w+', string) if time: return convert_to_sec(time.group(0)) def deprecated(func): + """ + @TODO: add documentation + """ def deprecated_fun(*args, **kwargs): warnings.warn('The {} method is deprecated and will be removed in a future release'.format(func.__name__) , DeprecationWarning, stacklevel=2) @@ -62,6 +80,9 @@ def deprecated_fun(*args, **kwargs): def get_os(): + """ + @TODO: add documentation + """ if platform == "linux" or platform == "linux2": os_name = 'linux' elif platform == "darwin": @@ -75,6 +96,9 @@ def get_os(): def cnv_options_to_args(options: dict): + """ + @TODO: add documentation + """ args = [] for k, v in options.items(): args.append('-{}'.format(k)) diff --git a/ffmpeg_streaming/ffprobe.py b/ffmpeg_streaming/ffprobe.py index e979dce..50d900b 100644 --- a/ffmpeg_streaming/ffprobe.py +++ b/ffmpeg_streaming/ffprobe.py @@ -21,6 +21,9 @@ 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) @@ -31,20 +34,35 @@ def __init__(self, filename, cmd='ffprobe'): 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') 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)) @@ -55,6 +73,9 @@ def video_size(self) -> Size: @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))