feat(server): read-write external assets (#9235)

* refactor: remove isReadOnly and isExternal usages

* chore: open api

* fix: linting

* remove mobile isReadOnly dependency

---------

Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
This commit is contained in:
Jason Rasmussen 2024-05-03 15:34:57 -04:00 committed by GitHub
parent d26ac431b8
commit 5b87abb021
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
57 changed files with 181 additions and 603 deletions

View File

@ -102,7 +102,6 @@ describe('/asset', () => {
utils.createAsset(user1.accessToken), utils.createAsset(user1.accessToken),
utils.createAsset(user1.accessToken, { utils.createAsset(user1.accessToken, {
isFavorite: true, isFavorite: true,
isReadOnly: true,
fileCreatedAt: yesterday.toISO(), fileCreatedAt: yesterday.toISO(),
fileModifiedAt: yesterday.toISO(), fileModifiedAt: yesterday.toISO(),
assetData: { filename: 'example.mp4' }, assetData: { filename: 'example.mp4' },

View File

@ -24,12 +24,12 @@ describe('/search', () => {
// let assetRidge: AssetFileUploadResponseDto; // let assetRidge: AssetFileUploadResponseDto;
// let assetPolemonium: AssetFileUploadResponseDto; // let assetPolemonium: AssetFileUploadResponseDto;
// let assetWood: AssetFileUploadResponseDto; // let assetWood: AssetFileUploadResponseDto;
// let assetGlarus: AssetFileUploadResponseDto;
let assetHeic: AssetFileUploadResponseDto; let assetHeic: AssetFileUploadResponseDto;
let assetRocks: AssetFileUploadResponseDto; let assetRocks: AssetFileUploadResponseDto;
let assetOneJpg6: AssetFileUploadResponseDto; let assetOneJpg6: AssetFileUploadResponseDto;
let assetOneHeic6: AssetFileUploadResponseDto; let assetOneHeic6: AssetFileUploadResponseDto;
let assetOneJpg5: AssetFileUploadResponseDto; let assetOneJpg5: AssetFileUploadResponseDto;
let assetGlarus: AssetFileUploadResponseDto;
let assetSprings: AssetFileUploadResponseDto; let assetSprings: AssetFileUploadResponseDto;
let assetLast: AssetFileUploadResponseDto; let assetLast: AssetFileUploadResponseDto;
let cities: string[]; let cities: string[];
@ -52,11 +52,12 @@ describe('/search', () => {
{ filename: '/formats/motionphoto/Samsung One UI 6.jpg' }, { filename: '/formats/motionphoto/Samsung One UI 6.jpg' },
{ filename: '/formats/motionphoto/Samsung One UI 6.heic' }, { filename: '/formats/motionphoto/Samsung One UI 6.heic' },
{ filename: '/formats/motionphoto/Samsung One UI 5.jpg' }, { filename: '/formats/motionphoto/Samsung One UI 5.jpg' },
{ filename: '/formats/raw/Nikon/D80/glarus.nef', dto: { isReadOnly: true } },
{ filename: '/metadata/gps-position/thompson-springs.jpg', dto: { isArchived: true } }, { filename: '/metadata/gps-position/thompson-springs.jpg', dto: { isArchived: true } },
// used for search suggestions // used for search suggestions
{ filename: '/formats/png/density_plot.png' }, { filename: '/formats/png/density_plot.png' },
{ filename: '/formats/raw/Nikon/D80/glarus.nef' },
{ filename: '/formats/raw/Nikon/D700/philadelphia.nef' }, { filename: '/formats/raw/Nikon/D700/philadelphia.nef' },
{ filename: '/albums/nature/orychophragmus_violaceus.jpg' }, { filename: '/albums/nature/orychophragmus_violaceus.jpg' },
{ filename: '/albums/nature/tanners_ridge.jpg' }, { filename: '/albums/nature/tanners_ridge.jpg' },
@ -93,9 +94,9 @@ describe('/search', () => {
{ latitude: 23.133_02, longitude: -82.383_04 }, // havana { latitude: 23.133_02, longitude: -82.383_04 }, // havana
{ latitude: 41.694_11, longitude: 44.833_68 }, // tbilisi { latitude: 41.694_11, longitude: 44.833_68 }, // tbilisi
{ latitude: 31.222_22, longitude: 121.458_06 }, // shanghai { latitude: 31.222_22, longitude: 121.458_06 }, // shanghai
{ latitude: 47.040_57, longitude: 9.068_04 }, // glarus
{ latitude: 38.9711, longitude: -109.7137 }, // thompson springs { latitude: 38.9711, longitude: -109.7137 }, // thompson springs
{ latitude: 40.714_27, longitude: -74.005_97 }, // new york { latitude: 40.714_27, longitude: -74.005_97 }, // new york
{ latitude: 47.040_57, longitude: 9.068_04 }, // glarus
{ latitude: 32.771_52, longitude: -89.116_73 }, // philadelphia { latitude: 32.771_52, longitude: -89.116_73 }, // philadelphia
{ latitude: 31.634_16, longitude: -7.999_94 }, // marrakesh { latitude: 31.634_16, longitude: -7.999_94 }, // marrakesh
{ latitude: 38.523_735_4, longitude: -78.488_619_4 }, // tanners ridge { latitude: 38.523_735_4, longitude: -78.488_619_4 }, // tanners ridge
@ -123,9 +124,9 @@ describe('/search', () => {
assetOneJpg6, assetOneJpg6,
assetOneHeic6, assetOneHeic6,
assetOneJpg5, assetOneJpg5,
assetGlarus,
assetSprings, assetSprings,
assetDensity, assetDensity,
// assetGlarus,
// assetPhiladelphia, // assetPhiladelphia,
// assetOrychophragmus, // assetOrychophragmus,
// assetRidge, // assetRidge,
@ -190,16 +191,7 @@ describe('/search', () => {
dto: { size: -1.5 }, dto: { size: -1.5 },
expected: ['size must not be less than 1', 'size must be an integer number'], expected: ['size must not be less than 1', 'size must be an integer number'],
}, },
...[ ...['isArchived', 'isFavorite', 'isEncoded', 'isMotion', 'isOffline', 'isVisible'].map((value) => ({
'isArchived',
'isFavorite',
'isReadOnly',
'isExternal',
'isEncoded',
'isMotion',
'isOffline',
'isVisible',
].map((value) => ({
should: `should reject ${value} not a boolean`, should: `should reject ${value} not a boolean`,
dto: { [value]: 'immich' }, dto: { [value]: 'immich' },
expected: [`${value} must be a boolean value`], expected: [`${value} must be a boolean value`],
@ -255,14 +247,6 @@ describe('/search', () => {
should: 'should search by isArchived (false)', should: 'should search by isArchived (false)',
deferred: () => ({ dto: { size: 1, isArchived: false }, assets: [assetLast] }), deferred: () => ({ dto: { size: 1, isArchived: false }, assets: [assetLast] }),
}, },
{
should: 'should search by isReadOnly (true)',
deferred: () => ({ dto: { isReadOnly: true }, assets: [assetGlarus] }),
},
{
should: 'should search by isReadOnly (false)',
deferred: () => ({ dto: { size: 1, isReadOnly: false }, assets: [assetLast] }),
},
{ {
should: 'should search by type (image)', should: 'should search by type (image)',
deferred: () => ({ dto: { size: 1, type: 'IMAGE' }, assets: [assetLast] }), deferred: () => ({ dto: { size: 1, type: 'IMAGE' }, assets: [assetLast] }),

View File

@ -34,7 +34,6 @@ describe('/timeline', () => {
utils.createAsset(user.accessToken), utils.createAsset(user.accessToken),
utils.createAsset(user.accessToken, { utils.createAsset(user.accessToken, {
isFavorite: true, isFavorite: true,
isReadOnly: true,
fileCreatedAt: yesterday.toISO(), fileCreatedAt: yesterday.toISO(),
fileModifiedAt: yesterday.toISO(), fileModifiedAt: yesterday.toISO(),
assetData: { filename: 'example.mp4' }, assetData: { filename: 'example.mp4' },

View File

@ -32,7 +32,6 @@ class Asset {
isFavorite = remote.isFavorite, isFavorite = remote.isFavorite,
isArchived = remote.isArchived, isArchived = remote.isArchived,
isTrashed = remote.isTrashed, isTrashed = remote.isTrashed,
isReadOnly = remote.isReadOnly,
isOffline = remote.isOffline, isOffline = remote.isOffline,
// workaround to nullify stackParentId for the parent asset until we refactor the mobile app // workaround to nullify stackParentId for the parent asset until we refactor the mobile app
// stack handling to properly handle it // stack handling to properly handle it
@ -55,7 +54,6 @@ class Asset {
isFavorite = local.isFavorite, isFavorite = local.isFavorite,
isArchived = false, isArchived = false,
isTrashed = false, isTrashed = false,
isReadOnly = false,
isOffline = false, isOffline = false,
stackCount = 0, stackCount = 0,
fileCreatedAt = local.createDateTime { fileCreatedAt = local.createDateTime {
@ -90,7 +88,6 @@ class Asset {
this.isTrashed = false, this.isTrashed = false,
this.stackParentId, this.stackParentId,
this.stackCount = 0, this.stackCount = 0,
this.isReadOnly = false,
this.isOffline = false, this.isOffline = false,
this.thumbhash, this.thumbhash,
}); });
@ -161,8 +158,6 @@ class Asset {
bool isTrashed; bool isTrashed;
bool isReadOnly;
bool isOffline; bool isOffline;
@ignore @ignore
@ -278,7 +273,6 @@ class Asset {
isFavorite != a.isFavorite || isFavorite != a.isFavorite ||
isArchived != a.isArchived || isArchived != a.isArchived ||
isTrashed != a.isTrashed || isTrashed != a.isTrashed ||
isReadOnly != a.isReadOnly ||
isOffline != a.isOffline || isOffline != a.isOffline ||
a.exifInfo?.latitude != exifInfo?.latitude || a.exifInfo?.latitude != exifInfo?.latitude ||
a.exifInfo?.longitude != exifInfo?.longitude || a.exifInfo?.longitude != exifInfo?.longitude ||
@ -324,7 +318,6 @@ class Asset {
isFavorite: isFavorite, isFavorite: isFavorite,
isArchived: isArchived, isArchived: isArchived,
isTrashed: isTrashed, isTrashed: isTrashed,
isReadOnly: isReadOnly,
isOffline: isOffline, isOffline: isOffline,
); );
} }
@ -345,7 +338,6 @@ class Asset {
isFavorite: a.isFavorite, isFavorite: a.isFavorite,
isArchived: a.isArchived, isArchived: a.isArchived,
isTrashed: a.isTrashed, isTrashed: a.isTrashed,
isReadOnly: a.isReadOnly,
isOffline: a.isOffline, isOffline: a.isOffline,
exifInfo: a.exifInfo?.copyWith(id: id) ?? exifInfo, exifInfo: a.exifInfo?.copyWith(id: id) ?? exifInfo,
thumbhash: a.thumbhash, thumbhash: a.thumbhash,
@ -380,7 +372,6 @@ class Asset {
bool? isFavorite, bool? isFavorite,
bool? isArchived, bool? isArchived,
bool? isTrashed, bool? isTrashed,
bool? isReadOnly,
bool? isOffline, bool? isOffline,
ExifInfo? exifInfo, ExifInfo? exifInfo,
String? stackParentId, String? stackParentId,
@ -405,7 +396,6 @@ class Asset {
isFavorite: isFavorite ?? this.isFavorite, isFavorite: isFavorite ?? this.isFavorite,
isArchived: isArchived ?? this.isArchived, isArchived: isArchived ?? this.isArchived,
isTrashed: isTrashed ?? this.isTrashed, isTrashed: isTrashed ?? this.isTrashed,
isReadOnly: isReadOnly ?? this.isReadOnly,
isOffline: isOffline ?? this.isOffline, isOffline: isOffline ?? this.isOffline,
exifInfo: exifInfo ?? this.exifInfo, exifInfo: exifInfo ?? this.exifInfo,
stackParentId: stackParentId ?? this.stackParentId, stackParentId: stackParentId ?? this.stackParentId,
@ -470,7 +460,6 @@ class Asset {
"height": ${height ?? "N/A"}, "height": ${height ?? "N/A"},
"isArchived": $isArchived, "isArchived": $isArchived,
"isTrashed": $isTrashed, "isTrashed": $isTrashed,
"isReadOnly": $isReadOnly,
"isOffline": $isOffline, "isOffline": $isOffline,
}"""; }""";
} }

View File

@ -62,64 +62,59 @@ const AssetSchema = CollectionSchema(
name: r'isOffline', name: r'isOffline',
type: IsarType.bool, type: IsarType.bool,
), ),
r'isReadOnly': PropertySchema(
id: 9,
name: r'isReadOnly',
type: IsarType.bool,
),
r'isTrashed': PropertySchema( r'isTrashed': PropertySchema(
id: 10, id: 9,
name: r'isTrashed', name: r'isTrashed',
type: IsarType.bool, type: IsarType.bool,
), ),
r'livePhotoVideoId': PropertySchema( r'livePhotoVideoId': PropertySchema(
id: 11, id: 10,
name: r'livePhotoVideoId', name: r'livePhotoVideoId',
type: IsarType.string, type: IsarType.string,
), ),
r'localId': PropertySchema( r'localId': PropertySchema(
id: 12, id: 11,
name: r'localId', name: r'localId',
type: IsarType.string, type: IsarType.string,
), ),
r'ownerId': PropertySchema( r'ownerId': PropertySchema(
id: 13, id: 12,
name: r'ownerId', name: r'ownerId',
type: IsarType.long, type: IsarType.long,
), ),
r'remoteId': PropertySchema( r'remoteId': PropertySchema(
id: 14, id: 13,
name: r'remoteId', name: r'remoteId',
type: IsarType.string, type: IsarType.string,
), ),
r'stackCount': PropertySchema( r'stackCount': PropertySchema(
id: 15, id: 14,
name: r'stackCount', name: r'stackCount',
type: IsarType.long, type: IsarType.long,
), ),
r'stackParentId': PropertySchema( r'stackParentId': PropertySchema(
id: 16, id: 15,
name: r'stackParentId', name: r'stackParentId',
type: IsarType.string, type: IsarType.string,
), ),
r'thumbhash': PropertySchema( r'thumbhash': PropertySchema(
id: 17, id: 16,
name: r'thumbhash', name: r'thumbhash',
type: IsarType.string, type: IsarType.string,
), ),
r'type': PropertySchema( r'type': PropertySchema(
id: 18, id: 17,
name: r'type', name: r'type',
type: IsarType.byte, type: IsarType.byte,
enumMap: _AssettypeEnumValueMap, enumMap: _AssettypeEnumValueMap,
), ),
r'updatedAt': PropertySchema( r'updatedAt': PropertySchema(
id: 19, id: 18,
name: r'updatedAt', name: r'updatedAt',
type: IsarType.dateTime, type: IsarType.dateTime,
), ),
r'width': PropertySchema( r'width': PropertySchema(
id: 20, id: 19,
name: r'width', name: r'width',
type: IsarType.int, type: IsarType.int,
) )
@ -239,18 +234,17 @@ void _assetSerialize(
writer.writeBool(offsets[6], object.isArchived); writer.writeBool(offsets[6], object.isArchived);
writer.writeBool(offsets[7], object.isFavorite); writer.writeBool(offsets[7], object.isFavorite);
writer.writeBool(offsets[8], object.isOffline); writer.writeBool(offsets[8], object.isOffline);
writer.writeBool(offsets[9], object.isReadOnly); writer.writeBool(offsets[9], object.isTrashed);
writer.writeBool(offsets[10], object.isTrashed); writer.writeString(offsets[10], object.livePhotoVideoId);
writer.writeString(offsets[11], object.livePhotoVideoId); writer.writeString(offsets[11], object.localId);
writer.writeString(offsets[12], object.localId); writer.writeLong(offsets[12], object.ownerId);
writer.writeLong(offsets[13], object.ownerId); writer.writeString(offsets[13], object.remoteId);
writer.writeString(offsets[14], object.remoteId); writer.writeLong(offsets[14], object.stackCount);
writer.writeLong(offsets[15], object.stackCount); writer.writeString(offsets[15], object.stackParentId);
writer.writeString(offsets[16], object.stackParentId); writer.writeString(offsets[16], object.thumbhash);
writer.writeString(offsets[17], object.thumbhash); writer.writeByte(offsets[17], object.type.index);
writer.writeByte(offsets[18], object.type.index); writer.writeDateTime(offsets[18], object.updatedAt);
writer.writeDateTime(offsets[19], object.updatedAt); writer.writeInt(offsets[19], object.width);
writer.writeInt(offsets[20], object.width);
} }
Asset _assetDeserialize( Asset _assetDeserialize(
@ -270,19 +264,18 @@ Asset _assetDeserialize(
isArchived: reader.readBoolOrNull(offsets[6]) ?? false, isArchived: reader.readBoolOrNull(offsets[6]) ?? false,
isFavorite: reader.readBoolOrNull(offsets[7]) ?? false, isFavorite: reader.readBoolOrNull(offsets[7]) ?? false,
isOffline: reader.readBoolOrNull(offsets[8]) ?? false, isOffline: reader.readBoolOrNull(offsets[8]) ?? false,
isReadOnly: reader.readBoolOrNull(offsets[9]) ?? false, isTrashed: reader.readBoolOrNull(offsets[9]) ?? false,
isTrashed: reader.readBoolOrNull(offsets[10]) ?? false, livePhotoVideoId: reader.readStringOrNull(offsets[10]),
livePhotoVideoId: reader.readStringOrNull(offsets[11]), localId: reader.readStringOrNull(offsets[11]),
localId: reader.readStringOrNull(offsets[12]), ownerId: reader.readLong(offsets[12]),
ownerId: reader.readLong(offsets[13]), remoteId: reader.readStringOrNull(offsets[13]),
remoteId: reader.readStringOrNull(offsets[14]), stackCount: reader.readLongOrNull(offsets[14]),
stackCount: reader.readLongOrNull(offsets[15]), stackParentId: reader.readStringOrNull(offsets[15]),
stackParentId: reader.readStringOrNull(offsets[16]), thumbhash: reader.readStringOrNull(offsets[16]),
thumbhash: reader.readStringOrNull(offsets[17]), type: _AssettypeValueEnumMap[reader.readByteOrNull(offsets[17])] ??
type: _AssettypeValueEnumMap[reader.readByteOrNull(offsets[18])] ??
AssetType.other, AssetType.other,
updatedAt: reader.readDateTime(offsets[19]), updatedAt: reader.readDateTime(offsets[18]),
width: reader.readIntOrNull(offsets[20]), width: reader.readIntOrNull(offsets[19]),
); );
return object; return object;
} }
@ -315,27 +308,25 @@ P _assetDeserializeProp<P>(
case 9: case 9:
return (reader.readBoolOrNull(offset) ?? false) as P; return (reader.readBoolOrNull(offset) ?? false) as P;
case 10: case 10:
return (reader.readBoolOrNull(offset) ?? false) as P; return (reader.readStringOrNull(offset)) as P;
case 11: case 11:
return (reader.readStringOrNull(offset)) as P; return (reader.readStringOrNull(offset)) as P;
case 12: case 12:
return (reader.readStringOrNull(offset)) as P;
case 13:
return (reader.readLong(offset)) as P; return (reader.readLong(offset)) as P;
case 14: case 13:
return (reader.readStringOrNull(offset)) as P; return (reader.readStringOrNull(offset)) as P;
case 15: case 14:
return (reader.readLongOrNull(offset)) as P; return (reader.readLongOrNull(offset)) as P;
case 15:
return (reader.readStringOrNull(offset)) as P;
case 16: case 16:
return (reader.readStringOrNull(offset)) as P; return (reader.readStringOrNull(offset)) as P;
case 17: case 17:
return (reader.readStringOrNull(offset)) as P;
case 18:
return (_AssettypeValueEnumMap[reader.readByteOrNull(offset)] ?? return (_AssettypeValueEnumMap[reader.readByteOrNull(offset)] ??
AssetType.other) as P; AssetType.other) as P;
case 19: case 18:
return (reader.readDateTime(offset)) as P; return (reader.readDateTime(offset)) as P;
case 20: case 19:
return (reader.readIntOrNull(offset)) as P; return (reader.readIntOrNull(offset)) as P;
default: default:
throw IsarError('Unknown property with id $propertyId'); throw IsarError('Unknown property with id $propertyId');
@ -1366,16 +1357,6 @@ extension AssetQueryFilter on QueryBuilder<Asset, Asset, QFilterCondition> {
}); });
} }
QueryBuilder<Asset, Asset, QAfterFilterCondition> isReadOnlyEqualTo(
bool value) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'isReadOnly',
value: value,
));
});
}
QueryBuilder<Asset, Asset, QAfterFilterCondition> isTrashedEqualTo( QueryBuilder<Asset, Asset, QAfterFilterCondition> isTrashedEqualTo(
bool value) { bool value) {
return QueryBuilder.apply(this, (query) { return QueryBuilder.apply(this, (query) {
@ -2527,18 +2508,6 @@ extension AssetQuerySortBy on QueryBuilder<Asset, Asset, QSortBy> {
}); });
} }
QueryBuilder<Asset, Asset, QAfterSortBy> sortByIsReadOnly() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'isReadOnly', Sort.asc);
});
}
QueryBuilder<Asset, Asset, QAfterSortBy> sortByIsReadOnlyDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'isReadOnly', Sort.desc);
});
}
QueryBuilder<Asset, Asset, QAfterSortBy> sortByIsTrashed() { QueryBuilder<Asset, Asset, QAfterSortBy> sortByIsTrashed() {
return QueryBuilder.apply(this, (query) { return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'isTrashed', Sort.asc); return query.addSortBy(r'isTrashed', Sort.asc);
@ -2793,18 +2762,6 @@ extension AssetQuerySortThenBy on QueryBuilder<Asset, Asset, QSortThenBy> {
}); });
} }
QueryBuilder<Asset, Asset, QAfterSortBy> thenByIsReadOnly() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'isReadOnly', Sort.asc);
});
}
QueryBuilder<Asset, Asset, QAfterSortBy> thenByIsReadOnlyDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'isReadOnly', Sort.desc);
});
}
QueryBuilder<Asset, Asset, QAfterSortBy> thenByIsTrashed() { QueryBuilder<Asset, Asset, QAfterSortBy> thenByIsTrashed() {
return QueryBuilder.apply(this, (query) { return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'isTrashed', Sort.asc); return query.addSortBy(r'isTrashed', Sort.asc);
@ -2995,12 +2952,6 @@ extension AssetQueryWhereDistinct on QueryBuilder<Asset, Asset, QDistinct> {
}); });
} }
QueryBuilder<Asset, Asset, QDistinct> distinctByIsReadOnly() {
return QueryBuilder.apply(this, (query) {
return query.addDistinctBy(r'isReadOnly');
});
}
QueryBuilder<Asset, Asset, QDistinct> distinctByIsTrashed() { QueryBuilder<Asset, Asset, QDistinct> distinctByIsTrashed() {
return QueryBuilder.apply(this, (query) { return QueryBuilder.apply(this, (query) {
return query.addDistinctBy(r'isTrashed'); return query.addDistinctBy(r'isTrashed');
@ -3136,12 +3087,6 @@ extension AssetQueryProperty on QueryBuilder<Asset, Asset, QQueryProperty> {
}); });
} }
QueryBuilder<Asset, bool, QQueryOperations> isReadOnlyProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'isReadOnly');
});
}
QueryBuilder<Asset, bool, QQueryOperations> isTrashedProperty() { QueryBuilder<Asset, bool, QQueryOperations> isTrashedProperty() {
return QueryBuilder.apply(this, (query) { return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'isTrashed'); return query.addPropertyName(r'isTrashed');

View File

@ -71,19 +71,6 @@ extension AssetListExtension on Iterable<Asset> {
return this; return this;
} }
/// Returns the assets that are present on a file system which has write permission
/// This filters out assets on readOnly external library to which we cannot perform any write operation
Iterable<Asset> writableOnly({
void Function()? errorCallback,
}) {
final bool onlyWritable = every((e) => !e.isReadOnly);
if (!onlyWritable) {
if (errorCallback != null) errorCallback();
return where((a) => !a.isReadOnly);
}
return this;
}
/// Filters out offline assets and returns those that are still accessible by the Immich server /// Filters out offline assets and returns those that are still accessible by the Immich server
Iterable<Asset> nonOfflineOnly({ Iterable<Asset> nonOfflineOnly({
void Function()? errorCallback, void Function()? errorCallback,

View File

@ -102,16 +102,6 @@ class BottomGalleryBar extends ConsumerWidget {
} }
void handleDelete() async { void handleDelete() async {
// Cannot delete readOnly / external assets. They are handled through library offline jobs
if (asset.isReadOnly) {
ImmichToast.show(
durationInSecond: 1,
context: context,
msg: 'asset_action_delete_err_read_only'.tr(),
gravity: ToastGravity.BOTTOM,
);
return;
}
Future<bool> onDelete(bool force) async { Future<bool> onDelete(bool force) async {
final isDeleted = await ref.read(assetProvider.notifier).deleteAssets( final isDeleted = await ref.read(assetProvider.notifier).deleteAssets(
{asset}, {asset},

View File

@ -42,7 +42,7 @@ class ExifBottomSheet extends HookConsumerWidget {
fontSize: 14, fontSize: 14,
), ),
), ),
if (asset.isRemote && !asset.isReadOnly) if (asset.isRemote)
IconButton( IconButton(
onPressed: () => handleEditDateTime( onPressed: () => handleEditDateTime(
ref, ref,

View File

@ -24,7 +24,7 @@ class ExifLocation extends StatelessWidget {
final hasCoordinates = exifInfo?.hasCoordinates ?? false; final hasCoordinates = exifInfo?.hasCoordinates ?? false;
// Guard no lat/lng // Guard no lat/lng
if (!hasCoordinates) { if (!hasCoordinates) {
return asset.isRemote && !asset.isReadOnly return asset.isRemote
? ListTile( ? ListTile(
minLeadingWidth: 0, minLeadingWidth: 0,
contentPadding: const EdgeInsets.all(0), contentPadding: const EdgeInsets.all(0),
@ -57,7 +57,7 @@ class ExifLocation extends StatelessWidget {
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
).tr(), ).tr(),
if (asset.isRemote && !asset.isReadOnly) if (asset.isRemote)
IconButton( IconButton(
onPressed: editLocation, onPressed: editLocation,
icon: const Icon(Icons.edit_outlined), icon: const Icon(Icons.edit_outlined),

View File

@ -63,6 +63,12 @@ abstract class _$AppRouter extends RootStackRouter {
child: const AllPeoplePage(), child: const AllPeoplePage(),
); );
}, },
AllPlacesRoute.name: (routeData) {
return AutoRoutePage<dynamic>(
routeData: routeData,
child: const AllPlacesPage(),
);
},
AllVideosRoute.name: (routeData) { AllVideosRoute.name: (routeData) {
return AutoRoutePage<dynamic>( return AutoRoutePage<dynamic>(
routeData: routeData, routeData: routeData,
@ -138,12 +144,6 @@ abstract class _$AppRouter extends RootStackRouter {
), ),
); );
}, },
AllPlacesRoute.name: (routeData) {
return AutoRoutePage<dynamic>(
routeData: routeData,
child: const AllPlacesPage(),
);
},
FailedBackupStatusRoute.name: (routeData) { FailedBackupStatusRoute.name: (routeData) {
return AutoRoutePage<dynamic>( return AutoRoutePage<dynamic>(
routeData: routeData, routeData: routeData,
@ -525,6 +525,20 @@ class AllPeopleRoute extends PageRouteInfo<void> {
static const PageInfo<void> page = PageInfo<void>(name); static const PageInfo<void> page = PageInfo<void>(name);
} }
/// generated route for
/// [AllPlacesPage]
class AllPlacesRoute extends PageRouteInfo<void> {
const AllPlacesRoute({List<PageRouteInfo>? children})
: super(
AllPlacesRoute.name,
initialChildren: children,
);
static const String name = 'AllPlacesRoute';
static const PageInfo<void> page = PageInfo<void>(name);
}
/// generated route for /// generated route for
/// [AllVideosPage] /// [AllVideosPage]
class AllVideosRoute extends PageRouteInfo<void> { class AllVideosRoute extends PageRouteInfo<void> {
@ -752,20 +766,6 @@ class CreateAlbumRouteArgs {
} }
} }
/// generated route for
/// [AllPlacesPage]
class AllPlacesRoute extends PageRouteInfo<void> {
const AllPlacesRoute({List<PageRouteInfo>? children})
: super(
AllPlacesRoute.name,
initialChildren: children,
);
static const String name = 'CuratedLocationRoute';
static const PageInfo<void> page = PageInfo<void>(name);
}
/// generated route for /// generated route for
/// [FailedBackupStatusPage] /// [FailedBackupStatusPage]
class FailedBackupStatusRoute extends PageRouteInfo<void> { class FailedBackupStatusRoute extends PageRouteInfo<void> {

View File

@ -184,11 +184,6 @@ class MultiselectGrid extends HookConsumerWidget {
currentUser, currentUser,
errorCallback: errorBuilder('home_page_delete_err_partner'.tr()), errorCallback: errorBuilder('home_page_delete_err_partner'.tr()),
) )
// Cannot delete readOnly / external assets. They are handled through library offline jobs
.writableOnly(
errorCallback:
errorBuilder('asset_action_delete_err_read_only'.tr()),
)
.toList(); .toList();
final isDeleted = await ref final isDeleted = await ref
.read(assetProvider.notifier) .read(assetProvider.notifier)
@ -238,13 +233,7 @@ class MultiselectGrid extends HookConsumerWidget {
final toDelete = ownedRemoteSelection( final toDelete = ownedRemoteSelection(
localErrorMessage: 'home_page_delete_remote_err_local'.tr(), localErrorMessage: 'home_page_delete_remote_err_local'.tr(),
ownerErrorMessage: 'home_page_delete_err_partner'.tr(), ownerErrorMessage: 'home_page_delete_err_partner'.tr(),
) ).toList();
// Cannot delete readOnly / external assets. They are handled through library offline jobs
.writableOnly(
errorCallback:
errorBuilder('asset_action_delete_err_read_only'.tr()),
)
.toList();
final isDeleted = await ref final isDeleted = await ref
.read(assetProvider.notifier) .read(assetProvider.notifier)
@ -372,12 +361,8 @@ class MultiselectGrid extends HookConsumerWidget {
final remoteAssets = ownedRemoteSelection( final remoteAssets = ownedRemoteSelection(
localErrorMessage: 'home_page_favorite_err_local'.tr(), localErrorMessage: 'home_page_favorite_err_local'.tr(),
ownerErrorMessage: 'home_page_favorite_err_partner'.tr(), ownerErrorMessage: 'home_page_favorite_err_partner'.tr(),
).writableOnly(
// Assume readOnly assets to be present in a read-only mount. So do not write sidecar
errorCallback: errorBuilder(
'multiselect_grid_edit_date_time_err_read_only'.tr(),
),
); );
if (remoteAssets.isNotEmpty) { if (remoteAssets.isNotEmpty) {
handleEditDateTime(ref, context, remoteAssets.toList()); handleEditDateTime(ref, context, remoteAssets.toList());
} }
@ -391,12 +376,8 @@ class MultiselectGrid extends HookConsumerWidget {
final remoteAssets = ownedRemoteSelection( final remoteAssets = ownedRemoteSelection(
localErrorMessage: 'home_page_favorite_err_local'.tr(), localErrorMessage: 'home_page_favorite_err_local'.tr(),
ownerErrorMessage: 'home_page_favorite_err_partner'.tr(), ownerErrorMessage: 'home_page_favorite_err_partner'.tr(),
).writableOnly(
// Assume readOnly assets to be present in a read-only mount. So do not write sidecar
errorCallback: errorBuilder(
'multiselect_grid_edit_gps_err_read_only'.tr(),
),
); );
if (remoteAssets.isNotEmpty) { if (remoteAssets.isNotEmpty) {
handleEditLocation(ref, context, remoteAssets.toList()); handleEditLocation(ref, context, remoteAssets.toList());
} }

View File

@ -955,7 +955,7 @@ void (empty response body)
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **uploadFile** # **uploadFile**
> AssetFileUploadResponseDto uploadFile(assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, key, xImmichChecksum, duration, isArchived, isFavorite, isOffline, isReadOnly, isVisible, libraryId, livePhotoData, sidecarData) > AssetFileUploadResponseDto uploadFile(assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, key, xImmichChecksum, duration, isArchived, isFavorite, isOffline, isVisible, libraryId, livePhotoData, sidecarData)
@ -989,14 +989,13 @@ final duration = duration_example; // String |
final isArchived = true; // bool | final isArchived = true; // bool |
final isFavorite = true; // bool | final isFavorite = true; // bool |
final isOffline = true; // bool | final isOffline = true; // bool |
final isReadOnly = true; // bool |
final isVisible = true; // bool | final isVisible = true; // bool |
final libraryId = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String | final libraryId = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
final livePhotoData = BINARY_DATA_HERE; // MultipartFile | final livePhotoData = BINARY_DATA_HERE; // MultipartFile |
final sidecarData = BINARY_DATA_HERE; // MultipartFile | final sidecarData = BINARY_DATA_HERE; // MultipartFile |
try { try {
final result = api_instance.uploadFile(assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, key, xImmichChecksum, duration, isArchived, isFavorite, isOffline, isReadOnly, isVisible, libraryId, livePhotoData, sidecarData); final result = api_instance.uploadFile(assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, key, xImmichChecksum, duration, isArchived, isFavorite, isOffline, isVisible, libraryId, livePhotoData, sidecarData);
print(result); print(result);
} catch (e) { } catch (e) {
print('Exception when calling AssetApi->uploadFile: $e\n'); print('Exception when calling AssetApi->uploadFile: $e\n');
@ -1018,7 +1017,6 @@ Name | Type | Description | Notes
**isArchived** | **bool**| | [optional] **isArchived** | **bool**| | [optional]
**isFavorite** | **bool**| | [optional] **isFavorite** | **bool**| | [optional]
**isOffline** | **bool**| | [optional] **isOffline** | **bool**| | [optional]
**isReadOnly** | **bool**| | [optional]
**isVisible** | **bool**| | [optional] **isVisible** | **bool**| | [optional]
**libraryId** | **String**| | [optional] **libraryId** | **String**| | [optional]
**livePhotoData** | **MultipartFile**| | [optional] **livePhotoData** | **MultipartFile**| | [optional]

View File

@ -18,10 +18,10 @@ Name | Type | Description | Notes
**hasMetadata** | **bool** | | **hasMetadata** | **bool** | |
**id** | **String** | | **id** | **String** | |
**isArchived** | **bool** | | **isArchived** | **bool** | |
**isExternal** | **bool** | | **isExternal** | **bool** | This property was deprecated in v1.104.0 | [optional]
**isFavorite** | **bool** | | **isFavorite** | **bool** | |
**isOffline** | **bool** | | **isOffline** | **bool** | |
**isReadOnly** | **bool** | | **isReadOnly** | **bool** | This property was deprecated in v1.104.0 | [optional]
**isTrashed** | **bool** | | **isTrashed** | **bool** | |
**libraryId** | **String** | | **libraryId** | **String** | |
**livePhotoVideoId** | **String** | | [optional] **livePhotoVideoId** | **String** | | [optional]

View File

@ -19,12 +19,10 @@ Name | Type | Description | Notes
**id** | **String** | | [optional] **id** | **String** | | [optional]
**isArchived** | **bool** | | [optional] **isArchived** | **bool** | | [optional]
**isEncoded** | **bool** | | [optional] **isEncoded** | **bool** | | [optional]
**isExternal** | **bool** | | [optional]
**isFavorite** | **bool** | | [optional] **isFavorite** | **bool** | | [optional]
**isMotion** | **bool** | | [optional] **isMotion** | **bool** | | [optional]
**isNotInAlbum** | **bool** | | [optional] **isNotInAlbum** | **bool** | | [optional]
**isOffline** | **bool** | | [optional] **isOffline** | **bool** | | [optional]
**isReadOnly** | **bool** | | [optional]
**isVisible** | **bool** | | [optional] **isVisible** | **bool** | | [optional]
**lensModel** | **String** | | [optional] **lensModel** | **String** | | [optional]
**libraryId** | **String** | | [optional] **libraryId** | **String** | | [optional]

View File

@ -15,12 +15,10 @@ Name | Type | Description | Notes
**deviceId** | **String** | | [optional] **deviceId** | **String** | | [optional]
**isArchived** | **bool** | | [optional] **isArchived** | **bool** | | [optional]
**isEncoded** | **bool** | | [optional] **isEncoded** | **bool** | | [optional]
**isExternal** | **bool** | | [optional]
**isFavorite** | **bool** | | [optional] **isFavorite** | **bool** | | [optional]
**isMotion** | **bool** | | [optional] **isMotion** | **bool** | | [optional]
**isNotInAlbum** | **bool** | | [optional] **isNotInAlbum** | **bool** | | [optional]
**isOffline** | **bool** | | [optional] **isOffline** | **bool** | | [optional]
**isReadOnly** | **bool** | | [optional]
**isVisible** | **bool** | | [optional] **isVisible** | **bool** | | [optional]
**lensModel** | **String** | | [optional] **lensModel** | **String** | | [optional]
**libraryId** | **String** | | [optional] **libraryId** | **String** | | [optional]

View File

@ -968,8 +968,6 @@ class AssetApi {
/// ///
/// * [bool] isOffline: /// * [bool] isOffline:
/// ///
/// * [bool] isReadOnly:
///
/// * [bool] isVisible: /// * [bool] isVisible:
/// ///
/// * [String] libraryId: /// * [String] libraryId:
@ -977,7 +975,7 @@ class AssetApi {
/// * [MultipartFile] livePhotoData: /// * [MultipartFile] livePhotoData:
/// ///
/// * [MultipartFile] sidecarData: /// * [MultipartFile] sidecarData:
Future<Response> uploadFileWithHttpInfo(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? xImmichChecksum, String? duration, bool? isArchived, bool? isFavorite, bool? isOffline, bool? isReadOnly, bool? isVisible, String? libraryId, MultipartFile? livePhotoData, MultipartFile? sidecarData, }) async { Future<Response> uploadFileWithHttpInfo(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? xImmichChecksum, String? duration, bool? isArchived, bool? isFavorite, bool? isOffline, bool? isVisible, String? libraryId, MultipartFile? livePhotoData, MultipartFile? sidecarData, }) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/asset/upload'; final path = r'/asset/upload';
@ -1037,10 +1035,6 @@ class AssetApi {
hasFields = true; hasFields = true;
mp.fields[r'isOffline'] = parameterToString(isOffline); mp.fields[r'isOffline'] = parameterToString(isOffline);
} }
if (isReadOnly != null) {
hasFields = true;
mp.fields[r'isReadOnly'] = parameterToString(isReadOnly);
}
if (isVisible != null) { if (isVisible != null) {
hasFields = true; hasFields = true;
mp.fields[r'isVisible'] = parameterToString(isVisible); mp.fields[r'isVisible'] = parameterToString(isVisible);
@ -1099,8 +1093,6 @@ class AssetApi {
/// ///
/// * [bool] isOffline: /// * [bool] isOffline:
/// ///
/// * [bool] isReadOnly:
///
/// * [bool] isVisible: /// * [bool] isVisible:
/// ///
/// * [String] libraryId: /// * [String] libraryId:
@ -1108,8 +1100,8 @@ class AssetApi {
/// * [MultipartFile] livePhotoData: /// * [MultipartFile] livePhotoData:
/// ///
/// * [MultipartFile] sidecarData: /// * [MultipartFile] sidecarData:
Future<AssetFileUploadResponseDto?> uploadFile(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? xImmichChecksum, String? duration, bool? isArchived, bool? isFavorite, bool? isOffline, bool? isReadOnly, bool? isVisible, String? libraryId, MultipartFile? livePhotoData, MultipartFile? sidecarData, }) async { Future<AssetFileUploadResponseDto?> uploadFile(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? xImmichChecksum, String? duration, bool? isArchived, bool? isFavorite, bool? isOffline, bool? isVisible, String? libraryId, MultipartFile? livePhotoData, MultipartFile? sidecarData, }) async {
final response = await uploadFileWithHttpInfo(assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, key: key, xImmichChecksum: xImmichChecksum, duration: duration, isArchived: isArchived, isFavorite: isFavorite, isOffline: isOffline, isReadOnly: isReadOnly, isVisible: isVisible, libraryId: libraryId, livePhotoData: livePhotoData, sidecarData: sidecarData, ); final response = await uploadFileWithHttpInfo(assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, key: key, xImmichChecksum: xImmichChecksum, duration: duration, isArchived: isArchived, isFavorite: isFavorite, isOffline: isOffline, isVisible: isVisible, libraryId: libraryId, livePhotoData: livePhotoData, sidecarData: sidecarData, );
if (response.statusCode >= HttpStatus.badRequest) { if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response)); throw ApiException(response.statusCode, await _decodeBodyBytes(response));
} }

View File

@ -23,10 +23,10 @@ class AssetResponseDto {
required this.hasMetadata, required this.hasMetadata,
required this.id, required this.id,
required this.isArchived, required this.isArchived,
required this.isExternal, this.isExternal,
required this.isFavorite, required this.isFavorite,
required this.isOffline, required this.isOffline,
required this.isReadOnly, this.isReadOnly,
required this.isTrashed, required this.isTrashed,
required this.libraryId, required this.libraryId,
this.livePhotoVideoId, this.livePhotoVideoId,
@ -74,13 +74,27 @@ class AssetResponseDto {
bool isArchived; bool isArchived;
bool isExternal; /// This property was deprecated in v1.104.0
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated
/// source code must fall back to having a nullable type.
/// Consider adding a "default:" property in the specification file to hide this note.
///
bool? isExternal;
bool isFavorite; bool isFavorite;
bool isOffline; bool isOffline;
bool isReadOnly; /// This property was deprecated in v1.104.0
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated
/// source code must fall back to having a nullable type.
/// Consider adding a "default:" property in the specification file to hide this note.
///
bool? isReadOnly;
bool isTrashed; bool isTrashed;
@ -178,10 +192,10 @@ class AssetResponseDto {
(hasMetadata.hashCode) + (hasMetadata.hashCode) +
(id.hashCode) + (id.hashCode) +
(isArchived.hashCode) + (isArchived.hashCode) +
(isExternal.hashCode) + (isExternal == null ? 0 : isExternal!.hashCode) +
(isFavorite.hashCode) + (isFavorite.hashCode) +
(isOffline.hashCode) + (isOffline.hashCode) +
(isReadOnly.hashCode) + (isReadOnly == null ? 0 : isReadOnly!.hashCode) +
(isTrashed.hashCode) + (isTrashed.hashCode) +
(libraryId.hashCode) + (libraryId.hashCode) +
(livePhotoVideoId == null ? 0 : livePhotoVideoId!.hashCode) + (livePhotoVideoId == null ? 0 : livePhotoVideoId!.hashCode) +
@ -220,10 +234,18 @@ class AssetResponseDto {
json[r'hasMetadata'] = this.hasMetadata; json[r'hasMetadata'] = this.hasMetadata;
json[r'id'] = this.id; json[r'id'] = this.id;
json[r'isArchived'] = this.isArchived; json[r'isArchived'] = this.isArchived;
if (this.isExternal != null) {
json[r'isExternal'] = this.isExternal; json[r'isExternal'] = this.isExternal;
} else {
// json[r'isExternal'] = null;
}
json[r'isFavorite'] = this.isFavorite; json[r'isFavorite'] = this.isFavorite;
json[r'isOffline'] = this.isOffline; json[r'isOffline'] = this.isOffline;
if (this.isReadOnly != null) {
json[r'isReadOnly'] = this.isReadOnly; json[r'isReadOnly'] = this.isReadOnly;
} else {
// json[r'isReadOnly'] = null;
}
json[r'isTrashed'] = this.isTrashed; json[r'isTrashed'] = this.isTrashed;
json[r'libraryId'] = this.libraryId; json[r'libraryId'] = this.libraryId;
if (this.livePhotoVideoId != null) { if (this.livePhotoVideoId != null) {
@ -287,10 +309,10 @@ class AssetResponseDto {
hasMetadata: mapValueOfType<bool>(json, r'hasMetadata')!, hasMetadata: mapValueOfType<bool>(json, r'hasMetadata')!,
id: mapValueOfType<String>(json, r'id')!, id: mapValueOfType<String>(json, r'id')!,
isArchived: mapValueOfType<bool>(json, r'isArchived')!, isArchived: mapValueOfType<bool>(json, r'isArchived')!,
isExternal: mapValueOfType<bool>(json, r'isExternal')!, isExternal: mapValueOfType<bool>(json, r'isExternal'),
isFavorite: mapValueOfType<bool>(json, r'isFavorite')!, isFavorite: mapValueOfType<bool>(json, r'isFavorite')!,
isOffline: mapValueOfType<bool>(json, r'isOffline')!, isOffline: mapValueOfType<bool>(json, r'isOffline')!,
isReadOnly: mapValueOfType<bool>(json, r'isReadOnly')!, isReadOnly: mapValueOfType<bool>(json, r'isReadOnly'),
isTrashed: mapValueOfType<bool>(json, r'isTrashed')!, isTrashed: mapValueOfType<bool>(json, r'isTrashed')!,
libraryId: mapValueOfType<String>(json, r'libraryId')!, libraryId: mapValueOfType<String>(json, r'libraryId')!,
livePhotoVideoId: mapValueOfType<String>(json, r'livePhotoVideoId'), livePhotoVideoId: mapValueOfType<String>(json, r'livePhotoVideoId'),
@ -365,10 +387,8 @@ class AssetResponseDto {
'hasMetadata', 'hasMetadata',
'id', 'id',
'isArchived', 'isArchived',
'isExternal',
'isFavorite', 'isFavorite',
'isOffline', 'isOffline',
'isReadOnly',
'isTrashed', 'isTrashed',
'libraryId', 'libraryId',
'localDateTime', 'localDateTime',

View File

@ -24,12 +24,10 @@ class MetadataSearchDto {
this.id, this.id,
this.isArchived, this.isArchived,
this.isEncoded, this.isEncoded,
this.isExternal,
this.isFavorite, this.isFavorite,
this.isMotion, this.isMotion,
this.isNotInAlbum, this.isNotInAlbum,
this.isOffline, this.isOffline,
this.isReadOnly,
this.isVisible, this.isVisible,
this.lensModel, this.lensModel,
this.libraryId, this.libraryId,
@ -148,14 +146,6 @@ class MetadataSearchDto {
/// ///
bool? isEncoded; bool? isEncoded;
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated
/// source code must fall back to having a nullable type.
/// Consider adding a "default:" property in the specification file to hide this note.
///
bool? isExternal;
/// ///
/// Please note: This property should have been non-nullable! Since the specification file /// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated /// does not include a default value (using the "default:" property), however, the generated
@ -188,14 +178,6 @@ class MetadataSearchDto {
/// ///
bool? isOffline; bool? isOffline;
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated
/// source code must fall back to having a nullable type.
/// Consider adding a "default:" property in the specification file to hide this note.
///
bool? isReadOnly;
/// ///
/// Please note: This property should have been non-nullable! Since the specification file /// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated /// does not include a default value (using the "default:" property), however, the generated
@ -426,12 +408,10 @@ class MetadataSearchDto {
other.id == id && other.id == id &&
other.isArchived == isArchived && other.isArchived == isArchived &&
other.isEncoded == isEncoded && other.isEncoded == isEncoded &&
other.isExternal == isExternal &&
other.isFavorite == isFavorite && other.isFavorite == isFavorite &&
other.isMotion == isMotion && other.isMotion == isMotion &&
other.isNotInAlbum == isNotInAlbum && other.isNotInAlbum == isNotInAlbum &&
other.isOffline == isOffline && other.isOffline == isOffline &&
other.isReadOnly == isReadOnly &&
other.isVisible == isVisible && other.isVisible == isVisible &&
other.lensModel == lensModel && other.lensModel == lensModel &&
other.libraryId == libraryId && other.libraryId == libraryId &&
@ -475,12 +455,10 @@ class MetadataSearchDto {
(id == null ? 0 : id!.hashCode) + (id == null ? 0 : id!.hashCode) +
(isArchived == null ? 0 : isArchived!.hashCode) + (isArchived == null ? 0 : isArchived!.hashCode) +
(isEncoded == null ? 0 : isEncoded!.hashCode) + (isEncoded == null ? 0 : isEncoded!.hashCode) +
(isExternal == null ? 0 : isExternal!.hashCode) +
(isFavorite == null ? 0 : isFavorite!.hashCode) + (isFavorite == null ? 0 : isFavorite!.hashCode) +
(isMotion == null ? 0 : isMotion!.hashCode) + (isMotion == null ? 0 : isMotion!.hashCode) +
(isNotInAlbum == null ? 0 : isNotInAlbum!.hashCode) + (isNotInAlbum == null ? 0 : isNotInAlbum!.hashCode) +
(isOffline == null ? 0 : isOffline!.hashCode) + (isOffline == null ? 0 : isOffline!.hashCode) +
(isReadOnly == null ? 0 : isReadOnly!.hashCode) +
(isVisible == null ? 0 : isVisible!.hashCode) + (isVisible == null ? 0 : isVisible!.hashCode) +
(lensModel == null ? 0 : lensModel!.hashCode) + (lensModel == null ? 0 : lensModel!.hashCode) +
(libraryId == null ? 0 : libraryId!.hashCode) + (libraryId == null ? 0 : libraryId!.hashCode) +
@ -511,7 +489,7 @@ class MetadataSearchDto {
(withStacked == null ? 0 : withStacked!.hashCode); (withStacked == null ? 0 : withStacked!.hashCode);
@override @override
String toString() => 'MetadataSearchDto[checksum=$checksum, city=$city, country=$country, createdAfter=$createdAfter, createdBefore=$createdBefore, deviceAssetId=$deviceAssetId, deviceId=$deviceId, encodedVideoPath=$encodedVideoPath, id=$id, isArchived=$isArchived, isEncoded=$isEncoded, isExternal=$isExternal, isFavorite=$isFavorite, isMotion=$isMotion, isNotInAlbum=$isNotInAlbum, isOffline=$isOffline, isReadOnly=$isReadOnly, isVisible=$isVisible, lensModel=$lensModel, libraryId=$libraryId, make=$make, model=$model, order=$order, originalFileName=$originalFileName, originalPath=$originalPath, page=$page, personIds=$personIds, previewPath=$previewPath, resizePath=$resizePath, size=$size, state=$state, takenAfter=$takenAfter, takenBefore=$takenBefore, thumbnailPath=$thumbnailPath, trashedAfter=$trashedAfter, trashedBefore=$trashedBefore, type=$type, updatedAfter=$updatedAfter, updatedBefore=$updatedBefore, webpPath=$webpPath, withArchived=$withArchived, withDeleted=$withDeleted, withExif=$withExif, withPeople=$withPeople, withStacked=$withStacked]'; String toString() => 'MetadataSearchDto[checksum=$checksum, city=$city, country=$country, createdAfter=$createdAfter, createdBefore=$createdBefore, deviceAssetId=$deviceAssetId, deviceId=$deviceId, encodedVideoPath=$encodedVideoPath, id=$id, isArchived=$isArchived, isEncoded=$isEncoded, isFavorite=$isFavorite, isMotion=$isMotion, isNotInAlbum=$isNotInAlbum, isOffline=$isOffline, isVisible=$isVisible, lensModel=$lensModel, libraryId=$libraryId, make=$make, model=$model, order=$order, originalFileName=$originalFileName, originalPath=$originalPath, page=$page, personIds=$personIds, previewPath=$previewPath, resizePath=$resizePath, size=$size, state=$state, takenAfter=$takenAfter, takenBefore=$takenBefore, thumbnailPath=$thumbnailPath, trashedAfter=$trashedAfter, trashedBefore=$trashedBefore, type=$type, updatedAfter=$updatedAfter, updatedBefore=$updatedBefore, webpPath=$webpPath, withArchived=$withArchived, withDeleted=$withDeleted, withExif=$withExif, withPeople=$withPeople, withStacked=$withStacked]';
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final json = <String, dynamic>{}; final json = <String, dynamic>{};
@ -570,11 +548,6 @@ class MetadataSearchDto {
} else { } else {
// json[r'isEncoded'] = null; // json[r'isEncoded'] = null;
} }
if (this.isExternal != null) {
json[r'isExternal'] = this.isExternal;
} else {
// json[r'isExternal'] = null;
}
if (this.isFavorite != null) { if (this.isFavorite != null) {
json[r'isFavorite'] = this.isFavorite; json[r'isFavorite'] = this.isFavorite;
} else { } else {
@ -595,11 +568,6 @@ class MetadataSearchDto {
} else { } else {
// json[r'isOffline'] = null; // json[r'isOffline'] = null;
} }
if (this.isReadOnly != null) {
json[r'isReadOnly'] = this.isReadOnly;
} else {
// json[r'isReadOnly'] = null;
}
if (this.isVisible != null) { if (this.isVisible != null) {
json[r'isVisible'] = this.isVisible; json[r'isVisible'] = this.isVisible;
} else { } else {
@ -754,12 +722,10 @@ class MetadataSearchDto {
id: mapValueOfType<String>(json, r'id'), id: mapValueOfType<String>(json, r'id'),
isArchived: mapValueOfType<bool>(json, r'isArchived'), isArchived: mapValueOfType<bool>(json, r'isArchived'),
isEncoded: mapValueOfType<bool>(json, r'isEncoded'), isEncoded: mapValueOfType<bool>(json, r'isEncoded'),
isExternal: mapValueOfType<bool>(json, r'isExternal'),
isFavorite: mapValueOfType<bool>(json, r'isFavorite'), isFavorite: mapValueOfType<bool>(json, r'isFavorite'),
isMotion: mapValueOfType<bool>(json, r'isMotion'), isMotion: mapValueOfType<bool>(json, r'isMotion'),
isNotInAlbum: mapValueOfType<bool>(json, r'isNotInAlbum'), isNotInAlbum: mapValueOfType<bool>(json, r'isNotInAlbum'),
isOffline: mapValueOfType<bool>(json, r'isOffline'), isOffline: mapValueOfType<bool>(json, r'isOffline'),
isReadOnly: mapValueOfType<bool>(json, r'isReadOnly'),
isVisible: mapValueOfType<bool>(json, r'isVisible'), isVisible: mapValueOfType<bool>(json, r'isVisible'),
lensModel: mapValueOfType<String>(json, r'lensModel'), lensModel: mapValueOfType<String>(json, r'lensModel'),
libraryId: mapValueOfType<String>(json, r'libraryId'), libraryId: mapValueOfType<String>(json, r'libraryId'),

View File

@ -20,12 +20,10 @@ class SmartSearchDto {
this.deviceId, this.deviceId,
this.isArchived, this.isArchived,
this.isEncoded, this.isEncoded,
this.isExternal,
this.isFavorite, this.isFavorite,
this.isMotion, this.isMotion,
this.isNotInAlbum, this.isNotInAlbum,
this.isOffline, this.isOffline,
this.isReadOnly,
this.isVisible, this.isVisible,
this.lensModel, this.lensModel,
this.libraryId, this.libraryId,
@ -104,14 +102,6 @@ class SmartSearchDto {
/// ///
bool? isEncoded; bool? isEncoded;
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated
/// source code must fall back to having a nullable type.
/// Consider adding a "default:" property in the specification file to hide this note.
///
bool? isExternal;
/// ///
/// Please note: This property should have been non-nullable! Since the specification file /// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated /// does not include a default value (using the "default:" property), however, the generated
@ -144,14 +134,6 @@ class SmartSearchDto {
/// ///
bool? isOffline; bool? isOffline;
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated
/// source code must fall back to having a nullable type.
/// Consider adding a "default:" property in the specification file to hide this note.
///
bool? isReadOnly;
/// ///
/// Please note: This property should have been non-nullable! Since the specification file /// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated /// does not include a default value (using the "default:" property), however, the generated
@ -306,12 +288,10 @@ class SmartSearchDto {
other.deviceId == deviceId && other.deviceId == deviceId &&
other.isArchived == isArchived && other.isArchived == isArchived &&
other.isEncoded == isEncoded && other.isEncoded == isEncoded &&
other.isExternal == isExternal &&
other.isFavorite == isFavorite && other.isFavorite == isFavorite &&
other.isMotion == isMotion && other.isMotion == isMotion &&
other.isNotInAlbum == isNotInAlbum && other.isNotInAlbum == isNotInAlbum &&
other.isOffline == isOffline && other.isOffline == isOffline &&
other.isReadOnly == isReadOnly &&
other.isVisible == isVisible && other.isVisible == isVisible &&
other.lensModel == lensModel && other.lensModel == lensModel &&
other.libraryId == libraryId && other.libraryId == libraryId &&
@ -343,12 +323,10 @@ class SmartSearchDto {
(deviceId == null ? 0 : deviceId!.hashCode) + (deviceId == null ? 0 : deviceId!.hashCode) +
(isArchived == null ? 0 : isArchived!.hashCode) + (isArchived == null ? 0 : isArchived!.hashCode) +
(isEncoded == null ? 0 : isEncoded!.hashCode) + (isEncoded == null ? 0 : isEncoded!.hashCode) +
(isExternal == null ? 0 : isExternal!.hashCode) +
(isFavorite == null ? 0 : isFavorite!.hashCode) + (isFavorite == null ? 0 : isFavorite!.hashCode) +
(isMotion == null ? 0 : isMotion!.hashCode) + (isMotion == null ? 0 : isMotion!.hashCode) +
(isNotInAlbum == null ? 0 : isNotInAlbum!.hashCode) + (isNotInAlbum == null ? 0 : isNotInAlbum!.hashCode) +
(isOffline == null ? 0 : isOffline!.hashCode) + (isOffline == null ? 0 : isOffline!.hashCode) +
(isReadOnly == null ? 0 : isReadOnly!.hashCode) +
(isVisible == null ? 0 : isVisible!.hashCode) + (isVisible == null ? 0 : isVisible!.hashCode) +
(lensModel == null ? 0 : lensModel!.hashCode) + (lensModel == null ? 0 : lensModel!.hashCode) +
(libraryId == null ? 0 : libraryId!.hashCode) + (libraryId == null ? 0 : libraryId!.hashCode) +
@ -371,7 +349,7 @@ class SmartSearchDto {
(withExif == null ? 0 : withExif!.hashCode); (withExif == null ? 0 : withExif!.hashCode);
@override @override
String toString() => 'SmartSearchDto[city=$city, country=$country, createdAfter=$createdAfter, createdBefore=$createdBefore, deviceId=$deviceId, isArchived=$isArchived, isEncoded=$isEncoded, isExternal=$isExternal, isFavorite=$isFavorite, isMotion=$isMotion, isNotInAlbum=$isNotInAlbum, isOffline=$isOffline, isReadOnly=$isReadOnly, isVisible=$isVisible, lensModel=$lensModel, libraryId=$libraryId, make=$make, model=$model, page=$page, personIds=$personIds, query=$query, size=$size, state=$state, takenAfter=$takenAfter, takenBefore=$takenBefore, trashedAfter=$trashedAfter, trashedBefore=$trashedBefore, type=$type, updatedAfter=$updatedAfter, updatedBefore=$updatedBefore, withArchived=$withArchived, withDeleted=$withDeleted, withExif=$withExif]'; String toString() => 'SmartSearchDto[city=$city, country=$country, createdAfter=$createdAfter, createdBefore=$createdBefore, deviceId=$deviceId, isArchived=$isArchived, isEncoded=$isEncoded, isFavorite=$isFavorite, isMotion=$isMotion, isNotInAlbum=$isNotInAlbum, isOffline=$isOffline, isVisible=$isVisible, lensModel=$lensModel, libraryId=$libraryId, make=$make, model=$model, page=$page, personIds=$personIds, query=$query, size=$size, state=$state, takenAfter=$takenAfter, takenBefore=$takenBefore, trashedAfter=$trashedAfter, trashedBefore=$trashedBefore, type=$type, updatedAfter=$updatedAfter, updatedBefore=$updatedBefore, withArchived=$withArchived, withDeleted=$withDeleted, withExif=$withExif]';
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final json = <String, dynamic>{}; final json = <String, dynamic>{};
@ -410,11 +388,6 @@ class SmartSearchDto {
} else { } else {
// json[r'isEncoded'] = null; // json[r'isEncoded'] = null;
} }
if (this.isExternal != null) {
json[r'isExternal'] = this.isExternal;
} else {
// json[r'isExternal'] = null;
}
if (this.isFavorite != null) { if (this.isFavorite != null) {
json[r'isFavorite'] = this.isFavorite; json[r'isFavorite'] = this.isFavorite;
} else { } else {
@ -435,11 +408,6 @@ class SmartSearchDto {
} else { } else {
// json[r'isOffline'] = null; // json[r'isOffline'] = null;
} }
if (this.isReadOnly != null) {
json[r'isReadOnly'] = this.isReadOnly;
} else {
// json[r'isReadOnly'] = null;
}
if (this.isVisible != null) { if (this.isVisible != null) {
json[r'isVisible'] = this.isVisible; json[r'isVisible'] = this.isVisible;
} else { } else {
@ -546,12 +514,10 @@ class SmartSearchDto {
deviceId: mapValueOfType<String>(json, r'deviceId'), deviceId: mapValueOfType<String>(json, r'deviceId'),
isArchived: mapValueOfType<bool>(json, r'isArchived'), isArchived: mapValueOfType<bool>(json, r'isArchived'),
isEncoded: mapValueOfType<bool>(json, r'isEncoded'), isEncoded: mapValueOfType<bool>(json, r'isEncoded'),
isExternal: mapValueOfType<bool>(json, r'isExternal'),
isFavorite: mapValueOfType<bool>(json, r'isFavorite'), isFavorite: mapValueOfType<bool>(json, r'isFavorite'),
isMotion: mapValueOfType<bool>(json, r'isMotion'), isMotion: mapValueOfType<bool>(json, r'isMotion'),
isNotInAlbum: mapValueOfType<bool>(json, r'isNotInAlbum'), isNotInAlbum: mapValueOfType<bool>(json, r'isNotInAlbum'),
isOffline: mapValueOfType<bool>(json, r'isOffline'), isOffline: mapValueOfType<bool>(json, r'isOffline'),
isReadOnly: mapValueOfType<bool>(json, r'isReadOnly'),
isVisible: mapValueOfType<bool>(json, r'isVisible'), isVisible: mapValueOfType<bool>(json, r'isVisible'),
lensModel: mapValueOfType<String>(json, r'lensModel'), lensModel: mapValueOfType<String>(json, r'lensModel'),
libraryId: mapValueOfType<String>(json, r'libraryId'), libraryId: mapValueOfType<String>(json, r'libraryId'),

View File

@ -105,7 +105,7 @@ void main() {
// TODO // TODO
}); });
//Future<AssetFileUploadResponseDto> uploadFile(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String key, String xImmichChecksum, String duration, bool isArchived, bool isFavorite, bool isOffline, bool isReadOnly, bool isVisible, String libraryId, MultipartFile livePhotoData, MultipartFile sidecarData }) async //Future<AssetFileUploadResponseDto> uploadFile(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String key, String xImmichChecksum, String duration, bool isArchived, bool isFavorite, bool isOffline, bool isVisible, String libraryId, MultipartFile livePhotoData, MultipartFile sidecarData }) async
test('test uploadFile', () async { test('test uploadFile', () async {
// TODO // TODO
}); });

View File

@ -67,6 +67,7 @@ void main() {
// TODO // TODO
}); });
// This property was deprecated in v1.104.0
// bool isExternal // bool isExternal
test('to test the property `isExternal`', () async { test('to test the property `isExternal`', () async {
// TODO // TODO
@ -82,6 +83,7 @@ void main() {
// TODO // TODO
}); });
// This property was deprecated in v1.104.0
// bool isReadOnly // bool isReadOnly
test('to test the property `isReadOnly`', () async { test('to test the property `isReadOnly`', () async {
// TODO // TODO

View File

@ -71,11 +71,6 @@ void main() {
// TODO // TODO
}); });
// bool isExternal
test('to test the property `isExternal`', () async {
// TODO
});
// bool isFavorite // bool isFavorite
test('to test the property `isFavorite`', () async { test('to test the property `isFavorite`', () async {
// TODO // TODO
@ -96,11 +91,6 @@ void main() {
// TODO // TODO
}); });
// bool isReadOnly
test('to test the property `isReadOnly`', () async {
// TODO
});
// bool isVisible // bool isVisible
test('to test the property `isVisible`', () async { test('to test the property `isVisible`', () async {
// TODO // TODO

View File

@ -51,11 +51,6 @@ void main() {
// TODO // TODO
}); });
// bool isExternal
test('to test the property `isExternal`', () async {
// TODO
});
// bool isFavorite // bool isFavorite
test('to test the property `isFavorite`', () async { test('to test the property `isFavorite`', () async {
// TODO // TODO
@ -76,11 +71,6 @@ void main() {
// TODO // TODO
}); });
// bool isReadOnly
test('to test the property `isReadOnly`', () async {
// TODO
});
// bool isVisible // bool isVisible
test('to test the property `isVisible`', () async { test('to test the property `isVisible`', () async {
// TODO // TODO

View File

@ -7297,6 +7297,8 @@
"type": "boolean" "type": "boolean"
}, },
"isExternal": { "isExternal": {
"deprecated": true,
"description": "This property was deprecated in v1.104.0",
"type": "boolean" "type": "boolean"
}, },
"isFavorite": { "isFavorite": {
@ -7306,6 +7308,8 @@
"type": "boolean" "type": "boolean"
}, },
"isReadOnly": { "isReadOnly": {
"deprecated": true,
"description": "This property was deprecated in v1.104.0",
"type": "boolean" "type": "boolean"
}, },
"isTrashed": { "isTrashed": {
@ -7388,10 +7392,8 @@
"hasMetadata", "hasMetadata",
"id", "id",
"isArchived", "isArchived",
"isExternal",
"isFavorite", "isFavorite",
"isOffline", "isOffline",
"isReadOnly",
"isTrashed", "isTrashed",
"libraryId", "libraryId",
"localDateTime", "localDateTime",
@ -7652,9 +7654,6 @@
"isOffline": { "isOffline": {
"type": "boolean" "type": "boolean"
}, },
"isReadOnly": {
"type": "boolean"
},
"isVisible": { "isVisible": {
"type": "boolean" "type": "boolean"
}, },
@ -8599,9 +8598,6 @@
"isEncoded": { "isEncoded": {
"type": "boolean" "type": "boolean"
}, },
"isExternal": {
"type": "boolean"
},
"isFavorite": { "isFavorite": {
"type": "boolean" "type": "boolean"
}, },
@ -8614,9 +8610,6 @@
"isOffline": { "isOffline": {
"type": "boolean" "type": "boolean"
}, },
"isReadOnly": {
"type": "boolean"
},
"isVisible": { "isVisible": {
"type": "boolean" "type": "boolean"
}, },
@ -9821,9 +9814,6 @@
"isEncoded": { "isEncoded": {
"type": "boolean" "type": "boolean"
}, },
"isExternal": {
"type": "boolean"
},
"isFavorite": { "isFavorite": {
"type": "boolean" "type": "boolean"
}, },
@ -9836,9 +9826,6 @@
"isOffline": { "isOffline": {
"type": "boolean" "type": "boolean"
}, },
"isReadOnly": {
"type": "boolean"
},
"isVisible": { "isVisible": {
"type": "boolean" "type": "boolean"
}, },

View File

@ -122,10 +122,12 @@ export type AssetResponseDto = {
hasMetadata: boolean; hasMetadata: boolean;
id: string; id: string;
isArchived: boolean; isArchived: boolean;
isExternal: boolean; /** This property was deprecated in v1.104.0 */
isExternal?: boolean;
isFavorite: boolean; isFavorite: boolean;
isOffline: boolean; isOffline: boolean;
isReadOnly: boolean; /** This property was deprecated in v1.104.0 */
isReadOnly?: boolean;
isTrashed: boolean; isTrashed: boolean;
libraryId: string; libraryId: string;
livePhotoVideoId?: string | null; livePhotoVideoId?: string | null;
@ -296,7 +298,6 @@ export type CreateAssetDto = {
isArchived?: boolean; isArchived?: boolean;
isFavorite?: boolean; isFavorite?: boolean;
isOffline?: boolean; isOffline?: boolean;
isReadOnly?: boolean;
isVisible?: boolean; isVisible?: boolean;
libraryId?: string; libraryId?: string;
livePhotoData?: Blob; livePhotoData?: Blob;
@ -622,12 +623,10 @@ export type MetadataSearchDto = {
id?: string; id?: string;
isArchived?: boolean; isArchived?: boolean;
isEncoded?: boolean; isEncoded?: boolean;
isExternal?: boolean;
isFavorite?: boolean; isFavorite?: boolean;
isMotion?: boolean; isMotion?: boolean;
isNotInAlbum?: boolean; isNotInAlbum?: boolean;
isOffline?: boolean; isOffline?: boolean;
isReadOnly?: boolean;
isVisible?: boolean; isVisible?: boolean;
lensModel?: string; lensModel?: string;
libraryId?: string; libraryId?: string;
@ -699,12 +698,10 @@ export type SmartSearchDto = {
deviceId?: string; deviceId?: string;
isArchived?: boolean; isArchived?: boolean;
isEncoded?: boolean; isEncoded?: boolean;
isExternal?: boolean;
isFavorite?: boolean; isFavorite?: boolean;
isMotion?: boolean; isMotion?: boolean;
isNotInAlbum?: boolean; isNotInAlbum?: boolean;
isOffline?: boolean; isOffline?: boolean;
isReadOnly?: boolean;
isVisible?: boolean; isVisible?: boolean;
lensModel?: string; lensModel?: string;
libraryId?: string; libraryId?: string;

View File

@ -113,6 +113,7 @@ export const DummyValue = {
PAGINATION: { take: 10, skip: 0 }, PAGINATION: { take: 10, skip: 0 },
EMAIL: 'user@immich.app', EMAIL: 'user@immich.app',
STRING: 'abcdefghi', STRING: 'abcdefghi',
NUMBER: 50,
BUFFER: Buffer.from('abcdefghi'), BUFFER: Buffer.from('abcdefghi'),
DATE: new Date(), DATE: new Date(),
TIME_BUCKET: '2024-01-01T00:00:00.000Z', TIME_BUCKET: '2024-01-01T00:00:00.000Z',

View File

@ -36,8 +36,10 @@ export class AssetResponseDto extends SanitizedAssetResponseDto {
isArchived!: boolean; isArchived!: boolean;
isTrashed!: boolean; isTrashed!: boolean;
isOffline!: boolean; isOffline!: boolean;
isExternal!: boolean; @PropertyLifecycle({ deprecatedAt: 'v1.104.0' })
isReadOnly!: boolean; isExternal?: boolean;
@PropertyLifecycle({ deprecatedAt: 'v1.104.0' })
isReadOnly?: boolean;
exifInfo?: ExifResponseDto; exifInfo?: ExifResponseDto;
smartInfo?: SmartInfoResponseDto; smartInfo?: SmartInfoResponseDto;
tags?: TagResponseDto[]; tags?: TagResponseDto[];
@ -124,9 +126,9 @@ export function mapAsset(entity: AssetEntity, options: AssetMapOptions = {}): As
.map((a) => mapAsset(a, { stripMetadata, auth: options.auth })) .map((a) => mapAsset(a, { stripMetadata, auth: options.auth }))
: undefined, : undefined,
stackCount: entity.stack?.assets?.length ?? null, stackCount: entity.stack?.assets?.length ?? null,
isExternal: entity.isExternal,
isOffline: entity.isOffline, isOffline: entity.isOffline,
isReadOnly: entity.isReadOnly, isExternal: false,
isReadOnly: false,
hasMetadata: true, hasMetadata: true,
}; };
} }

View File

@ -97,9 +97,6 @@ export class CreateAssetDto {
@ValidateBoolean({ optional: true }) @ValidateBoolean({ optional: true })
isOffline?: boolean; isOffline?: boolean;
@ValidateBoolean({ optional: true })
isReadOnly?: boolean;
// The properties below are added to correctly generate the API docs // The properties below are added to correctly generate the API docs
// and client SDKs. Validation should be handled in the controller. // and client SDKs. Validation should be handled in the controller.
@ApiProperty({ type: 'string', format: 'binary' }) @ApiProperty({ type: 'string', format: 'binary' })

View File

@ -33,9 +33,6 @@ class BaseSearchDto {
@ValidateBoolean({ optional: true }) @ValidateBoolean({ optional: true })
isEncoded?: boolean; isEncoded?: boolean;
@ValidateBoolean({ optional: true })
isExternal?: boolean;
@ValidateBoolean({ optional: true }) @ValidateBoolean({ optional: true })
isFavorite?: boolean; isFavorite?: boolean;
@ -45,9 +42,6 @@ class BaseSearchDto {
@ValidateBoolean({ optional: true }) @ValidateBoolean({ optional: true })
isOffline?: boolean; isOffline?: boolean;
@ValidateBoolean({ optional: true })
isReadOnly?: boolean;
@ValidateBoolean({ optional: true }) @ValidateBoolean({ optional: true })
isVisible?: boolean; isVisible?: boolean;

View File

@ -106,9 +106,6 @@ export class AssetEntity {
@Column({ type: 'boolean', default: false }) @Column({ type: 'boolean', default: false })
isExternal!: boolean; isExternal!: boolean;
@Column({ type: 'boolean', default: false })
isReadOnly!: boolean;
@Column({ type: 'boolean', default: false }) @Column({ type: 'boolean', default: false })
isOffline!: boolean; isOffline!: boolean;

View File

@ -108,10 +108,6 @@ export interface IEntityJob extends IBaseJob {
source?: 'upload' | 'sidecar-write'; source?: 'upload' | 'sidecar-write';
} }
export interface IAssetDeletionJob extends IEntityJob {
fromExternal?: boolean;
}
export interface ILibraryFileJob extends IEntityJob { export interface ILibraryFileJob extends IEntityJob {
ownerId: string; ownerId: string;
assetPath: string; assetPath: string;
@ -225,7 +221,7 @@ export type JobItem =
// Asset Deletion // Asset Deletion
| { name: JobName.PERSON_CLEANUP; data?: IBaseJob } | { name: JobName.PERSON_CLEANUP; data?: IBaseJob }
| { name: JobName.ASSET_DELETION; data: IAssetDeletionJob } | { name: JobName.ASSET_DELETION; data: IEntityJob }
| { name: JobName.ASSET_DELETION_CHECK; data?: IBaseJob } | { name: JobName.ASSET_DELETION_CHECK; data?: IBaseJob }
// Library Management // Library Management

View File

@ -56,11 +56,9 @@ export type SearchIdOptions = SearchAssetIDOptions & SearchUserIdOptions;
export interface SearchStatusOptions { export interface SearchStatusOptions {
isArchived?: boolean; isArchived?: boolean;
isEncoded?: boolean; isEncoded?: boolean;
isExternal?: boolean;
isFavorite?: boolean; isFavorite?: boolean;
isMotion?: boolean; isMotion?: boolean;
isOffline?: boolean; isOffline?: boolean;
isReadOnly?: boolean;
isVisible?: boolean; isVisible?: boolean;
isNotInAlbum?: boolean; isNotInAlbum?: boolean;
type?: AssetType; type?: AssetType;

View File

@ -0,0 +1,14 @@
import { MigrationInterface, QueryRunner } from "typeorm";
export class RemoveIsReadOnly1714698592332 implements MigrationInterface {
name = 'RemoveIsReadOnly1714698592332'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "isReadOnly"`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "assets" ADD "isReadOnly" boolean NOT NULL DEFAULT false`);
}
}

View File

@ -22,7 +22,6 @@ SELECT
"entity"."isFavorite" AS "entity_isFavorite", "entity"."isFavorite" AS "entity_isFavorite",
"entity"."isArchived" AS "entity_isArchived", "entity"."isArchived" AS "entity_isArchived",
"entity"."isExternal" AS "entity_isExternal", "entity"."isExternal" AS "entity_isExternal",
"entity"."isReadOnly" AS "entity_isReadOnly",
"entity"."isOffline" AS "entity_isOffline", "entity"."isOffline" AS "entity_isOffline",
"entity"."checksum" AS "entity_checksum", "entity"."checksum" AS "entity_checksum",
"entity"."duration" AS "entity_duration", "entity"."duration" AS "entity_duration",
@ -105,7 +104,6 @@ SELECT
"AssetEntity"."isFavorite" AS "AssetEntity_isFavorite", "AssetEntity"."isFavorite" AS "AssetEntity_isFavorite",
"AssetEntity"."isArchived" AS "AssetEntity_isArchived", "AssetEntity"."isArchived" AS "AssetEntity_isArchived",
"AssetEntity"."isExternal" AS "AssetEntity_isExternal", "AssetEntity"."isExternal" AS "AssetEntity_isExternal",
"AssetEntity"."isReadOnly" AS "AssetEntity_isReadOnly",
"AssetEntity"."isOffline" AS "AssetEntity_isOffline", "AssetEntity"."isOffline" AS "AssetEntity_isOffline",
"AssetEntity"."checksum" AS "AssetEntity_checksum", "AssetEntity"."checksum" AS "AssetEntity_checksum",
"AssetEntity"."duration" AS "AssetEntity_duration", "AssetEntity"."duration" AS "AssetEntity_duration",
@ -141,7 +139,6 @@ SELECT
"AssetEntity"."isFavorite" AS "AssetEntity_isFavorite", "AssetEntity"."isFavorite" AS "AssetEntity_isFavorite",
"AssetEntity"."isArchived" AS "AssetEntity_isArchived", "AssetEntity"."isArchived" AS "AssetEntity_isArchived",
"AssetEntity"."isExternal" AS "AssetEntity_isExternal", "AssetEntity"."isExternal" AS "AssetEntity_isExternal",
"AssetEntity"."isReadOnly" AS "AssetEntity_isReadOnly",
"AssetEntity"."isOffline" AS "AssetEntity_isOffline", "AssetEntity"."isOffline" AS "AssetEntity_isOffline",
"AssetEntity"."checksum" AS "AssetEntity_checksum", "AssetEntity"."checksum" AS "AssetEntity_checksum",
"AssetEntity"."duration" AS "AssetEntity_duration", "AssetEntity"."duration" AS "AssetEntity_duration",
@ -226,7 +223,6 @@ SELECT
"bd93d5747511a4dad4923546c51365bf1a803774"."isFavorite" AS "bd93d5747511a4dad4923546c51365bf1a803774_isFavorite", "bd93d5747511a4dad4923546c51365bf1a803774"."isFavorite" AS "bd93d5747511a4dad4923546c51365bf1a803774_isFavorite",
"bd93d5747511a4dad4923546c51365bf1a803774"."isArchived" AS "bd93d5747511a4dad4923546c51365bf1a803774_isArchived", "bd93d5747511a4dad4923546c51365bf1a803774"."isArchived" AS "bd93d5747511a4dad4923546c51365bf1a803774_isArchived",
"bd93d5747511a4dad4923546c51365bf1a803774"."isExternal" AS "bd93d5747511a4dad4923546c51365bf1a803774_isExternal", "bd93d5747511a4dad4923546c51365bf1a803774"."isExternal" AS "bd93d5747511a4dad4923546c51365bf1a803774_isExternal",
"bd93d5747511a4dad4923546c51365bf1a803774"."isReadOnly" AS "bd93d5747511a4dad4923546c51365bf1a803774_isReadOnly",
"bd93d5747511a4dad4923546c51365bf1a803774"."isOffline" AS "bd93d5747511a4dad4923546c51365bf1a803774_isOffline", "bd93d5747511a4dad4923546c51365bf1a803774"."isOffline" AS "bd93d5747511a4dad4923546c51365bf1a803774_isOffline",
"bd93d5747511a4dad4923546c51365bf1a803774"."checksum" AS "bd93d5747511a4dad4923546c51365bf1a803774_checksum", "bd93d5747511a4dad4923546c51365bf1a803774"."checksum" AS "bd93d5747511a4dad4923546c51365bf1a803774_checksum",
"bd93d5747511a4dad4923546c51365bf1a803774"."duration" AS "bd93d5747511a4dad4923546c51365bf1a803774_duration", "bd93d5747511a4dad4923546c51365bf1a803774"."duration" AS "bd93d5747511a4dad4923546c51365bf1a803774_duration",
@ -308,7 +304,6 @@ FROM
"AssetEntity"."isFavorite" AS "AssetEntity_isFavorite", "AssetEntity"."isFavorite" AS "AssetEntity_isFavorite",
"AssetEntity"."isArchived" AS "AssetEntity_isArchived", "AssetEntity"."isArchived" AS "AssetEntity_isArchived",
"AssetEntity"."isExternal" AS "AssetEntity_isExternal", "AssetEntity"."isExternal" AS "AssetEntity_isExternal",
"AssetEntity"."isReadOnly" AS "AssetEntity_isReadOnly",
"AssetEntity"."isOffline" AS "AssetEntity_isOffline", "AssetEntity"."isOffline" AS "AssetEntity_isOffline",
"AssetEntity"."checksum" AS "AssetEntity_checksum", "AssetEntity"."checksum" AS "AssetEntity_checksum",
"AssetEntity"."duration" AS "AssetEntity_duration", "AssetEntity"."duration" AS "AssetEntity_duration",
@ -405,7 +400,6 @@ SELECT
"AssetEntity"."isFavorite" AS "AssetEntity_isFavorite", "AssetEntity"."isFavorite" AS "AssetEntity_isFavorite",
"AssetEntity"."isArchived" AS "AssetEntity_isArchived", "AssetEntity"."isArchived" AS "AssetEntity_isArchived",
"AssetEntity"."isExternal" AS "AssetEntity_isExternal", "AssetEntity"."isExternal" AS "AssetEntity_isExternal",
"AssetEntity"."isReadOnly" AS "AssetEntity_isReadOnly",
"AssetEntity"."isOffline" AS "AssetEntity_isOffline", "AssetEntity"."isOffline" AS "AssetEntity_isOffline",
"AssetEntity"."checksum" AS "AssetEntity_checksum", "AssetEntity"."checksum" AS "AssetEntity_checksum",
"AssetEntity"."duration" AS "AssetEntity_duration", "AssetEntity"."duration" AS "AssetEntity_duration",
@ -451,7 +445,6 @@ SELECT
"AssetEntity"."isFavorite" AS "AssetEntity_isFavorite", "AssetEntity"."isFavorite" AS "AssetEntity_isFavorite",
"AssetEntity"."isArchived" AS "AssetEntity_isArchived", "AssetEntity"."isArchived" AS "AssetEntity_isArchived",
"AssetEntity"."isExternal" AS "AssetEntity_isExternal", "AssetEntity"."isExternal" AS "AssetEntity_isExternal",
"AssetEntity"."isReadOnly" AS "AssetEntity_isReadOnly",
"AssetEntity"."isOffline" AS "AssetEntity_isOffline", "AssetEntity"."isOffline" AS "AssetEntity_isOffline",
"AssetEntity"."checksum" AS "AssetEntity_checksum", "AssetEntity"."checksum" AS "AssetEntity_checksum",
"AssetEntity"."duration" AS "AssetEntity_duration", "AssetEntity"."duration" AS "AssetEntity_duration",
@ -519,7 +512,6 @@ SELECT
"AssetEntity"."isFavorite" AS "AssetEntity_isFavorite", "AssetEntity"."isFavorite" AS "AssetEntity_isFavorite",
"AssetEntity"."isArchived" AS "AssetEntity_isArchived", "AssetEntity"."isArchived" AS "AssetEntity_isArchived",
"AssetEntity"."isExternal" AS "AssetEntity_isExternal", "AssetEntity"."isExternal" AS "AssetEntity_isExternal",
"AssetEntity"."isReadOnly" AS "AssetEntity_isReadOnly",
"AssetEntity"."isOffline" AS "AssetEntity_isOffline", "AssetEntity"."isOffline" AS "AssetEntity_isOffline",
"AssetEntity"."checksum" AS "AssetEntity_checksum", "AssetEntity"."checksum" AS "AssetEntity_checksum",
"AssetEntity"."duration" AS "AssetEntity_duration", "AssetEntity"."duration" AS "AssetEntity_duration",
@ -608,7 +600,6 @@ SELECT
"asset"."isFavorite" AS "asset_isFavorite", "asset"."isFavorite" AS "asset_isFavorite",
"asset"."isArchived" AS "asset_isArchived", "asset"."isArchived" AS "asset_isArchived",
"asset"."isExternal" AS "asset_isExternal", "asset"."isExternal" AS "asset_isExternal",
"asset"."isReadOnly" AS "asset_isReadOnly",
"asset"."isOffline" AS "asset_isOffline", "asset"."isOffline" AS "asset_isOffline",
"asset"."checksum" AS "asset_checksum", "asset"."checksum" AS "asset_checksum",
"asset"."duration" AS "asset_duration", "asset"."duration" AS "asset_duration",
@ -667,7 +658,6 @@ SELECT
"stackedAssets"."isFavorite" AS "stackedAssets_isFavorite", "stackedAssets"."isFavorite" AS "stackedAssets_isFavorite",
"stackedAssets"."isArchived" AS "stackedAssets_isArchived", "stackedAssets"."isArchived" AS "stackedAssets_isArchived",
"stackedAssets"."isExternal" AS "stackedAssets_isExternal", "stackedAssets"."isExternal" AS "stackedAssets_isExternal",
"stackedAssets"."isReadOnly" AS "stackedAssets_isReadOnly",
"stackedAssets"."isOffline" AS "stackedAssets_isOffline", "stackedAssets"."isOffline" AS "stackedAssets_isOffline",
"stackedAssets"."checksum" AS "stackedAssets_checksum", "stackedAssets"."checksum" AS "stackedAssets_checksum",
"stackedAssets"."duration" AS "stackedAssets_duration", "stackedAssets"."duration" AS "stackedAssets_duration",
@ -784,7 +774,6 @@ SELECT
"asset"."isFavorite" AS "asset_isFavorite", "asset"."isFavorite" AS "asset_isFavorite",
"asset"."isArchived" AS "asset_isArchived", "asset"."isArchived" AS "asset_isArchived",
"asset"."isExternal" AS "asset_isExternal", "asset"."isExternal" AS "asset_isExternal",
"asset"."isReadOnly" AS "asset_isReadOnly",
"asset"."isOffline" AS "asset_isOffline", "asset"."isOffline" AS "asset_isOffline",
"asset"."checksum" AS "asset_checksum", "asset"."checksum" AS "asset_checksum",
"asset"."duration" AS "asset_duration", "asset"."duration" AS "asset_duration",
@ -843,7 +832,6 @@ SELECT
"stackedAssets"."isFavorite" AS "stackedAssets_isFavorite", "stackedAssets"."isFavorite" AS "stackedAssets_isFavorite",
"stackedAssets"."isArchived" AS "stackedAssets_isArchived", "stackedAssets"."isArchived" AS "stackedAssets_isArchived",
"stackedAssets"."isExternal" AS "stackedAssets_isExternal", "stackedAssets"."isExternal" AS "stackedAssets_isExternal",
"stackedAssets"."isReadOnly" AS "stackedAssets_isReadOnly",
"stackedAssets"."isOffline" AS "stackedAssets_isOffline", "stackedAssets"."isOffline" AS "stackedAssets_isOffline",
"stackedAssets"."checksum" AS "stackedAssets_checksum", "stackedAssets"."checksum" AS "stackedAssets_checksum",
"stackedAssets"."duration" AS "stackedAssets_duration", "stackedAssets"."duration" AS "stackedAssets_duration",
@ -891,7 +879,6 @@ SELECT
"asset"."isFavorite" AS "asset_isFavorite", "asset"."isFavorite" AS "asset_isFavorite",
"asset"."isArchived" AS "asset_isArchived", "asset"."isArchived" AS "asset_isArchived",
"asset"."isExternal" AS "asset_isExternal", "asset"."isExternal" AS "asset_isExternal",
"asset"."isReadOnly" AS "asset_isReadOnly",
"asset"."isOffline" AS "asset_isOffline", "asset"."isOffline" AS "asset_isOffline",
"asset"."checksum" AS "asset_checksum", "asset"."checksum" AS "asset_checksum",
"asset"."duration" AS "asset_duration", "asset"."duration" AS "asset_duration",
@ -950,7 +937,6 @@ SELECT
"stackedAssets"."isFavorite" AS "stackedAssets_isFavorite", "stackedAssets"."isFavorite" AS "stackedAssets_isFavorite",
"stackedAssets"."isArchived" AS "stackedAssets_isArchived", "stackedAssets"."isArchived" AS "stackedAssets_isArchived",
"stackedAssets"."isExternal" AS "stackedAssets_isExternal", "stackedAssets"."isExternal" AS "stackedAssets_isExternal",
"stackedAssets"."isReadOnly" AS "stackedAssets_isReadOnly",
"stackedAssets"."isOffline" AS "stackedAssets_isOffline", "stackedAssets"."isOffline" AS "stackedAssets_isOffline",
"stackedAssets"."checksum" AS "stackedAssets_checksum", "stackedAssets"."checksum" AS "stackedAssets_checksum",
"stackedAssets"."duration" AS "stackedAssets_duration", "stackedAssets"."duration" AS "stackedAssets_duration",

View File

@ -165,7 +165,6 @@ FROM
"AssetFaceEntity__AssetFaceEntity_asset"."isFavorite" AS "AssetFaceEntity__AssetFaceEntity_asset_isFavorite", "AssetFaceEntity__AssetFaceEntity_asset"."isFavorite" AS "AssetFaceEntity__AssetFaceEntity_asset_isFavorite",
"AssetFaceEntity__AssetFaceEntity_asset"."isArchived" AS "AssetFaceEntity__AssetFaceEntity_asset_isArchived", "AssetFaceEntity__AssetFaceEntity_asset"."isArchived" AS "AssetFaceEntity__AssetFaceEntity_asset_isArchived",
"AssetFaceEntity__AssetFaceEntity_asset"."isExternal" AS "AssetFaceEntity__AssetFaceEntity_asset_isExternal", "AssetFaceEntity__AssetFaceEntity_asset"."isExternal" AS "AssetFaceEntity__AssetFaceEntity_asset_isExternal",
"AssetFaceEntity__AssetFaceEntity_asset"."isReadOnly" AS "AssetFaceEntity__AssetFaceEntity_asset_isReadOnly",
"AssetFaceEntity__AssetFaceEntity_asset"."isOffline" AS "AssetFaceEntity__AssetFaceEntity_asset_isOffline", "AssetFaceEntity__AssetFaceEntity_asset"."isOffline" AS "AssetFaceEntity__AssetFaceEntity_asset_isOffline",
"AssetFaceEntity__AssetFaceEntity_asset"."checksum" AS "AssetFaceEntity__AssetFaceEntity_asset_checksum", "AssetFaceEntity__AssetFaceEntity_asset"."checksum" AS "AssetFaceEntity__AssetFaceEntity_asset_checksum",
"AssetFaceEntity__AssetFaceEntity_asset"."duration" AS "AssetFaceEntity__AssetFaceEntity_asset_duration", "AssetFaceEntity__AssetFaceEntity_asset"."duration" AS "AssetFaceEntity__AssetFaceEntity_asset_duration",
@ -263,7 +262,6 @@ FROM
"AssetEntity"."isFavorite" AS "AssetEntity_isFavorite", "AssetEntity"."isFavorite" AS "AssetEntity_isFavorite",
"AssetEntity"."isArchived" AS "AssetEntity_isArchived", "AssetEntity"."isArchived" AS "AssetEntity_isArchived",
"AssetEntity"."isExternal" AS "AssetEntity_isExternal", "AssetEntity"."isExternal" AS "AssetEntity_isExternal",
"AssetEntity"."isReadOnly" AS "AssetEntity_isReadOnly",
"AssetEntity"."isOffline" AS "AssetEntity_isOffline", "AssetEntity"."isOffline" AS "AssetEntity_isOffline",
"AssetEntity"."checksum" AS "AssetEntity_checksum", "AssetEntity"."checksum" AS "AssetEntity_checksum",
"AssetEntity"."duration" AS "AssetEntity_duration", "AssetEntity"."duration" AS "AssetEntity_duration",
@ -393,7 +391,6 @@ SELECT
"AssetFaceEntity__AssetFaceEntity_asset"."isFavorite" AS "AssetFaceEntity__AssetFaceEntity_asset_isFavorite", "AssetFaceEntity__AssetFaceEntity_asset"."isFavorite" AS "AssetFaceEntity__AssetFaceEntity_asset_isFavorite",
"AssetFaceEntity__AssetFaceEntity_asset"."isArchived" AS "AssetFaceEntity__AssetFaceEntity_asset_isArchived", "AssetFaceEntity__AssetFaceEntity_asset"."isArchived" AS "AssetFaceEntity__AssetFaceEntity_asset_isArchived",
"AssetFaceEntity__AssetFaceEntity_asset"."isExternal" AS "AssetFaceEntity__AssetFaceEntity_asset_isExternal", "AssetFaceEntity__AssetFaceEntity_asset"."isExternal" AS "AssetFaceEntity__AssetFaceEntity_asset_isExternal",
"AssetFaceEntity__AssetFaceEntity_asset"."isReadOnly" AS "AssetFaceEntity__AssetFaceEntity_asset_isReadOnly",
"AssetFaceEntity__AssetFaceEntity_asset"."isOffline" AS "AssetFaceEntity__AssetFaceEntity_asset_isOffline", "AssetFaceEntity__AssetFaceEntity_asset"."isOffline" AS "AssetFaceEntity__AssetFaceEntity_asset_isOffline",
"AssetFaceEntity__AssetFaceEntity_asset"."checksum" AS "AssetFaceEntity__AssetFaceEntity_asset_checksum", "AssetFaceEntity__AssetFaceEntity_asset"."checksum" AS "AssetFaceEntity__AssetFaceEntity_asset_checksum",
"AssetFaceEntity__AssetFaceEntity_asset"."duration" AS "AssetFaceEntity__AssetFaceEntity_asset_duration", "AssetFaceEntity__AssetFaceEntity_asset"."duration" AS "AssetFaceEntity__AssetFaceEntity_asset_duration",

View File

@ -27,7 +27,6 @@ FROM
"asset"."isFavorite" AS "asset_isFavorite", "asset"."isFavorite" AS "asset_isFavorite",
"asset"."isArchived" AS "asset_isArchived", "asset"."isArchived" AS "asset_isArchived",
"asset"."isExternal" AS "asset_isExternal", "asset"."isExternal" AS "asset_isExternal",
"asset"."isReadOnly" AS "asset_isReadOnly",
"asset"."isOffline" AS "asset_isOffline", "asset"."isOffline" AS "asset_isOffline",
"asset"."checksum" AS "asset_checksum", "asset"."checksum" AS "asset_checksum",
"asset"."duration" AS "asset_duration", "asset"."duration" AS "asset_duration",
@ -58,7 +57,6 @@ FROM
"stackedAssets"."isFavorite" AS "stackedAssets_isFavorite", "stackedAssets"."isFavorite" AS "stackedAssets_isFavorite",
"stackedAssets"."isArchived" AS "stackedAssets_isArchived", "stackedAssets"."isArchived" AS "stackedAssets_isArchived",
"stackedAssets"."isExternal" AS "stackedAssets_isExternal", "stackedAssets"."isExternal" AS "stackedAssets_isExternal",
"stackedAssets"."isReadOnly" AS "stackedAssets_isReadOnly",
"stackedAssets"."isOffline" AS "stackedAssets_isOffline", "stackedAssets"."isOffline" AS "stackedAssets_isOffline",
"stackedAssets"."checksum" AS "stackedAssets_checksum", "stackedAssets"."checksum" AS "stackedAssets_checksum",
"stackedAssets"."duration" AS "stackedAssets_duration", "stackedAssets"."duration" AS "stackedAssets_duration",
@ -123,7 +121,6 @@ SELECT
"asset"."isFavorite" AS "asset_isFavorite", "asset"."isFavorite" AS "asset_isFavorite",
"asset"."isArchived" AS "asset_isArchived", "asset"."isArchived" AS "asset_isArchived",
"asset"."isExternal" AS "asset_isExternal", "asset"."isExternal" AS "asset_isExternal",
"asset"."isReadOnly" AS "asset_isReadOnly",
"asset"."isOffline" AS "asset_isOffline", "asset"."isOffline" AS "asset_isOffline",
"asset"."checksum" AS "asset_checksum", "asset"."checksum" AS "asset_checksum",
"asset"."duration" AS "asset_duration", "asset"."duration" AS "asset_duration",
@ -154,7 +151,6 @@ SELECT
"stackedAssets"."isFavorite" AS "stackedAssets_isFavorite", "stackedAssets"."isFavorite" AS "stackedAssets_isFavorite",
"stackedAssets"."isArchived" AS "stackedAssets_isArchived", "stackedAssets"."isArchived" AS "stackedAssets_isArchived",
"stackedAssets"."isExternal" AS "stackedAssets_isExternal", "stackedAssets"."isExternal" AS "stackedAssets_isExternal",
"stackedAssets"."isReadOnly" AS "stackedAssets_isReadOnly",
"stackedAssets"."isOffline" AS "stackedAssets_isOffline", "stackedAssets"."isOffline" AS "stackedAssets_isOffline",
"stackedAssets"."checksum" AS "stackedAssets_checksum", "stackedAssets"."checksum" AS "stackedAssets_checksum",
"stackedAssets"."duration" AS "stackedAssets_duration", "stackedAssets"."duration" AS "stackedAssets_duration",
@ -333,7 +329,6 @@ SELECT
"asset"."isFavorite" AS "asset_isFavorite", "asset"."isFavorite" AS "asset_isFavorite",
"asset"."isArchived" AS "asset_isArchived", "asset"."isArchived" AS "asset_isArchived",
"asset"."isExternal" AS "asset_isExternal", "asset"."isExternal" AS "asset_isExternal",
"asset"."isReadOnly" AS "asset_isReadOnly",
"asset"."isOffline" AS "asset_isOffline", "asset"."isOffline" AS "asset_isOffline",
"asset"."checksum" AS "asset_checksum", "asset"."checksum" AS "asset_checksum",
"asset"."duration" AS "asset_duration", "asset"."duration" AS "asset_duration",

View File

@ -41,7 +41,6 @@ FROM
"SharedLinkEntity__SharedLinkEntity_assets"."isFavorite" AS "SharedLinkEntity__SharedLinkEntity_assets_isFavorite", "SharedLinkEntity__SharedLinkEntity_assets"."isFavorite" AS "SharedLinkEntity__SharedLinkEntity_assets_isFavorite",
"SharedLinkEntity__SharedLinkEntity_assets"."isArchived" AS "SharedLinkEntity__SharedLinkEntity_assets_isArchived", "SharedLinkEntity__SharedLinkEntity_assets"."isArchived" AS "SharedLinkEntity__SharedLinkEntity_assets_isArchived",
"SharedLinkEntity__SharedLinkEntity_assets"."isExternal" AS "SharedLinkEntity__SharedLinkEntity_assets_isExternal", "SharedLinkEntity__SharedLinkEntity_assets"."isExternal" AS "SharedLinkEntity__SharedLinkEntity_assets_isExternal",
"SharedLinkEntity__SharedLinkEntity_assets"."isReadOnly" AS "SharedLinkEntity__SharedLinkEntity_assets_isReadOnly",
"SharedLinkEntity__SharedLinkEntity_assets"."isOffline" AS "SharedLinkEntity__SharedLinkEntity_assets_isOffline", "SharedLinkEntity__SharedLinkEntity_assets"."isOffline" AS "SharedLinkEntity__SharedLinkEntity_assets_isOffline",
"SharedLinkEntity__SharedLinkEntity_assets"."checksum" AS "SharedLinkEntity__SharedLinkEntity_assets_checksum", "SharedLinkEntity__SharedLinkEntity_assets"."checksum" AS "SharedLinkEntity__SharedLinkEntity_assets_checksum",
"SharedLinkEntity__SharedLinkEntity_assets"."duration" AS "SharedLinkEntity__SharedLinkEntity_assets_duration", "SharedLinkEntity__SharedLinkEntity_assets"."duration" AS "SharedLinkEntity__SharedLinkEntity_assets_duration",
@ -108,7 +107,6 @@ FROM
"4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6"."isFavorite" AS "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6_isFavorite", "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6"."isFavorite" AS "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6_isFavorite",
"4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6"."isArchived" AS "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6_isArchived", "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6"."isArchived" AS "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6_isArchived",
"4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6"."isExternal" AS "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6_isExternal", "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6"."isExternal" AS "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6_isExternal",
"4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6"."isReadOnly" AS "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6_isReadOnly",
"4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6"."isOffline" AS "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6_isOffline", "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6"."isOffline" AS "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6_isOffline",
"4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6"."checksum" AS "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6_checksum", "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6"."checksum" AS "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6_checksum",
"4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6"."duration" AS "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6_duration", "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6"."duration" AS "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6_duration",
@ -231,7 +229,6 @@ SELECT
"SharedLinkEntity__SharedLinkEntity_assets"."isFavorite" AS "SharedLinkEntity__SharedLinkEntity_assets_isFavorite", "SharedLinkEntity__SharedLinkEntity_assets"."isFavorite" AS "SharedLinkEntity__SharedLinkEntity_assets_isFavorite",
"SharedLinkEntity__SharedLinkEntity_assets"."isArchived" AS "SharedLinkEntity__SharedLinkEntity_assets_isArchived", "SharedLinkEntity__SharedLinkEntity_assets"."isArchived" AS "SharedLinkEntity__SharedLinkEntity_assets_isArchived",
"SharedLinkEntity__SharedLinkEntity_assets"."isExternal" AS "SharedLinkEntity__SharedLinkEntity_assets_isExternal", "SharedLinkEntity__SharedLinkEntity_assets"."isExternal" AS "SharedLinkEntity__SharedLinkEntity_assets_isExternal",
"SharedLinkEntity__SharedLinkEntity_assets"."isReadOnly" AS "SharedLinkEntity__SharedLinkEntity_assets_isReadOnly",
"SharedLinkEntity__SharedLinkEntity_assets"."isOffline" AS "SharedLinkEntity__SharedLinkEntity_assets_isOffline", "SharedLinkEntity__SharedLinkEntity_assets"."isOffline" AS "SharedLinkEntity__SharedLinkEntity_assets_isOffline",
"SharedLinkEntity__SharedLinkEntity_assets"."checksum" AS "SharedLinkEntity__SharedLinkEntity_assets_checksum", "SharedLinkEntity__SharedLinkEntity_assets"."checksum" AS "SharedLinkEntity__SharedLinkEntity_assets_checksum",
"SharedLinkEntity__SharedLinkEntity_assets"."duration" AS "SharedLinkEntity__SharedLinkEntity_assets_duration", "SharedLinkEntity__SharedLinkEntity_assets"."duration" AS "SharedLinkEntity__SharedLinkEntity_assets_duration",

View File

@ -151,6 +151,14 @@ GROUP BY
ORDER BY ORDER BY
"users"."createdAt" ASC "users"."createdAt" ASC
-- UserRepository.updateUsage
UPDATE "users"
SET
"quotaUsageInBytes" = "quotaUsageInBytes" + 50,
"updatedAt" = CURRENT_TIMESTAMP
WHERE
"id" = $1
-- UserRepository.syncUsage -- UserRepository.syncUsage
UPDATE "users" UPDATE "users"
SET SET

View File

@ -253,7 +253,7 @@ export class AssetRepository implements IAssetRepository {
@Chunked() @Chunked()
async softDeleteAll(ids: string[]): Promise<void> { async softDeleteAll(ids: string[]): Promise<void> {
await this.repository.softDelete({ id: In(ids), isExternal: false }); await this.repository.softDelete({ id: In(ids) });
} }
@Chunked() @Chunked()

View File

@ -112,6 +112,7 @@ export class UserRepository implements IUserRepository {
return stats; return stats;
} }
@GenerateSql({ params: [DummyValue.UUID, DummyValue.NUMBER] })
async updateUsage(id: string, delta: number): Promise<void> { async updateUsage(id: string, delta: number): Promise<void> {
await this.userRepository.increment({ id }, 'quotaUsageInBytes', delta); await this.userRepository.increment({ id }, 'quotaUsageInBytes', delta);
} }

View File

@ -295,7 +295,6 @@ export class AssetServiceV1 {
livePhotoVideo: livePhotoAssetId === null ? null : ({ id: livePhotoAssetId } as AssetEntity), livePhotoVideo: livePhotoAssetId === null ? null : ({ id: livePhotoAssetId } as AssetEntity),
originalFileName: file.originalName, originalFileName: file.originalName,
sidecarPath: sidecarPath || null, sidecarPath: sidecarPath || null,
isReadOnly: dto.isReadOnly ?? false,
isOffline: dto.isOffline ?? false, isOffline: dto.isOffline ?? false,
}); });

View File

@ -685,61 +685,6 @@ describe(AssetService.name, () => {
}); });
}); });
it('should only delete generated files for readonly assets', async () => {
assetMock.getById.mockResolvedValue(assetStub.readOnly);
await sut.handleAssetDeletion({ id: assetStub.readOnly.id });
expect(jobMock.queue.mock.calls).toEqual([
[
{
name: JobName.DELETE_FILES,
data: {
files: [
assetStub.readOnly.thumbnailPath,
assetStub.readOnly.previewPath,
assetStub.readOnly.encodedVideoPath,
],
},
},
],
]);
expect(assetMock.remove).toHaveBeenCalledWith(assetStub.readOnly);
});
it('should not process assets from external library without fromExternal flag', async () => {
assetMock.getById.mockResolvedValue(assetStub.external);
await sut.handleAssetDeletion({ id: assetStub.external.id });
expect(jobMock.queue).not.toHaveBeenCalled();
expect(jobMock.queueAll).not.toHaveBeenCalled();
expect(assetMock.remove).not.toHaveBeenCalled();
});
it('should process assets from external library with fromExternal flag', async () => {
assetMock.getById.mockResolvedValue(assetStub.external);
await sut.handleAssetDeletion({ id: assetStub.external.id, fromExternal: true });
expect(assetMock.remove).toHaveBeenCalledWith(assetStub.external);
expect(jobMock.queue.mock.calls).toEqual([
[
{
name: JobName.DELETE_FILES,
data: {
files: [
assetStub.external.thumbnailPath,
assetStub.external.previewPath,
assetStub.external.encodedVideoPath,
],
},
},
],
]);
});
it('should delete a live photo', async () => { it('should delete a live photo', async () => {
assetMock.getById.mockResolvedValue(assetStub.livePhotoStillAsset); assetMock.getById.mockResolvedValue(assetStub.livePhotoStillAsset);

View File

@ -33,7 +33,7 @@ import { IAssetStackRepository } from 'src/interfaces/asset-stack.interface';
import { IAssetRepository } from 'src/interfaces/asset.interface'; import { IAssetRepository } from 'src/interfaces/asset.interface';
import { ClientEvent, IEventRepository } from 'src/interfaces/event.interface'; import { ClientEvent, IEventRepository } from 'src/interfaces/event.interface';
import { import {
IAssetDeletionJob, IEntityJob,
IJobRepository, IJobRepository,
ISidecarWriteJob, ISidecarWriteJob,
JOBS_ASSET_PAGINATION_SIZE, JOBS_ASSET_PAGINATION_SIZE,
@ -371,8 +371,8 @@ export class AssetService {
return JobStatus.SUCCESS; return JobStatus.SUCCESS;
} }
async handleAssetDeletion(job: IAssetDeletionJob): Promise<JobStatus> { async handleAssetDeletion(job: IEntityJob): Promise<JobStatus> {
const { id, fromExternal } = job; const { id } = job;
const asset = await this.assetRepository.getById(id, { const asset = await this.assetRepository.getById(id, {
faces: { faces: {
@ -387,11 +387,6 @@ export class AssetService {
return JobStatus.FAILED; return JobStatus.FAILED;
} }
// Ignore requests that are not from external library job but is for an external asset
if (!fromExternal && (!asset.library || asset.library.type === LibraryType.EXTERNAL)) {
return JobStatus.SKIPPED;
}
// Replace the parent of the stack children with a new asset // Replace the parent of the stack children with a new asset
if (asset.stack?.primaryAssetId === id) { if (asset.stack?.primaryAssetId === id) {
const stackAssetIds = asset.stack.assets.map((a) => a.id); const stackAssetIds = asset.stack.assets.map((a) => a.id);
@ -414,18 +409,15 @@ export class AssetService {
// TODO refactor this to use cascades // TODO refactor this to use cascades
if (asset.livePhotoVideoId) { if (asset.livePhotoVideoId) {
await this.jobRepository.queue({ await this.jobRepository.queue({ name: JobName.ASSET_DELETION, data: { id: asset.livePhotoVideoId } });
name: JobName.ASSET_DELETION,
data: { id: asset.livePhotoVideoId, fromExternal },
});
} }
const files = [asset.thumbnailPath, asset.previewPath, asset.encodedVideoPath]; await this.jobRepository.queue({
if (!(asset.isExternal || asset.isReadOnly)) { name: JobName.DELETE_FILES,
files.push(asset.sidecarPath, asset.originalPath); data: {
} files: [asset.thumbnailPath, asset.previewPath, asset.encodedVideoPath, asset.sidecarPath, asset.originalPath],
},
await this.jobRepository.queue({ name: JobName.DELETE_FILES, data: { files } }); });
return JobStatus.SUCCESS; return JobStatus.SUCCESS;
} }

View File

@ -368,7 +368,6 @@ describe(LibraryService.name, () => {
type: AssetType.IMAGE, type: AssetType.IMAGE,
originalFileName: 'photo.jpg', originalFileName: 'photo.jpg',
sidecarPath: null, sidecarPath: null,
isReadOnly: true,
isExternal: true, isExternal: true,
}, },
], ],
@ -416,7 +415,6 @@ describe(LibraryService.name, () => {
type: AssetType.IMAGE, type: AssetType.IMAGE,
originalFileName: 'photo.jpg', originalFileName: 'photo.jpg',
sidecarPath: '/data/user1/photo.jpg.xmp', sidecarPath: '/data/user1/photo.jpg.xmp',
isReadOnly: true,
isExternal: true, isExternal: true,
}, },
], ],
@ -463,7 +461,6 @@ describe(LibraryService.name, () => {
type: AssetType.VIDEO, type: AssetType.VIDEO,
originalFileName: 'video.mp4', originalFileName: 'video.mp4',
sidecarPath: null, sidecarPath: null,
isReadOnly: true,
isExternal: true, isExternal: true,
}, },
], ],
@ -1458,10 +1455,7 @@ describe(LibraryService.name, () => {
await expect(sut.handleOfflineRemoval({ id: libraryStub.externalLibrary1.id })).resolves.toBe(JobStatus.SUCCESS); await expect(sut.handleOfflineRemoval({ id: libraryStub.externalLibrary1.id })).resolves.toBe(JobStatus.SUCCESS);
expect(jobMock.queueAll).toHaveBeenCalledWith([ expect(jobMock.queueAll).toHaveBeenCalledWith([
{ { name: JobName.ASSET_DELETION, data: { id: assetStub.image1.id } },
name: JobName.ASSET_DELETION,
data: { id: assetStub.image1.id, fromExternal: true },
},
]); ]);
}); });
}); });

View File

@ -387,7 +387,7 @@ export class LibraryService {
const assetIds = await this.repository.getAssetIds(job.id, true); const assetIds = await this.repository.getAssetIds(job.id, true);
this.logger.debug(`Will delete ${assetIds.length} asset(s) in library ${job.id}`); this.logger.debug(`Will delete ${assetIds.length} asset(s) in library ${job.id}`);
await this.jobRepository.queueAll( await this.jobRepository.queueAll(
assetIds.map((assetId) => ({ name: JobName.ASSET_DELETION, data: { id: assetId, fromExternal: true } })), assetIds.map((assetId) => ({ name: JobName.ASSET_DELETION, data: { id: assetId } })),
); );
if (assetIds.length === 0) { if (assetIds.length === 0) {
@ -503,7 +503,6 @@ export class LibraryService {
type: assetType, type: assetType,
originalFileName, originalFileName,
sidecarPath, sidecarPath,
isReadOnly: true,
isExternal: true, isExternal: true,
}); });
assetId = addedAsset.id; assetId = addedAsset.id;
@ -580,7 +579,7 @@ export class LibraryService {
for await (const assets of assetPagination) { for await (const assets of assetPagination) {
this.logger.debug(`Removing ${assets.length} offline assets`); this.logger.debug(`Removing ${assets.length} offline assets`);
await this.jobRepository.queueAll( await this.jobRepository.queueAll(
assets.map((asset) => ({ name: JobName.ASSET_DELETION, data: { id: asset.id, fromExternal: true } })), assets.map((asset) => ({ name: JobName.ASSET_DELETION, data: { id: asset.id } })),
); );
} }

View File

@ -440,7 +440,6 @@ export class MetadataService {
originalPath: motionPath, originalPath: motionPath,
originalFileName: asset.originalFileName, originalFileName: asset.originalFileName,
isVisible: false, isVisible: false,
isReadOnly: false,
deviceAssetId: 'NONE', deviceAssetId: 'NONE',
deviceId: 'NONE', deviceId: 'NONE',
}); });

View File

@ -558,26 +558,5 @@ describe(StorageTemplateService.name, () => {
); );
expect(assetMock.update).not.toHaveBeenCalled(); expect(assetMock.update).not.toHaveBeenCalled();
}); });
it('should not move read-only asset', async () => {
assetMock.getAll.mockResolvedValue({
items: [
{
...assetStub.image,
originalPath: 'upload/library/user-id/2023/2023-02-23/asset-id+1.jpg',
isReadOnly: true,
},
],
hasNextPage: false,
});
userMock.getList.mockResolvedValue([userStub.user1]);
await sut.handleMigration();
expect(assetMock.getAll).toHaveBeenCalled();
expect(storageMock.rename).not.toHaveBeenCalled();
expect(storageMock.copyFile).not.toHaveBeenCalled();
expect(assetMock.update).not.toHaveBeenCalled();
});
}); });
}); });

View File

@ -170,7 +170,7 @@ export class StorageTemplateService {
} }
async moveAsset(asset: AssetEntity, metadata: MoveAssetMetadata) { async moveAsset(asset: AssetEntity, metadata: MoveAssetMetadata) {
if (asset.isReadOnly || asset.isExternal || StorageCore.isAndroidMotionPath(asset.originalPath)) { if (asset.isExternal || StorageCore.isAndroidMotionPath(asset.originalPath)) {
// External assets are not affected by storage template // External assets are not affected by storage template
// TODO: shouldn't this only apply to external assets? // TODO: shouldn't this only apply to external assets?
return; return;

View File

@ -67,7 +67,7 @@ export function searchAssetBuilder(
}); });
} }
const status = _.pick(options, ['isExternal', 'isFavorite', 'isOffline', 'isReadOnly', 'isVisible', 'type']); const status = _.pick(options, ['isFavorite', 'isOffline', 'isVisible', 'type']);
const { const {
isArchived, isArchived,
isEncoded, isEncoded,

View File

@ -45,7 +45,6 @@ export const assetStub = {
sharedLinks: [], sharedLinks: [],
faces: [], faces: [],
sidecarPath: null, sidecarPath: null,
isReadOnly: false,
deletedAt: null, deletedAt: null,
isOffline: false, isOffline: false,
isExternal: false, isExternal: false,
@ -82,7 +81,6 @@ export const assetStub = {
originalFileName: 'IMG_456.jpg', originalFileName: 'IMG_456.jpg',
faces: [], faces: [],
sidecarPath: null, sidecarPath: null,
isReadOnly: false,
isOffline: false, isOffline: false,
isExternal: false, isExternal: false,
libraryId: 'library-id', libraryId: 'library-id',
@ -113,7 +111,6 @@ export const assetStub = {
localDateTime: new Date('2023-02-23T05:06:29.716Z'), localDateTime: new Date('2023-02-23T05:06:29.716Z'),
isFavorite: true, isFavorite: true,
isArchived: false, isArchived: false,
isReadOnly: false,
isOffline: false, isOffline: false,
libraryId: 'library-id', libraryId: 'library-id',
library: libraryStub.uploadLibrary1, library: libraryStub.uploadLibrary1,
@ -150,7 +147,6 @@ export const assetStub = {
localDateTime: new Date('2023-02-23T05:06:29.716Z'), localDateTime: new Date('2023-02-23T05:06:29.716Z'),
isFavorite: true, isFavorite: true,
isArchived: false, isArchived: false,
isReadOnly: false,
duration: null, duration: null,
isVisible: true, isVisible: true,
isExternal: false, isExternal: false,
@ -195,7 +191,6 @@ export const assetStub = {
localDateTime: new Date('2023-02-23T05:06:29.716Z'), localDateTime: new Date('2023-02-23T05:06:29.716Z'),
isFavorite: true, isFavorite: true,
isArchived: false, isArchived: false,
isReadOnly: false,
duration: null, duration: null,
isVisible: true, isVisible: true,
isExternal: false, isExternal: false,
@ -235,7 +230,6 @@ export const assetStub = {
localDateTime: new Date('2023-02-23T05:06:29.716Z'), localDateTime: new Date('2023-02-23T05:06:29.716Z'),
isFavorite: true, isFavorite: true,
isArchived: false, isArchived: false,
isReadOnly: false,
isExternal: true, isExternal: true,
duration: null, duration: null,
isVisible: true, isVisible: true,
@ -275,7 +269,6 @@ export const assetStub = {
localDateTime: new Date('2023-02-23T05:06:29.716Z'), localDateTime: new Date('2023-02-23T05:06:29.716Z'),
isFavorite: true, isFavorite: true,
isArchived: false, isArchived: false,
isReadOnly: false,
isExternal: false, isExternal: false,
duration: null, duration: null,
isVisible: true, isVisible: true,
@ -315,7 +308,6 @@ export const assetStub = {
localDateTime: new Date('2023-02-23T05:06:29.716Z'), localDateTime: new Date('2023-02-23T05:06:29.716Z'),
isFavorite: true, isFavorite: true,
isArchived: false, isArchived: false,
isReadOnly: false,
isExternal: true, isExternal: true,
duration: null, duration: null,
isVisible: true, isVisible: true,
@ -356,7 +348,6 @@ export const assetStub = {
localDateTime: new Date('2023-02-23T05:06:29.716Z'), localDateTime: new Date('2023-02-23T05:06:29.716Z'),
isFavorite: true, isFavorite: true,
isArchived: false, isArchived: false,
isReadOnly: false,
duration: null, duration: null,
isVisible: true, isVisible: true,
livePhotoVideo: null, livePhotoVideo: null,
@ -396,7 +387,6 @@ export const assetStub = {
isFavorite: true, isFavorite: true,
isArchived: false, isArchived: false,
isExternal: false, isExternal: false,
isReadOnly: false,
isOffline: false, isOffline: false,
libraryId: 'library-id', libraryId: 'library-id',
library: libraryStub.uploadLibrary1, library: libraryStub.uploadLibrary1,
@ -436,7 +426,6 @@ export const assetStub = {
localDateTime: new Date('2023-02-23T05:06:29.716Z'), localDateTime: new Date('2023-02-23T05:06:29.716Z'),
isFavorite: true, isFavorite: true,
isArchived: false, isArchived: false,
isReadOnly: false,
isExternal: false, isExternal: false,
isOffline: false, isOffline: false,
libraryId: 'library-id', libraryId: 'library-id',
@ -527,7 +516,6 @@ export const assetStub = {
localDateTime: new Date('2023-02-22T05:06:29.716Z'), localDateTime: new Date('2023-02-22T05:06:29.716Z'),
isFavorite: false, isFavorite: false,
isArchived: false, isArchived: false,
isReadOnly: false,
isExternal: false, isExternal: false,
isOffline: false, isOffline: false,
libraryId: 'library-id', libraryId: 'library-id',
@ -570,7 +558,6 @@ export const assetStub = {
localDateTime: new Date('2023-02-23T05:06:29.716Z'), localDateTime: new Date('2023-02-23T05:06:29.716Z'),
isFavorite: true, isFavorite: true,
isArchived: false, isArchived: false,
isReadOnly: false,
isExternal: false, isExternal: false,
isOffline: false, isOffline: false,
libraryId: 'library-id', libraryId: 'library-id',
@ -606,7 +593,6 @@ export const assetStub = {
localDateTime: new Date('2023-02-23T05:06:29.716Z'), localDateTime: new Date('2023-02-23T05:06:29.716Z'),
isFavorite: true, isFavorite: true,
isArchived: false, isArchived: false,
isReadOnly: false,
isExternal: false, isExternal: false,
isOffline: false, isOffline: false,
libraryId: 'library-id', libraryId: 'library-id',
@ -643,7 +629,6 @@ export const assetStub = {
localDateTime: new Date('2023-02-23T05:06:29.716Z'), localDateTime: new Date('2023-02-23T05:06:29.716Z'),
isFavorite: true, isFavorite: true,
isArchived: false, isArchived: false,
isReadOnly: true,
isExternal: false, isExternal: false,
isOffline: false, isOffline: false,
libraryId: 'library-id', libraryId: 'library-id',
@ -681,7 +666,6 @@ export const assetStub = {
localDateTime: new Date('2023-02-23T05:06:29.716Z'), localDateTime: new Date('2023-02-23T05:06:29.716Z'),
isFavorite: true, isFavorite: true,
isArchived: false, isArchived: false,
isReadOnly: false,
isExternal: false, isExternal: false,
isOffline: false, isOffline: false,
libraryId: 'library-id', libraryId: 'library-id',
@ -719,7 +703,6 @@ export const assetStub = {
localDateTime: new Date('2023-02-23T05:06:29.716Z'), localDateTime: new Date('2023-02-23T05:06:29.716Z'),
isFavorite: true, isFavorite: true,
isArchived: false, isArchived: false,
isReadOnly: false,
isExternal: true, isExternal: true,
duration: null, duration: null,
isVisible: true, isVisible: true,
@ -758,7 +741,6 @@ export const assetStub = {
localDateTime: new Date('2023-02-23T05:06:29.716Z'), localDateTime: new Date('2023-02-23T05:06:29.716Z'),
isFavorite: true, isFavorite: true,
isArchived: false, isArchived: false,
isReadOnly: false,
isExternal: true, isExternal: true,
duration: null, duration: null,
isVisible: true, isVisible: true,
@ -797,7 +779,6 @@ export const assetStub = {
localDateTime: new Date('2023-02-23T05:06:29.716Z'), localDateTime: new Date('2023-02-23T05:06:29.716Z'),
isFavorite: true, isFavorite: true,
isArchived: false, isArchived: false,
isReadOnly: false,
duration: null, duration: null,
isVisible: true, isVisible: true,
isExternal: false, isExternal: false,

View File

@ -59,7 +59,6 @@ const assetResponse: AssetResponseDto = {
thumbhash: null, thumbhash: null,
fileModifiedAt: today, fileModifiedAt: today,
isExternal: false, isExternal: false,
isReadOnly: false,
isOffline: false, isOffline: false,
fileCreatedAt: today, fileCreatedAt: today,
localDateTime: today, localDateTime: today,
@ -210,7 +209,6 @@ export const sharedLinkStub = {
isFavorite: false, isFavorite: false,
isArchived: false, isArchived: false,
isExternal: false, isExternal: false,
isReadOnly: false,
isOffline: false, isOffline: false,
libraryId: 'library-id', libraryId: 'library-id',
library: libraryStub.uploadLibrary1, library: libraryStub.uploadLibrary1,

View File

@ -193,9 +193,7 @@
{/if} {/if}
{#if isOwner} {#if isOwner}
{#if !asset.isReadOnly || !asset.isExternal} <CircleIconButton color="opaque" icon={mdiDeleteOutline} on:click={() => dispatch('delete')} title="Delete" />
<CircleIconButton color="opaque" icon={mdiDeleteOutline} on:click={() => dispatch('delete')} title="Delete" />
{/if}
<div <div
use:clickOutside={{ use:clickOutside={{
onOutclick: () => (isShowAssetOptions = false), onOutclick: () => (isShowAssetOptions = false),

View File

@ -308,23 +308,15 @@
{/if} {/if}
<div class="px-4 py-4"> <div class="px-4 py-4">
{#if !asset.exifInfo && !asset.isExternal} {#if asset.exifInfo}
<p class="text-sm">NO EXIF INFO AVAILABLE</p>
{:else if !asset.exifInfo && asset.isExternal}
<div class="flex gap-4 py-4">
<div>
<p class="break-all">
Metadata not loaded for {asset.originalPath}
</p>
</div>
</div>
{:else}
<div class="flex h-10 w-full items-center justify-between text-sm"> <div class="flex h-10 w-full items-center justify-between text-sm">
<h2>DETAILS</h2> <h2>DETAILS</h2>
</div> </div>
{:else}
<p class="text-sm">NO EXIF INFO AVAILABLE</p>
{/if} {/if}
{#if asset.exifInfo?.dateTimeOriginal && !asset.isReadOnly} {#if asset.exifInfo?.dateTimeOriginal}
{@const assetDateTimeOriginal = DateTime.fromISO(asset.exifInfo.dateTimeOriginal, { {@const assetDateTimeOriginal = DateTime.fromISO(asset.exifInfo.dateTimeOriginal, {
zone: asset.exifInfo.timeZone ?? undefined, zone: asset.exifInfo.timeZone ?? undefined,
})} })}
@ -374,7 +366,7 @@
</div> </div>
{/if} {/if}
</button> </button>
{:else if !asset.exifInfo?.dateTimeOriginal && !asset.isReadOnly && isOwner} {:else if !asset.exifInfo?.dateTimeOriginal && isOwner}
<div class="flex justify-between place-items-start gap-4 py-4"> <div class="flex justify-between place-items-start gap-4 py-4">
<div class="flex gap-4"> <div class="flex gap-4">
<div> <div>
@ -385,43 +377,6 @@
<Icon path={mdiPencil} size="20" /> <Icon path={mdiPencil} size="20" />
</div> </div>
</div> </div>
{:else if asset.exifInfo?.dateTimeOriginal && asset.isReadOnly}
{@const assetDateTimeOriginal = DateTime.fromISO(asset.exifInfo.dateTimeOriginal, {
zone: asset.exifInfo.timeZone ?? undefined,
})}
<div class="flex justify-between place-items-start gap-4 py-4">
<div class="flex gap-4">
<div>
<Icon path={mdiCalendar} size="24" />
</div>
<div>
<p>
{assetDateTimeOriginal.toLocaleString(
{
month: 'short',
day: 'numeric',
year: 'numeric',
},
{ locale: $locale },
)}
</p>
<div class="flex gap-2 text-sm">
<p>
{assetDateTimeOriginal.toLocaleString(
{
weekday: 'short',
hour: 'numeric',
minute: '2-digit',
timeZoneName: 'longOffset',
},
{ locale: $locale },
)}
</p>
</div>
</div>
</div>
</div>
{/if} {/if}
{#if isShowChangeDate} {#if isShowChangeDate}
@ -501,7 +456,7 @@
</div> </div>
{/if} {/if}
{#if asset.exifInfo?.city && !asset.isReadOnly} {#if asset.exifInfo?.city}
<button <button
type="button" type="button"
class="flex w-full text-left justify-between place-items-start gap-4 py-4" class="flex w-full text-left justify-between place-items-start gap-4 py-4"
@ -534,7 +489,7 @@
</div> </div>
{/if} {/if}
</button> </button>
{:else if !asset.exifInfo?.city && !asset.isReadOnly && isOwner} {:else if !asset.exifInfo?.city && isOwner}
<button <button
type="button" type="button"
class="flex w-full text-left justify-between place-items-start gap-4 py-4 rounded-lg hover:dark:text-immich-dark-primary hover:text-immich-primary" class="flex w-full text-left justify-between place-items-start gap-4 py-4 rounded-lg hover:dark:text-immich-dark-primary hover:text-immich-primary"
@ -552,26 +507,6 @@
<Icon path={mdiPencil} size="20" /> <Icon path={mdiPencil} size="20" />
</div> </div>
</button> </button>
{:else if asset.exifInfo?.city && asset.isReadOnly}
<div class="flex justify-between place-items-start gap-4 py-4">
<div class="flex gap-4">
<div><Icon path={mdiMapMarkerOutline} size="24" /></div>
<div>
<p>{asset.exifInfo.city}</p>
{#if asset.exifInfo?.state}
<div class="flex gap-2 text-sm">
<p>{asset.exifInfo.state}</p>
</div>
{/if}
{#if asset.exifInfo?.country}
<div class="flex gap-2 text-sm">
<p>{asset.exifInfo.country}</p>
</div>
{/if}
</div>
</div>
</div>
{/if} {/if}
{#if isShowChangeLocation} {#if isShowChangeLocation}
<ChangeLocation <ChangeLocation

View File

@ -34,7 +34,7 @@
const handleDelete = async () => { const handleDelete = async () => {
loading = true; loading = true;
const ids = [...getOwnedAssets()].filter((a) => !a.isExternal).map((a) => a.id); const ids = [...getOwnedAssets()].map((a) => a.id);
await deleteAssets(force, onAssetDelete, ids); await deleteAssets(force, onAssetDelete, ids);
clearSelect(); clearSelect();
isShowConfirmation = false; isShowConfirmation = false;

View File

@ -47,7 +47,7 @@
$: timelineY = element?.scrollTop || 0; $: timelineY = element?.scrollTop || 0;
$: isEmpty = $assetStore.initialized && $assetStore.buckets.length === 0; $: isEmpty = $assetStore.initialized && $assetStore.buckets.length === 0;
$: idsSelectedAssets = [...$selectedAssets].filter((a) => !a.isExternal).map((a) => a.id); $: idsSelectedAssets = [...$selectedAssets].map(({ id }) => id);
$: { $: {
void assetStore.updateViewport(viewport); void assetStore.updateViewport(viewport);

View File

@ -258,9 +258,9 @@ export const getAssetType = (type: AssetTypeEnum) => {
}; };
export const getSelectedAssets = (assets: Set<AssetResponseDto>, user: UserResponseDto | null): string[] => { export const getSelectedAssets = (assets: Set<AssetResponseDto>, user: UserResponseDto | null): string[] => {
const ids = [...assets].filter((a) => !a.isExternal && user && a.ownerId === user.id).map((a) => a.id); const ids = [...assets].filter((a) => user && a.ownerId === user.id).map((a) => a.id);
const numberOfIssues = [...assets].filter((a) => a.isExternal || (user && a.ownerId !== user.id)).length; const numberOfIssues = [...assets].filter((a) => user && a.ownerId !== user.id).length;
if (numberOfIssues > 0) { if (numberOfIssues > 0) {
notificationController.show({ notificationController.show({
message: `Can't change metadata of ${numberOfIssues} asset${numberOfIssues > 1 ? 's' : ''}`, message: `Can't change metadata of ${numberOfIssues} asset${numberOfIssues > 1 ? 's' : ''}`,

View File

@ -22,9 +22,7 @@ export const assetFactory = Sync.makeFactory<AssetResponseDto>({
isTrashed: Sync.each(() => faker.datatype.boolean()), isTrashed: Sync.each(() => faker.datatype.boolean()),
duration: '0:00:00.00000', duration: '0:00:00.00000',
checksum: Sync.each(() => faker.string.alphanumeric(28)), checksum: Sync.each(() => faker.string.alphanumeric(28)),
isExternal: Sync.each(() => faker.datatype.boolean()),
isOffline: Sync.each(() => faker.datatype.boolean()), isOffline: Sync.each(() => faker.datatype.boolean()),
isReadOnly: Sync.each(() => faker.datatype.boolean()),
hasMetadata: Sync.each(() => faker.datatype.boolean()), hasMetadata: Sync.each(() => faker.datatype.boolean()),
stackCount: null, stackCount: null,
}); });