mirror of
https://github.com/immich-app/immich
synced 2026-08-15 13:03:57 +00:00
feat: all local assets in album asset selection
This commit is contained in:
parent
36cb86c886
commit
241231c73e
6 changed files with 108 additions and 36 deletions
|
|
@ -49,7 +49,8 @@ class TimelineFactory {
|
|||
return group == GroupAssetsBy.auto ? GroupAssetsBy.day : group;
|
||||
}
|
||||
|
||||
TimelineService main(List<String> timelineUsers) => TimelineService(_timelineRepository.main(timelineUsers, groupBy));
|
||||
TimelineService main(List<String> timelineUsers, {bool ignoreBackupSelection = false}) =>
|
||||
TimelineService(_timelineRepository.main(timelineUsers, groupBy, ignoreBackupSelection: ignoreBackupSelection));
|
||||
|
||||
TimelineService localAlbum({required String albumId}) =>
|
||||
TimelineService(_timelineRepository.localAlbum(albumId, groupBy));
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import 'local_asset.entity.dart';
|
|||
import 'local_album.entity.dart';
|
||||
import 'local_album_asset.entity.dart';
|
||||
|
||||
mergedAsset:
|
||||
mergedAsset(:ignore_backup_selection AS BOOLEAN):
|
||||
SELECT
|
||||
rae.id as remote_id,
|
||||
(SELECT lae.id FROM local_asset_entity lae WHERE lae.checksum = rae.checksum LIMIT 1) as local_id,
|
||||
|
|
@ -73,20 +73,25 @@ FROM
|
|||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM remote_asset_entity rae WHERE rae.checksum = lae.checksum AND rae.owner_id IN :user_ids
|
||||
)
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM local_album_asset_entity laa
|
||||
INNER JOIN local_album_entity la on laa.album_id = la.id
|
||||
WHERE laa.asset_id = lae.id AND la.backup_selection = 0 -- selected
|
||||
)
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM local_album_asset_entity laa
|
||||
INNER JOIN local_album_entity la on laa.album_id = la.id
|
||||
WHERE laa.asset_id = lae.id AND la.backup_selection = 2 -- excluded
|
||||
AND (
|
||||
:ignore_backup_selection
|
||||
OR (
|
||||
EXISTS (
|
||||
SELECT 1 FROM local_album_asset_entity laa
|
||||
INNER JOIN local_album_entity la on laa.album_id = la.id
|
||||
WHERE laa.asset_id = lae.id AND la.backup_selection = 0 -- selected
|
||||
)
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM local_album_asset_entity laa
|
||||
INNER JOIN local_album_entity la on laa.album_id = la.id
|
||||
WHERE laa.asset_id = lae.id AND la.backup_selection = 2 -- excluded
|
||||
)
|
||||
)
|
||||
)
|
||||
ORDER BY created_at DESC
|
||||
LIMIT $limit;
|
||||
|
||||
mergedBucket(:group_by AS INTEGER):
|
||||
mergedBucket(:group_by AS INTEGER, :ignore_backup_selection AS BOOLEAN):
|
||||
SELECT
|
||||
COUNT(*) as asset_count,
|
||||
bucket_date
|
||||
|
|
@ -106,7 +111,7 @@ FROM
|
|||
FROM
|
||||
remote_asset_entity rae
|
||||
LEFT JOIN
|
||||
stack_entity se ON rae.stack_id = se.id
|
||||
stack_entity se ON rae.stack_id = se.id
|
||||
WHERE
|
||||
rae.deleted_at IS NULL
|
||||
AND rae.visibility = 0 -- timeline visibility
|
||||
|
|
@ -126,15 +131,20 @@ FROM
|
|||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM remote_asset_entity rae WHERE rae.checksum = lae.checksum AND rae.owner_id IN :user_ids
|
||||
)
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM local_album_asset_entity laa
|
||||
INNER JOIN local_album_entity la on laa.album_id = la.id
|
||||
WHERE laa.asset_id = lae.id AND la.backup_selection = 0 -- selected
|
||||
)
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM local_album_asset_entity laa
|
||||
INNER JOIN local_album_entity la on laa.album_id = la.id
|
||||
WHERE laa.asset_id = lae.id AND la.backup_selection = 2 -- excluded
|
||||
AND (
|
||||
:ignore_backup_selection
|
||||
OR (
|
||||
EXISTS (
|
||||
SELECT 1 FROM local_album_asset_entity laa
|
||||
INNER JOIN local_album_entity la on laa.album_id = la.id
|
||||
WHERE laa.asset_id = lae.id AND la.backup_selection = 0 -- selected
|
||||
)
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM local_album_asset_entity laa
|
||||
INNER JOIN local_album_entity la on laa.album_id = la.id
|
||||
WHERE laa.asset_id = lae.id AND la.backup_selection = 2 -- excluded
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
GROUP BY bucket_date
|
||||
|
|
|
|||
|
|
@ -50,26 +50,39 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
|
|||
.map((users) => users..add(userId));
|
||||
}
|
||||
|
||||
TimelineQuery main(List<String> userIds, GroupAssetsBy groupBy) => (
|
||||
bucketSource: () => _watchMainBucket(userIds, groupBy: groupBy),
|
||||
assetSource: (offset, count) => _getMainBucketAssets(userIds, offset: offset, count: count),
|
||||
origin: TimelineOrigin.main,
|
||||
TimelineQuery main(List<String> userIds, GroupAssetsBy groupBy, {bool ignoreBackupSelection = false}) => (
|
||||
bucketSource: () => _watchMainBucket(userIds, groupBy: groupBy, ignoreBackupSelection: ignoreBackupSelection),
|
||||
assetSource: (offset, count) =>
|
||||
_getMainBucketAssets(userIds, offset: offset, count: count, ignoreBackupSelection: ignoreBackupSelection),
|
||||
origin: .main,
|
||||
);
|
||||
|
||||
Stream<List<Bucket>> _watchMainBucket(List<String> userIds, {GroupAssetsBy groupBy = GroupAssetsBy.day}) {
|
||||
if (groupBy == GroupAssetsBy.none) {
|
||||
Stream<List<Bucket>> _watchMainBucket(
|
||||
List<String> userIds, {
|
||||
GroupAssetsBy groupBy = .day,
|
||||
bool ignoreBackupSelection = false,
|
||||
}) {
|
||||
if (groupBy == .none) {
|
||||
throw UnsupportedError("GroupAssetsBy.none is not supported for watchMainBucket");
|
||||
}
|
||||
|
||||
return _db.mergedAssetDrift.mergedBucket(userIds: userIds, groupBy: groupBy.index).map((row) {
|
||||
final date = row.bucketDate.truncateDate(groupBy);
|
||||
return TimeBucket(date: date, assetCount: row.assetCount);
|
||||
}).watch();
|
||||
return _db.mergedAssetDrift
|
||||
.mergedBucket(userIds: userIds, groupBy: groupBy.index, ignoreBackupSelection: ignoreBackupSelection)
|
||||
.map((row) {
|
||||
final date = row.bucketDate.truncateDate(groupBy);
|
||||
return TimeBucket(date: date, assetCount: row.assetCount);
|
||||
})
|
||||
.watch();
|
||||
}
|
||||
|
||||
Future<List<BaseAsset>> _getMainBucketAssets(List<String> userIds, {required int offset, required int count}) {
|
||||
Future<List<BaseAsset>> _getMainBucketAssets(
|
||||
List<String> userIds, {
|
||||
required int offset,
|
||||
required int count,
|
||||
bool ignoreBackupSelection = false,
|
||||
}) {
|
||||
return _db.mergedAssetDrift
|
||||
.mergedAsset(userIds: userIds, limit: (_) => Limit(count, offset))
|
||||
.mergedAsset(userIds: userIds, limit: (_) => Limit(count, offset), ignoreBackupSelection: ignoreBackupSelection)
|
||||
.map(
|
||||
(row) => row.remoteId != null && row.ownerId != null
|
||||
? RemoteAsset(
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ class DriftAssetSelectionTimelinePage extends ConsumerWidget {
|
|||
),
|
||||
timelineServiceProvider.overrideWith((ref) {
|
||||
final timelineUsers = ref.watch(timelineUsersProvider).valueOrNull ?? [];
|
||||
final timelineService = ref.watch(timelineFactoryProvider).main(timelineUsers);
|
||||
final timelineService = ref.watch(timelineFactoryProvider).main(timelineUsers, ignoreBackupSelection: true);
|
||||
ref.onDispose(timelineService.dispose);
|
||||
return timelineService;
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -43,7 +43,9 @@ void main() {
|
|||
),
|
||||
);
|
||||
|
||||
final buckets = await db.mergedAssetDrift.mergedBucket(groupBy: GroupAssetsBy.day.index, userIds: [userId]).get();
|
||||
final buckets = await db.mergedAssetDrift
|
||||
.mergedBucket(groupBy: GroupAssetsBy.day.index, userIds: [userId], ignoreBackupSelection: false)
|
||||
.get();
|
||||
|
||||
expect(buckets, hasLength(1));
|
||||
expect(buckets.single.assetCount, 1);
|
||||
|
|
|
|||
|
|
@ -102,4 +102,50 @@ void main() {
|
|||
expect(remote.localId, local.id);
|
||||
});
|
||||
});
|
||||
|
||||
group('main backup selection', () {
|
||||
test('excludes local assets whose album is not selected for backup', () async {
|
||||
final user = await ctx.newUser();
|
||||
final album = await ctx.newLocalAlbum(backupSelection: .none);
|
||||
final asset = await ctx.newLocalAsset();
|
||||
await ctx.newLocalAlbumAsset(albumId: album.id, assetId: asset.id);
|
||||
|
||||
final query = sut.main([user.id], .day);
|
||||
|
||||
expect(await query.bucketSource().first, isEmpty);
|
||||
expect(await query.assetSource(0, 10), isEmpty);
|
||||
});
|
||||
|
||||
test('excludes local assets in an excluded album even when also in a selected album', () async {
|
||||
final user = await ctx.newUser();
|
||||
final selected = await ctx.newLocalAlbum(backupSelection: .selected);
|
||||
final excluded = await ctx.newLocalAlbum(backupSelection: .excluded);
|
||||
final asset = await ctx.newLocalAsset();
|
||||
await ctx.newLocalAlbumAsset(albumId: selected.id, assetId: asset.id);
|
||||
await ctx.newLocalAlbumAsset(albumId: excluded.id, assetId: asset.id);
|
||||
|
||||
final query = sut.main([user.id], .day);
|
||||
|
||||
expect(await query.bucketSource().first, isEmpty);
|
||||
expect(await query.assetSource(0, 10), isEmpty);
|
||||
});
|
||||
|
||||
test('ignoreBackupSelection includes local assets regardless of album selection', () async {
|
||||
final user = await ctx.newUser();
|
||||
final none = await ctx.newLocalAlbum(backupSelection: .none);
|
||||
final excluded = await ctx.newLocalAlbum(backupSelection: .excluded);
|
||||
final unselectedAsset = await ctx.newLocalAsset();
|
||||
final excludedAsset = await ctx.newLocalAsset();
|
||||
await ctx.newLocalAlbumAsset(albumId: none.id, assetId: unselectedAsset.id);
|
||||
await ctx.newLocalAlbumAsset(albumId: excluded.id, assetId: excludedAsset.id);
|
||||
|
||||
final query = sut.main([user.id], .day, ignoreBackupSelection: true);
|
||||
|
||||
final buckets = await query.bucketSource().first;
|
||||
expect(buckets.fold<int>(0, (sum, bucket) => sum + bucket.assetCount), 2);
|
||||
|
||||
final assets = await query.assetSource(0, 10);
|
||||
expect(assets.map((asset) => (asset as LocalAsset).id), containsAll([unselectedAsset.id, excludedAsset.id]));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue