Correct missing Exif data

Format missed information
This commit is contained in:
Michał 2023-01-31 22:08:37 +00:00
parent da1579555b
commit c02d618844
6 changed files with 266 additions and 179 deletions
gallery
__init__.pyapi.pymetadata.pyrouting.py
templates
user/themes/default/ui/wrappers

View file

@ -96,8 +96,8 @@ def create_app(test_config=None):
app.register_blueprint(auth.blueprint) app.register_blueprint(auth.blueprint)
# Load routes for home and images # Load routes for home and images
from . import gallery from . import routing
app.register_blueprint(gallery.blueprint) app.register_blueprint(routing.blueprint)
app.add_url_rule('/', endpoint='index') app.add_url_rule('/', endpoint='index')
# Load APIs # Load APIs

View file

@ -11,14 +11,6 @@ from uuid import uuid4
blueprint = Blueprint('viewsbp', __name__, url_prefix='/api') blueprint = Blueprint('viewsbp', __name__, url_prefix='/api')
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}"
@blueprint.route('/uploads/<file>/<int:quality>', methods=['GET']) @blueprint.route('/uploads/<file>/<int:quality>', methods=['GET'])
def uploads(file, quality): def uploads(file, quality):
# If quality is 0, return original file # If quality is 0, return original file
@ -127,6 +119,5 @@ def metadata(id):
abort(404) abort(404)
exif = mt.metadata.yoink(os.path.join(current_app.config['UPLOAD_FOLDER'], img['file_name'])) exif = mt.metadata.yoink(os.path.join(current_app.config['UPLOAD_FOLDER'], img['file_name']))
filesize = os.path.getsize(os.path.join(current_app.config['UPLOAD_FOLDER'], img['file_name']))
return jsonify({'metadata': exif, 'filesize': {'bytes': filesize, 'human': human_size(filesize)}}) return jsonify(exif)

View file

