Commit 677773ee authored by Birte Kristina Friesel's avatar Birte Kristina Friesel
Browse files

move thumbnail logic into Thumbnail class

parent 7b508ff1
Loading
Loading
Loading
Loading
+211 −151
Original line number Diff line number Diff line
@@ -19,10 +19,6 @@ geocoder = None
location_cache = dict()


class ProgressBar(Bar):
    suffix = "%(percent).0f%% [%(elapsed_td)s/%(eta_td)s]"


def rotate_image(image, exif_tag):
    if "Image Orientation" not in exif_tag:
        return image
@@ -45,14 +41,171 @@ def format_f(value, precision=1):
    return f"{value:.{precision}f}"


def format_gps(exif_tag):
class ProgressBar(Bar):
    suffix = "%(percent).0f%% [%(elapsed_td)s/%(eta_td)s]"


class GPSData:
    def __init__(self, lat, lon, location):
        self.lat = lat
        self.lon = lon
        self.location = location


class ThumbnailHTML:
    def __init__(self):
        self.gps = None
        self.datetime = None
        self.file_link = None
        self.make = None
        self.focus = None

    def set_datetime(self, dt):
        self.datetime = dt.strftime("""<span class="datetime">%d.%m.%Y %H:%M</span>""")

    def set_focus(self, f_num, exposure, focal_length, focal_length35, iso):
        entries = list()
        if f_num is not None:
            entries.append(f"""<span class="fnumber">f/{format_f(f_num)}</span>""")

        if exposure is not None:
            if exposure >= 1:
                entries.append(
                    f"""<span class="exposure">{format_f(exposure)}s</span>"""
                )
            elif exposure >= 1e-3:
                entries.append(
                    f"""<span class="exposure">{format_f(exposure * 1e3)}ms</span>"""
                )
            else:
                entries.append(
                    f"""<span class="exposure">{format_f(exposure * 1e6)}µs</span>"""
                )

        if focal_length is not None:
            entry = f"{format_f(focal_length)}mm"
            if focal_length35 is not None and focal_length35 != focal_length:
                entry += f" (≙ {format_f(focal_length35)}mm)"
            entries.append(f"""<span class="focal">{entry}</span>""")

        if iso is not None:
            entries.append(f"""<span class="iso">ISO{iso}</span>""")

        self.focus = " ".join(entries)

    def set_gps(self, gps):
        self.gps = f"""<span class="gps"><a href="https://www.openstreetmap.org/?mlat={gps.lat}&mlon={gps.lon}#map=13/{gps.lat}/{gps.lon}">{gps.location}</a></span>"""

    def set_makemodel(self, make, model):
        self.make = f"""<span class="makemodel">{make} {model}</span>"""

    def to_html(self, index, filename, thumbname):
        exif_lines = (self.datetime, self.gps, self.make, self.focus)
        exif_html = """ <span class="sep">•</span> """.join(filter(bool, exif_lines))

        buf = """<div class="image-container">\n"""
        buf += f"""<a href="{filename}" class="glightbox" data-gallery="gallery1" data-description=".gdesc{index}">"""
        buf += f"""<img src="{thumbname}" alt="{filename}" />"""
        buf += "</a>"
        buf += "</div>"
        buf += f"""<div class="glightbox-desc gdesc{index}">\n"""
        buf += (
            f"""<p><span class="download"><a href="{filename}">{filename}</a></span>"""
        )
        buf += f"""<span class="sep">•</span>{exif_html}</p>\n"""
        buf += "</div>\n"

        return buf


