From de2d72e5dedc5b044bc9b1539fabe19ec2e81888 Mon Sep 17 00:00:00 2001 From: Fluffy-Bean Date: Sat, 4 Mar 2023 21:08:42 +0000 Subject: [PATCH] Submitted to PyLint --- gallery/api.py | 4 +- gallery/metadata.py | 705 ----------------------------------- gallery/metadata/__init__.py | 117 ++++++ gallery/metadata/helpers.py | 407 ++++++++++++++++++++ gallery/metadata/mapping.py | 62 +++ gallery/routing.py | 4 +- gallery/theme_manager.py | 2 +- 7 files changed, 591 insertions(+), 710 deletions(-) delete mode 100644 gallery/metadata.py create mode 100644 gallery/metadata/__init__.py create mode 100644 gallery/metadata/helpers.py create mode 100644 gallery/metadata/mapping.py diff --git a/gallery/api.py b/gallery/api.py index 0b31971..04533d9 100644 --- a/gallery/api.py +++ b/gallery/api.py @@ -180,8 +180,8 @@ def metadata(img_id): if img is None: abort(404) - exif = mt.metadata.yoink( - os.path.join(current_app.config['UPLOAD_FOLDER'], img.file_name)) + img_path = os.path.join(current_app.config['UPLOAD_FOLDER'], img.file_name) + exif = mt.Metadata(img_path).yoink() return jsonify(exif) diff --git a/gallery/metadata.py b/gallery/metadata.py deleted file mode 100644 index 22af64e..0000000 --- a/gallery/metadata.py +++ /dev/null @@ -1,705 +0,0 @@ -import PIL -from PIL import Image -from PIL.ExifTags import TAGS, GPSTAGS -from datetime import datetime -import os - - -class metadata: - def yoink(filename): - exif = metadata.getFile(filename) - file_size = os.path.getsize(filename) - file_name = os.path.basename(filename) - file_resolution = Image.open(filename).size - - if exif: - unformatted_exif = metadata.format(exif, file_size, file_name, - file_resolution) - else: - # No EXIF data, get some basic informaton from the file - unformatted_exif = { - 'File': { - 'Name': { - 'type': 'text', - 'raw': file_name - }, - 'Size': { - 'type': 'number', - 'raw': file_size, - 'formatted': metadata.human_size(file_size) - }, - 'Format': { - 'type': 'text', - 'raw': file_name.split('.')[-1] - }, - 'Width': { - 'type': 'number', - 'raw': file_resolution[0] - }, - 'Height': { - 'type': 'number', - 'raw': file_resolution[1] - }, - } - } - - formatted_exif = {} - - for section in unformatted_exif: - tmp = {} - - for value in unformatted_exif[section]: - if unformatted_exif[section][value]['raw'] != None: - raw_type = unformatted_exif[section][value]['raw'] - if isinstance(raw_type, PIL.TiffImagePlugin.IFDRational): - raw_type = raw_type.__float__() - elif isinstance(raw_type, bytes): - raw_type = raw_type.decode('utf-8') - - tmp[value] = unformatted_exif[section][value] - - if len(tmp) > 0: - formatted_exif[section] = tmp - - return formatted_exif - - def getFile(filename): - try: - file = Image.open(filename) - raw = file._getexif() - exif = {} - - for tag, value in TAGS.items(): - - if tag in raw: - data = raw[tag] - else: - data = None - - exif[value] = {"tag": tag, "raw": data} - - file.close() - - return exif - except Exception as e: - return False - - def format(raw, file_size, file_name, file_resolution): - exif = {} - - exif['Photographer'] = { - 'Artist': { - 'type': 'text', - 'raw': raw["Artist"]["raw"] - }, - 'Comment': { - 'type': 'text', - 'raw': raw["UserComment"]["raw"] - }, - 'Description': { - 'type': 'text', - 'raw': raw["ImageDescription"]["raw"] - }, - 'Copyright': { - 'type': 'text', - 'raw': raw["Copyright"]["raw"] - }, - } - exif['Camera'] = { - 'Model': { - 'type': 'text', - 'raw': raw['Model']['raw'] - }, - 'Make': { - 'type': 'text', - 'raw': raw['Make']['raw'] - }, - 'Camera Type': { - 'type': 'text', - 'raw': raw['BodySerialNumber']['raw'] - }, - 'Lens Make': { - 'type': 'text', - 'raw': raw['LensMake']['raw'], - }, - 'Lense Model': { - 'type': 'text', - 'raw': raw['LensModel']['raw'], - }, - 'Lense Spec': { - 'type': - 'text', - 'raw': - raw['LensSpecification']['raw'], - 'formatted': - metadata.lensSpecification(raw['LensSpecification']['raw']) - }, - 'Component Config': { - 'type': - 'text', - 'raw': - raw['ComponentsConfiguration']['raw'], - 'formatted': - metadata.componentsConfiguration( - raw['ComponentsConfiguration']['raw']) - }, - 'Date Processed': { - 'type': 'date', - 'raw': raw['DateTime']['raw'], - 'formatted': metadata.date(raw['DateTime']['raw']) - }, - 'Date Digitized': { - 'type': 'date', - 'raw': raw["DateTimeDigitized"]["raw"], - 'formatted': metadata.date(raw["DateTimeDigitized"]["raw"]) - }, - 'Time Offset': { - 'type': 'text', - 'raw': raw["OffsetTime"]["raw"] - }, - 'Time Offset - Original': { - 'type': 'text', - 'raw': raw["OffsetTimeOriginal"]["raw"] - }, - 'Time Offset - Digitized': { - 'type': 'text', - 'raw': raw["OffsetTimeDigitized"]["raw"] - }, - 'Date Original': { - 'type': 'date', - 'raw': raw["DateTimeOriginal"]["raw"], - 'formatted': metadata.date(raw["DateTimeOriginal"]["raw"]) - }, - 'FNumber': { - 'type': 'fnumber', - 'raw': raw["FNumber"]["raw"], - 'formatted': metadata.fnumber(raw["FNumber"]["raw"]) - }, - 'Focal Length': { - 'type': 'focal', - 'raw': raw["FocalLength"]["raw"], - 'formatted': metadata.focal(raw["FocalLength"]["raw"]) - }, - 'Focal Length (35mm format)': { - 'type': 'focal', - 'raw': raw["FocalLengthIn35mmFilm"]["raw"], - 'formatted': - metadata.focal(raw["FocalLengthIn35mmFilm"]["raw"]) - }, - 'Max Aperture': { - 'type': 'fnumber', - 'raw': raw["MaxApertureValue"]["raw"], - 'formatted': metadata.fnumber(raw["MaxApertureValue"]["raw"]) - }, - 'Aperture': { - 'type': 'fnumber', - 'raw': raw["ApertureValue"]["raw"], - 'formatted': metadata.fnumber(raw["ApertureValue"]["raw"]) - }, - 'Shutter Speed': { - 'type': 'shutter', - 'raw': raw["ShutterSpeedValue"]["raw"], - 'formatted': metadata.shutter(raw["ShutterSpeedValue"]["raw"]) - }, - 'ISO Speed Ratings': { - 'type': 'number', - 'raw': raw["ISOSpeedRatings"]["raw"], - 'formatted': metadata.iso(raw["ISOSpeedRatings"]["raw"]) - }, - 'ISO Speed': { - 'type': 'iso', - 'raw': raw["ISOSpeed"]["raw"], - 'formatted': metadata.iso(raw["ISOSpeed"]["raw"]) - }, - 'Sensitivity Type': { - 'type': - 'number', - 'raw': - raw["SensitivityType"]["raw"], - 'formatted': - metadata.sensitivityType(raw["SensitivityType"]["raw"]) - }, - 'Exposure Bias': { - 'type': 'ev', - 'raw': raw["ExposureBiasValue"]["raw"], - 'formatted': metadata.ev(raw["ExposureBiasValue"]["raw"]) - }, - 'Exposure Time': { - 'type': 'shutter', - 'raw': raw["ExposureTime"]["raw"], - 'formatted': metadata.shutter(raw["ExposureTime"]["raw"]) - }, - 'Exposure Mode': { - 'type': 'number', - 'raw': raw["ExposureMode"]["raw"], - 'formatted': metadata.exposureMode(raw["ExposureMode"]["raw"]) - }, - 'Exposure Program': { - 'type': - 'number', - 'raw': - raw["ExposureProgram"]["raw"], - 'formatted': - metadata.exposureProgram(raw["ExposureProgram"]["raw"]) - }, - 'White Balance': { - 'type': 'number', - 'raw': raw["WhiteBalance"]["raw"], - 'formatted': metadata.whiteBalance(raw["WhiteBalance"]["raw"]) - }, - 'Flash': { - 'type': 'number', - 'raw': raw["Flash"]["raw"], - 'formatted': metadata.flash(raw["Flash"]["raw"]) - }, - 'Metering Mode': { - 'type': 'number', - 'raw': raw["MeteringMode"]["raw"], - 'formatted': metadata.meteringMode(raw["MeteringMode"]["raw"]) - }, - 'Light Source': { - 'type': 'number', - 'raw': raw["LightSource"]["raw"], - 'formatted': metadata.lightSource(raw["LightSource"]["raw"]) - }, - 'Scene Capture Type': { - 'type': - 'number', - 'raw': - raw["SceneCaptureType"]["raw"], - 'formatted': - metadata.sceneCaptureType(raw["SceneCaptureType"]["raw"]) - }, - 'Scene Type': { - 'type': 'number', - 'raw': raw["SceneType"]["raw"], - 'formatted': metadata.sceneType(raw["SceneType"]["raw"]) - }, - 'Rating': { - 'type': 'number', - 'raw': raw["Rating"]["raw"], - 'formatted': metadata.rating(raw["Rating"]["raw"]) - }, - 'Rating Percent': { - 'type': 'number', - 'raw': raw["RatingPercent"]["raw"], - 'formatted': - metadata.ratingPercent(raw["RatingPercent"]["raw"]) - }, - } - exif['Software'] = { - 'Software': { - 'type': 'text', - 'raw': raw['Software']['raw'] - }, - 'Colour Space': { - 'type': 'number', - 'raw': raw['ColorSpace']['raw'], - 'formatted': metadata.colorSpace(raw['ColorSpace']['raw']) - }, - 'Compression': { - 'type': 'number', - 'raw': raw['Compression']['raw'], - 'formatted': metadata.compression(raw['Compression']['raw']) - }, - } - exif['File'] = { - 'Name': { - 'type': 'text', - 'raw': file_name - }, - 'Size': { - 'type': 'number', - 'raw': file_size, - 'formatted': metadata.human_size(file_size) - }, - 'Format': { - 'type': 'text', - 'raw': file_name.split('.')[-1] - }, - 'Width': { - 'type': 'number', - 'raw': file_resolution[0] - }, - 'Height': { - 'type': 'number', - 'raw': file_resolution[1] - }, - 'Orientation': { - 'type': 'number', - 'raw': raw["Orientation"]["raw"], - 'formatted': metadata.orientation(raw["Orientation"]["raw"]) - }, - 'Xresolution': { - 'type': 'number', - 'raw': raw["XResolution"]["raw"] - }, - 'Yresolution': { - 'type': 'number', - 'raw': raw["YResolution"]["raw"] - }, - 'Resolution Units': { - 'type': 'number', - 'raw': raw["ResolutionUnit"]["raw"], - 'formatted': - metadata.resolutionUnit(raw["ResolutionUnit"]["raw"]) - }, - } - #exif['Raw'] = {} - #for key in raw: - # try: - # exif['Raw'][key] = { - # 'type': 'text', - # 'raw': raw[key]['raw'].decode('utf-8') - # } - # except: - # exif['Raw'][key] = { - # 'type': 'text', - # 'raw': str(raw[key]['raw']) - # } - - return exif - - def human_size(num, suffix="B"): - for unit in ["", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"]: - if abs(num) < 1024.0: - return f"{num:3.1f}{unit}{suffix}" - num /= 1024.0 - return f"{num:.1f}Yi{suffix}" - - def date(date): - date_format = '%Y:%m:%d %H:%M:%S' - - if date: - return str(datetime.strptime(date, date_format)) - else: - return None - - def fnumber(value): - if value != None: - return 'f/' + str(value) - else: - return None - - def iso(value): - if value != None: - return 'ISO ' + str(value) - else: - return None - - def shutter(value): - if value != None: - return str(value) + 's' - else: - return None - - def focal(value): - if value != None: - try: - return str(value[0] / value[1]) + 'mm' - except: - return str(value) + 'mm' - else: - return None - - def ev(value): - if value != None: - return str(value) + 'EV' - else: - return None - - def colorSpace(value): - types = {1: 'sRGB', 65535: 'Uncalibrated', 0: 'Reserved'} - try: - return types[int(value)] - except: - return None - - def flash(value): - types = { - 0: - 'Flash did not fire', - 1: - 'Flash fired', - 5: - 'Strobe return light not detected', - 7: - 'Strobe return light detected', - 9: - 'Flash fired, compulsory flash mode', - 13: - 'Flash fired, compulsory flash mode, return light not detected', - 15: - 'Flash fired, compulsory flash mode, return light detected', - 16: - 'Flash did not fire, compulsory flash mode', - 24: - 'Flash did not fire, auto mode', - 25: - 'Flash fired, auto mode', - 29: - 'Flash fired, auto mode, return light not detected', - 31: - 'Flash fired, auto mode, return light detected', - 32: - 'No flash function', - 65: - 'Flash fired, red-eye reduction mode', - 69: - 'Flash fired, red-eye reduction mode, return light not detected', - 71: - 'Flash fired, red-eye reduction mode, return light detected', - 73: - 'Flash fired, compulsory flash mode, red-eye reduction mode', - 77: - 'Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected', - 79: - 'Flash fired, compulsory flash mode, red-eye reduction mode, return light detected', - 89: - 'Flash fired, auto mode, red-eye reduction mode', - 93: - 'Flash fired, auto mode, return light not detected, red-eye reduction mode', - 95: - 'Flash fired, auto mode, return light detected, red-eye reduction mode' - } - try: - return types[int(value)] - except: - return None - - def exposureProgram(value): - types = { - 0: 'Not defined', - 1: 'Manual', - 2: 'Normal program', - 3: 'Aperture priority', - 4: 'Shutter priority', - 5: 'Creative program', - 6: 'Action program', - 7: 'Portrait mode', - 8: 'Landscape mode' - } - try: - return types[int(value)] - except: - return None - - def meteringMode(value): - types = { - 0: 'Unknown', - 1: 'Average', - 2: 'Center-Weighted Average', - 3: 'Spot', - 4: 'Multi-Spot', - 5: 'Pattern', - 6: 'Partial', - 255: 'Other' - } - try: - return types[int(value)] - except: - return None - - def resolutionUnit(value): - types = { - 1: 'No absolute unit of measurement', - 2: 'Inch', - 3: 'Centimeter' - } - try: - return types[int(value)] - except: - return None - - def lightSource(value): - types = { - 0: 'Unknown', - 1: 'Daylight', - 2: 'Fluorescent', - 3: 'Tungsten (incandescent light)', - 4: 'Flash', - 9: 'Fine weather', - 10: 'Cloudy weather', - 11: 'Shade', - 12: 'Daylight fluorescent (D 5700 - 7100K)', - 13: 'Day white fluorescent (N 4600 - 5400K)', - 14: 'Cool white fluorescent (W 3900 - 4500K)', - 15: 'White fluorescent (WW 3200 - 3700K)', - 17: 'Standard light A', - 18: 'Standard light B', - 19: 'Standard light C', - 20: 'D55', - 21: 'D65', - 22: 'D75', - 23: 'D50', - 24: 'ISO studio tungsten', - 255: 'Other light source', - } - try: - return types[int(value)] - except: - return None - - def sceneCaptureType(value): - types = { - 0: 'Standard', - 1: 'Landscape', - 2: 'Portrait', - 3: 'Night scene', - } - try: - return types[int(value)] - except: - return None - - def sceneType(value): - if value: - return 'Directly photographed image' - else: - return None - - def whiteBalance(value): - types = { - 0: 'Auto white balance', - 1: 'Manual white balance', - } - try: - return types[int(value)] - except: - return None - - def exposureMode(value): - types = { - 0: 'Auto exposure', - 1: 'Manual exposure', - 2: 'Auto bracket', - } - try: - return types[int(value)] - except: - return None - - def sensitivityType(value): - types = { - 0: - 'Unknown', - 1: - 'Standard Output Sensitivity', - 2: - 'Recommended Exposure Index', - 3: - 'ISO Speed', - 4: - 'Standard Output Sensitivity and Recommended Exposure Index', - 5: - 'Standard Output Sensitivity and ISO Speed', - 6: - 'Recommended Exposure Index and ISO Speed', - 7: - 'Standard Output Sensitivity, Recommended Exposure Index and ISO Speed', - } - try: - return types[int(value)] - except: - return None - - def lensSpecification(value): - if value: - return str(value[0] / value[1]) + 'mm - ' + str( - value[2] / value[3]) + 'mm' - else: - return None - - def compression(value): - types = { - 1: 'Uncompressed', - 2: 'CCITT 1D', - 3: 'T4/Group 3 Fax', - 4: 'T6/Group 4 Fax', - 5: 'LZW', - 6: 'JPEG (old-style)', - 7: 'JPEG', - 8: 'Adobe Deflate', - 9: 'JBIG B&W', - 10: 'JBIG Color', - 99: 'JPEG', - 262: 'Kodak 262', - 32766: 'Next', - 32767: 'Sony ARW Compressed', - 32769: 'Packed RAW', - 32770: 'Samsung SRW Compressed', - 32771: 'CCIRLEW', - 32772: 'Samsung SRW Compressed 2', - 32773: 'PackBits', - 32809: 'Thunderscan', - 32867: 'Kodak KDC Compressed', - 32895: 'IT8CTPAD', - 32896: 'IT8LW', - 32897: 'IT8MP', - 32898: 'IT8BL', - 32908: 'PixarFilm', - 32909: 'PixarLog', - 32946: 'Deflate', - 32947: 'DCS', - 33003: 'Aperio JPEG 2000 YCbCr', - 33005: 'Aperio JPEG 2000 RGB', - 34661: 'JBIG', - 34676: 'SGILog', - 34677: 'SGILog24', - 34712: 'JPEG 2000', - 34713: 'Nikon NEF Compressed', - 34715: 'JBIG2 TIFF FX', - 34718: '(MDI) Binary Level Codec', - 34719: '(MDI) Progressive Transform Codec', - 34720: '(MDI) Vector', - 34887: 'ESRI Lerc', - 34892: 'Lossy JPEG', - 34925: 'LZMA2', - 34926: 'Zstd', - 34927: 'WebP', - 34933: 'PNG', - 34934: 'JPEG XR', - 65000: 'Kodak DCR Compressed', - 65535: 'Pentax PEF Compressed', - } - try: - return types[int(value)] - except: - return None - - def orientation(value): - types = { - 1: 'Horizontal (normal)', - 2: 'Mirror horizontal', - 3: 'Rotate 180', - 4: 'Mirror vertical', - 5: 'Mirror horizontal and rotate 270 CW', - 6: 'Rotate 90 CW', - 7: 'Mirror horizontal and rotate 90 CW', - 8: 'Rotate 270 CW', - } - try: - return types[int(value)] - except: - return None - - def componentsConfiguration(value): - types = { - 0: '', - 1: 'Y', - 2: 'Cb', - 3: 'Cr', - 4: 'R', - 5: 'G', - 6: 'B', - } - try: - return ''.join([types[int(x)] for x in value]) - except: - return None - - def rating(value): - return str(value) + ' stars' - - def ratingPercent(value): - return str(value) + '%' \ No newline at end of file diff --git a/gallery/metadata/__init__.py b/gallery/metadata/__init__.py new file mode 100644 index 0000000..8bb4e6e --- /dev/null +++ b/gallery/metadata/__init__.py @@ -0,0 +1,117 @@ +""" +OnlyLegs - Metatada Parser +Parse metadata from images if available +otherwise get some basic information from the file +""" +import os +import logging + +from PIL import Image +from PIL.ExifTags import TAGS, GPSTAGS + +from .helpers import * +from .mapping import * + +class Metadata: + """ + Metadata parser + """ + def __init__(self, file_path): + """ + Initialize the metadata parser + """ + self.file_path = file_path + img_exif = {} + + try: + file = Image.open(file_path) + tags = file._getexif() + img_exif = {} + + for tag, value in TAGS.items(): + if tag in tags: + img_exif[value] = tags[tag] + + img_exif['FileName'] = os.path.basename(file_path) + img_exif['FileSize'] = os.path.getsize(file_path) + img_exif['FileFormat'] = img_exif['FileName'].split('.')[-1] + img_exif['FileWidth'], img_exif['FileHeight'] = file.size + + file.close() + except TypeError: + img_exif['FileName'] = os.path.basename(file_path) + img_exif['FileSize'] = os.path.getsize(file_path) + img_exif['FileFormat'] = img_exif['FileName'].split('.')[-1] + img_exif['FileWidth'], img_exif['FileHeight'] = file.size + + self.encoded = img_exif + + def yoink(self): + """ + Yoinks the metadata from the image + """ + if not os.path.isfile(self.file_path): + return None + return self.format_data(self.encoded) + + def format_data(self, encoded_exif): # pylint: disable=R0912 # For now, this is fine + """ + Formats the data into a dictionary + """ + exif = { + 'Photographer': {}, + 'Camera': {}, + 'Software': {}, + 'File': {}, + } + + for data in encoded_exif: + if data in PHOTOGRAHER_MAPPING: + exif['Photographer'][PHOTOGRAHER_MAPPING[data][0]] = { + 'raw': encoded_exif[data], + } + elif data in CAMERA_MAPPING: + if len(CAMERA_MAPPING[data]) == 2: + exif['Camera'][CAMERA_MAPPING[data][0]] = { + 'raw': encoded_exif[data], + 'formatted': + getattr(helpers, CAMERA_MAPPING[data][1])(encoded_exif[data]), # pylint: disable=E0602 + } + else: + exif['Camera'][CAMERA_MAPPING[data][0]] = { + 'raw': encoded_exif[data], + } + elif data in SOFTWARE_MAPPING: + if len(SOFTWARE_MAPPING[data]) == 2: + exif['Software'][SOFTWARE_MAPPING[data][0]] = { + 'raw': encoded_exif[data], + 'formatted': + getattr(helpers, SOFTWARE_MAPPING[data][1])(encoded_exif[data]), # pylint: disable=E0602 + } + else: + exif['Software'][SOFTWARE_MAPPING[data][0]] = { + 'raw': encoded_exif[data], + } + elif data in FILE_MAPPING: + if len(FILE_MAPPING[data]) == 2: + exif['File'][FILE_MAPPING[data][0]] = { + 'raw': encoded_exif[data], + 'formatted': + getattr(helpers, FILE_MAPPING[data][1])(encoded_exif[data]), # pylint: disable=E0602 + } + else: + exif['File'][FILE_MAPPING[data][0]] = { + 'raw': encoded_exif[data] + } + + # Remove empty keys + if len(exif['Photographer']) == 0: + del exif['Photographer'] + if len(exif['Camera']) == 0: + del exif['Camera'] + if len(exif['Software']) == 0: + del exif['Software'] + if len(exif['File']) == 0: + del exif['File'] + + return exif diff --git a/gallery/metadata/helpers.py b/gallery/metadata/helpers.py new file mode 100644 index 0000000..5dc4703 --- /dev/null +++ b/gallery/metadata/helpers.py @@ -0,0 +1,407 @@ +""" +OnlyLegs - Metadata Parser +Metadata formatting helpers +""" +from datetime import datetime + +def human_size(value): + """ + Formats the size of a file in a human readable format + """ + for unit in ["", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"]: + if abs(value) < 1024.0: + return f"{value:3.1f}{unit}B" + value /= 1024.0 + + return f"{value:.1f}YiB" + + +def date_format(value): + """ + Formats the date into a standard format + """ + return str(datetime.strptime(value, '%Y:%m:%d %H:%M:%S')) + + +def fnumber(value): + """ + Formats the f-number into a standard format + """ + return 'f/' + str(value) + + +def iso(value): + """ + Formats the ISO into a standard format + """ + return 'ISO ' + str(value) + + +def shutter(value): + """ + Formats the shutter speed into a standard format + """ + return str(value) + 's' + + +def focal_length(value): + """ + Formats the focal length into a standard format + """ + try: + return str(value[0] / value[1]) + 'mm' + except TypeError: + return str(value) + 'mm' + + +def exposure(value): + """ + Formats the exposure value into a standard format + """ + return str(value) + 'EV' + + +def color_space(value): + """ + Maps the value of the color space to a human readable format + """ + value_map = { + 0: 'Reserved', + 1: 'sRGB', + 65535: 'Uncalibrated' + } + try: + return value_map[int(value)] + except KeyError: + return None + + +def flash(value): + """ + Maps the value of the flash to a human readable format + """ + value_map = { + 0: 'Flash did not fire', + 1: 'Flash fired', + 5: 'Strobe return light not detected', + 7: 'Strobe return light detected', + 9: 'Flash fired, compulsory flash mode', + 13: 'Flash fired, compulsory flash mode, return light not detected', + 15: 'Flash fired, compulsory flash mode, return light detected', + 16: 'Flash did not fire, compulsory flash mode', + 24: 'Flash did not fire, auto mode', + 25: 'Flash fired, auto mode', + 29: 'Flash fired, auto mode, return light not detected', + 31: 'Flash fired, auto mode, return light detected', + 32: 'No flash function', + 65: 'Flash fired, red-eye reduction mode', + 69: 'Flash fired, red-eye reduction mode, return light not detected', + 71: 'Flash fired, red-eye reduction mode, return light detected', + 73: 'Flash fired, compulsory flash mode, red-eye reduction mode', + 77: 'Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected', + 79: 'Flash fired, compulsory flash mode, red-eye reduction mode, return light detected', + 89: 'Flash fired, auto mode, red-eye reduction mode', + 93: 'Flash fired, auto mode, return light not detected, red-eye reduction mode', + 95: 'Flash fired, auto mode, return light detected, red-eye reduction mode' + } + try: + return value_map[int(value)] + except KeyError: + return None + + +def exposure_program(value): + """ + Maps the value of the exposure program to a human readable format + """ + value_map = { + 0: 'Not defined', + 1: 'Manual', + 2: 'Normal program', + 3: 'Aperture priority', + 4: 'Shutter priority', + 5: 'Creative program', + 6: 'Action program', + 7: 'Portrait mode', + 8: 'Landscape mode' + } + try: + return value_map[int(value)] + except KeyError: + return None + + +def metering_mode(value): + """ + Maps the value of the metering mode to a human readable format + """ + value_map = { + 0: 'Unknown', + 1: 'Average', + 2: 'Center-Weighted Average', + 3: 'Spot', + 4: 'Multi-Spot', + 5: 'Pattern', + 6: 'Partial', + 255: 'Other' + } + try: + return value_map[int(value)] + except KeyError: + return None + + +def resolution_unit(value): + """ + Maps the value of the resolution unit to a human readable format + """ + value_map = { + 1: 'No absolute unit of measurement', + 2: 'Inch', + 3: 'Centimeter' + } + try: + return value_map[int(value)] + except KeyError: + return None + + +def light_source(value): + """ + Maps the value of the light source to a human readable format + """ + value_map = { + 0: 'Unknown', + 1: 'Daylight', + 2: 'Fluorescent', + 3: 'Tungsten (incandescent light)', + 4: 'Flash', + 9: 'Fine weather', + 10: 'Cloudy weather', + 11: 'Shade', + 12: 'Daylight fluorescent (D 5700 - 7100K)', + 13: 'Day white fluorescent (N 4600 - 5400K)', + 14: 'Cool white fluorescent (W 3900 - 4500K)', + 15: 'White fluorescent (WW 3200 - 3700K)', + 17: 'Standard light A', + 18: 'Standard light B', + 19: 'Standard light C', + 20: 'D55', + 21: 'D65', + 22: 'D75', + 23: 'D50', + 24: 'ISO studio tungsten', + 255: 'Other light source', + } + try: + return value_map[int(value)] + except KeyError: + return None + + +def scene_capture_type(value): + """ + Maps the value of the scene capture type to a human readable format + """ + value_map = { + 0: 'Standard', + 1: 'Landscape', + 2: 'Portrait', + 3: 'Night scene', + } + try: + return value_map[int(value)] + except KeyError: + return None + + +def scene_type(value): # pylint: disable=W0613 # Itss fiiiineeee + """ + Maps the value of the scene type to a human readable format + """ + return 'Directly photographed image' + + +def white_balance(value): + """ + Maps the value of the white balance to a human readable format + """ + value_map = { + 0: 'Auto white balance', + 1: 'Manual white balance', + } + try: + return value_map[int(value)] + except KeyError: + return None + + +def exposure_mode(value): + """ + Maps the value of the exposure mode to a human readable format + """ + value_map = { + 0: 'Auto exposure', + 1: 'Manual exposure', + 2: 'Auto bracket', + } + try: + return value_map[int(value)] + except KeyError: + return None + + +def sensitivity_type(value): + """ + Maps the value of the sensitivity type to a human readable format + """ + value_map = { + 0: + 'Unknown', + 1: + 'Standard Output Sensitivity', + 2: + 'Recommended Exposure Index', + 3: + 'ISO Speed', + 4: + 'Standard Output Sensitivity and Recommended Exposure Index', + 5: + 'Standard Output Sensitivity and ISO Speed', + 6: + 'Recommended Exposure Index and ISO Speed', + 7: + 'Standard Output Sensitivity, Recommended Exposure Index and ISO Speed', + } + try: + return value_map[int(value)] + except KeyError: + return None + + +def lens_specification(value): + """ + Maps the value of the lens specification to a human readable format + """ + return str(value[0] / value[1]) + 'mm - ' + str(value[2] / value[3]) + 'mm' + + +def compression_type(value): + """ + Maps the value of the compression type to a human readable format + """ + value_map = { + 1: 'Uncompressed', + 2: 'CCITT 1D', + 3: 'T4/Group 3 Fax', + 4: 'T6/Group 4 Fax', + 5: 'LZW', + 6: 'JPEG (old-style)', + 7: 'JPEG', + 8: 'Adobe Deflate', + 9: 'JBIG B&W', + 10: 'JBIG Color', + 99: 'JPEG', + 262: 'Kodak 262', + 32766: 'Next', + 32767: 'Sony ARW Compressed', + 32769: 'Packed RAW', + 32770: 'Samsung SRW Compressed', + 32771: 'CCIRLEW', + 32772: 'Samsung SRW Compressed 2', + 32773: 'PackBits', + 32809: 'Thunderscan', + 32867: 'Kodak KDC Compressed', + 32895: 'IT8CTPAD', + 32896: 'IT8LW', + 32897: 'IT8MP', + 32898: 'IT8BL', + 32908: 'PixarFilm', + 32909: 'PixarLog', + 32946: 'Deflate', + 32947: 'DCS', + 33003: 'Aperio JPEG 2000 YCbCr', + 33005: 'Aperio JPEG 2000 RGB', + 34661: 'JBIG', + 34676: 'SGILog', + 34677: 'SGILog24', + 34712: 'JPEG 2000', + 34713: 'Nikon NEF Compressed', + 34715: 'JBIG2 TIFF FX', + 34718: '(MDI) Binary Level Codec', + 34719: '(MDI) Progressive Transform Codec', + 34720: '(MDI) Vector', + 34887: 'ESRI Lerc', + 34892: 'Lossy JPEG', + 34925: 'LZMA2', + 34926: 'Zstd', + 34927: 'WebP', + 34933: 'PNG', + 34934: 'JPEG XR', + 65000: 'Kodak DCR Compressed', + 65535: 'Pentax PEF Compressed', + } + try: + return value_map[int(value)] + except KeyError: + return None + + +def orientation(value): + """ + Maps the value of the orientation to a human readable format + """ + value_map = { + 1: 'Horizontal (normal)', + 2: 'Mirror horizontal', + 3: 'Rotate 180', + 4: 'Mirror vertical', + 5: 'Mirror horizontal and rotate 270 CW', + 6: 'Rotate 90 CW', + 7: 'Mirror horizontal and rotate 90 CW', + 8: 'Rotate 270 CW', + } + try: + return value_map[int(value)] + except KeyError: + return None + + +def components_configuration(value): + """ + Maps the value of the components configuration to a human readable format + """ + value_map = { + 0: '', + 1: 'Y', + 2: 'Cb', + 3: 'Cr', + 4: 'R', + 5: 'G', + 6: 'B', + } + try: + return ''.join([value_map[int(x)] for x in value]) + except KeyError: + return None + + +def rating(value): + """ + Maps the value of the rating to a human readable format + """ + return str(value) + ' stars' + + +def rating_percent(value): + """ + Maps the value of the rating to a human readable format + """ + return str(value) + '%' + + +def pixel_dimension(value): + """ + Maps the value of the pixel dimension to a human readable format + """ + return str(value) + 'px' diff --git a/gallery/metadata/mapping.py b/gallery/metadata/mapping.py new file mode 100644 index 0000000..d99c1df --- /dev/null +++ b/gallery/metadata/mapping.py @@ -0,0 +1,62 @@ +""" +OnlyLegs - Metatada Parser +Mapping for metadata +""" +PHOTOGRAHER_MAPPING = { + 'Artist': ['Artist'], + 'UserComment': ['Comment'], + 'ImageDescription': ['Description'], + 'Copyright': ['Copyright'], +} +CAMERA_MAPPING = { + 'Model': ['Model'], + 'Make': ['Make'], + 'BodySerialNumber': ['Camera Type'], + 'LensMake': ['Lens Make'], + 'LenseModel': ['Lens Model'], + 'LensSpecification': ['Lens Specification', 'lens_specification'], + 'ComponentsConfiguration': ['Components Configuration', 'components_configuration'], + 'DateTime': ['Date Processed', 'date_format'], + 'DateTimeDigitized': ['Time Digitized', 'date_format'], + 'OffsetTime': ['Time Offset'], + 'OffsetTimeOriginal': ['Time Offset - Original'], + 'OffsetTimeDigitized': ['Time Offset - Digitized'], + 'DateTimeOriginal': ['Date Original', 'date_format'], + 'FNumber': ['F-Stop', 'fnumber'], + 'FocalLength': ['Focal Length', 'focal_length'], + 'FocalLengthIn35mmFilm': ['Focal Length (35mm format)', 'focal_length'], + 'MaxApertureValue': ['Max Aperture', 'fnumber'], + 'ApertureValue': ['Aperture', 'fnumber'], + 'ShutterSpeedValue': ['Shutter Speed', 'shutter'], + 'ISOSpeedRatings': ['ISO Speed Ratings', 'iso'], + 'ISOSpeed': ['ISO Speed', 'iso'], + 'SensitivityType': ['Sensitivity Type', 'sensitivity_type'], + 'ExposureBiasValue': ['Exposure Bias', 'ev'], + 'ExposureTime': ['Exposure Time', 'shutter'], + 'ExposureMode': ['Exposure Mode', 'exposure_mode'], + 'ExposureProgram': ['Exposure Program', 'exposure_program'], + 'WhiteBalance': ['White Balance', 'white_balance'], + 'Flash': ['Flash', 'flash'], + 'MeteringMode': ['Metering Mode', 'metering_mode'], + 'LightSource': ['Light Source', 'light_source'], + 'SceneCaptureType': ['Scene Capture Type', 'scene_capture_type'], + 'SceneType': ['Scene Type', 'scene_type'], + 'Rating': ['Rating', 'rating'], + 'RatingPercent': ['Rating Percent', 'rating_percent'], +} +SOFTWARE_MAPPING = { + 'Software': ['Software'], + 'ColorSpace': ['Colour Space', 'color_space'], + 'Compression': ['Compression', 'compression_type'], +} +FILE_MAPPING = { + 'FileName': ['Name'], + 'FileSize': ['Size', 'human_size'], + 'FileFormat': ['Format'], + 'FileWidth': ['Width', 'pixel_dimension'], + 'FileHeight': ['Height', 'pixel_dimension'], + 'Orientation': ['Orientation', 'orientation'], + 'XResolution': ['X-resolution'], + 'YResolution': ['Y-resolution'], + 'ResolutionUnit': ['Resolution Units', 'resolution_unit'], +} diff --git a/gallery/routing.py b/gallery/routing.py index 0388786..9389bcf 100644 --- a/gallery/routing.py +++ b/gallery/routing.py @@ -40,8 +40,8 @@ def image(image_id): if img is None: abort(404) - exif = mt.metadata.yoink( - os.path.join(current_app.config['UPLOAD_FOLDER'], img.file_name)) + img_path = os.path.join(current_app.config['UPLOAD_FOLDER'], img.file_name) + exif = mt.Metadata(img_path).yoink() return render_template('image.html', image=img, exif=exif) diff --git a/gallery/theme_manager.py b/gallery/theme_manager.py index 72b075c..73c5702 100644 --- a/gallery/theme_manager.py +++ b/gallery/theme_manager.py @@ -45,7 +45,7 @@ class CompileTheme(): print("No sass file found!") sys.exit(1) - with open(os.path.join(css_dest, 'style.css'), encoding='utf-8') as file: + with open(os.path.join(css_dest, 'style.css'), 'w', encoding='utf-8') as file: try: file.write(sass.compile(filename=sass_path,output_style='compressed')) except sass.CompileError as err: