Skip to main content

get the viewer working

ID
3b652f6
date
2023-05-13 08:58:55+00:00
author
Alex Chan <alex@alexwlchan.net>
parent
2779ea2
message
get the viewer working
changed files
4 files, 189 additions, 17 deletions

Changed files

get_structural_metadata.swift (3944) → get_structural_metadata.swift (3963)

diff --git a/get_structural_metadata.swift b/get_structural_metadata.swift
index 57ac5de..cd52d19 100644
--- a/get_structural_metadata.swift
+++ b/get_structural_metadata.swift
@@ -30,7 +30,7 @@ PHAssetCollection
 
 struct AssetData: Codable {
   var localIdentifier: String
-  var creationDate: Date?
+  var creationDate: String?
 }
 
 var allAssets: [AssetData] = []
@@ -41,7 +41,7 @@ PHAsset
     allAssets.append(
       AssetData(
         localIdentifier: asset.localIdentifier,
-        creationDate: asset.creationDate
+        creationDate: asset.creationDate?.ISO8601Format()
       )
     )
   })

server.py (1715) → server.py (2691)

diff --git a/server.py b/server.py
index d79b08d..b4137da 100755
--- a/server.py
+++ b/server.py
@@ -11,11 +11,41 @@ app = Flask(__name__)
 app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 24 * 60 * 60
 
 
+class PhotosData:
+    def __init__(self):
+        data = json.loads(subprocess.check_output(['swift', 'get_structural_metadata.swift']))
+
+        all_assets = sorted(data['assets'], key=lambda a: a['creationDate'])
+
+        all_albums = data['albums']
+
+        for alb in all_albums:
+            alb['assetIdentifiers'] = set(alb['assetIdentifiers'])
+
+        for asset in all_assets:
+            asset['albums'] = [alb['localizedTitle'] for alb in all_albums if asset['localIdentifier'] in alb['assetIdentifiers']]
+
+            if 'Flagged' in asset['albums']:
+                asset['state'] = 'Flagged'
+                asset['albums'].remove('Flagged')
+            elif 'Rejected' in asset['albums']:
+                asset['state'] = 'Rejected'
+                asset['albums'].remove('Rejected')
+            else:
+                asset['state'] = 'Unknown'
+
+            assert 'Flagged' not in asset['albums']
+            assert 'Rejected' not in asset['albums']
+
+        self.all_assets = all_assets
+
+
+photos_data = PhotosData()
+
+
 @app.route("/")
 def index():
-    data = json.load(open('out.json'))
-
-    all_assets = sorted(data['assets'], key=lambda a: a['creationDate'])
+    all_assets = photos_data.all_assets
 
     try:
         local_identifier = request.args['localIdentifier']
@@ -33,7 +63,8 @@ def index():
 
 @functools.cache
 def get_thumbnail_path(local_identifier):
-    return subprocess.check_output(['swift', 'get_asset_jpeg.swift', local_identifier, '65']).decode('utf8')
+    # 85 * 2x
+    return subprocess.check_output(['swift', 'get_asset_jpeg.swift', local_identifier, '170']).decode('utf8')
 
 
 @app.route('/thumbnail')

templates/index.html (861) → templates/index.html (6732)

diff --git a/templates/index.html b/templates/index.html
index dd28497..049eff0 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -1,32 +1,161 @@
 <style>
-  .thumbnail {
+  body {
+    text-align: center;
+    padding: 0;
+    margin: 10px;
+    font-family: -apple-system;
+/*    background: black;*/
+  }
+
+  a {
+    text-decoration: none;
+  }
+
+  #thumbnails {
+    margin-bottom: 1em;
+    height: 85px;
+  }
+
+  #thumbnails div.thumbnail {
+    display: inline-block;
+    border: 1px solid lightgrey;
+    margin: 1px;
+    width: 65px;
+    height: 65px;
+    padding: 1px;
+  }
+
+  #thumbnails div.thumbnail img {
+    width: 65px;
+    height: 65px;
+  }
+
+  #thumbnails div.thumbnail .state {
+    position: absolute;
+    width: 15px;
+    height: 17px;
+    color: white;
+    text-align: left;
+    padding-left: 3px;
+    padding-top: 1px;
+    font-size: 13px;
+    line-height: 16px;
+    border-bottom-right-radius: 18px;
+    margin-left: -3px;
+    margin-top: -3px;
+    font-family: serif;
+  }
+
+  #thumbnails div.this_asset div.thumbnail .state {
+    width: 19px;
+    height: 21px;
+    color: white;
+    text-align: left;
+    padding-left: 4px;
+    padding-top: 2px;
+    font-size: 17px;
+    line-height: 16px;
+    border-bottom-right-radius: 18px;
+    margin-left: -3px;
+    margin-top: -3px;
+    font-family: serif;
+  }
+
+  #thumbnails div.thumbnail.state-Flagged {
+    border-color: green;
+    border-width: 2px;
+    margin: 0;
+  }
+
+  #thumbnails div.thumbnail.state-Flagged .state {
+    background: green;
+  }
+
+  #thumbnails div.thumbnail.state-Rejected {
+    border-color: red;
+    border-width: 2px;
+    margin: 0;
+  }
+
+  #thumbnails div.thumbnail.state-Rejected .state {
+    background: red;
+  }
+
+  #thumbnails div.this_asset {
+    display: inline-block;
+  }
+
+  #thumbnails div.this_asset div.thumbnail {
+    width: 85px;
+    height: 85px;
+  }
+
+  #thumbnails div.this_asset div.thumbnail img {
+    width: 85px;
+    height: 85px;
+  }
+
+  #thumbnails img {
     object-fit: cover;
     aspect-ratio: 1 / 1;
-    border: 2px solid red;
   }
 
-  .thumbnail_small {
+  #thumbnails .placeholder {
     width: 65px;
     height: 65px;
+    display: inline-block;
+    border: 2px solid lightgrey;
+    opacity: 0.5;
   }
 
   .thumbnail_big {
     width: 85px;
     height: 85px;
   }
+
+  img#big {
+    max-width: calc(100vw - 20px);
+    /* screen height - 85px (thumbnail bar) - 20px (body padding) -1em (margin below thumbnail bar) - 18px (metadata) - 1em (margin below metadata)*/
+    max-height: calc(100vh - 85px - 20px - 1em - 18px - 1em);
+  }
 </style>
 
-{% for a in prev_five %}
-  <img src="{{ url_for('thumbnail', localIdentifier=a['localIdentifier']) }}" class="thumbnail thumbnail_small">
-{% endfor %}
+<div id="thumbnails">
+  {% set placeholder_prev_five = 5 - prev_five|length %}
+  {% for _ in range(placeholder_prev_five) %}
+    <!-- from subtle patterns -->
+    <img src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAkGBwgHBgkIBwgKCgkLDRYPDQwMDRsUFRAWIB0iIiAdHx8kKDQsJCYxJx8fLT0tMTU3Ojo6Iys/RD84QzQ5OjcBCgoKDQwNGg8PGjclHyU3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3N//AABEIAFwAXAMBIgACEQEDEQH/xAAXAAEBAQEAAAAAAAAAAAAAAAAAAQcC/8QAMxAAAQMDAwIEBAUEAwAAAAAAAQARIQIxQSIyURJhQnGBoVJicrEDEzPR8EORssEjc4L/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEQMRAD8A2gdt3s6fTu9u7J5RXbqw6Z0sKjnB5QB8m727sn/XfvYjsgnZFXODyyfQ1JPOQgfQ3/rjsn0QM9XCfQwzOQn0aRcvkIH0wPF1cJ5W8XVdT6dNImoHhXygDcKpKBjinxdV0/x8T3dMcUjcDdPVqRuBu6B/jkZdP+T+kwGXu6Y+XIy6NWf06mHDoA48VurlL7TqMdXPKdvFbq5KSYpLEx1csgX2lqjnnnyS+zSbvyg1FqS1R8Qyybtp6SZflAd9mnPmEvsPRkvkJfbpz5oC+09Lai2QgPD06QJIMwnlpA3C6CZp0gSQJdB20imSAgY+GkXpu6ewEGnkpYPtFN6eUHNgINIQMcU26e6dNdX6dXSBDJ38Ig0906Kq5oqNIEMEBn0vJjqym4mmxt1ZjlRnej0fPr2V3E02w+Y5QBqPSIJzn1TfAek3cX9U3aZHcX9U3xIzH+0DdbTmMoNVtLSWTfyM6f5dN2CGnT/LoAmRpaSKZdBIcQKS5FNigmbdMtTlBMzplqbFAx1WFNwLFPmsKYIFig+L4cU2KC3V8MMLFAZg+Bp6cJ+Wa5pq6QIZMdXENhPy/wAyerpaGFvRAZ9HZu6M+hm75jlIOg7bd/NLvQYFvJA3vTI+481N8GMxf1V36TAHskV6TA7Y7IG+CGzCDXgxOn7eafqbhaY54TffExE8IA1T8M6cpfU234bFN0mTTIaH7ID1ajJBhodAuOr4bNYoMVZpgNYpfXc02aAU+e5Fmse6Bjra0dvNOgfiajU2B3T58iBw3KdAr1VVdP8AtA+Q7fsOXSDpqinztwkbTs9m5dL6aop+3CBu01RSPb903OK4H9m7JfTXFP24ZN0V2HoyBvivHox4TfulrYnhN29vWJ4Tdv8AR4nhA3TVJFsOeE3ajJG3DpumoORZ4nhLzVJG14coF2qvUNuHS7VXqFsOOWS83qG18oPivVg8hA+fxY8uWTpor1fiFj90+bxY5byRqDP4hY/dAix2ezful4qGj27Ml6hSdpqIZSnUQCIJIbhkFvFcU/xmS8fiW729EGogVTSXjhlKdRAqkEEscILf9THMSl9/MdXP7KUnq3AHT1ThAX3S1PUHwUFvumoberlL7pq8PVdR4JMmkOCUJiomTTIJQXgmavD1XKDnx4e7IYFRuaWYnuhjqNzSQATdA7+PHLfuh/L/AKparzn1T7irpfLLqiimsE1ByCzoP//Z" class="placeholder">
+  {% endfor %}
+
+  {% for asset in prev_five %}
+    {% include "thumbnail.html" %}
+  {% endfor %}
+
+
+  <div class="this_asset">
+    {% set asset = this_asset %}
+    {% include "thumbnail.html" %}
+  </div>
+
+  {% for asset in next_five %}
+    {% include "thumbnail.html" %}
+  {% endfor %}
 
-<img src="{{ url_for('thumbnail', localIdentifier=this_asset['localIdentifier']) }}" class="thumbnail thumbnail_big">
+  {% set placeholder_next_five = 5 - next_five|length %}
+  {% for _ in range(placeholder_next_five) %}
+    <img src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAkGBwgHBgkIBwgKCgkLDRYPDQwMDRsUFRAWIB0iIiAdHx8kKDQsJCYxJx8fLT0tMTU3Ojo6Iys/RD84QzQ5OjcBCgoKDQwNGg8PGjclHyU3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3N//AABEIAFwAXAMBIgACEQEDEQH/xAAXAAEBAQEAAAAAAAAAAAAAAAAAAQcC/8QAMxAAAQMDAwIEBAUEAwAAAAAAAQARIQIxQSIyURJhQnGBoVJicrEDEzPR8EORssEjc4L/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEQMRAD8A2gdt3s6fTu9u7J5RXbqw6Z0sKjnB5QB8m727sn/XfvYjsgnZFXODyyfQ1JPOQgfQ3/rjsn0QM9XCfQwzOQn0aRcvkIH0wPF1cJ5W8XVdT6dNImoHhXygDcKpKBjinxdV0/x8T3dMcUjcDdPVqRuBu6B/jkZdP+T+kwGXu6Y+XIy6NWf06mHDoA48VurlL7TqMdXPKdvFbq5KSYpLEx1csgX2lqjnnnyS+zSbvyg1FqS1R8Qyybtp6SZflAd9mnPmEvsPRkvkJfbpz5oC+09Lai2QgPD06QJIMwnlpA3C6CZp0gSQJdB20imSAgY+GkXpu6ewEGnkpYPtFN6eUHNgINIQMcU26e6dNdX6dXSBDJ38Ig0906Kq5oqNIEMEBn0vJjqym4mmxt1ZjlRnej0fPr2V3E02w+Y5QBqPSIJzn1TfAek3cX9U3aZHcX9U3xIzH+0DdbTmMoNVtLSWTfyM6f5dN2CGnT/LoAmRpaSKZdBIcQKS5FNigmbdMtTlBMzplqbFAx1WFNwLFPmsKYIFig+L4cU2KC3V8MMLFAZg+Bp6cJ+Wa5pq6QIZMdXENhPy/wAyerpaGFvRAZ9HZu6M+hm75jlIOg7bd/NLvQYFvJA3vTI+481N8GMxf1V36TAHskV6TA7Y7IG+CGzCDXgxOn7eafqbhaY54TffExE8IA1T8M6cpfU234bFN0mTTIaH7ID1ajJBhodAuOr4bNYoMVZpgNYpfXc02aAU+e5Fmse6Bjra0dvNOgfiajU2B3T58iBw3KdAr1VVdP8AtA+Q7fsOXSDpqinztwkbTs9m5dL6aop+3CBu01RSPb903OK4H9m7JfTXFP24ZN0V2HoyBvivHox4TfulrYnhN29vWJ4Tdv8AR4nhA3TVJFsOeE3ajJG3DpumoORZ4nhLzVJG14coF2qvUNuHS7VXqFsOOWS83qG18oPivVg8hA+fxY8uWTpor1fiFj90+bxY5byRqDP4hY/dAix2ezful4qGj27Ml6hSdpqIZSnUQCIJIbhkFvFcU/xmS8fiW729EGogVTSXjhlKdRAqkEEscILf9THMSl9/MdXP7KUnq3AHT1ThAX3S1PUHwUFvumoberlL7pq8PVdR4JMmkOCUJiomTTIJQXgmavD1XKDnx4e7IYFRuaWYnuhjqNzSQATdA7+PHLfuh/L/AKparzn1T7irpfLLqiimsE1ByCzoP//Z" class="placeholder">
+  {% endfor %}
+</div>
 
-{% for a in next_five %}
-  <img src="{{ url_for('thumbnail', localIdentifier=a['localIdentifier']) }}" class="thumbnail thumbnail_small">
-{% endfor %}
+<p class="metadata">
+  {% if this_asset['albums'] %}
+    <strong>albums:</strong> {{ this_asset['albums'] | join(', ') }} /
+  {% endif %}
+  <strong>state:</strong> {{ this_asset['state'] }} /
+  <strong>creation date:</strong> {{ this_asset['creationDate'] }}
+</p>
 
-<img src="{{ url_for('image', localIdentifier=this_asset['localIdentifier']) }}">
+<img id="big" src="{{ url_for('image', localIdentifier=this_asset['localIdentifier']) }}">
 
 
 <!--{% for a in assets %}

templates/thumbnail.html (0) → templates/thumbnail.html (455)

diff --git a/templates/thumbnail.html b/templates/thumbnail.html
new file mode 100644
index 0000000..143e700
--- /dev/null
+++ b/templates/thumbnail.html
@@ -0,0 +1,12 @@
+<a href="{{ url_for('index', localIdentifier=asset['localIdentifier'])}}">
+  <div class="thumbnail state-{{ asset['state'] }}">
+    {% if asset['state'] == 'Flagged' %}
+      <div class="state">✓</div>
+    {% elif asset['state'] == 'Rejected' %}
+      <div class="state">✘</div>
+    {% endif %}
+    <img
+      src="{{ url_for('thumbnail', localIdentifier=asset['localIdentifier']) }}"
+      class="thumbnail state-{{ asset['state'] }}">
+  </div>
+</a>