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:
parent
1706852291
commit
f15cc032d7
98
tornado_gallery/gallery.py
Normal file
98
tornado_gallery/gallery.py
Normal 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
|
63
tornado_gallery/metadata.py
Normal file
63
tornado_gallery/metadata.py
Normal 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
95
tornado_gallery/photo.py
Normal 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
|
Loading…
Reference in New Issue
Block a user