@ -2,38 +2,65 @@ import PIL
from PIL import Image from PIL import Image
from PIL.ExifTags import TAGS, GPSTAGS from PIL.ExifTags import TAGS, GPSTAGS
from datetime import datetime from datetime import datetime
import os
class metadata: class metadata:
def yoink(filename): def yoink(filename):
exif = metadata.getFile(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: if exif:
formatted = metadata.format(exif) unformatted_exif = metadata.format(exif, file_size, file_name, file_resolution)
else: else:
return None # 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]
},
}
}
return metadata.deleteEmpty(formatted) formatted_exif = {}
def deleteEmpty(dict):
new_dict = {}
for section in dict: for section in unformatted_exif:
tmp = {} tmp = {}
for value in dict[section]:
if dict[section][value]['raw'] != None: for value in unformatted_exif[section]:
if isinstance(dict[section][value]['raw'], PIL.TiffImagePlugin.IFDRational): if unformatted_exif[section][value]['raw'] != None:
dict[section][value]['raw'] = dict[section][value]['raw'].__float__() raw_type = unformatted_exif[section][value]['raw']
elif isinstance(dict[section][value]['raw'], bytes): if isinstance(raw_type, PIL.TiffImagePlugin.IFDRational):
dict[section][value]['raw'] = dict[section][value]['raw'].decode('utf-8') raw_type = raw_type.__float__()
elif isinstance(raw_type, bytes):
raw_type = raw_type.decode('utf-8')
tmp[value] = dict[section][value] tmp[value] = unformatted_exif[section][value]
if len(tmp) > 0: if len(tmp) > 0:
new_dict[section] = tmp formatted_exif[section] = tmp
return new_dict return formatted_exif
def getFile(filename): def getFile(filename):
try: try:
@ -54,9 +81,9 @@ class metadata:
return exif return exif
except Exception as e: except Exception as e:
return None return False
def format(raw): def format(raw, file_size, file_name, file_resolution):
exif = {} exif = {}
exif['Photographer'] = { exif['Photographer'] = {
@ -72,11 +99,6 @@ class metadata:
'type': 'text', 'type': 'text',
'raw': raw["ImageDescription"]["raw"] 'raw': raw["ImageDescription"]["raw"]
}, },
'Date Digitized': {
'type': 'date',
'raw': raw["DateTimeDigitized"]["raw"],
'formatted': metadata.date(raw["DateTimeDigitized"]["raw"])
},
'Copyright': { 'Copyright': {
'type': 'text', 'type': 'text',
'raw': raw["Copyright"]["raw"] 'raw': raw["Copyright"]["raw"]
@ -91,6 +113,14 @@ class metadata:
'type': 'text', 'type': 'text',
'raw': raw['Make']['raw'] 'raw': raw['Make']['raw']
}, },
'Camera Type': {
'type': 'text',
'raw': raw['BodySerialNumber']['raw']
},
'Lens Make': {
'type': 'text',
'raw': raw['LensMake']['raw'],
},
'Lense Model': { 'Lense Model': {
'type': 'text', 'type': 'text',
'raw': raw['LensModel']['raw'], 'raw': raw['LensModel']['raw'],
@ -110,24 +140,28 @@ class metadata:
'raw': raw['DateTime']['raw'], 'raw': raw['DateTime']['raw'],
'formatted': metadata.date(raw['DateTime']['raw']) 'formatted': metadata.date(raw['DateTime']['raw'])
}, },
} 'Date Digitized': {
exif['Software'] = { 'type': 'date',
'Software': { 'raw': raw["DateTimeDigitized"]["raw"],
'formatted': metadata.date(raw["DateTimeDigitized"]["raw"])
},
'Time Offset': {
'type': 'text', 'type': 'text',
'raw': raw['Software']['raw'] 'raw': raw["OffsetTime"]["raw"]
}, },
'Colour Space': { 'Time Offset - Original': {
'type': 'number', 'type': 'text',
'raw': raw['ColorSpace']['raw'], 'raw': raw["OffsetTimeOriginal"]["raw"]
'formatted': metadata.colorSpace(raw['ColorSpace']['raw'])
}, },
'Compression': { 'Time Offset - Digitized': {
'type': 'number', 'type': 'text',
'raw': raw['Compression']['raw'], 'raw': raw["OffsetTimeDigitized"]["raw"]
'formatted': metadata.compression(raw['Compression']['raw']) },
'Date Original': {
'type': 'date',
'raw': raw["DateTimeOriginal"]["raw"],
'formatted': metadata.date(raw["DateTimeOriginal"]["raw"])
}, },
}
exif['Photo'] = {
'FNumber': { 'FNumber': {
'type': 'fnumber', 'type': 'fnumber',
'raw': raw["FNumber"]["raw"], 'raw': raw["FNumber"]["raw"],
@ -135,11 +169,13 @@ class metadata:
}, },
'Focal Length': { 'Focal Length': {
'type': 'focal', 'type': 'focal',
'raw': raw["FocalLength"]["raw"] 'raw': raw["FocalLength"]["raw"],
'formatted': metadata.focal(raw["FocalLength"]["raw"])
}, },
'Focal Length - Film': { 'Focal Length (35mm format)': {
'type': 'focal', 'type': 'focal',
'raw': raw["FocalLengthIn35mmFilm"]["raw"] 'raw': raw["FocalLengthIn35mmFilm"]["raw"],
'formatted': metadata.focal(raw["FocalLengthIn35mmFilm"]["raw"])
}, },
'Max Aperture': { 'Max Aperture': {
'type': 'fnumber', 'type': 'fnumber',
@ -220,16 +256,55 @@ class metadata:
'type': 'number', 'type': 'number',
'raw': raw["SceneType"]["raw"], 'raw': raw["SceneType"]["raw"],
'formatted': metadata.sceneType(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'] = { 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': { 'Width': {
'type': 'number', 'type': 'number',
'raw': raw["ImageWidth"]["raw"] 'raw': file_resolution[0]
}, },
'Height': { 'Height': {
'type': 'number', 'type': 'number',
'raw': raw["ImageLength"]["raw"] 'raw': file_resolution[1]
}, },
'Orientation': { 'Orientation': {
'type': 'number', 'type': 'number',
@ -250,8 +325,27 @@ class metadata:
'formatted': metadata.resolutionUnit(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 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): def date(date):
date_format = '%Y:%m:%d %H:%M:%S' date_format = '%Y:%m:%d %H:%M:%S'
@ -281,7 +375,10 @@ class metadata:
def focal(value): def focal(value):
if value != None: if value != None:
return str(value[0] / value[1]) + 'mm' try:
return str(value[0] / value[1]) + 'mm'
except:
return str(value) + 'mm'
else: else:
return None return None
@ -297,7 +394,6 @@ class metadata:
65535: 'Uncalibrated', 65535: 'Uncalibrated',
0: 'Reserved' 0: 'Reserved'
} }
try: try:
return types[int(value)] return types[int(value)]
except: except:
@ -328,7 +424,6 @@ class metadata:
93: 'Flash fired, auto mode, return light not detected, 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' 95: 'Flash fired, auto mode, return light detected, red-eye reduction mode'
} }
try: try:
return types[int(value)] return types[int(value)]
except: except:
@ -346,7 +441,6 @@ class metadata:
7: 'Portrait mode', 7: 'Portrait mode',
8: 'Landscape mode' 8: 'Landscape mode'
} }
try: try:
return types[int(value)] return types[int(value)]
except: except:
@ -363,7 +457,6 @@ class metadata:
6: 'Partial', 6: 'Partial',
255: 'Other' 255: 'Other'
} }
try: try:
return types[int(value)] return types[int(value)]
except: except:
@ -375,7 +468,6 @@ class metadata:
2: 'Inch', 2: 'Inch',
3: 'Centimeter' 3: 'Centimeter'
} }
try: try:
return types[int(value)] return types[int(value)]
except: except:
@ -405,7 +497,6 @@ class metadata:
24: 'ISO studio tungsten', 24: 'ISO studio tungsten',
255: 'Other light source', 255: 'Other light source',
} }
try: try:
return types[int(value)] return types[int(value)]
except: except:
@ -418,7 +509,6 @@ class metadata:
2: 'Portrait', 2: 'Portrait',
3: 'Night scene', 3: 'Night scene',
} }
try: try:
return types[int(value)] return types[int(value)]
except: except:
@ -435,7 +525,6 @@ class metadata:
0: 'Auto white balance', 0: 'Auto white balance',
1: 'Manual white balance', 1: 'Manual white balance',
} }
try: try:
return types[int(value)] return types[int(value)]
except: except:
@ -447,7 +536,6 @@ class metadata:
1: 'Manual exposure', 1: 'Manual exposure',
2: 'Auto bracket', 2: 'Auto bracket',
} }
try: try:
return types[int(value)] return types[int(value)]
except: except:
@ -464,7 +552,6 @@ class metadata:
6: 'Recommended Exposure Index and ISO Speed', 6: 'Recommended Exposure Index and ISO Speed',
7: 'Standard Output Sensitivity, Recommended Exposure Index and ISO Speed', 7: 'Standard Output Sensitivity, Recommended Exposure Index and ISO Speed',
} }
try: try:
return types[int(value)] return types[int(value)]
except: except:
@ -479,9 +566,55 @@ class metadata:
def compression(value): def compression(value):
types = { types = {
1: 'Uncompressed', 1: 'Uncompressed',
6: 'JPEG compression', 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: try:
return types[int(value)] return types[int(value)]
except: except:
@ -498,7 +631,6 @@ class metadata:
7: 'Mirror horizontal and rotate 90 CW', 7: 'Mirror horizontal and rotate 90 CW',
8: 'Rotate 270 CW', 8: 'Rotate 270 CW',
} }
try: try:
return types[int(value)] return types[int(value)]
except: except:
@ -514,8 +646,13 @@ class metadata:
5: 'G', 5: 'G',
6: 'B', 6: 'B',
} }
try: try:
return ''.join([types[int(x)] for x in value]) return ''.join([types[int(x)] for x in value])
except: except:
return None return None
def rating(value):
return str(value) + ' stars'
def ratingPercent(value):
return str(value) + '%'

