Submitted to PyLint

This commit is contained in:
Michał 2023-03-04 21:08:42 +00:00
parent 7ed3b455dd
commit de2d72e5de
7 changed files with 591 additions and 710 deletions

View file

@ -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)

View file

@ -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) + '%'

View file

@ -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

407
gallery/metadata/helpers.py Normal file
View file

@ -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'

View file

@ -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'],
}

View file

@ -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)

View file

@ -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: