浏览代码

Resolve "provide thumbnails"

Eric Tröbs 4 年之前
父节点
当前提交
accc2ba95c

+ 12 - 5
pycs/frontend/WebServer.py

@@ -1,5 +1,5 @@
 from glob import glob
 from glob import glob
-from os import path, mkdir, getcwd
+from os import path, mkdir
 from os.path import exists
 from os.path import exists
 from time import time
 from time import time
 
 
@@ -154,8 +154,9 @@ class WebServer:
             # return default success response
             # return default success response
             return response()
             return response()
 
 
-        @self.__flask.route('/projects/<project_identifier>/data/<file_identifier>', methods=['GET'])
-        def get_file(project_identifier, file_identifier):
+        @self.__flask.route('/projects/<project_identifier>/data/<file_identifier>', defaults={'size': None}, methods=['GET'])
+        @self.__flask.route('/projects/<project_identifier>/data/<file_identifier>/<size>', methods=['GET'])
+        def get_file(project_identifier, file_identifier, size):
             # abort if project id is not valid
             # abort if project id is not valid
             if project_identifier not in app_status['projects'].keys():
             if project_identifier not in app_status['projects'].keys():
                 return make_response('project does not exist', 500)
                 return make_response('project does not exist', 500)
@@ -168,11 +169,17 @@ class WebServer:
 
 
             target_object = project['data'][file_identifier]
             target_object = project['data'][file_identifier]
 
 
+            # resize image to requested size
+            if size is not None:
+                target_object = target_object.resize(size)
+
             # construct directory and filename
             # construct directory and filename
-            file_directory = path.join(getcwd(), 'projects', project['id'], 'data')
-            file_name = target_object['id'] + target_object['extension']
+            file_directory, file_name = target_object.get_file()
 
 
             # return data
             # return data
+            print(file_directory, file_name)
+            print(path.join(file_directory, file_name))
+            print(path.exists(path.join(file_directory, file_name)))
             return send_from_directory(file_directory, file_name)
             return send_from_directory(file_directory, file_name)
 
 
         # finally start web server
         # finally start web server

+ 44 - 0
pycs/projects/MediaFile.py

@@ -1,6 +1,50 @@
+from os import path, getcwd
+
+from PIL import Image
+
 from pycs.observable import ObservableDict
 from pycs.observable import ObservableDict
 
 
 
 
 class MediaFile(ObservableDict):
 class MediaFile(ObservableDict):
     def __init__(self, obj, parent):
     def __init__(self, obj, parent):
         super().__init__(obj, parent)
         super().__init__(obj, parent)
+
+    def __get_file(self, id):
+        file_directory = path.join(getcwd(), 'projects', self.parent['id'], 'data')
+        file_name = id + self['extension']
+
+        return file_directory, file_name
+
+    def get_file(self):
+        return self.__get_file(self['id'])
+
+    def resize(self, maximum_width):
+        # check if resized file already exists
+        resized = MediaFile(self, self.parent)
+        resized['id'] = self['id'] + '-' + maximum_width
+
+        target_directory, target_name = self.__get_file(resized['id'])
+        target_path = path.join(target_directory, target_name)
+
+        if path.exists(target_path):
+            return resized
+
+        # load full size image
+        current_directory, current_name = self.get_file()
+        image = Image.open(path.join(current_directory, current_name))
+        image_width, image_height = image.size
+
+        # calculate target height
+        maximum_width = int(maximum_width)
+        maximum_height = int(maximum_width * image_height / image_width)
+
+        # return self if requested size is larger than the image
+        if image_width < maximum_width:
+            return self
+
+        # resize image
+        resized_image = image.resize((maximum_width, maximum_height))
+
+        # save to file
+        resized_image.save(target_path, quality=80)
+        return resized

+ 10 - 1
webui/src/components/media/annotated-image.vue

@@ -1,6 +1,7 @@
 <template>
 <template>
   <div class="annotated-image" v-on="events">
   <div class="annotated-image" v-on="events">
-    <img alt="media" :src="mediaUrl" ref="image">
+    <img alt="media" ref="image"
+         :src="mediaUrl" :srcset="mediaUrlSet" :sizes="mediaUrlSizes"/>
 
 
     <div style="position: absolute; top: 0.5rem; left: 0.5rem">{{ data }}</div>
     <div style="position: absolute; top: 0.5rem; left: 0.5rem">{{ data }}</div>
 
 
@@ -41,6 +42,7 @@ export default {
   },
   },
   data: function() {
   data: function() {
     return {
     return {
+      sizes: [600, 800, 1200, 1600, 2000, 3000],
       image: {
       image: {
         left: 0,
         left: 0,
         top: 0,
         top: 0,
@@ -55,6 +57,12 @@ export default {
     mediaUrl: function() {
     mediaUrl: function() {
       return this.socket.media(this.project.id, this.data.id);
       return this.socket.media(this.project.id, this.data.id);
     },
     },
+    mediaUrlSet: function() {
+      return this.sizes.map(e => this.mediaUrl + '/' + e + ' ' + e + 'w').join(',');
+    },
+    mediaUrlSizes: function() {
+      return this.sizes.map(e => '(max-width: ' + e + 'px) ' + e + 'px').join(',');
+    },
     events: function() {
     events: function() {
       return {
       return {
         'touchstart': this.press,
         'touchstart': this.press,
@@ -63,6 +71,7 @@ export default {
         'mousedown': this.press,
         'mousedown': this.press,
         'drag': this.track,
         'drag': this.track,
         'dragend': this.release,
         'dragend': this.release,
+        'dragstart': e => e.stopPropagation()
       }
       }
     }
     }
   },
   },

+ 27 - 5
webui/src/components/media/media-selector.vue

@@ -1,11 +1,13 @@
 <template>
 <template>
   <div class="media-selector">
   <div class="media-selector">
     <div class="element"
     <div class="element"
-         v-for="(m, index) in media"
+         v-for="m in mediaObjects"
          v-bind:key="m.id"
          v-bind:key="m.id"
-         @click="$emit('click', index)">
-      <img alt="media" :src="socket.media(projectId, m.id)">
-      <div class="active" v-if="index === current"/>
+         @click="$emit('click', m.index)">
+      <img alt="media" :src="m.src"
+           :srcset="m.srcset" :sizes="m.sizes">
+
+      <div class="active" v-if="m.index === current"/>
     </div>
     </div>
   </div>
   </div>
 </template>
 </template>
@@ -13,7 +15,27 @@
 <script>
 <script>
 export default {
 export default {
   name: "media-selector",
   name: "media-selector",
-  props: ['projectId', 'media', 'current', 'socket']
+  props: ['projectId', 'media', 'current', 'socket'],
+  data: function() {
+    return {
+      sizes: [200, 350, 500]
+    }
+  },
+  computed: {
+    mediaObjects: function() {
+      let index = 0;
+      return this.media.map(e => {
+        const src = this.socket.media(this.projectId, e.id);
+
+        return {
+          index: index++,
+          src: src,
+          srcset: this.sizes.map(e => src + '/' + e + ' ' + e + 'w').join(','),
+          sizes: '5rem'
+        }
+      });
+    }
+  }
 }
 }
 </script>
 </script>