class Thumbnail:
    def __init__(self, filename, im, size=250, with_gps=False):
        self.filename = filename
        self.size = size
        self.exif_dt = None
        self.gps = None

        with open(filename, "rb") as f:
            self.exif_tag = exifread.process_file(f)

        self.thumbname = f".thumbnails/{filename}"
        if not filename.lower().endswith((".jpeg", ".jpg")):
            self.thumbname += ".jpg"

        im = rotate_image(im, self.exif_tag)
        im.thumbnail((self.size * 2, self.size * 2))
        im.convert("RGB").save(self.thumbname, "JPEG")

        self.html = ThumbnailHTML()

        self._get_datetime()
        self._get_focus()
        self._get_makemodel()

        if with_gps:
            self._get_gps()

    def _get_datetime(self):
        dt = None

        try:
        lat = exif_tag["GPS GPSLatitude"]
        latref = exif_tag["GPS GPSLatitudeRef"].values[0]
        lon = exif_tag["GPS GPSLongitude"]
        lonref = exif_tag["GPS GPSLongitudeRef"].values[0]
            dt = datetime.strptime(
                self.exif_tag["EXIF DateTimeOriginal"].values, "%Y:%m:%d %H:%M:%S"
            )
        except (KeyError, ValueError):
            try:
                dt = datetime.strptime(
                    self.exif_tag["Image DateTimeOriginal"].values, "%Y:%m:%d %H:%M:%S"
                )
            except (KeyError, ValueError):
                pass

        if dt:
            self.exif_dt = dt
            self.html.set_datetime(dt)

    def _get_focus(self):
        entries = list()

        f_num = None
        exposure = None
        focal_length = None
        focal_length35 = None
        iso = None

        try:
            f_num = float(self.exif_tag["EXIF FNumber"].values[0])
        except (KeyError, ZeroDivisionError):
            pass

        try:
            exposure = float(self.exif_tag["EXIF ExposureTime"].values[0])
        except (KeyError, ZeroDivisionError):
            pass

        try:
            focal_length = float(self.exif_tag["EXIF FocalLength"].values[0])
            focal_length35 = float(
                self.exif_tag["EXIF FocalLengthIn35mmFilm"].values[0]
            )
        except (KeyError, ZeroDivisionError):
            pass

        try:
            iso = self.exif_tag["EXIF ISOSpeedRatings"].values[0]
        except KeyError:
        return None
            pass

        self.html.set_focus(f_num, exposure, focal_length, focal_length35, iso)

    def _get_gps(self):
        try:
            lat = self.exif_tag["GPS GPSLatitude"]
            latref = self.exif_tag["GPS GPSLatitudeRef"].values[0]
            lon = self.exif_tag["GPS GPSLongitude"]
            lonref = self.exif_tag["GPS GPSLongitudeRef"].values[0]
        except KeyError:
            return

        try:
            lat = (
@@ -66,10 +219,10 @@ def format_gps(exif_tag):
                + float(lon.values[2]) / 3600
            )
        except (IndexError, ZeroDivisionError):
        return None
            return

        if abs(lat) < 0.01 and abs(lon) < 0.01:
        return None
            return

        if latref == "S":
            lat = -lat
@@ -83,7 +236,9 @@ def format_gps(exif_tag):
        latlon = f"{lat:.3f}/{lon:.3f}"

        if latlon in location_cache:
        return f"""<span class="gps"><a href="https://www.openstreetmap.org/?mlat={lat}&mlon={lon}#map=13/{lat}/{lon}">{location_cache[latlon]}</a></span>"""
            self.gps = GPSData(lat, lon, location_cache[latlon])
            self.html.set_gps(self.gps)
            return

        if geocoder is None:
            from geopy.geocoders import Nominatim
@@ -93,66 +248,20 @@ def format_gps(exif_tag):
        # zoom level: 12/13 -> city, 14/15 -> district, 16/17 -> street, 18 -> house no
        try:
            res = geocoder.reverse((lat, lon), zoom=args.nominatim_zoom)
    except TypeError as e:
        return None

            location = res.address.split(",")[0]
            location_cache[latlon] = location
        except TypeError as e:
            location = latlon

    return f"""<span class="gps"><a href="https://www.openstreetmap.org/?mlat={lat}&mlon={lon}#map=13/{lat}/{lon}">{location}</a></span>"""


def format_fsi(exif_tag):
    entries = list()

    try:
        f_num = float(exif_tag["EXIF FNumber"].values[0])
        entries.append(f"""<span class="fnumber">f/{format_f(f_num)}</span>""")
    except (KeyError, ZeroDivisionError):
        pass

    try:
        exposure = float(exif_tag["EXIF ExposureTime"].values[0])
        if exposure >= 1:
            entries.append(f"""<span class="exposure">{format_f(exposure)}s</span>""")
        elif exposure >= 1e-3:
            entries.append(
                f"""<span class="exposure">{format_f(exposure * 1e3)}ms</span>"""
            )
        else:
            entries.append(
                f"""<span class="exposure">{format_f(exposure * 1e6)}µs</span>"""
            )
    except (KeyError, ZeroDivisionError):
        pass

    try:
        focal_length = float(exif_tag["EXIF FocalLength"].values[0])
        entry = f"{format_f(focal_length)}mm"
        try:
            focal_length35 = float(exif_tag["EXIF FocalLengthIn35mmFilm"].values[0])
            entry += f" (≙ {format_f(focal_length35)}mm)"
        except (KeyError, ZeroDivisionError):
            pass
        entries.append(f"""<span class="focal">{entry}</span>""")
    except (KeyError, ZeroDivisionError):
        pass

    try:
        iso = exif_tag["EXIF ISOSpeedRatings"].values[0]
        entries.append(f"""<span class="iso">ISO{iso}</span>""")
    except KeyError:
        pass

    return " ".join(entries)

        self.gps = GPSData(lat, lon, location)
        self.html.set_gps(self.gps)

