|
|
|
@ -3,31 +3,79 @@ from sqlalchemy.orm import relationship
|
|
|
|
|
from sqlalchemy.schema import UniqueConstraint
|
|
|
|
|
from photoapp.dbutils import Base
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
import enum as py_enum
|
|
|
|
|
import uuid
|
|
|
|
|
import enum
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# file extensions we allow
|
|
|
|
|
known_extensions = ["jpg", "png", "cr2", "xmp", "mp4", "mov"]
|
|
|
|
|
class fcategory(py_enum.Enum):
|
|
|
|
|
image = 0
|
|
|
|
|
raw = 1
|
|
|
|
|
video = 2
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ftypes = dict(jpg=dict(category=fcategory.image,
|
|
|
|
|
extensions={"jpeg"},
|
|
|
|
|
mimes={"image/jpeg"}),
|
|
|
|
|
png=dict(category=fcategory.image,
|
|
|
|
|
mimes={"image/png"}),
|
|
|
|
|
gif=dict(category=fcategory.image,
|
|
|
|
|
mimes={"image/gif"}),
|
|
|
|
|
cr2=dict(category=fcategory.raw,
|
|
|
|
|
mimes={"image/x-canon-cr2"}),
|
|
|
|
|
xmp=dict(category=fcategory.raw,
|
|
|
|
|
mimes={"application/octet-stream-xmp"}),
|
|
|
|
|
psd=dict(category=fcategory.raw,
|
|
|
|
|
mimes={"image/vnd.adobe.photoshop"}),
|
|
|
|
|
mp4=dict(category=fcategory.video,
|
|
|
|
|
mimes={"audio/mp4",
|
|
|
|
|
"video/mp4"}),
|
|
|
|
|
mov=dict(category=fcategory.video,
|
|
|
|
|
mimes={"video/quicktime"}))
|
|
|
|
|
|
|
|
|
|
# set various defaults in ftypes
|
|
|
|
|
# it should look like:
|
|
|
|
|
# ftypes = {
|
|
|
|
|
# <default_extension>: {
|
|
|
|
|
# "category": <fcategory key>, # required, fcategory type
|
|
|
|
|
# "extensions": {<*default_extension|optional extensions>}, # optional, defaults to set([<default_extension>])
|
|
|
|
|
# "mimes": { # required, mimes mapped to this type
|
|
|
|
|
# <mime string>
|
|
|
|
|
# },
|
|
|
|
|
# "type": <str(*image|video)>, # optional, defaults to str("image")
|
|
|
|
|
# },
|
|
|
|
|
# }
|
|
|
|
|
for extension, entry in ftypes.items():
|
|
|
|
|
entry["extensions"] = entry.get("extensions", set()) | set([extension])
|
|
|
|
|
entry["type"] = entry.get("type", "image")
|
|
|
|
|
|
|
|
|
|
# categorizaiton of media type based on extension
|
|
|
|
|
regular_images = ["jpg", "png"] # we can pull metadata out of these
|
|
|
|
|
files_raw = ["cr2", "xmp"] # treated as black boxes
|
|
|
|
|
files_video = ["mp4", "mov"]
|
|
|
|
|
|
|
|
|
|
# extensions with well-known aliases
|
|
|
|
|
mapped_extensions = {"jpg": {"jpeg", }} # target: aliases
|
|
|
|
|
# file extensions we allow
|
|
|
|
|
known_extensions = set.union(*[i["extensions"] for i in ftypes.values()])
|
|
|
|
|
|
|
|
|
|
# allowed file types (based on magic identification)
|
|
|
|
|
# TODO enforce this
|
|
|
|
|
known_mimes = {"image/png",
|
|
|
|
|
"image/jpeg",
|
|
|
|
|
"image/gif",
|
|
|
|
|
"application/octet-stream-xmp",
|
|
|
|
|
"image/x-canon-cr2",
|
|
|
|
|
"audio/mp4",
|
|
|
|
|
"video/mp4",
|
|
|
|
|
"video/quicktime"}
|
|
|
|
|
known_mimes = set.union(*[i["mimes"] for i in ftypes.values()])
|
|
|
|
|
|
|
|
|
|
# categorizaiton of media type based on extension
|
|
|
|
|
# we can pull metadata out of these
|
|
|
|
|
# jpg, png, gif etc
|
|
|
|
|
regular_images = set([extension for extension, ftype in ftypes.items() if ftype["category"] == fcategory.image])
|
|
|
|
|
# "derived" files, treated as black boxes, we can't open them because proprietary
|
|
|
|
|
# cr2, xmp, etc
|
|
|
|
|
files_raw = set([extension for extension, ftype in ftypes.items() if ftype["category"] == fcategory.raw])
|
|
|
|
|
# video types
|
|
|
|
|
# mp4, mov, etc
|
|
|
|
|
files_video = set([extension for extension, ftype in ftypes.items() if ftype["category"] == fcategory.video])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def mime2ext(mime):
|
|
|
|
|
"""
|
|
|
|
|
Given a mime type return the canonical file extension
|
|
|
|
|
"""
|
|
|
|
|
for ext, ftype in ftypes.items():
|
|
|
|
|
if mime in ftype["mimes"]:
|
|
|
|
|
return ext
|
|
|
|
|
raise Exception(f"Unknown mime: '{mime}'")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def genuuid():
|
|
|
|
@ -35,10 +83,10 @@ def genuuid():
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def map_extension(ext):
|
|
|
|
|
for target, aliases in mapped_extensions.items():
|
|
|
|
|
if ext in aliases:
|
|
|
|
|
return target
|
|
|
|
|
return ext
|
|
|
|
|
for known_ext, ftype in ftypes.items():
|
|
|
|
|
if ext in ftype["extensions"]:
|
|
|
|
|
return known_ext
|
|
|
|
|
raise Exception(f"Unknown extension: '{ext}'")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def generate_storage_path(timestamp, sha, extension):
|
|
|
|
|