diff --git a/wrolpi/files/progress.py b/wrolpi/files/progress.py index 584b292b..37e7e704 100644 --- a/wrolpi/files/progress.py +++ b/wrolpi/files/progress.py @@ -278,28 +278,33 @@ class RecentlyViewedConfig(ConfigFile): diff.removed = {i for i in diff.removed if oldest and db_rows[i]['viewed'] >= oldest and i not in malformed} - # Without deletes (no database version), progress written after the config is kept; a newer plain view keeps - # its `viewed`, but it is not a newer place to resume. - if not self.import_deletes: - for key, row in list(diff.changed.items()): - if db_rows[key]['viewed'] >= row['viewed']: - if db_rows[key]['progress'] is not None: - del diff.changed[key] - else: - diff.changed[key] = dict(row, viewed=db_rows[key]['viewed']) - - # The files of new entries which are not yet in the database; a deleted file's history goes with it. + # New entries which are in the database (outside the newest views), and which are not. from wrolpi.files.lib import glob_shared_stem from wrolpi.files.models import FileGroup media_directory = get_media_directory() - known = set() + known = dict() with get_db_session() as session: for paths in chunks([str(media_directory / i) for i in diff.added], 500): - known.update(str(i) for i, in - session.query(FileGroup.primary_path).filter(FileGroup.primary_path.in_(paths))) + known.update((str(path.relative_to(media_directory)), _viewed_row(*values)) for path, *values in + session.query(FileGroup.primary_path, FileGroup.viewed, FileGroup.progress, + FileGroup.position).filter(FileGroup.primary_path.in_(paths))) + + # Without deletes (no database version), progress written after the config is kept; a newer plain view keeps + # its `viewed`, but it is not a newer place to resume. This applies to a file that fell out of the newest + # views too. + if not self.import_deletes: + for rows, current in ((diff.changed, db_rows), (diff.added, known)): + for key, row in list(rows.items()): + if key in current and current[key]['viewed'] and current[key]['viewed'] >= row['viewed']: + if current[key]['progress'] is not None: + del rows[key] + else: + rows[key] = dict(row, viewed=current[key]['viewed']) + + # The files of new entries which are not yet in the database; a deleted file's history goes with it. for key, row in list(diff.added.items()): absolute_path = media_directory / key - if str(absolute_path) in known: + if key in known: continue if absolute_path.is_file(): row['files'] = glob_shared_stem(absolute_path) diff --git a/wrolpi/files/test/test_recently_viewed_import.py b/wrolpi/files/test/test_recently_viewed_import.py index ef423f8e..977c3b84 100644 --- a/wrolpi/files/test/test_recently_viewed_import.py +++ b/wrolpi/files/test/test_recently_viewed_import.py @@ -156,3 +156,32 @@ async def test_refresh_changing_primary_file_counts(test_session, test_directory test_session.expire_all() assert test_session.query(FileGroup).one().primary_path != test_directory / 'video.mp4' assert get_config_version(test_session, 'recently_viewed.yaml') > 0 + + +@pytest.mark.asyncio +async def test_without_deletes_newer_history_outside_the_window_is_kept(test_session, test_directory, async_client, + make_files_structure, monkeypatch): + """Without a database version, a newer view in the database wins even when it fell out of the newest views: its + progress is kept, or (a plain view) its `viewed` is kept and the config's position is taken.""" + monkeypatch.setattr('wrolpi.files.progress.RECENTLY_VIEWED_LIMIT', 2) + now = dates.now() + file_groups = {name: FileGroup.from_paths(test_session, *make_files_structure([name])) + for name in ('progress.txt', 'plain.txt', 'b.txt', 'c.txt')} + file_groups['progress.txt'].viewed, file_groups['progress.txt'].progress = now - timedelta(hours=3), 0.8 + file_groups['plain.txt'].viewed = now - timedelta(hours=3) + file_groups['b.txt'].viewed, file_groups['c.txt'].viewed = now - timedelta(hours=2), now - timedelta(hours=1) + test_session.commit() + config = get_recently_viewed_config() + config.get_file().parent.mkdir(exist_ok=True) + config.get_file().write_text(yaml.dump(dict(version=1, files=[ + dict(path='progress.txt', viewed=(now - timedelta(days=1)).isoformat(), progress=0.1), + dict(path='plain.txt', viewed=(now - timedelta(days=1)).isoformat(), progress=0.5, + position=dict(kind='time', seconds=60)), + ]))) + + config.import_config() + + found = viewed(test_session) + assert (found['progress.txt'].viewed, found['progress.txt'].progress) == (now - timedelta(hours=3), 0.8) + assert (found['plain.txt'].viewed, found['plain.txt'].progress, found['plain.txt'].position) == \ + (now - timedelta(hours=3), 0.5, dict(kind='time', seconds=60.0))