1
0
mirror of https://github.com/sjlongland/tornado-gallery.git synced 2025-10-16 07:39:24 +10:00

Initial implementation.

Work in progress.
This commit is contained in:
Stuart Longland 2018-03-25 20:22:15 +10:00
parent 1706852291
commit f15cc032d7
Signed by: stuartl
GPG Key ID: 6AA32EFB18079BAA
3 changed files with 256 additions and 0 deletions

View File

@ -0,0 +1,98 @@
#!/usr/bin/env python
from weakref import WeakValueDictionary
from collections import OrderedDict
import os.path
from time import time
from .metadata import parse_meta
from .photo import Photo
class Gallery(object):
"""
Representation of a photo gallery.
"""
_INSTANCE = WeakValueDictionary()
def __init__(self, fs_cache, meta_cache, dir):
dir = os.path.realpath(dir)
self._dir = dir
self._title = None
self._desc = None
self._meta_cache = meta_cache
self._meta_mtime = 0
self._fs_cache = fs_cache
self._content = None
self._content_mtime = 0
assert self.name not in self._INSTANCE
self._INSTANCE[self.name] = self
@property
def fs_cache(self):
return self._fs_cache
@property
def meta_cache(self):
return self._meta_cache
@property
def dir(self):
return self._dir
@property
def name(self):
return os.path.basename(self.dir)
@property
def title(self):
self._parse_metadata()
return self._title
@property
def desc(self):
self._parse_metadata()
return self._desc
@property
def content(self):
content_mtime_now = self._content_mtime_now
if self._content_mtime < content_mtime_now:
content = {}
for name in self._fs_cache[self.dir]:
# Grab the file extension and analyse
(_, ext) = name.rsplit('.',1)
if ext.lower() not in ('jpg', 'jpe', 'jpeg', 'gif',
'png', 'tif', 'tiff'):
continue
# This is a photo.
content[name] = Photo(self, name)
self._content = OrderedDict(sorted(content.items(),
key=lambda i : i[0]))
self._content_mtime = content_mtime_now
return self._content.copy()
@property
def _meta_file(self):
return os.path.join(self.dir, 'info.txt')
@property
def _meta_mtime_now(self):
return self._fs_cache[self._meta_file].stat.st_mtime
@property
def _dir_mtime_now(self):
return self._fs_cache[self.dir].stat.st_mtime
def _parse_metadata(self):
meta_mtime_now = self._meta_mtime_now
if meta_mtime_now > self._meta_mtime:
data = self._meta_cache[meta_file]
self._title = data.get('.title', self.name)
self._desc = data.get('.desc', 'No description given')
self._meta_mtime = meta_mtime_now

View File

@ -0,0 +1,63 @@
#!/usr/bin/env python
from os.path import realpath
from time import time
def parse_meta(filename):
"""
Parse a raw metadata file and return the output.
"""
return dict([
line.split('\t',1)
for line in open(filename, mode='rt')
])
class MetadataCache(object):
"""
Store the metadata for lots of files and keep them cached.
"""
def __init__(self, cache_duration=300.0, fs_cache=None):
self._cache_duration = float(cache_duration)
self._file = {}
self._fs_cache = None
def __getitem__(self, filename):
"""
Retrieve the content of the given metadata file.
"""
filename = realpath(filename)
# Retrieve the modification time of the file
if self._fs_cache is not None:
mtime_now = self._fs_cache[filename].stat.st_mtime
else:
mtime_now = 0
try:
(_, mtime_last, data) = self._file[filename]
if mtime_last == mtime_now:
# File is still being used, refresh expiry.
self._store_file(filename, mtime_last, data)
return data
except KeyError:
pass
# Not in cache, try to read it in.
data = parse_meta(filename)
self._store_file(filename, mtime_now, data)
return data
def _store_file(self, filename, mtime, data):
self._file[filename] = (time() + self._cache_duration, mtime, data)
def purge(self):
"""
Purge the cache of expired content.
"""
for filename, (expiry, _, _) in list(self._file.items()):
if expiry < time():
self._file.pop(filename, None)

95
tornado_gallery/photo.py Normal file
View File

@ -0,0 +1,95 @@
#!/usr/bin/env python
from weakref import ref
import os.path
from time import time
from .metadata import parse_meta
from .photo import Photo
class Photo(object):
"""
Representation of a photo in a gallery.
"""
def __init__(self, gallery, name):
self._gallery = ref(gallery)
self._name = name
self._title = None
self._desc = None
self._meta_cache = meta_cache
self._meta_mtime = 0
self._fs_cache = fs_cache
self._content = None
self._content_mtime = 0
assert self.name not in self._INSTANCE
self._INSTANCE[self.name] = self
@property
def fs_cache(self):
return self._fs_cache
@property
def meta_cache(self):
return self._meta_cache
@property
def dir(self):
return self._dir
@property
def name(self):
return os.path.basename(self.dir)
@property
def title(self):
self._parse_metadata()
return self._title
@property
def desc(self):
self._parse_metadata()
return self._desc
@property
def content(self):
content_mtime_now = self._content_mtime_now
if self._content_mtime < content_mtime_now:
content = {}
for name in self._fs_cache[self.dir]:
# Grab the file extension and analyse
(_, ext) = name.rsplit('.',1)
if ext.lower() not in ('jpg', 'jpe', 'jpeg', 'gif',
'png', 'tif', 'tiff'):
continue
# This is a photo.
content[name] = Photo(self, name)
self._content = OrderedDict(sorted(content.items(),
key=lambda i : i[0]))
self._content_mtime = content_mtime_now
return self._content.copy()
@property
def _meta_file(self):
return os.path.join(self.dir, 'info.txt')
@property
def _meta_mtime_now(self):
return self._fs_cache[self._meta_file].stat.st_mtime
@property
def _dir_mtime_now(self):
return self._fs_cache[self.dir].stat.st_mtime
def _parse_metadata(self):
meta_mtime_now = self._meta_mtime_now
if meta_mtime_now > self._meta_mtime:
data = self._meta_cache[meta_file]
self._title = data.get('.title', self.name)
self._desc = data.get('.desc', 'No description given')
self._meta_mtime = meta_mtime_now