def format_make_model_lens(exif_tag):
    def _get_makemodel(self):
        try:
        make = exif_tag["Image Make"].values
        model = exif_tag["Image Model"].values
            make = self.exif_tag["Image Make"].values
            model = self.exif_tag["Image Model"].values
        except KeyError:
        return None
            return

        if model.startswith(make):
            model = model[len(make) :]
@@ -160,44 +269,17 @@ def format_make_model_lens(exif_tag):
                model = model[1:]

        try:
        lens = exif_tag["EXIF LensModel"]
            lens = self.exif_tag["EXIF LensModel"]
            if lens:
                model += f" + {lens}"
        except KeyError:
            # Unknown or built-in lens
            pass

    return f"""<span class="makemodel">{make} {model}</span>"""


def format_exif(exif_tag):
    exif_lines = list()
    dt = None

    try:
        dt = datetime.strptime(
            exif_tag["EXIF DateTimeOriginal"].values, "%Y:%m:%d %H:%M:%S"
        )
    except (KeyError, ValueError):
        try:
            dt = datetime.strptime(
                exif_tag["Image DateTimeOriginal"].values, "%Y:%m:%d %H:%M:%S"
            )
        except (KeyError, ValueError):
            pass

    if dt:
        exif_lines.append(
            dt.strftime("""<span class="datetime">%d.%m.%Y %H:%M</span>""")
        )

    if args.with_nominatim:
        exif_lines.append(format_gps(exif_tag))

    exif_lines.append(format_make_model_lens(exif_tag))
    exif_lines.append(format_fsi(exif_tag))
        self.html.set_makemodel(make, model)

    return """ <span class="sep">•</span> """.join(filter(bool, exif_lines))
    def to_html(self, index):
        return self.html.to_html(i, self.filename, self.thumbname)


def copy_files(base_dir):
@@ -230,20 +312,6 @@ def copy_files(base_dir):
        f.write(main_css)


def create_thumbnail_html(index, thumbname, filename, title):
    buf = """<div class="image-container">\n"""
    buf += f"""<a href="{filename}" class="glightbox" data-gallery="gallery1" data-description=".gdesc{index}">"""
    buf += f"""<img src="{thumbname}" alt="{filename}" />"""
    buf += "</a>"
    buf += "</div>"
    buf += f"""<div class="glightbox-desc gdesc{index}">\n"""
    buf += f"""<p><span class="download"><a href="{filename}">{filename}</a></span>"""
    buf += f"""<span class="sep">•</span>{title}</p>\n"""
    buf += "</div>\n"

    return buf


if __name__ == "__main__":

    parser = argparse.ArgumentParser(
@@ -283,28 +351,20 @@ if __name__ == "__main__":
            html_buf += f.read()

    filenames = args.images
    thumbnails = list()

    for i, filename in enumerate(ProgressBar(max=len(filenames)).iter(filenames)):
        with open(filename, "rb") as f:
            exif_tag = exifread.process_file(f)

        try:
            im = Image.open(filename)
        except PIL.UnidentifiedImageError:
            continue

        im = rotate_image(im, exif_tag)

        im.thumbnail((args.size * 2, args.size * 2))

        thumbname = f".thumbnails/{filename}"

        if not filename.lower().endswith((".jpeg", ".jpg")):
            thumbname += ".jpg"

        im.convert("RGB").save(thumbname, "JPEG")
        thumbnails.append(
            Thumbnail(filename, im, size=args.size, with_gps=args.with_nominatim)
        )

        html_buf += create_thumbnail_html(i, thumbname, filename, format_exif(exif_tag))
    for i, thumbnail in enumerate(thumbnails):
        html_buf += thumbnail.to_html(i)

    with open(f"{base_dir}/share/html_end", "r") as f:
        html_buf += f.read()