From 12ad13e38dfc6c2c1bd7354fa6183b07864e15a6 Mon Sep 17 00:00:00 2001 From: Louis Sautier Date: Fri, 13 Nov 2020 23:20:55 +0100 Subject: [PATCH] =?UTF-8?q?Add=20shortcuts=20to=20tracks=20(audio=5Ftracks?= =?UTF-8?q?,=20video=5Ftracks=E2=80=A6),=20fixes=20#95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also add the following to improve tests: * Chapters to sample.mkv. * An image test file. * A MediaInfo XML output for a file containing tracks of type "Other". --- docs/index.rst | 13 ++++--- pymediainfo/__init__.py | 67 +++++++++++++++++++++++++++++++++++-- tests/data/empty.gif | Bin 0 -> 43 bytes tests/data/other_track.xml | 35 +++++++++++++++++++ tests/data/sample.mkv | Bin 5904 -> 6096 bytes tests/test_pymediainfo.py | 40 ++++++++++++++++++++++ 6 files changed, 148 insertions(+), 7 deletions(-) create mode 100644 tests/data/empty.gif create mode 100644 tests/data/other_track.xml diff --git a/docs/index.rst b/docs/index.rst index f6a77f8..a8e075f 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -40,15 +40,20 @@ Getting information from an image from pymediainfo import MediaInfo media_info = MediaInfo.parse("/home/user/image.jpg") - for track in media_info.tracks: - if track.track_type == "Image": - print(f"{track.format} of {track.width}×{track.height} pixels.") + # Tracks can be accessed via the 'tracks' attribute or through shortcuts + # such as 'image_tracks', 'audio_tracks', 'video_tracks', etc. + general_track = media_info.general_tracks[0] + image_track = media_info.image_tracks[0] + print( + f"{image_track.format} of {image_track.width}×{image_track.height} pixels" + f" and {general_track.file_size} bytes." + ) Will return something like: .. code-block:: none - JPEG of 828×828 pixels. + JPEG of 828×828 pixels and 19098 bytes. Getting information from a video -------------------------------- diff --git a/pymediainfo/__init__.py b/pymediainfo/__init__.py index f95b9d5..694424d 100644 --- a/pymediainfo/__init__.py +++ b/pymediainfo/__init__.py @@ -10,7 +10,7 @@ import sys import warnings import xml.etree.ElementTree as ET -from typing import Any, Dict, Optional, Tuple, Union +from typing import Any, Dict, List, Optional, Tuple, Union from pkg_resources import DistributionNotFound, get_distribution @@ -25,7 +25,7 @@ class Track: An object associated with a media file track. Each :class:`Track` attribute corresponds to attributes parsed from MediaInfo's output. - All attributes are lower case. Attributes that are present several times such as Duration + All attributes are lower case. Attributes that are present several times such as `Duration` yield a second attribute starting with `other_` which is a list of all alternative attribute values. @@ -38,7 +38,7 @@ class Track: >>> t.duration 3000 - >>> t.to_data()["other_duration"] + >>> t.other_duration ['3 s 0 ms', '3 s 0 ms', '3 s 0 ms', '00:00:03.000', '00:00:03.000'] >>> type(t.non_existing) @@ -166,6 +166,65 @@ def __init__(self, xml: str, encoding_errors: str = "strict"): for xml_track in xml_dom.iterfind(xpath): self.tracks.append(Track(xml_track)) + def _tracks(self, track_type: str) -> List[Track]: + return [track for track in self.tracks if track.track_type == track_type] + + @property + def general_tracks(self) -> List[Track]: + """ + :return: All :class:`Track`\\s of type ``General``. + :rtype: list of :class:`Track`\\s + """ + return self._tracks("General") + + @property + def video_tracks(self) -> List[Track]: + """ + :return: All :class:`Track`\\s of type ``Video``. + :rtype: list of :class:`Track`\\s + """ + return self._tracks("Video") + + @property + def audio_tracks(self) -> List[Track]: + """ + :return: All :class:`Track`\\s of type ``Audio``. + :rtype: list of :class:`Track`\\s + """ + return self._tracks("Audio") + + @property + def text_tracks(self) -> List[Track]: + """ + :return: All :class:`Track`\\s of type ``Text``. + :rtype: list of :class:`Track`\\s + """ + return self._tracks("Text") + + @property + def other_tracks(self) -> List[Track]: + """ + :return: All :class:`Track`\\s of type ``Other``. + :rtype: list of :class:`Track`\\s + """ + return self._tracks("Other") + + @property + def image_tracks(self) -> List[Track]: + """ + :return: All :class:`Track`\\s of type ``Image``. + :rtype: list of :class:`Track`\\s + """ + return self._tracks("Image") + + @property + def menu_tracks(self) -> List[Track]: + """ + :return: All :class:`Track`\\s of type ``Menu``. + :rtype: list of :class:`Track`\\s + """ + return self._tracks("Menu") + @staticmethod def _normalize_filename(filename: Any) -> Any: # TODO: wait for https://github.com/python/typeshed/pull/4582 pylint: disable=fixme @@ -264,6 +323,8 @@ def can_parse(cls, library_file: Optional[str] = None) -> bool: """ Checks whether media files can be analyzed using libmediainfo. + :param str library_file: path to the libmediainfo library, this should only be used if + the library cannot be auto-detected. :rtype: bool """ try: diff --git a/tests/data/empty.gif b/tests/data/empty.gif new file mode 100644 index 0000000000000000000000000000000000000000..3c51d740ca8120d8d798f4b7060f586a1fdfdbff GIT binary patch literal 43 mcmZ?wbhEHbWMp7u_`m=Kia%KxK};PG0g_>0Vsc?*um%8taRh7t literal 0 HcmV?d00001 diff --git a/tests/data/other_track.xml b/tests/data/other_track.xml new file mode 100644 index 0000000..96fff6c --- /dev/null +++ b/tests/data/other_track.xml @@ -0,0 +1,35 @@ + + + + + test.mxf + + + 2 + MPEG Video + Version 2 + + + 3 + PCM + + + 1-Material + Time code + MXF TC + 25.000 FPS + 00:00:00:00 + Material Package + Yes + + + 1-Source + Time code + MXF TC + 25.000 FPS + 00:00:00:00 + Source Package + Yes + + + diff --git a/tests/data/sample.mkv b/tests/data/sample.mkv index 71c8ac9e37c797e77355174410d17c9c2ba6f36f..dbfe03622d3267bc8674a7987642e5f2373e5edd 100644 GIT binary patch delta 371 zcmbQBcR_!G5xe*tLEqUWdnQ`SDk=Ex?g?JqA{25sJ$Ox%7?dmEyu1L&6)k?_zkg%l ze*St<&y{J8;#$`?HuF0<_%BxO8?f zK!HnQ2gl~Fn7XOQ7^+t~^teo3BhV-CPEOx+`oy5OTvvqnbHA?HyV*-Hn8gF+)Pt@& z_qpzEWN^I=#CtlP_t&STZT{20ZDsM1j;@aTmclhf^Cvbk%mB*HXk=`d)Y|2okyucY zTBKlL=v>pBnwS3O>BPcRE>I{qmK7H}7EN9*I-T*tWP33gRiZpGxm?Ul(8xg7&`{UV SSi#89%EZFT$awNTu^<3XEQx#o delta 297 zcmcbhKS6JT5xdwOLEqUWYbRRD3XA#f?g?JqA{25sJ$Ox%XyzOLa~m7C@lTu}Q7`Jb zGR=`!>-xrKekTXthQ&FVNvTP>ISOTldd7O@3fc-lf!xHBqWt3QM3A7Voe-?CLa)J5pX#j@~d1VT~O`8CMn0Zx6YeG1cO<)K(2MnEiQJ