diff --git a/etiquette/etiquette.py b/etiquette/etiquette.py
index 2341162..89fa35c 100644
--- a/etiquette/etiquette.py
+++ b/etiquette/etiquette.py
@@ -33,6 +33,7 @@ MOTD_STRINGS = [
#'Buckle up, it\'s time to:',
]
+THUMBDIR = phototagger.DEFAULT_THUMBDIR
ERROR_INVALID_ACTION = 'Invalid action'
ERROR_NO_TAG_GIVEN = 'No tag name supplied'
ERROR_TAG_TOO_SHORT = 'Not enough valid chars'
@@ -317,7 +318,7 @@ def get_album_html(albumid):
response = flask.render_template(
'album.html',
album=album,
- child_albums=album['sub_albums'],
+ child_albums=[jsonify_album(P_album(x)) for x in album['sub_albums']],
photos=album['photos'],
)
return response
@@ -392,7 +393,8 @@ def get_photo_json(photoid):
def get_search_core():
print(request.args)
- # EXTENSION
+ # FILENAME & EXTENSION
+ filename_terms = request.args.get('filename', None)
extension_string = request.args.get('extension', None)
extension_not_string = request.args.get('extension_not', None)
mimetype_string = request.args.get('mimetype', None)
@@ -464,6 +466,7 @@ def get_search_core():
'created': created,
'extension': extension_list,
'extension_not': extension_not_list,
+ 'filename': filename_terms,
'has_tags': has_tags,
'mimetype': mimetype_list,
'tag_musts': tag_musts,
diff --git a/etiquette/etiquette_easy.py b/etiquette/etiquette_easy.py
index bd61eaf..ae3566b 100644
--- a/etiquette/etiquette_easy.py
+++ b/etiquette/etiquette_easy.py
@@ -2,6 +2,8 @@
# py -i etiquette_easy.py
import phototagger
+import os
+import sys
P = phototagger.PhotoDB()
import traceback
diff --git a/etiquette/phototagger.py b/etiquette/phototagger.py
index 10e894f..7c8b13f 100644
--- a/etiquette/phototagger.py
+++ b/etiquette/phototagger.py
@@ -61,6 +61,7 @@ SQL_ALBUM_COLUMNS = [
'id',
'title',
'description',
+ 'associated_directory'
]
SQL_PHOTO_COLUMNS = [
'id',
@@ -112,11 +113,12 @@ PRAGMA cache_size = 10000;
CREATE TABLE IF NOT EXISTS albums(
id TEXT,
title TEXT,
- description TEXT
+ description TEXT,
+ associated_directory TEXT COLLATE NOCASE
);
CREATE TABLE IF NOT EXISTS photos(
id TEXT,
- filepath TEXT,
+ filepath TEXT COLLATE NOCASE,
extension TEXT,
width INT,
height INT,
@@ -160,7 +162,7 @@ CREATE INDEX IF NOT EXISTS index_albumrel_photoid on album_photo_rel(photoid);
-- Photo
CREATE INDEX IF NOT EXISTS index_photo_id on photos(id);
-CREATE INDEX IF NOT EXISTS index_photo_path on photos(filepath);
+CREATE INDEX IF NOT EXISTS index_photo_path on photos(filepath COLLATE NOCASE);
CREATE INDEX IF NOT EXISTS index_photo_created on photos(created);
CREATE INDEX IF NOT EXISTS index_photo_extension on photos(extension);
@@ -213,6 +215,10 @@ def _helper_extension(ext):
ext = set(ext)
return ext
+def _helper_filenamefilter(subject, terms):
+ basename = subject.lower()
+ return all(term in basename for term in terms)
+
def _helper_minmax(key, value, minimums, maximums):
'''
When searching, this function dissects a hyphenated range string
@@ -686,10 +692,27 @@ class PDBAlbumMixin:
def get_album(self, id):
return self.get_thing_by_id('album', id)
+ def get_album_by_path(self, filepath):
+ '''
+ Return the album with the `associated_directory` of this value, NOT case-sensitive.
+ '''
+ filepath = os.path.abspath(filepath)
+ self.cur.execute('SELECT * FROM albums WHERE associated_directory == ?', [filepath])
+ f = self.cur.fetchone()
+ if f is None:
+ raise NoSuchAlbum(filepath)
+ return self.get_album(f[SQL_ALBUM['id']])
+
def get_albums(self):
yield from self.get_things(thing_type='album')
- def new_album(self, title=None, description=None, photos=None, commit=True):
+ def new_album(self,
+ associated_directory=None,
+ commit=True,
+ description=None,
+ photos=None,
+ title=None,
+ ):
'''
Create a new album. Photos can be added now or later.
'''
@@ -697,6 +720,9 @@ class PDBAlbumMixin:
albumid = self.generate_id('tags')
title = title or ''
description = description or ''
+ if associated_directory is not None:
+ associated_directory = os.path.abspath(associated_directory)
+
if not isinstance(title, str):
raise TypeError('Title must be string, not %s' % type(title))
@@ -707,8 +733,9 @@ class PDBAlbumMixin:
data[SQL_ALBUM['id']] = albumid
data[SQL_ALBUM['title']] = title
data[SQL_ALBUM['description']] = description
+ data[SQL_ALBUM['associated_directory']] = associated_directory
- self.cur.execute('INSERT INTO albums VALUES(?, ?, ?)', data)
+ self.cur.execute('INSERT INTO albums VALUES(?, ?, ?, ?)', data)
album = Album(self, data)
if photos:
for photo in photos:
@@ -716,6 +743,7 @@ class PDBAlbumMixin:
album.add_photo(photo, commit=False)
if commit:
+ log.debug('Committing - new Album')
self.commit()
return album
@@ -851,6 +879,7 @@ class PDBPhotoMixin:
created=None,
extension=None,
extension_not=None,
+ filename=None,
has_tags=None,
mimetype=None,
tag_musts=None,
@@ -878,6 +907,10 @@ class PDBPhotoMixin:
extension_not:
A string or list of strings of unacceptable file extensions.
+ filename:
+ A string or list of strings which will be split into words. The file's basename
+ must include every word, NOT case-sensitive.
+
has_tags:
If True, require that the Photo has >=1 tag.
If False, require that the Photo has no tags.
@@ -934,6 +967,11 @@ class PDBPhotoMixin:
extension_not = _helper_extension(extension_not)
mimetype = _helper_extension(mimetype)
+ if filename is not None:
+ if not isinstance(filename, str):
+ filename = ' '.join(filename)
+ filename = set(term.lower() for term in filename.strip().split(' '))
+
if (tag_musts or tag_mays or tag_forbids) and tag_expression:
raise XORException('Expression filter cannot be used with musts, mays, forbids')
@@ -984,6 +1022,11 @@ class PDBPhotoMixin:
continue
if mimetype and photo.mimetype() not in mimetype:
+ #print('Failed mimetype')
+ continue
+
+ if filename and not _helper_filenamefilter(subject=photo.basename, terms=filename):
+ #print('Failed filename')
continue
if any(not fetch[SQL_PHOTO[key]] or fetch[SQL_PHOTO[key]] > value for (key, value) in maximums.items()):
@@ -1173,7 +1216,9 @@ class PhotoDB(PDBAlbumMixin, PDBPhotoMixin, PDBTagMixin):
def digest_directory(self, directory, exclude_directories=None, exclude_filenames=None, commit=True):
'''
- Create an album, and add the directory's contents to it.
+ Create an album, and add the directory's contents to it recursively.
+
+ If a Photo object already exists for a file, it will be added to the correct album.
'''
if not os.path.isdir(directory):
raise ValueError('Not a directory: %s' % directory)
@@ -1189,22 +1234,41 @@ class PhotoDB(PDBAlbumMixin, PDBPhotoMixin, PDBTagMixin):
]
directory = spinal.str_to_fp(directory)
+ directory.correct_case()
generator = spinal.walk_generator(
directory,
exclude_directories=exclude_directories,
exclude_filenames=exclude_filenames,
yield_style='nested',
)
- album = self.new_album(title=directory.basename, commit=False)
+ try:
+ album = self.get_album_by_path(directory.absolute_path)
+ except NoSuchAlbum:
+ album = self.new_album(
+ associated_directory=directory.absolute_path,
+ commit=False,
+ title=directory.basename,
+ )
+
albums = {directory.absolute_path: album}
for (current_location, directories, files) in generator:
current_album = albums.get(current_location.absolute_path, None)
if current_album is None:
- current_album = self.new_album(title=current_location.basename, commit=False)
- print('Created %s' % current_album.title)
+ try:
+ current_album = self.get_album_by_path(current_location.absolute_path)
+ except NoSuchAlbum:
+ current_album = self.new_album(
+ associated_directory=current_location.absolute_path,
+ commit=False,
+ title=current_location.basename,
+ )
+ print('Created %s' % current_album.title)
albums[current_location.absolute_path] = current_album
parent = albums[current_location.parent.absolute_path]
- parent.add(current_album, commit=False)
+ try:
+ parent.add(current_album, commit=False)
+ except GroupExists:
+ pass
#print('Added to %s' % parent.title)
for filepath in files:
try:
@@ -1561,6 +1625,7 @@ class Album(ObjectBase, GroupableMixin):
self.photodb = photodb
self.id = row_tuple[SQL_ALBUM['id']]
self.title = row_tuple[SQL_ALBUM['title']]
+ self.name = 'Album %s' % self.id
self.description = row_tuple[SQL_ALBUM['description']]
self.group_getter = self.photodb.get_album
@@ -1736,14 +1801,7 @@ class Photo(ObjectBase):
special:
For videos, you can provide a `timestamp` to take the thumbnail from.
'''
- chunked_id = chunk_sequence(self.id, 3)
- basename = chunked_id[-1]
- folder = chunked_id[:-1]
- folder = os.sep.join(folder)
- folder = os.path.join(self.photodb.thumbnail_folder, folder)
- if folder:
- os.makedirs(folder, exist_ok=True)
- hopeful_filepath = os.path.join(folder, basename) + '.jpg'
+ hopeful_filepath = self.make_thumbnail_filepath()
return_filepath = None
mime = self.mimetype()
@@ -1828,6 +1886,17 @@ class Photo(ObjectBase):
return False
+ def make_thumbnail_filepath(self):
+ chunked_id = chunk_sequence(self.id, 3)
+ basename = chunked_id[-1]
+ folder = chunked_id[:-1]
+ folder = os.sep.join(folder)
+ folder = os.path.join(self.photodb.thumbnail_folder, folder)
+ if folder:
+ os.makedirs(folder, exist_ok=True)
+ hopeful_filepath = os.path.join(folder, basename) + '.jpg'
+ return hopeful_filepath
+
def mimetype(self):
return get_mimetype(self.real_filepath)
diff --git a/etiquette/templates/photo.html b/etiquette/templates/photo.html
index 7b06909..d3ee6a4 100644
--- a/etiquette/templates/photo.html
+++ b/etiquette/templates/photo.html
@@ -6,6 +6,9 @@
+ {% set filename = photo["id"] + "." + photo["extension"] %}
+ {% set link = "/file/" + filename %}
+ {% set mimetype=photo["mimetype"] %}