Release: d4bf62e70e70b47431f7471f25637ecf
This commit is contained in:
parent
488c70884b
commit
aa39140bc8
164
__init__.py
164
__init__.py
@ -16,6 +16,7 @@ media (Media Tools)
|
||||
**Submodules:**
|
||||
|
||||
* :func:`media.get_media_data`
|
||||
* :class:`media.image`
|
||||
|
||||
**Unittest:**
|
||||
|
||||
@ -24,7 +25,7 @@ media (Media Tools)
|
||||
__DEPENDENCIES__ = []
|
||||
|
||||
import logging
|
||||
from media import metadata
|
||||
from PIL import Image, ImageEnhance
|
||||
|
||||
logger_name = 'MEDIA'
|
||||
logger = logging.getLogger(logger_name)
|
||||
@ -37,14 +38,161 @@ __INTERPRETER__ = (3, )
|
||||
"""The Tested Interpreter-Versions"""
|
||||
|
||||
|
||||
KEY_ALBUM = 'album'
|
||||
KEY_APERTURE = 'aperture'
|
||||
KEY_ARTIST = 'artist'
|
||||
KEY_BITRATE = 'bitrate'
|
||||
KEY_CAMERA = 'camera'
|
||||
KEY_DURATION = 'duration'
|
||||
KEY_EXPOSURE_PROGRAM = 'exposure_program'
|
||||
KEY_EXPOSURE_TIME = 'exposure_time'
|
||||
KEY_FLASH = 'flash'
|
||||
KEY_FOCAL_LENGTH = 'focal_length'
|
||||
KEY_GENRE = 'genre'
|
||||
KEY_GPS = 'gps'
|
||||
KEY_HEIGHT = 'height'
|
||||
KEY_ISO = 'iso'
|
||||
KEY_ORIENTATION = 'orientation'
|
||||
KEY_RATIO = 'ratio'
|
||||
KEY_SIZE = 'size'
|
||||
KEY_TIME = 'time' # USE time.localtime(value) or datetime.fromtimestamp(value) to convert the timestamp
|
||||
KEY_TIME_IS_SUBSTITUTION = 'tm_is_subst'
|
||||
KEY_TITLE = 'title'
|
||||
KEY_TRACK = 'track'
|
||||
KEY_WIDTH = 'width'
|
||||
KEY_YEAR = 'year'
|
||||
|
||||
|
||||
def get_media_data(full_path):
|
||||
ft = metadata.get_filetype(full_path)
|
||||
from media.metadata import get_audio_data, get_image_data, get_video_data
|
||||
from media.common import get_filetype, FILETYPE_AUDIO, FILETYPE_IMAGE, FILETYPE_VIDEO
|
||||
#
|
||||
if ft == metadata.FILETYPE_AUDIO:
|
||||
return metadata.get_audio_data(full_path)
|
||||
elif ft == metadata.FILETYPE_IMAGE:
|
||||
return metadata.get_image_data(full_path)
|
||||
elif ft == metadata.FILETYPE_VIDEO:
|
||||
return metadata.get_video_data(full_path)
|
||||
ft = get_filetype(full_path)
|
||||
#
|
||||
if ft == FILETYPE_AUDIO:
|
||||
return get_audio_data(full_path)
|
||||
elif ft == FILETYPE_IMAGE:
|
||||
return get_image_data(full_path)
|
||||
elif ft == FILETYPE_VIDEO:
|
||||
return get_video_data(full_path)
|
||||
else:
|
||||
logger.warning('Filetype not known: %s', full_path)
|
||||
|
||||
|
||||
ORIENTATION_NORMAL = 1
|
||||
ORIENTATION_VERTICAL_MIRRORED = 2
|
||||
ORIENTATION_HALF_ROTATED = 3
|
||||
ORIENTATION_HORIZONTAL_MIRRORED = 4
|
||||
ORIENTATION_LEFT_ROTATED = 6
|
||||
ORIENTATION_RIGHT_ROTATED = 8
|
||||
|
||||
JOIN_TOP_LEFT = 1
|
||||
JOIN_TOP_RIGHT = 2
|
||||
JOIN_BOT_LEFT = 3
|
||||
JOIN_BOT_RIGHT = 4
|
||||
JOIN_CENTER = 5
|
||||
|
||||
|
||||
class image(object):
|
||||
def __init__(self, media_instance=None):
|
||||
if media_instance is not None:
|
||||
self.load_from_file(media_instance)
|
||||
else:
|
||||
self._im = None
|
||||
|
||||
def load_from_file(self, media_instance):
|
||||
from media.convert import get_pil_image
|
||||
#
|
||||
self._im = get_pil_image(media_instance)
|
||||
if self._im is None:
|
||||
return False
|
||||
return True
|
||||
|
||||
def save(self, full_path):
|
||||
if self._im is None:
|
||||
logger.warning('No image available to be saved (%s)', repr(full_path))
|
||||
return False
|
||||
else:
|
||||
logger.debug('Saving image to %s', repr(full_path))
|
||||
with open(full_path, 'w') as fh:
|
||||
im = self._im.convert('RGB')
|
||||
im.save(fh, 'JPEG')
|
||||
return True
|
||||
|
||||
def resize(self, max_size):
|
||||
if self._im is None:
|
||||
logger.warning('No image available to be resized')
|
||||
return False
|
||||
else:
|
||||
logger.debug('Resizing picture to max %d pixel in whatever direction', max_size)
|
||||
x, y = self._im.size
|
||||
xy_max = max(x, y)
|
||||
self._im = self._im.resize((int(x * float(max_size) / xy_max), int(y * float(max_size) / xy_max)), Image.NEAREST).rotate(0)
|
||||
return True
|
||||
|
||||
def rotate_by_orientation(self, orientation):
|
||||
if self._im is None:
|
||||
logger.warning('No image available, rotation not possible')
|
||||
return False
|
||||
|
||||
if orientation == ORIENTATION_HALF_ROTATED:
|
||||
angle = 180
|
||||
elif orientation == ORIENTATION_LEFT_ROTATED:
|
||||
angle = 270
|
||||
elif orientation == ORIENTATION_RIGHT_ROTATED:
|
||||
angle = 90
|
||||
else:
|
||||
logger.warning('Orientation %s unknown for rotation', repr(orientation))
|
||||
return False
|
||||
logger.debug('Rotating picture by %d°', angle)
|
||||
self._im = self._im.rotate(angle, expand=True)
|
||||
return True
|
||||
|
||||
def join(self, join_image, join_pos=JOIN_TOP_RIGHT, opacity=0.7):
|
||||
from media.convert import get_pil_image
|
||||
|
||||
def rgba_copy(im):
|
||||
if im.mode != 'RGBA':
|
||||
return im.convert('RGBA')
|
||||
else:
|
||||
return im.copy()
|
||||
|
||||
if self._im is None:
|
||||
logger.warning('No image available, joining not possible')
|
||||
return False
|
||||
|
||||
# ensure type of join_image is PIL.Image
|
||||
join_image = get_pil_image(join_image)
|
||||
if join_image is None:
|
||||
logger.warning('Image to be joined is not supported %s', repr(join_image))
|
||||
return False
|
||||
|
||||
im2 = rgba_copy(join_image)
|
||||
# change opacity of im2
|
||||
alpha = im2.split()[3]
|
||||
alpha = ImageEnhance.Brightness(alpha).enhance(opacity)
|
||||
im2.putalpha(alpha)
|
||||
|
||||
self._im = rgba_copy(self._im)
|
||||
|
||||
# create a transparent layer
|
||||
layer = Image.new('RGBA', self._im.size, (0, 0, 0, 0))
|
||||
# draw im2 in layer
|
||||
if join_pos == JOIN_TOP_LEFT:
|
||||
layer.paste(im2, (0, 0))
|
||||
elif join_pos == JOIN_TOP_RIGHT:
|
||||
layer.paste(im2, ((self._im.size[0] - im2.size[0]), 0))
|
||||
elif join_pos == JOIN_BOT_LEFT:
|
||||
layer.paste(im2, (0, (self._im.size[1] - im2.size[1])))
|
||||
elif join_pos == JOIN_BOT_RIGHT:
|
||||
layer.paste(im2, ((self._im.size[0] - im2.size[0]), (self._im.size[1] - im2.size[1])))
|
||||
elif join_pos == JOIN_CENTER:
|
||||
layer.paste(im2, (int((self._im.size[0] - im2.size[0]) / 2), int((self._im.size[1] - im2.size[1]) / 2)))
|
||||
else:
|
||||
logger.warning("Join position value %s is not supported", join_pos)
|
||||
return False
|
||||
|
||||
logger.debug('Joining two images')
|
||||
self._im = Image.composite(layer, self._im, layer)
|
||||
|
||||
return True
|
||||
|
@ -1,34 +1,168 @@
|
||||
<?xml version="1.0" ?>
|
||||
<coverage branch-rate="0.9762" branches-covered="41" branches-valid="42" complexity="0" line-rate="0.9857" lines-covered="207" lines-valid="210" timestamp="1580454618005" version="4.5">
|
||||
<coverage branch-rate="0.9643" branches-covered="81" branches-valid="84" complexity="0" line-rate="0.9759" lines-covered="324" lines-valid="332" timestamp="1580584174451" version="4.5">
|
||||
<!-- Generated by coverage.py: https://coverage.readthedocs.io -->
|
||||
<!-- Based on https://raw.githubusercontent.com/cobertura/web/master/htdocs/xml/coverage-04.dtd -->
|
||||
<sources/>
|
||||
<packages>
|
||||
<package branch-rate="0.9762" complexity="0" line-rate="0.9857" name=".user_data.data.dirk.prj.unittest.media.pylibs.media">
|
||||
<package branch-rate="0.9643" complexity="0" line-rate="0.9759" name=".user_data.data.dirk.prj.unittest.media.pylibs.media">
|
||||
<classes>
|
||||
<class branch-rate="1" complexity="0" filename="/user_data/data/dirk/prj/unittest/media/pylibs/media/__init__.py" line-rate="1" name="__init__.py">
|
||||
<class branch-rate="0.9737" complexity="0" filename="/user_data/data/dirk/prj/unittest/media/pylibs/media/__init__.py" line-rate="0.9924" name="__init__.py">
|
||||
<methods/>
|
||||
<lines>
|
||||
<line hits="1" number="4"/>
|
||||
<line hits="1" number="24"/>
|
||||
<line hits="1" number="26"/>
|
||||
<line hits="1" number="25"/>
|
||||
<line hits="1" number="27"/>
|
||||
<line hits="1" number="29"/>
|
||||
<line hits="1" number="28"/>
|
||||
<line hits="1" number="30"/>
|
||||
<line hits="1" number="33"/>
|
||||
<line hits="1" number="36"/>
|
||||
<line hits="1" number="40"/>
|
||||
<line hits="1" number="31"/>
|
||||
<line hits="1" number="34"/>
|
||||
<line hits="1" number="37"/>
|
||||
<line hits="1" number="41"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="43"/>
|
||||
<line hits="1" number="42"/>
|
||||
<line hits="1" number="43"/>
|
||||
<line hits="1" number="44"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="45"/>
|
||||
<line hits="1" number="45"/>
|
||||
<line hits="1" number="46"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="47"/>
|
||||
<line hits="1" number="47"/>
|
||||
<line hits="1" number="48"/>
|
||||
<line hits="1" number="49"/>
|
||||
<line hits="1" number="50"/>
|
||||
<line hits="1" number="51"/>
|
||||
<line hits="1" number="52"/>
|
||||
<line hits="1" number="53"/>
|
||||
<line hits="1" number="54"/>
|
||||
<line hits="1" number="55"/>
|
||||
<line hits="1" number="56"/>
|
||||
<line hits="1" number="57"/>
|
||||
<line hits="1" number="58"/>
|
||||
<line hits="1" number="59"/>
|
||||
<line hits="1" number="60"/>
|
||||
<line hits="1" number="61"/>
|
||||
<line hits="1" number="62"/>
|
||||
<line hits="1" number="63"/>
|
||||
<line hits="1" number="66"/>
|
||||
<line hits="1" number="67"/>
|
||||
<line hits="1" number="68"/>
|
||||
<line hits="1" number="70"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="72"/>
|
||||
<line hits="1" number="73"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="74"/>
|
||||
<line hits="1" number="75"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="76"/>
|
||||
<line hits="1" number="77"/>
|
||||
<line hits="1" number="79"/>
|
||||
<line hits="1" number="82"/>
|
||||
<line hits="1" number="83"/>
|
||||
<line hits="1" number="84"/>
|
||||
<line hits="1" number="85"/>
|
||||
<line hits="1" number="86"/>
|
||||
<line hits="1" number="87"/>
|
||||
<line hits="1" number="89"/>
|
||||
<line hits="1" number="90"/>
|
||||
<line hits="1" number="91"/>
|
||||
<line hits="1" number="92"/>
|
||||
<line hits="1" number="93"/>
|
||||
<line hits="1" number="96"/>
|
||||
<line hits="1" number="97"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="98"/>
|
||||
<line hits="1" number="99"/>
|
||||
<line hits="1" number="101"/>
|
||||
<line hits="1" number="103"/>
|
||||
<line hits="1" number="104"/>
|
||||
<line hits="1" number="106"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="107"/>
|
||||
<line hits="1" number="108"/>
|
||||
<line hits="1" number="109"/>
|
||||
<line hits="1" number="111"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="112"/>
|
||||
<line hits="1" number="113"/>
|
||||
<line hits="1" number="114"/>
|
||||
<line hits="1" number="116"/>
|
||||
<line hits="1" number="117"/>
|
||||
<line hits="1" number="118"/>
|
||||
<line hits="1" number="119"/>
|
||||
<line hits="1" number="120"/>
|
||||
<line hits="1" number="122"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="123"/>
|
||||
<line hits="1" number="124"/>
|
||||
<line hits="1" number="125"/>
|
||||
<line hits="1" number="127"/>
|
||||
<line hits="1" number="128"/>
|
||||
<line hits="1" number="129"/>
|
||||
<line hits="1" number="130"/>
|
||||
<line hits="1" number="131"/>
|
||||
<line hits="1" number="133"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="134"/>
|
||||
<line hits="1" number="135"/>
|
||||
<line hits="1" number="136"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="138"/>
|
||||
<line hits="1" number="139"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="140"/>
|
||||
<line hits="1" number="141"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="142"/>
|
||||
<line hits="1" number="143"/>
|
||||
<line hits="1" number="145"/>
|
||||
<line hits="1" number="146"/>
|
||||
<line hits="1" number="147"/>
|
||||
<line hits="1" number="148"/>
|
||||
<line hits="1" number="149"/>
|
||||
<line hits="1" number="151"/>
|
||||
<line hits="1" number="152"/>
|
||||
<line hits="1" number="154"/>
|
||||
<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="158" number="155"/>
|
||||
<line hits="1" number="156"/>
|
||||
<line hits="0" number="158"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="160"/>
|
||||
<line hits="1" number="161"/>
|
||||
<line hits="1" number="162"/>
|
||||
<line hits="1" number="165"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="166"/>
|
||||
<line hits="1" number="167"/>
|
||||
<line hits="1" number="168"/>
|
||||
<line hits="1" number="170"/>
|
||||
<line hits="1" number="172"/>
|
||||
<line hits="1" number="173"/>
|
||||
<line hits="1" number="174"/>
|
||||
<line hits="1" number="176"/>
|
||||
<line hits="1" number="179"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="181"/>
|
||||
<line hits="1" number="182"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="183"/>
|
||||
<line hits="1" number="184"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="185"/>
|
||||
<line hits="1" number="186"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="187"/>
|
||||
<line hits="1" number="188"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="189"/>
|
||||
<line hits="1" number="190"/>
|
||||
<line hits="1" number="192"/>
|
||||
<line hits="1" number="193"/>
|
||||
<line hits="1" number="195"/>
|
||||
<line hits="1" number="196"/>
|
||||
<line hits="1" number="198"/>
|
||||
</lines>
|
||||
</class>
|
||||
<class branch-rate="0.9722" complexity="0" filename="/user_data/data/dirk/prj/unittest/media/pylibs/media/metadata.py" line-rate="0.9845" name="metadata.py">
|
||||
<class branch-rate="1" complexity="0" filename="/user_data/data/dirk/prj/unittest/media/pylibs/media/common.py" line-rate="1" name="common.py">
|
||||
<methods/>
|
||||
<lines>
|
||||
<line hits="1" number="1"/>
|
||||
<line hits="1" number="3"/>
|
||||
<line hits="1" number="4"/>
|
||||
<line hits="1" number="5"/>
|
||||
<line hits="1" number="7"/>
|
||||
<line hits="1" number="8"/>
|
||||
<line hits="1" number="9"/>
|
||||
<line hits="1" number="12"/>
|
||||
<line hits="1" number="13"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="14"/>
|
||||
<line hits="1" number="15"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="16"/>
|
||||
<line hits="1" number="17"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="18"/>
|
||||
<line hits="1" number="19"/>
|
||||
</lines>
|
||||
</class>
|
||||
<class branch-rate="0.9" complexity="0" filename="/user_data/data/dirk/prj/unittest/media/pylibs/media/convert.py" line-rate="0.8667" name="convert.py">
|
||||
<methods/>
|
||||
<lines>
|
||||
<line hits="1" number="1"/>
|
||||
@ -38,12 +172,46 @@
|
||||
<line hits="1" number="5"/>
|
||||
<line hits="1" number="8"/>
|
||||
<line hits="1" number="9"/>
|
||||
<line hits="1" number="10"/>
|
||||
<line hits="1" number="11"/>
|
||||
<line hits="1" number="12"/>
|
||||
<line hits="1" number="13"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="14"/>
|
||||
<line hits="1" number="15"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="16"/>
|
||||
<line hits="1" number="17"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="18"/>
|
||||
<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="22" number="19"/>
|
||||
<line hits="1" number="20"/>
|
||||
<line hits="0" number="22"/>
|
||||
<line hits="1" number="23"/>
|
||||
<line hits="1" number="24"/>
|
||||
<line hits="0" number="25"/>
|
||||
<line hits="0" number="26"/>
|
||||
<line hits="0" number="27"/>
|
||||
<line hits="1" number="28"/>
|
||||
<line hits="1" number="29"/>
|
||||
<line hits="1" number="30"/>
|
||||
<line hits="1" number="31"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="32"/>
|
||||
<line hits="1" number="33"/>
|
||||
<line hits="1" number="35"/>
|
||||
</lines>
|
||||
</class>
|
||||
<class branch-rate="0.9667" complexity="0" filename="/user_data/data/dirk/prj/unittest/media/pylibs/media/metadata.py" line-rate="0.9808" name="metadata.py">
|
||||
<methods/>
|
||||
<lines>
|
||||
<line hits="1" number="1"/>
|
||||
<line hits="1" number="2"/>
|
||||
<line hits="1" number="3"/>
|
||||
<line hits="1" number="4"/>
|
||||
<line hits="1" number="5"/>
|
||||
<line hits="1" number="6"/>
|
||||
<line hits="1" number="9"/>
|
||||
<line hits="1" number="12"/>
|
||||
<line hits="1" number="13"/>
|
||||
<line hits="1" number="16"/>
|
||||
<line hits="1" number="17"/>
|
||||
<line hits="1" number="18"/>
|
||||
<line hits="1" number="19"/>
|
||||
<line hits="1" number="20"/>
|
||||
<line hits="1" number="21"/>
|
||||
@ -58,8 +226,6 @@
|
||||
<line hits="1" number="30"/>
|
||||
<line hits="1" number="31"/>
|
||||
<line hits="1" number="32"/>
|
||||
<line hits="1" number="33"/>
|
||||
<line hits="1" number="34"/>
|
||||
<line hits="1" number="35"/>
|
||||
<line hits="1" number="36"/>
|
||||
<line hits="1" number="37"/>
|
||||
@ -67,163 +233,129 @@
|
||||
<line hits="1" number="39"/>
|
||||
<line hits="1" number="40"/>
|
||||
<line hits="1" number="41"/>
|
||||
<line hits="1" number="42"/>
|
||||
<line hits="1" number="43"/>
|
||||
<line hits="1" number="44"/>
|
||||
<line hits="1" number="47"/>
|
||||
<line hits="1" number="48"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="49"/>
|
||||
<line hits="1" number="50"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="51"/>
|
||||
<line hits="1" number="51"/>
|
||||
<line hits="1" number="52"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="53"/>
|
||||
<line hits="1" number="54"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="54"/>
|
||||
<line hits="1" number="55"/>
|
||||
<line hits="1" number="56"/>
|
||||
<line hits="1" number="57"/>
|
||||
<line hits="1" number="58"/>
|
||||
<line hits="1" number="59"/>
|
||||
<line hits="1" number="60"/>
|
||||
<line hits="1" number="61"/>
|
||||
<line hits="1" number="62"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="59"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="60"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="61"/>
|
||||
<line hits="1" number="63"/>
|
||||
<line hits="1" number="64"/>
|
||||
<line hits="1" number="65"/>
|
||||
<line hits="1" number="66"/>
|
||||
<line hits="1" number="67"/>
|
||||
<line hits="1" number="68"/>
|
||||
<line hits="1" number="69"/>
|
||||
<line hits="1" number="70"/>
|
||||
<line hits="1" number="71"/>
|
||||
<line hits="1" number="72"/>
|
||||
<line hits="1" number="73"/>
|
||||
<line hits="1" number="74"/>
|
||||
<line hits="1" number="75"/>
|
||||
<line hits="1" number="76"/>
|
||||
<line hits="1" number="77"/>
|
||||
<line hits="1" number="78"/>
|
||||
<line hits="1" number="79"/>
|
||||
<line hits="1" number="80"/>
|
||||
<line hits="1" number="81"/>
|
||||
<line hits="1" number="82"/>
|
||||
<line hits="1" number="83"/>
|
||||
<line hits="1" number="84"/>
|
||||
<line hits="1" number="85"/>
|
||||
<line hits="1" number="88"/>
|
||||
<line hits="1" number="89"/>
|
||||
<line hits="0" number="86"/>
|
||||
<line hits="0" number="87"/>
|
||||
<line hits="0" number="88"/>
|
||||
<line hits="1" number="90"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="91"/>
|
||||
<line hits="1" number="92"/>
|
||||
<line hits="1" number="93"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="95"/>
|
||||
<line hits="1" number="96"/>
|
||||
<line hits="1" number="97"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="93"/>
|
||||
<line hits="1" number="94"/>
|
||||
<line hits="1" number="95"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="97"/>
|
||||
<line hits="1" number="98"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="100"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="101"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="102"/>
|
||||
<line hits="1" number="104"/>
|
||||
<line hits="1" number="105"/>
|
||||
<line hits="1" number="99"/>
|
||||
<line hits="1" number="100"/>
|
||||
<line hits="1" number="101"/>
|
||||
<line hits="1" number="102"/>
|
||||
<line hits="1" number="103"/>
|
||||
<line hits="1" number="106"/>
|
||||
<line hits="1" number="107"/>
|
||||
<line hits="1" number="108"/>
|
||||
<line hits="1" number="109"/>
|
||||
<line hits="1" number="110"/>
|
||||
<line hits="1" number="111"/>
|
||||
<line hits="1" number="112"/>
|
||||
<line hits="1" number="115"/>
|
||||
<line hits="1" number="114"/>
|
||||
<line hits="1" number="116"/>
|
||||
<line hits="1" number="117"/>
|
||||
<line hits="1" number="118"/>
|
||||
<line hits="1" number="119"/>
|
||||
<line hits="1" number="120"/>
|
||||
<line hits="1" number="121"/>
|
||||
<line hits="1" number="122"/>
|
||||
<line hits="1" number="123"/>
|
||||
<line hits="1" number="124"/>
|
||||
<line hits="1" number="125"/>
|
||||
<line hits="1" number="126"/>
|
||||
<line hits="0" number="127"/>
|
||||
<line hits="0" number="128"/>
|
||||
<line hits="0" number="129"/>
|
||||
<line hits="1" number="127"/>
|
||||
<line hits="1" number="128"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="129"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="130"/>
|
||||
<line hits="1" number="131"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="132"/>
|
||||
<line hits="1" number="133"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="134"/>
|
||||
<line hits="1" number="132"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="133"/>
|
||||
<line hits="1" number="134"/>
|
||||
<line hits="1" number="135"/>
|
||||
<line hits="1" number="136"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="138"/>
|
||||
<line hits="1" number="139"/>
|
||||
<line hits="1" number="140"/>
|
||||
<line hits="1" number="141"/>
|
||||
<line hits="1" number="142"/>
|
||||
<line hits="1" number="143"/>
|
||||
<line hits="1" number="144"/>
|
||||
<line hits="1" number="145"/>
|
||||
<line hits="1" number="146"/>
|
||||
<line hits="1" number="147"/>
|
||||
<line hits="1" number="148"/>
|
||||
<line hits="1" number="149"/>
|
||||
<line hits="1" number="150"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="150"/>
|
||||
<line hits="1" number="151"/>
|
||||
<line hits="1" number="152"/>
|
||||
<line hits="1" number="153"/>
|
||||
<line hits="1" number="155"/>
|
||||
<line hits="1" number="156"/>
|
||||
<line hits="1" number="157"/>
|
||||
<line hits="1" number="158"/>
|
||||
<line hits="1" number="159"/>
|
||||
<line hits="1" number="160"/>
|
||||
<line hits="1" number="161"/>
|
||||
<line hits="1" number="162"/>
|
||||
<line hits="1" number="163"/>
|
||||
<line hits="1" number="164"/>
|
||||
<line hits="1" number="165"/>
|
||||
<line hits="1" number="166"/>
|
||||
<line hits="1" number="167"/>
|
||||
<line hits="1" number="168"/>
|
||||
<line hits="1" number="169"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="170"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="171"/>
|
||||
<line hits="1" number="172"/>
|
||||
<line hits="1" number="173"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="174"/>
|
||||
<line hits="1" number="175"/>
|
||||
<line hits="1" number="176"/>
|
||||
<line hits="1" number="180"/>
|
||||
<line hits="1" number="181"/>
|
||||
<line hits="1" number="182"/>
|
||||
<line hits="1" number="185"/>
|
||||
<line hits="1" number="186"/>
|
||||
<line hits="1" number="187"/>
|
||||
<line hits="1" number="170"/>
|
||||
<line hits="1" number="171"/>
|
||||
<line hits="1" number="188"/>
|
||||
<line hits="1" number="189"/>
|
||||
<line hits="1" number="190"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="191"/>
|
||||
<line hits="1" number="192"/>
|
||||
<line hits="1" number="191"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="192"/>
|
||||
<line hits="1" number="193"/>
|
||||
<line hits="1" number="194"/>
|
||||
<line hits="1" number="197"/>
|
||||
<line hits="1" number="198"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="194"/>
|
||||
<line hits="1" number="195"/>
|
||||
<line hits="1" number="196"/>
|
||||
<line hits="1" number="199"/>
|
||||
<line hits="1" number="200"/>
|
||||
<line hits="1" number="201"/>
|
||||
<line hits="1" number="204"/>
|
||||
<line hits="1" number="205"/>
|
||||
<line hits="1" number="206"/>
|
||||
<line hits="1" number="207"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="208"/>
|
||||
<line hits="1" number="209"/>
|
||||
<line hits="1" number="210"/>
|
||||
<line hits="1" number="211"/>
|
||||
<line hits="1" number="212"/>
|
||||
<line hits="1" number="229"/>
|
||||
<line hits="1" number="230"/>
|
||||
<line hits="1" number="231"/>
|
||||
<line hits="1" number="232"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="233"/>
|
||||
<line hits="1" number="234"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="235"/>
|
||||
<line hits="1" number="236"/>
|
||||
<line hits="1" number="237"/>
|
||||
<line hits="1" number="240"/>
|
||||
<line hits="1" number="241"/>
|
||||
<line hits="1" number="242"/>
|
||||
<line hits="1" number="245"/>
|
||||
<line hits="1" number="246"/>
|
||||
<line hits="1" number="247"/>
|
||||
<line hits="1" number="248"/>
|
||||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="249"/>
|
||||
<line hits="1" number="250"/>
|
||||
<line hits="1" number="251"/>
|
||||
<line hits="1" number="252"/>
|
||||
<line hits="1" number="253"/>
|
||||
<line hits="1" number="254"/>
|
||||
<line hits="1" number="255"/>
|
||||
<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="exit" number="256"/>
|
||||
<line hits="1" number="257"/>
|
||||
<line hits="1" number="258"/>
|
||||
<line hits="1" number="259"/>
|
||||
<line hits="1" number="262"/>
|
||||
<line hits="1" number="263"/>
|
||||
<line hits="1" number="264"/>
|
||||
<line hits="1" number="265"/>
|
||||
<line hits="1" number="213"/>
|
||||
<line hits="1" number="214"/>
|
||||
<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="exit" number="215"/>
|
||||
<line hits="1" number="216"/>
|
||||
<line hits="1" number="217"/>
|
||||
<line hits="1" number="218"/>
|
||||
<line hits="1" number="221"/>
|
||||
<line hits="1" number="222"/>
|
||||
<line hits="1" number="223"/>
|
||||
<line hits="1" number="224"/>
|
||||
</lines>
|
||||
</class>
|
||||
</classes>
|
||||
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
19
common.py
Normal file
19
common.py
Normal file
@ -0,0 +1,19 @@
|
||||
import os
|
||||
|
||||
FILETYPE_AUDIO = 'audio'
|
||||
FILETYPE_IMAGE = 'image'
|
||||
FILETYPE_VIDEO = 'video'
|
||||
|
||||
EXTENTIONS_AUDIO = ['.mp3', ]
|
||||
EXTENTIONS_IMAGE = ['.jpg', '.jpeg', '.jpe', '.png', '.tif', '.tiff', '.gif', ]
|
||||
EXTENTIONS_VIDEO = ['.avi', '.mpg', '.mpeg', '.mpe', '.mov', '.qt', '.mp4', '.webm', '.ogv', '.flv', '.3gp', ]
|
||||
|
||||
|
||||
def get_filetype(full_path):
|
||||
ext = os.path.splitext(full_path.lower())[1]
|
||||
if ext in EXTENTIONS_AUDIO:
|
||||
return FILETYPE_AUDIO
|
||||
elif ext in EXTENTIONS_IMAGE:
|
||||
return FILETYPE_IMAGE
|
||||
elif ext in EXTENTIONS_VIDEO:
|
||||
return FILETYPE_VIDEO
|
35
convert.py
Normal file
35
convert.py
Normal file
@ -0,0 +1,35 @@
|
||||
import io
|
||||
from media import common, logger
|
||||
from PIL import Image
|
||||
import subprocess
|
||||
import platform
|
||||
|
||||
|
||||
def get_pil_image(media_instance):
|
||||
try:
|
||||
media_instance = media_instance._im
|
||||
except AttributeError:
|
||||
pass
|
||||
#
|
||||
if type(media_instance) == str:
|
||||
ft = common.get_filetype(media_instance)
|
||||
if ft == common.FILETYPE_IMAGE:
|
||||
return Image.open(media_instance).copy()
|
||||
elif ft == common.FILETYPE_VIDEO:
|
||||
if platform.system() == 'Linux':
|
||||
cmd = 'ffmpeg -ss 0.5 -i "' + media_instance + '" -vframes 1 -f image2pipe pipe:1 2> /dev/null'
|
||||
else:
|
||||
cmd = 'ffmpeg -ss 0.5 -i "' + media_instance + '" -vframes 1 -f image2pipe pipe:1 2> NULL'
|
||||
try:
|
||||
data = subprocess.check_output(cmd, shell=True)
|
||||
except subprocess.CalledProcessError:
|
||||
logger.warning('ffmpeg seems to be not installed')
|
||||
return None
|
||||
ffmpeg_handle = io.BytesIO(data)
|
||||
im = Image.open(ffmpeg_handle)
|
||||
return im.copy()
|
||||
logger.warning('Filetype is not supported (%s)', media_instance)
|
||||
elif type(media_instance) == Image.Image:
|
||||
return media_instance.copy()
|
||||
else:
|
||||
logger.warning('Instance type is not supported: %s' % type(media_instance))
|
131
metadata.py
131
metadata.py
@ -1,87 +1,46 @@
|
||||
import logging
|
||||
import os
|
||||
from PIL import Image
|
||||
import media
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
|
||||
logger_name = 'MEDIA'
|
||||
logger = logging.getLogger(logger_name)
|
||||
logger = media.logger
|
||||
|
||||
FILETYPE_AUDIO = 'audio'
|
||||
FILETYPE_IMAGE = 'image'
|
||||
FILETYPE_VIDEO = 'video'
|
||||
|
||||
EXTENTIONS_AUDIO = ['.mp3', ]
|
||||
EXTENTIONS_IMAGE = ['.jpg', '.jpeg', '.jpe', '.png', '.tif', '.tiff', '.gif', ]
|
||||
EXTENTIONS_VIDEO = ['.avi', '.mpg', '.mpeg', '.mpe', '.mov', '.qt', '.mp4', '.webm', '.ogv', '.flv', '.3gp', ]
|
||||
|
||||
KEY_ALBUM = 'album'
|
||||
KEY_APERTURE = 'aperture'
|
||||
KEY_ARTIST = 'artist'
|
||||
KEY_BITRATE = 'bitrate'
|
||||
KEY_CAMERA = 'camera'
|
||||
KEY_DURATION = 'duration'
|
||||
KEY_EXPOSURE_PROGRAM = 'exposure_program'
|
||||
KEY_EXPOSURE_TIME = 'exposure_time'
|
||||
KEY_FLASH = 'flash'
|
||||
KEY_FOCAL_LENGTH = 'focal_length'
|
||||
KEY_GENRE = 'genre'
|
||||
KEY_GPS = 'gps'
|
||||
KEY_HEIGHT = 'height'
|
||||
KEY_ISO = 'iso'
|
||||
KEY_ORIENTATION = 'orientation'
|
||||
KEY_RATIO = 'ratio'
|
||||
KEY_SIZE = 'size'
|
||||
KEY_TIME = 'time' # USE time.localtime(value) or datetime.fromtimestamp(value) to convert the timestamp
|
||||
KEY_TIME_IS_SUBSTITUTION = 'tm_is_subst'
|
||||
KEY_TITLE = 'title'
|
||||
KEY_TRACK = 'track'
|
||||
KEY_WIDTH = 'width'
|
||||
KEY_YEAR = 'year'
|
||||
|
||||
__KEY_CAMERA_VENDOR__ = 'camera_vendor'
|
||||
__KEY_CAMERA_MODEL__ = 'camera_model'
|
||||
|
||||
|
||||
def get_filetype(full_path):
|
||||
ext = os.path.splitext(full_path.lower())[1]
|
||||
if ext in EXTENTIONS_AUDIO:
|
||||
return FILETYPE_AUDIO
|
||||
elif ext in EXTENTIONS_IMAGE:
|
||||
return FILETYPE_IMAGE
|
||||
elif ext in EXTENTIONS_VIDEO:
|
||||
return FILETYPE_VIDEO
|
||||
|
||||
|
||||
def get_audio_data(full_path):
|
||||
conv_key_dict = {}
|
||||
conv_key_dict['album'] = (str, KEY_ALBUM)
|
||||
conv_key_dict['TAG:album'] = (str, KEY_ALBUM)
|
||||
conv_key_dict['TAG:artist'] = (str, KEY_ARTIST)
|
||||
conv_key_dict['artist'] = (str, KEY_ARTIST)
|
||||
conv_key_dict['bit_rate'] = (__int_conv__, KEY_BITRATE)
|
||||
conv_key_dict['duration'] = (float, KEY_DURATION)
|
||||
conv_key_dict['TAG:genre'] = (str, KEY_GENRE)
|
||||
conv_key_dict['genre'] = (str, KEY_GENRE)
|
||||
conv_key_dict['TAG:title'] = (str, KEY_TITLE)
|
||||
conv_key_dict['title'] = (str, KEY_TITLE)
|
||||
conv_key_dict['TAG:track'] = (__int_conv__, KEY_TRACK)
|
||||
conv_key_dict['track'] = (__int_conv__, KEY_TRACK)
|
||||
conv_key_dict['TAG:date'] = (__int_conv__, KEY_YEAR)
|
||||
conv_key_dict['date'] = (__int_conv__, KEY_YEAR)
|
||||
conv_key_dict['album'] = (str, media.KEY_ALBUM)
|
||||
conv_key_dict['TAG:album'] = (str, media.KEY_ALBUM)
|
||||
conv_key_dict['TAG:artist'] = (str, media.KEY_ARTIST)
|
||||
conv_key_dict['artist'] = (str, media.KEY_ARTIST)
|
||||
conv_key_dict['bit_rate'] = (__int_conv__, media.KEY_BITRATE)
|
||||
conv_key_dict['duration'] = (float, media.KEY_DURATION)
|
||||
conv_key_dict['TAG:genre'] = (str, media.KEY_GENRE)
|
||||
conv_key_dict['genre'] = (str, media.KEY_GENRE)
|
||||
conv_key_dict['TAG:title'] = (str, media.KEY_TITLE)
|
||||
conv_key_dict['title'] = (str, media.KEY_TITLE)
|
||||
conv_key_dict['TAG:track'] = (__int_conv__, media.KEY_TRACK)
|
||||
conv_key_dict['track'] = (__int_conv__, media.KEY_TRACK)
|
||||
conv_key_dict['TAG:date'] = (__int_conv__, media.KEY_YEAR)
|
||||
conv_key_dict['date'] = (__int_conv__, media.KEY_YEAR)
|
||||
return __adapt__data__(__get_xxprobe_data__(full_path, conv_key_dict), full_path)
|
||||
|
||||
|
||||
def get_video_data(full_path):
|
||||
conv_key_dict = {}
|
||||
conv_key_dict['creation_time'] = (__vid_datetime_conv__, KEY_TIME)
|
||||
conv_key_dict['TAG:creation_time'] = (__vid_datetime_conv__, KEY_TIME)
|
||||
conv_key_dict['bit_rate'] = (__int_conv__, KEY_BITRATE)
|
||||
conv_key_dict['duration'] = (float, KEY_DURATION)
|
||||
conv_key_dict['height'] = (__int_conv__, KEY_HEIGHT)
|
||||
conv_key_dict['width'] = (__int_conv__, KEY_WIDTH)
|
||||
conv_key_dict['display_aspect_ratio'] = (__ratio_conv__, KEY_RATIO)
|
||||
conv_key_dict['creation_time'] = (__vid_datetime_conv__, media.KEY_TIME)
|
||||
conv_key_dict['TAG:creation_time'] = (__vid_datetime_conv__, media.KEY_TIME)
|
||||
conv_key_dict['bit_rate'] = (__int_conv__, media.KEY_BITRATE)
|
||||
conv_key_dict['duration'] = (float, media.KEY_DURATION)
|
||||
conv_key_dict['height'] = (__int_conv__, media.KEY_HEIGHT)
|
||||
conv_key_dict['width'] = (__int_conv__, media.KEY_WIDTH)
|
||||
conv_key_dict['display_aspect_ratio'] = (__ratio_conv__, media.KEY_RATIO)
|
||||
return __adapt__data__(__get_xxprobe_data__(full_path, conv_key_dict), full_path)
|
||||
|
||||
|
||||
@ -90,25 +49,25 @@ def get_image_data(full_path):
|
||||
|
||||
|
||||
def __adapt__data__(data, full_path):
|
||||
data[KEY_SIZE] = os.path.getsize(full_path)
|
||||
data[media.KEY_SIZE] = os.path.getsize(full_path)
|
||||
# Join Camera Vendor and Camera Model
|
||||
if __KEY_CAMERA_MODEL__ in data and __KEY_CAMERA_VENDOR__ in data:
|
||||
model = data.pop(__KEY_CAMERA_MODEL__)
|
||||
vendor = data.pop(__KEY_CAMERA_VENDOR__)
|
||||
data[KEY_CAMERA] = '%s: %s' % (vendor, model)
|
||||
data[media.KEY_CAMERA] = '%s: %s' % (vendor, model)
|
||||
# Add time if not exists
|
||||
if KEY_TIME not in data:
|
||||
if KEY_YEAR in data and KEY_TRACK in data:
|
||||
if data[KEY_YEAR] != 0: # ignore year 0 - must be wrong
|
||||
if media.KEY_TIME not in data:
|
||||
if media.KEY_YEAR in data and media.KEY_TRACK in data:
|
||||
if data[media.KEY_YEAR] != 0: # ignore year 0 - must be wrong
|
||||
# Use a date where track 1 is the newest in the given year
|
||||
minute = int(data[KEY_TRACK] / 60)
|
||||
second = (data[KEY_TRACK] - 60 * minute) % 60
|
||||
minute = int(data[media.KEY_TRACK] / 60)
|
||||
second = (data[media.KEY_TRACK] - 60 * minute) % 60
|
||||
#
|
||||
data[KEY_TIME] = int(time.mktime((data[KEY_YEAR], 1, 1, 0, 59 - minute, 59 - second, 0, 0, 0)))
|
||||
data[KEY_TIME_IS_SUBSTITUTION] = True
|
||||
data[media.KEY_TIME] = int(time.mktime((data[media.KEY_YEAR], 1, 1, 0, 59 - minute, 59 - second, 0, 0, 0)))
|
||||
data[media.KEY_TIME_IS_SUBSTITUTION] = True
|
||||
else:
|
||||
data[KEY_TIME] = int(os.path.getmtime(full_path))
|
||||
data[KEY_TIME_IS_SUBSTITUTION] = True
|
||||
data[media.KEY_TIME] = int(os.path.getmtime(full_path))
|
||||
data[media.KEY_TIME_IS_SUBSTITUTION] = True
|
||||
return data
|
||||
|
||||
|
||||
@ -154,19 +113,19 @@ def __get_exif_data__(full_path):
|
||||
else:
|
||||
conv_key_dict = {}
|
||||
# IMAGE
|
||||
conv_key_dict[0x9003] = (__datetime_conv__, KEY_TIME)
|
||||
conv_key_dict[0x8822] = (__exposure_program_conv__, KEY_EXPOSURE_PROGRAM)
|
||||
conv_key_dict[0x829A] = (__num_denum_conv__, KEY_EXPOSURE_TIME)
|
||||
conv_key_dict[0x9209] = (__flash_conv__, KEY_FLASH)
|
||||
conv_key_dict[0x829D] = (__num_denum_conv__, KEY_APERTURE)
|
||||
conv_key_dict[0x920A] = (__num_denum_conv__, KEY_FOCAL_LENGTH)
|
||||
conv_key_dict[0x8825] = (__gps_conv__, KEY_GPS)
|
||||
conv_key_dict[0xA003] = (__int_conv__, KEY_HEIGHT)
|
||||
conv_key_dict[0x8827] = (__int_conv__, KEY_ISO)
|
||||
conv_key_dict[0x9003] = (__datetime_conv__, media.KEY_TIME)
|
||||
conv_key_dict[0x8822] = (__exposure_program_conv__, media.KEY_EXPOSURE_PROGRAM)
|
||||
conv_key_dict[0x829A] = (__num_denum_conv__, media.KEY_EXPOSURE_TIME)
|
||||
conv_key_dict[0x9209] = (__flash_conv__, media.KEY_FLASH)
|
||||
conv_key_dict[0x829D] = (__num_denum_conv__, media.KEY_APERTURE)
|
||||
conv_key_dict[0x920A] = (__num_denum_conv__, media.KEY_FOCAL_LENGTH)
|
||||
conv_key_dict[0x8825] = (__gps_conv__, media.KEY_GPS)
|
||||
conv_key_dict[0xA003] = (__int_conv__, media.KEY_HEIGHT)
|
||||
conv_key_dict[0x8827] = (__int_conv__, media.KEY_ISO)
|
||||
conv_key_dict[0x010F] = (str, __KEY_CAMERA_VENDOR__)
|
||||
conv_key_dict[0x0110] = (str, __KEY_CAMERA_MODEL__)
|
||||
conv_key_dict[0x0112] = (__int_conv__, KEY_ORIENTATION)
|
||||
conv_key_dict[0xA002] = (__int_conv__, KEY_WIDTH)
|
||||
conv_key_dict[0x0112] = (__int_conv__, media.KEY_ORIENTATION)
|
||||
conv_key_dict[0xA002] = (__int_conv__, media.KEY_WIDTH)
|
||||
for key in conv_key_dict:
|
||||
if key in exif:
|
||||
tp, name = conv_key_dict[key]
|
||||
|
Loading…
x
Reference in New Issue
Block a user