View file

@ -15,14 +15,6 @@ dt = datetime.now()
blueprint = Blueprint('gallery', __name__) blueprint = Blueprint('gallery', __name__)
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}"
@blueprint.route('/') @blueprint.route('/')
def index(): def index():
db = get_db() db = get_db()
@ -42,18 +34,8 @@ def image(id):
abort(404) abort(404)
exif = mt.metadata.yoink(os.path.join(current_app.config['UPLOAD_FOLDER'], image['file_name'])) exif = mt.metadata.yoink(os.path.join(current_app.config['UPLOAD_FOLDER'], image['file_name']))
file_size = human_size(os.path.getsize(os.path.join(current_app.config['UPLOAD_FOLDER'], image['file_name'])))
try:
width = exif['File']['Width']['value']
height = exif['File']['Height']['value']
except:
try:
width, height = Image.open(os.path.join(current_app.config['UPLOAD_FOLDER'], image['file_name'])).size
except:
width, height = 0, 0
return render_template('image.html', image=image, exif=exif, file_size=file_size, width=width, height=height) return render_template('image.html', image=image, exif=exif)
@blueprint.route('/group') @blueprint.route('/group')

View file

@ -19,8 +19,8 @@
src="/api/uploads/{{ image['file_name'] }}/1000" src="/api/uploads/{{ image['file_name'] }}/1000"
onload="imgFade(this)" style="opacity:0;" onload="imgFade(this)" style="opacity:0;"
onerror="this.src='/static/images/error.png'" onerror="this.src='/static/images/error.png'"
width="{{ width }}" width="{{ exif['File']['Width']['raw'] }}"
height="{{ height }}" height="{{ exif['File']['Height']['raw'] }}"
/> />
</div> </div>
@ -121,86 +121,69 @@
<td>Upload date</td> <td>Upload date</td>
<td><span class="time">{{ image['created_at'] }}</span></td> <td><span class="time">{{ image['created_at'] }}</span></td>
</tr> </tr>
<tr>
<td>Filename</td>
<td>{{ image['file_name'] }}</td>
</tr>
<tr>
<td>File size</td>
<td>{{ file_size }}</td>
</tr>
</table> </table>
</div> </div>
</div> </div>
{% if exif %} {% for tag in exif %}
{% for tag in exif %} <div class="image-info">
<div class="image-info"> {% if tag == 'Photographer' %}
{% if tag == 'Photographer' %} <div class="image-info__header">
<div class="image-info__header"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="-5 -2 24 24" fill="currentColor">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-5 -2 24 24" fill="currentColor"> <path d="M3.534 10.07a1 1 0 1 1 .733 1.86A3.579 3.579 0 0 0 2 15.26V17a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-1.647a3.658 3.658 0 0 0-2.356-3.419 1 1 0 1 1 .712-1.868A5.658 5.658 0 0 1 14 15.353V17a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3v-1.74a5.579 5.579 0 0 1 3.534-5.19zM7 0a4 4 0 0 1 4 4v2a4 4 0 1 1-8 0V4a4 4 0 0 1 4-4zm0 2a2 2 0 0 0-2 2v2a2 2 0 1 0 4 0V4a2 2 0 0 0-2-2z"></path>
<path d="M3.534 10.07a1 1 0 1 1 .733 1.86A3.579 3.579 0 0 0 2 15.26V17a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-1.647a3.658 3.658 0 0 0-2.356-3.419 1 1 0 1 1 .712-1.868A5.658 5.658 0 0 1 14 15.353V17a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3v-1.74a5.579 5.579 0 0 1 3.534-5.19zM7 0a4 4 0 0 1 4 4v2a4 4 0 1 1-8 0V4a4 4 0 0 1 4-4zm0 2a2 2 0 0 0-2 2v2a2 2 0 1 0 4 0V4a2 2 0 0 0-2-2z"></path> </svg>
</svg> <h2>Photographer</h2>
<h2>Photographer</h2>
</div>
{% elif tag == 'Camera' %}
<div class="image-info__header">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-2 -4 24 24" fill="currentColor">
<path d="M5.676 5H4a2 2 0 0 0-2 2v5a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-1.676l-.387-1.501A2.002 2.002 0 0 0 12 2H8a2 2 0 0 0-1.937 1.499L5.676 5zm-1.55-2C4.57 1.275 6.136 0 8 0h4a4.002 4.002 0 0 1 3.874 3H16a4 4 0 0 1 4 4v5a4 4 0 0 1-4 4H4a4 4 0 0 1-4-4V7a4 4 0 0 1 4-4h.126zM10 13a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm0-2a2 2 0 1 0 0-4 2 2 0 0 0 0 4zm6-3a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"></path>
</svg>
<h2>Camera</h2>
</div>
{% elif tag == 'Software' %}
<div class="image-info__header">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-2 -4 24 24" fill="currentColor">
<path d="M2 13v1h3V2H2v9h1v2H2zM1 0h5a1 1 0 0 1 1 1v14a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1V1a1 1 0 0 1 1-1zm9 3h8a2 2 0 0 1 2 2v6a2 2 0 0 1-2 2h-8a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2zm0 2v6h8V5h-8zm2 9h4a1 1 0 0 1 0 2h-4a1 1 0 0 1 0-2z"></path>
</svg>
<h2>Software</h2>
</div>
{% elif tag == 'Photo' %}
<div class="image-info__header">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-4 -2 24 24" fill="currentColor">
<path d="M14 8.322V2H2v12h3.576l3.97-5.292A3 3 0 0 1 14 8.322zm0 3.753l-1.188-2.066a1 1 0 0 0-1.667-.101L8.076 14H14v-1.925zM14 16H2v2h12v-2zM2 0h12a2 2 0 0 1 2 2v16a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2zm4 9a3 3 0 1 1 0-6 3 3 0 0 1 0 6zm0-2a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"></path>
</svg>
<h2>Photo</h2>
</div>
{% elif tag == 'File' %}
<div class="image-info__header">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-4 -2 24 24" fill="currentColor">
<path d="M10.298 2H3a1 1 0 0 0-1 1v14a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V4.961L10.298 2zM3 0h8l5 4v13a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3z"></path>
</svg>
<h2>File</h2>
</div>
{% else %}
<div class="image-info__header">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-2 -2 24 24" fill="currentColor">
<path d="M14.95 7.879l-.707-.707a1 1 0 0 1 1.414-1.415l.707.707 1.414-1.414-2.828-2.828L2.222 14.95l2.828 2.828 1.414-1.414L5.05 14.95a1 1 0 0 1 1.414-1.414L7.88 14.95l1.414-1.414-.707-.708A1 1 0 0 1 10 11.414l.707.707 1.414-1.414-1.414-1.414a1 1 0 0 1 1.414-1.414l1.415 1.414 1.414-1.414zM.808 13.536L13.536.808a2 2 0 0 1 2.828 0l2.828 2.828a2 2 0 0 1 0 2.828L6.464 19.192a2 2 0 0 1-2.828 0L.808 16.364a2 2 0 0 1 0-2.828z"></path>
</svg>
<h2>{{tag}}</h2>
</div>
{% endif %}
<div class="image-info__content">
<table>
{% for subtag in exif[tag] %}
<tr>
<td>{{subtag}}</td>
{% if exif[tag][subtag]['formatted'] %}
{% if exif[tag][subtag]['type'] == 'date' %}
<td><span class="time">{{exif[tag][subtag]['formatted']}}</span></td>
{% else %}
<td>{{exif[tag][subtag]['formatted']}}</td>
{% endif %}
{% elif exif[tag][subtag]['raw'] %}
<td>{{exif[tag][subtag]['raw']}}</td>
{% else %}
<td class="empty-table">sad noises</td>
{% endif %}
</tr>
{% endfor %}
</table>
</div> </div>
{% elif tag == 'Camera' %}
<div class="image-info__header">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-2 -4 24 24" fill="currentColor">
<path d="M5.676 5H4a2 2 0 0 0-2 2v5a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-1.676l-.387-1.501A2.002 2.002 0 0 0 12 2H8a2 2 0 0 0-1.937 1.499L5.676 5zm-1.55-2C4.57 1.275 6.136 0 8 0h4a4.002 4.002 0 0 1 3.874 3H16a4 4 0 0 1 4 4v5a4 4 0 0 1-4 4H4a4 4 0 0 1-4-4V7a4 4 0 0 1 4-4h.126zM10 13a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm0-2a2 2 0 1 0 0-4 2 2 0 0 0 0 4zm6-3a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"></path>
</svg>
<h2>Camera</h2>
</div>
{% elif tag == 'Software' %}
<div class="image-info__header">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-2 -4 24 24" fill="currentColor">
<path d="M2 13v1h3V2H2v9h1v2H2zM1 0h5a1 1 0 0 1 1 1v14a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1V1a1 1 0 0 1 1-1zm9 3h8a2 2 0 0 1 2 2v6a2 2 0 0 1-2 2h-8a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2zm0 2v6h8V5h-8zm2 9h4a1 1 0 0 1 0 2h-4a1 1 0 0 1 0-2z"></path>
</svg>
<h2>Software</h2>
</div>
{% elif tag == 'Photo' %}
<div class="image-info__header">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-4 -2 24 24" fill="currentColor">
<path d="M14 8.322V2H2v12h3.576l3.97-5.292A3 3 0 0 1 14 8.322zm0 3.753l-1.188-2.066a1 1 0 0 0-1.667-.101L8.076 14H14v-1.925zM14 16H2v2h12v-2zM2 0h12a2 2 0 0 1 2 2v16a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2zm4 9a3 3 0 1 1 0-6 3 3 0 0 1 0 6zm0-2a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"></path>
</svg>
<h2>Photo</h2>
</div>
{% else %}
<div class="image-info__header">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-2 -2 24 24" fill="currentColor">
<path d="M14.95 7.879l-.707-.707a1 1 0 0 1 1.414-1.415l.707.707 1.414-1.414-2.828-2.828L2.222 14.95l2.828 2.828 1.414-1.414L5.05 14.95a1 1 0 0 1 1.414-1.414L7.88 14.95l1.414-1.414-.707-.708A1 1 0 0 1 10 11.414l.707.707 1.414-1.414-1.414-1.414a1 1 0 0 1 1.414-1.414l1.415 1.414 1.414-1.414zM.808 13.536L13.536.808a2 2 0 0 1 2.828 0l2.828 2.828a2 2 0 0 1 0 2.828L6.464 19.192a2 2 0 0 1-2.828 0L.808 16.364a2 2 0 0 1 0-2.828z"></path>
</svg>
<h2>{{tag}}</h2>
</div>
{% endif %}
<div class="image-info__content">
<table>
{% for subtag in exif[tag] %}
<tr>
<td>{{subtag}}</td>
{% if exif[tag][subtag]['formatted'] %}
{% if exif[tag][subtag]['type'] == 'date' %}
<td><span class="time">{{exif[tag][subtag]['formatted']}}</span></td>
{% else %}
<td>{{exif[tag][subtag]['formatted']}}</td>
{% endif %}
{% elif exif[tag][subtag]['raw'] %}
<td>{{exif[tag][subtag]['raw']}}</td>
{% else %}
<td class="empty-table">Oops, an error</td>
{% endif %}
</tr>
{% endfor %}
</table>
</div> </div>
{% endfor %} </div>
{% endif %} {% endfor %}
</div> </div>
{% endblock %} {% endblock %}

View file

@ -170,7 +170,7 @@
overflow: hidden overflow: hidden
table table
margin: 0 0 0 1rem margin: 0
padding: 0 padding: 0
max-width: 100% max-width: 100%
@ -190,7 +190,10 @@
padding: 0.25rem 1rem 0.25rem 0 padding: 0.25rem 1rem 0.25rem 0
width: 50% width: 50%
max-width: 0
overflow: hidden
text-overflow: ellipsis
white-space: nowrap white-space: nowrap
font-size: 1rem font-size: 1rem
@ -264,15 +267,6 @@
.image-wrapper .image-wrapper
padding-bottom: 4rem padding-bottom: 4rem
.image-info__content
table
margin: 0
tr
td:first-child
width: auto
td:last-child
width: 75%
.image-info__header .image-info__header
background-image: none background-image: none
background-color: $black2 background-color: $black2