diff --git a/etiquette/constants.py b/etiquette/constants.py index 5e91ba9..2eb5d14 100644 --- a/etiquette/constants.py +++ b/etiquette/constants.py @@ -42,7 +42,7 @@ ffmpeg = _load_ffmpeg() # Database ######################################################################################### -DATABASE_VERSION = 14 +DATABASE_VERSION = 15 DB_VERSION_PRAGMA = f''' PRAGMA user_version = {DATABASE_VERSION}; ''' @@ -91,6 +91,7 @@ CREATE INDEX IF NOT EXISTS index_bookmarks_author_id on bookmarks(author_id); CREATE TABLE IF NOT EXISTS photos( id TEXT PRIMARY KEY NOT NULL, filepath TEXT COLLATE NOCASE, + dev_ino TEXT, override_filename TEXT COLLATE NOCASE, extension TEXT, width INT, @@ -108,6 +109,7 @@ CREATE TABLE IF NOT EXISTS photos( ); CREATE INDEX IF NOT EXISTS index_photos_id on photos(id); CREATE INDEX IF NOT EXISTS index_photos_filepath on photos(filepath COLLATE NOCASE); +CREATE INDEX IF NOT EXISTS index_photos_dev_ino on photos(dev_ino); CREATE INDEX IF NOT EXISTS index_photos_override_filename on photos(override_filename COLLATE NOCASE); CREATE INDEX IF NOT EXISTS index_photos_created on photos(created); diff --git a/etiquette/objects.py b/etiquette/objects.py index 4c57ad3..57b997c 100644 --- a/etiquette/objects.py +++ b/etiquette/objects.py @@ -951,16 +951,19 @@ class Photo(ObjectBase): self.photodb.log.debug('Reloading metadata for %s', self) self.bytes = None + self.dev_ino = None self.width = None self.height = None self.area = None self.ratio = None self.duration = None - try: - self.bytes = self.real_path.size - except pathclass.NotExists: - pass + if self.real_path.is_file: + stat = self.real_path.stat + self.bytes = stat.st_size + (dev, ino) = (stat.st_dev, stat.st_ino) + if dev and ino: + self.dev_ino = f'{dev},{ino}' if self.bytes is None: pass @@ -986,6 +989,7 @@ class Photo(ObjectBase): 'ratio': self.ratio, 'duration': self.duration, 'bytes': self.bytes, + 'dev_ino': self.dev_ino, } self.photodb.sql_update(table='photos', pairs=data, where_key='id') diff --git a/etiquette/photodb.py b/etiquette/photodb.py index 7e0bf58..789cd5a 100644 --- a/etiquette/photodb.py +++ b/etiquette/photodb.py @@ -389,6 +389,16 @@ class PDBPhotoMixin: def get_photo(self, id): return self.get_thing_by_id('photo', id) + def get_photo_by_inode(self, dev, ino): + dev_ino = f'{dev},{ino}' + query = 'SELECT * FROM photos WHERE dev_ino == ?' + bindings = [dev_ino] + photo_row = self.sql_select_one(query, bindings) + if photo_row is None: + raise exceptions.NoSuchPhoto(dev_ino) + photo = self.get_cached_instance('photo', photo_row) + return photo + def get_photo_by_path(self, filepath): filepath = pathclass.Path(filepath) query = 'SELECT * FROM photos WHERE filepath == ?' @@ -470,6 +480,7 @@ class PDBPhotoMixin: 'author_id': author_id, 'searchhidden': searchhidden, # These will be filled in during the metadata stage. + 'dev_ino': None, 'bytes': None, 'width': None, 'height': None, diff --git a/utilities/database_upgrader/database_upgrader.py b/utilities/database_upgrader/database_upgrader.py index ec06876..a5ceace 100644 --- a/utilities/database_upgrader/database_upgrader.py +++ b/utilities/database_upgrader/database_upgrader.py @@ -261,6 +261,66 @@ def upgrade_13_to_14(photodb): photodb.config['user']['max_username_length'] = photodb.config['user'].pop('max_length') photodb.save_config() +def upgrade_14_to_15(photodb): + ''' + Added the dev_ino column to photos. + ''' + photodb.sql_execute('PRAGMA foreign_keys = OFF') + photodb.sql_execute('BEGIN') + photodb.sql_execute('ALTER TABLE photos RENAME TO photos_old') + photodb.sql_execute(''' + CREATE TABLE photos( + id TEXT PRIMARY KEY NOT NULL, + filepath TEXT COLLATE NOCASE, + dev_ino TEXT, + override_filename TEXT COLLATE NOCASE, + extension TEXT, + width INT, + height INT, + ratio REAL, + area INT, + duration INT, + bytes INT, + created INT, + thumbnail TEXT, + tagged_at INT, + author_id TEXT, + searchhidden INT, + FOREIGN KEY(author_id) REFERENCES users(id) + ); + ''') + photodb.sql_execute(''' + INSERT INTO photos SELECT + id, + filepath, + NULL, + override_filename, + extension, + width, + height, + ratio, + area, + duration, + bytes, + created, + thumbnail, + tagged_at, + author_id, + searchhidden + FROM photos_old + ''') + photodb.sql_execute('DROP TABLE photos_old') + photodb.sql_execute('CREATE INDEX index_photos_dev_ino ON photos(dev_ino);') + for photo in photodb.get_photos_by_recent(): + if not photo.real_path.is_file: + continue + stat = photo.real_path.stat + (dev, ino) = (stat.st_dev, stat.st_ino) + if dev == 0 or ino == 0: + continue + dev_ino = f'{dev},{ino}' + photodb.sql_execute('UPDATE photos SET dev_ino = ? WHERE id == ?', [dev_ino, photo.id]) + def upgrade_all(data_directory): ''' Given the directory containing a phototagger database, apply all of the