mirror of
				https://github.com/immich-app/immich.git
				synced 2025-10-31 02:27:08 -04:00 
			
		
		
		
	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:
		
							parent
							
								
									d26ac431b8
								
							
						
					
					
						commit
						5b87abb021
					
				| @ -102,7 +102,6 @@ describe('/asset', () => { | ||||
|       utils.createAsset(user1.accessToken), | ||||
|       utils.createAsset(user1.accessToken, { | ||||
|         isFavorite: true, | ||||
|         isReadOnly: true, | ||||
|         fileCreatedAt: yesterday.toISO(), | ||||
|         fileModifiedAt: yesterday.toISO(), | ||||
|         assetData: { filename: 'example.mp4' }, | ||||
|  | ||||
| @ -24,12 +24,12 @@ describe('/search', () => { | ||||
|   // let assetRidge: AssetFileUploadResponseDto;
 | ||||
|   // let assetPolemonium: AssetFileUploadResponseDto;
 | ||||
|   // let assetWood: AssetFileUploadResponseDto;
 | ||||
|   // let assetGlarus: AssetFileUploadResponseDto;
 | ||||
|   let assetHeic: AssetFileUploadResponseDto; | ||||
|   let assetRocks: AssetFileUploadResponseDto; | ||||
|   let assetOneJpg6: AssetFileUploadResponseDto; | ||||
|   let assetOneHeic6: AssetFileUploadResponseDto; | ||||
|   let assetOneJpg5: AssetFileUploadResponseDto; | ||||
|   let assetGlarus: AssetFileUploadResponseDto; | ||||
|   let assetSprings: AssetFileUploadResponseDto; | ||||
|   let assetLast: AssetFileUploadResponseDto; | ||||
|   let cities: string[]; | ||||
| @ -52,11 +52,12 @@ describe('/search', () => { | ||||
|       { filename: '/formats/motionphoto/Samsung One UI 6.jpg' }, | ||||
|       { filename: '/formats/motionphoto/Samsung One UI 6.heic' }, | ||||
|       { 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 } }, | ||||
| 
 | ||||
|       // used for search suggestions
 | ||||
|       { filename: '/formats/png/density_plot.png' }, | ||||
|       { filename: '/formats/raw/Nikon/D80/glarus.nef' }, | ||||
|       { filename: '/formats/raw/Nikon/D700/philadelphia.nef' }, | ||||
|       { filename: '/albums/nature/orychophragmus_violaceus.jpg' }, | ||||
|       { filename: '/albums/nature/tanners_ridge.jpg' }, | ||||
| @ -93,9 +94,9 @@ describe('/search', () => { | ||||
|       { latitude: 23.133_02, longitude: -82.383_04 }, // havana
 | ||||
|       { latitude: 41.694_11, longitude: 44.833_68 }, // tbilisi
 | ||||
|       { 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: 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: 31.634_16, longitude: -7.999_94 }, // marrakesh
 | ||||
|       { latitude: 38.523_735_4, longitude: -78.488_619_4 }, // tanners ridge
 | ||||
| @ -123,9 +124,9 @@ describe('/search', () => { | ||||
|       assetOneJpg6, | ||||
|       assetOneHeic6, | ||||
|       assetOneJpg5, | ||||
|       assetGlarus, | ||||
|       assetSprings, | ||||
|       assetDensity, | ||||
|       // assetGlarus,
 | ||||
|       // assetPhiladelphia,
 | ||||
|       // assetOrychophragmus,
 | ||||
|       // assetRidge,
 | ||||
| @ -190,16 +191,7 @@ describe('/search', () => { | ||||
|         dto: { size: -1.5 }, | ||||
|         expected: ['size must not be less than 1', 'size must be an integer number'], | ||||
|       }, | ||||
|       ...[ | ||||
|         'isArchived', | ||||
|         'isFavorite', | ||||
|         'isReadOnly', | ||||
|         'isExternal', | ||||
|         'isEncoded', | ||||
|         'isMotion', | ||||
|         'isOffline', | ||||
|         'isVisible', | ||||
|       ].map((value) => ({ | ||||
|       ...['isArchived', 'isFavorite', 'isEncoded', 'isMotion', 'isOffline', 'isVisible'].map((value) => ({ | ||||
|         should: `should reject ${value} not a boolean`, | ||||
|         dto: { [value]: 'immich' }, | ||||
|         expected: [`${value} must be a boolean value`], | ||||
| @ -255,14 +247,6 @@ describe('/search', () => { | ||||
|         should: 'should search by isArchived (false)', | ||||
|         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)', | ||||
|         deferred: () => ({ dto: { size: 1, type: 'IMAGE' }, assets: [assetLast] }), | ||||
|  | ||||
| @ -34,7 +34,6 @@ describe('/timeline', () => { | ||||
|       utils.createAsset(user.accessToken), | ||||
|       utils.createAsset(user.accessToken, { | ||||
|         isFavorite: true, | ||||
|         isReadOnly: true, | ||||
|         fileCreatedAt: yesterday.toISO(), | ||||
|         fileModifiedAt: yesterday.toISO(), | ||||
|         assetData: { filename: 'example.mp4' }, | ||||
|  | ||||
| @ -32,7 +32,6 @@ class Asset { | ||||
|         isFavorite = remote.isFavorite, | ||||
|         isArchived = remote.isArchived, | ||||
|         isTrashed = remote.isTrashed, | ||||
|         isReadOnly = remote.isReadOnly, | ||||
|         isOffline = remote.isOffline, | ||||
|         // workaround to nullify stackParentId for the parent asset until we refactor the mobile app | ||||
|         // stack handling to properly handle it | ||||
| @ -55,7 +54,6 @@ class Asset { | ||||
|         isFavorite = local.isFavorite, | ||||
|         isArchived = false, | ||||
|         isTrashed = false, | ||||
|         isReadOnly = false, | ||||
|         isOffline = false, | ||||
|         stackCount = 0, | ||||
|         fileCreatedAt = local.createDateTime { | ||||
| @ -90,7 +88,6 @@ class Asset { | ||||
|     this.isTrashed = false, | ||||
|     this.stackParentId, | ||||
|     this.stackCount = 0, | ||||
|     this.isReadOnly = false, | ||||
|     this.isOffline = false, | ||||
|     this.thumbhash, | ||||
|   }); | ||||
| @ -161,8 +158,6 @@ class Asset { | ||||
| 
 | ||||
|   bool isTrashed; | ||||
| 
 | ||||
|   bool isReadOnly; | ||||
| 
 | ||||
|   bool isOffline; | ||||
| 
 | ||||
|   @ignore | ||||
| @ -278,7 +273,6 @@ class Asset { | ||||
|         isFavorite != a.isFavorite || | ||||
|         isArchived != a.isArchived || | ||||
|         isTrashed != a.isTrashed || | ||||
|         isReadOnly != a.isReadOnly || | ||||
|         isOffline != a.isOffline || | ||||
|         a.exifInfo?.latitude != exifInfo?.latitude || | ||||
|         a.exifInfo?.longitude != exifInfo?.longitude || | ||||
| @ -324,7 +318,6 @@ class Asset { | ||||
|           isFavorite: isFavorite, | ||||
|           isArchived: isArchived, | ||||
|           isTrashed: isTrashed, | ||||
|           isReadOnly: isReadOnly, | ||||
|           isOffline: isOffline, | ||||
|         ); | ||||
|       } | ||||
| @ -345,7 +338,6 @@ class Asset { | ||||
|           isFavorite: a.isFavorite, | ||||
|           isArchived: a.isArchived, | ||||
|           isTrashed: a.isTrashed, | ||||
|           isReadOnly: a.isReadOnly, | ||||
|           isOffline: a.isOffline, | ||||
|           exifInfo: a.exifInfo?.copyWith(id: id) ?? exifInfo, | ||||
|           thumbhash: a.thumbhash, | ||||
| @ -380,7 +372,6 @@ class Asset { | ||||
|     bool? isFavorite, | ||||
|     bool? isArchived, | ||||
|     bool? isTrashed, | ||||
|     bool? isReadOnly, | ||||
|     bool? isOffline, | ||||
|     ExifInfo? exifInfo, | ||||
|     String? stackParentId, | ||||
| @ -405,7 +396,6 @@ class Asset { | ||||
|         isFavorite: isFavorite ?? this.isFavorite, | ||||
|         isArchived: isArchived ?? this.isArchived, | ||||
|         isTrashed: isTrashed ?? this.isTrashed, | ||||
|         isReadOnly: isReadOnly ?? this.isReadOnly, | ||||
|         isOffline: isOffline ?? this.isOffline, | ||||
|         exifInfo: exifInfo ?? this.exifInfo, | ||||
|         stackParentId: stackParentId ?? this.stackParentId, | ||||
| @ -470,7 +460,6 @@ class Asset { | ||||
|   "height": ${height ?? "N/A"}, | ||||
|   "isArchived": $isArchived, | ||||
|   "isTrashed": $isTrashed, | ||||
|   "isReadOnly": $isReadOnly, | ||||
|   "isOffline": $isOffline, | ||||
| }"""; | ||||
|   } | ||||
|  | ||||
							
								
								
									
										135
									
								
								mobile/lib/entities/asset.entity.g.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										135
									
								
								mobile/lib/entities/asset.entity.g.dart
									
									
									
										generated
									
									
									
								
							| @ -62,64 +62,59 @@ const AssetSchema = CollectionSchema( | ||||
|       name: r'isOffline', | ||||
|       type: IsarType.bool, | ||||
|     ), | ||||
|     r'isReadOnly': PropertySchema( | ||||
|       id: 9, | ||||
|       name: r'isReadOnly', | ||||
|       type: IsarType.bool, | ||||
|     ), | ||||
|     r'isTrashed': PropertySchema( | ||||
|       id: 10, | ||||
|       id: 9, | ||||
|       name: r'isTrashed', | ||||
|       type: IsarType.bool, | ||||
|     ), | ||||
|     r'livePhotoVideoId': PropertySchema( | ||||
|       id: 11, | ||||
|       id: 10, | ||||
|       name: r'livePhotoVideoId', | ||||
|       type: IsarType.string, | ||||
|     ), | ||||
|     r'localId': PropertySchema( | ||||
|       id: 12, | ||||
|       id: 11, | ||||
|       name: r'localId', | ||||
|       type: IsarType.string, | ||||
|     ), | ||||
|     r'ownerId': PropertySchema( | ||||
|       id: 13, | ||||
|       id: 12, | ||||
|       name: r'ownerId', | ||||
|       type: IsarType.long, | ||||
|     ), | ||||
|     r'remoteId': PropertySchema( | ||||
|       id: 14, | ||||
|       id: 13, | ||||
|       name: r'remoteId', | ||||
|       type: IsarType.string, | ||||
|     ), | ||||
|     r'stackCount': PropertySchema( | ||||
|       id: 15, | ||||
|       id: 14, | ||||
|       name: r'stackCount', | ||||
|       type: IsarType.long, | ||||
|     ), | ||||
|     r'stackParentId': PropertySchema( | ||||
|       id: 16, | ||||
|       id: 15, | ||||
|       name: r'stackParentId', | ||||
|       type: IsarType.string, | ||||
|     ), | ||||
|     r'thumbhash': PropertySchema( | ||||
|       id: 17, | ||||
|       id: 16, | ||||
|       name: r'thumbhash', | ||||
|       type: IsarType.string, | ||||
|     ), | ||||
|     r'type': PropertySchema( | ||||
|       id: 18, | ||||
|       id: 17, | ||||
|       name: r'type', | ||||
|       type: IsarType.byte, | ||||
|       enumMap: _AssettypeEnumValueMap, | ||||
|     ), | ||||
|     r'updatedAt': PropertySchema( | ||||
|       id: 19, | ||||
|       id: 18, | ||||
|       name: r'updatedAt', | ||||
|       type: IsarType.dateTime, | ||||
|     ), | ||||
|     r'width': PropertySchema( | ||||
|       id: 20, | ||||
|       id: 19, | ||||
|       name: r'width', | ||||
|       type: IsarType.int, | ||||
|     ) | ||||
| @ -239,18 +234,17 @@ void _assetSerialize( | ||||
|   writer.writeBool(offsets[6], object.isArchived); | ||||
|   writer.writeBool(offsets[7], object.isFavorite); | ||||
|   writer.writeBool(offsets[8], object.isOffline); | ||||
|   writer.writeBool(offsets[9], object.isReadOnly); | ||||
|   writer.writeBool(offsets[10], object.isTrashed); | ||||
|   writer.writeString(offsets[11], object.livePhotoVideoId); | ||||
|   writer.writeString(offsets[12], object.localId); | ||||
|   writer.writeLong(offsets[13], object.ownerId); | ||||
|   writer.writeString(offsets[14], object.remoteId); | ||||
|   writer.writeLong(offsets[15], object.stackCount); | ||||
|   writer.writeString(offsets[16], object.stackParentId); | ||||
|   writer.writeString(offsets[17], object.thumbhash); | ||||
|   writer.writeByte(offsets[18], object.type.index); | ||||
|   writer.writeDateTime(offsets[19], object.updatedAt); | ||||
|   writer.writeInt(offsets[20], object.width); | ||||
|   writer.writeBool(offsets[9], object.isTrashed); | ||||
|   writer.writeString(offsets[10], object.livePhotoVideoId); | ||||
|   writer.writeString(offsets[11], object.localId); | ||||
|   writer.writeLong(offsets[12], object.ownerId); | ||||
|   writer.writeString(offsets[13], object.remoteId); | ||||
|   writer.writeLong(offsets[14], object.stackCount); | ||||
|   writer.writeString(offsets[15], object.stackParentId); | ||||
|   writer.writeString(offsets[16], object.thumbhash); | ||||
|   writer.writeByte(offsets[17], object.type.index); | ||||
|   writer.writeDateTime(offsets[18], object.updatedAt); | ||||
|   writer.writeInt(offsets[19], object.width); | ||||
| } | ||||
| 
 | ||||
| Asset _assetDeserialize( | ||||
| @ -270,19 +264,18 @@ Asset _assetDeserialize( | ||||
|     isArchived: reader.readBoolOrNull(offsets[6]) ?? false, | ||||
|     isFavorite: reader.readBoolOrNull(offsets[7]) ?? false, | ||||
|     isOffline: reader.readBoolOrNull(offsets[8]) ?? false, | ||||
|     isReadOnly: reader.readBoolOrNull(offsets[9]) ?? false, | ||||
|     isTrashed: reader.readBoolOrNull(offsets[10]) ?? false, | ||||
|     livePhotoVideoId: reader.readStringOrNull(offsets[11]), | ||||
|     localId: reader.readStringOrNull(offsets[12]), | ||||
|     ownerId: reader.readLong(offsets[13]), | ||||
|     remoteId: reader.readStringOrNull(offsets[14]), | ||||
|     stackCount: reader.readLongOrNull(offsets[15]), | ||||
|     stackParentId: reader.readStringOrNull(offsets[16]), | ||||
|     thumbhash: reader.readStringOrNull(offsets[17]), | ||||
|     type: _AssettypeValueEnumMap[reader.readByteOrNull(offsets[18])] ?? | ||||
|     isTrashed: reader.readBoolOrNull(offsets[9]) ?? false, | ||||
|     livePhotoVideoId: reader.readStringOrNull(offsets[10]), | ||||
|     localId: reader.readStringOrNull(offsets[11]), | ||||
|     ownerId: reader.readLong(offsets[12]), | ||||
|     remoteId: reader.readStringOrNull(offsets[13]), | ||||
|     stackCount: reader.readLongOrNull(offsets[14]), | ||||
|     stackParentId: reader.readStringOrNull(offsets[15]), | ||||
|     thumbhash: reader.readStringOrNull(offsets[16]), | ||||
|     type: _AssettypeValueEnumMap[reader.readByteOrNull(offsets[17])] ?? | ||||
|         AssetType.other, | ||||
|     updatedAt: reader.readDateTime(offsets[19]), | ||||
|     width: reader.readIntOrNull(offsets[20]), | ||||
|     updatedAt: reader.readDateTime(offsets[18]), | ||||
|     width: reader.readIntOrNull(offsets[19]), | ||||
|   ); | ||||
|   return object; | ||||
| } | ||||
| @ -315,27 +308,25 @@ P _assetDeserializeProp<P>( | ||||
|     case 9: | ||||
|       return (reader.readBoolOrNull(offset) ?? false) as P; | ||||
|     case 10: | ||||
|       return (reader.readBoolOrNull(offset) ?? false) as P; | ||||
|       return (reader.readStringOrNull(offset)) as P; | ||||
|     case 11: | ||||
|       return (reader.readStringOrNull(offset)) as P; | ||||
|     case 12: | ||||
|       return (reader.readStringOrNull(offset)) as P; | ||||
|     case 13: | ||||
|       return (reader.readLong(offset)) as P; | ||||
|     case 14: | ||||
|     case 13: | ||||
|       return (reader.readStringOrNull(offset)) as P; | ||||
|     case 15: | ||||
|     case 14: | ||||
|       return (reader.readLongOrNull(offset)) as P; | ||||
|     case 15: | ||||
|       return (reader.readStringOrNull(offset)) as P; | ||||
|     case 16: | ||||
|       return (reader.readStringOrNull(offset)) as P; | ||||
|     case 17: | ||||
|       return (reader.readStringOrNull(offset)) as P; | ||||
|     case 18: | ||||
|       return (_AssettypeValueEnumMap[reader.readByteOrNull(offset)] ?? | ||||
|           AssetType.other) as P; | ||||
|     case 19: | ||||
|     case 18: | ||||
|       return (reader.readDateTime(offset)) as P; | ||||
|     case 20: | ||||
|     case 19: | ||||
|       return (reader.readIntOrNull(offset)) as P; | ||||
|     default: | ||||
|       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( | ||||
|       bool value) { | ||||
|     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() { | ||||
|     return QueryBuilder.apply(this, (query) { | ||||
|       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() { | ||||
|     return QueryBuilder.apply(this, (query) { | ||||
|       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() { | ||||
|     return QueryBuilder.apply(this, (query) { | ||||
|       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() { | ||||
|     return QueryBuilder.apply(this, (query) { | ||||
|       return query.addPropertyName(r'isTrashed'); | ||||
|  | ||||
| @ -71,19 +71,6 @@ extension AssetListExtension on Iterable<Asset> { | ||||
|     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 | ||||
|   Iterable<Asset> nonOfflineOnly({ | ||||
|     void Function()? errorCallback, | ||||
|  | ||||
| @ -102,16 +102,6 @@ class BottomGalleryBar extends ConsumerWidget { | ||||
|     } | ||||
| 
 | ||||
|     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 { | ||||
|         final isDeleted = await ref.read(assetProvider.notifier).deleteAssets( | ||||
|           {asset}, | ||||
|  | ||||
| @ -42,7 +42,7 @@ class ExifBottomSheet extends HookConsumerWidget { | ||||
|             fontSize: 14, | ||||
|           ), | ||||
|         ), | ||||
|         if (asset.isRemote && !asset.isReadOnly) | ||||
|         if (asset.isRemote) | ||||
|           IconButton( | ||||
|             onPressed: () => handleEditDateTime( | ||||
|               ref, | ||||
|  | ||||
| @ -24,7 +24,7 @@ class ExifLocation extends StatelessWidget { | ||||
|     final hasCoordinates = exifInfo?.hasCoordinates ?? false; | ||||
|     // Guard no lat/lng | ||||
|     if (!hasCoordinates) { | ||||
|       return asset.isRemote && !asset.isReadOnly | ||||
|       return asset.isRemote | ||||
|           ? ListTile( | ||||
|               minLeadingWidth: 0, | ||||
|               contentPadding: const EdgeInsets.all(0), | ||||
| @ -57,7 +57,7 @@ class ExifLocation extends StatelessWidget { | ||||
|                     fontWeight: FontWeight.w600, | ||||
|                   ), | ||||
|                 ).tr(), | ||||
|                 if (asset.isRemote && !asset.isReadOnly) | ||||
|                 if (asset.isRemote) | ||||
|                   IconButton( | ||||
|                     onPressed: editLocation, | ||||
|                     icon: const Icon(Icons.edit_outlined), | ||||
|  | ||||
| @ -63,6 +63,12 @@ abstract class _$AppRouter extends RootStackRouter { | ||||
|         child: const AllPeoplePage(), | ||||
|       ); | ||||
|     }, | ||||
|     AllPlacesRoute.name: (routeData) { | ||||
|       return AutoRoutePage<dynamic>( | ||||
|         routeData: routeData, | ||||
|         child: const AllPlacesPage(), | ||||
|       ); | ||||
|     }, | ||||
|     AllVideosRoute.name: (routeData) { | ||||
|       return AutoRoutePage<dynamic>( | ||||
|         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) { | ||||
|       return AutoRoutePage<dynamic>( | ||||
|         routeData: routeData, | ||||
| @ -525,6 +525,20 @@ class AllPeopleRoute extends PageRouteInfo<void> { | ||||
|   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 | ||||
| /// [AllVideosPage] | ||||
| 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 | ||||
| /// [FailedBackupStatusPage] | ||||
| class FailedBackupStatusRoute extends PageRouteInfo<void> { | ||||
|  | ||||
| @ -184,11 +184,6 @@ class MultiselectGrid extends HookConsumerWidget { | ||||
|               currentUser, | ||||
|               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(); | ||||
|         final isDeleted = await ref | ||||
|             .read(assetProvider.notifier) | ||||
| @ -238,13 +233,7 @@ class MultiselectGrid extends HookConsumerWidget { | ||||
|         final toDelete = ownedRemoteSelection( | ||||
|           localErrorMessage: 'home_page_delete_remote_err_local'.tr(), | ||||
|           ownerErrorMessage: '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 | ||||
|             .read(assetProvider.notifier) | ||||
| @ -372,12 +361,8 @@ class MultiselectGrid extends HookConsumerWidget { | ||||
|         final remoteAssets = ownedRemoteSelection( | ||||
|           localErrorMessage: 'home_page_favorite_err_local'.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) { | ||||
|           handleEditDateTime(ref, context, remoteAssets.toList()); | ||||
|         } | ||||
| @ -391,12 +376,8 @@ class MultiselectGrid extends HookConsumerWidget { | ||||
|         final remoteAssets = ownedRemoteSelection( | ||||
|           localErrorMessage: 'home_page_favorite_err_local'.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) { | ||||
|           handleEditLocation(ref, context, remoteAssets.toList()); | ||||
|         } | ||||
|  | ||||
							
								
								
									
										6
									
								
								mobile/openapi/doc/AssetApi.md
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										6
									
								
								mobile/openapi/doc/AssetApi.md
									
									
									
										generated
									
									
									
								
							| @ -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) | ||||
| 
 | ||||
| # **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 isFavorite = true; // bool |  | ||||
| final isOffline = true; // bool |  | ||||
| final isReadOnly = true; // bool |  | ||||
| final isVisible = true; // bool |  | ||||
| final libraryId = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |  | ||||
| final livePhotoData = BINARY_DATA_HERE; // MultipartFile |  | ||||
| final sidecarData = BINARY_DATA_HERE; // MultipartFile |  | ||||
| 
 | ||||
| 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); | ||||
| } catch (e) { | ||||
|     print('Exception when calling AssetApi->uploadFile: $e\n'); | ||||
| @ -1018,7 +1017,6 @@ Name | Type | Description  | Notes | ||||
|  **isArchived** | **bool**|  | [optional]  | ||||
|  **isFavorite** | **bool**|  | [optional]  | ||||
|  **isOffline** | **bool**|  | [optional]  | ||||
|  **isReadOnly** | **bool**|  | [optional]  | ||||
|  **isVisible** | **bool**|  | [optional]  | ||||
|  **libraryId** | **String**|  | [optional]  | ||||
|  **livePhotoData** | **MultipartFile**|  | [optional]  | ||||
|  | ||||
							
								
								
									
										4
									
								
								mobile/openapi/doc/AssetResponseDto.md
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										4
									
								
								mobile/openapi/doc/AssetResponseDto.md
									
									
									
										generated
									
									
									
								
							| @ -18,10 +18,10 @@ Name | Type | Description | Notes | ||||
| **hasMetadata** | **bool** |  |  | ||||
| **id** | **String** |  |  | ||||
| **isArchived** | **bool** |  |  | ||||
| **isExternal** | **bool** |  |  | ||||
| **isExternal** | **bool** | This property was deprecated in v1.104.0 | [optional]  | ||||
| **isFavorite** | **bool** |  |  | ||||
| **isOffline** | **bool** |  |  | ||||
| **isReadOnly** | **bool** |  |  | ||||
| **isReadOnly** | **bool** | This property was deprecated in v1.104.0 | [optional]  | ||||
| **isTrashed** | **bool** |  |  | ||||
| **libraryId** | **String** |  |  | ||||
| **livePhotoVideoId** | **String** |  | [optional]  | ||||
|  | ||||
							
								
								
									
										2
									
								
								mobile/openapi/doc/MetadataSearchDto.md
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								mobile/openapi/doc/MetadataSearchDto.md
									
									
									
										generated
									
									
									
								
							| @ -19,12 +19,10 @@ Name | Type | Description | Notes | ||||
| **id** | **String** |  | [optional]  | ||||
| **isArchived** | **bool** |  | [optional]  | ||||
| **isEncoded** | **bool** |  | [optional]  | ||||
| **isExternal** | **bool** |  | [optional]  | ||||
| **isFavorite** | **bool** |  | [optional]  | ||||
| **isMotion** | **bool** |  | [optional]  | ||||
| **isNotInAlbum** | **bool** |  | [optional]  | ||||
| **isOffline** | **bool** |  | [optional]  | ||||
| **isReadOnly** | **bool** |  | [optional]  | ||||
| **isVisible** | **bool** |  | [optional]  | ||||
| **lensModel** | **String** |  | [optional]  | ||||
| **libraryId** | **String** |  | [optional]  | ||||
|  | ||||
							
								
								
									
										2
									
								
								mobile/openapi/doc/SmartSearchDto.md
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								mobile/openapi/doc/SmartSearchDto.md
									
									
									
										generated
									
									
									
								
							| @ -15,12 +15,10 @@ Name | Type | Description | Notes | ||||
| **deviceId** | **String** |  | [optional]  | ||||
| **isArchived** | **bool** |  | [optional]  | ||||
| **isEncoded** | **bool** |  | [optional]  | ||||
| **isExternal** | **bool** |  | [optional]  | ||||
| **isFavorite** | **bool** |  | [optional]  | ||||
| **isMotion** | **bool** |  | [optional]  | ||||
| **isNotInAlbum** | **bool** |  | [optional]  | ||||
| **isOffline** | **bool** |  | [optional]  | ||||
| **isReadOnly** | **bool** |  | [optional]  | ||||
| **isVisible** | **bool** |  | [optional]  | ||||
| **lensModel** | **String** |  | [optional]  | ||||
| **libraryId** | **String** |  | [optional]  | ||||
|  | ||||
							
								
								
									
										14
									
								
								mobile/openapi/lib/api/asset_api.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										14
									
								
								mobile/openapi/lib/api/asset_api.dart
									
									
									
										generated
									
									
									
								
							| @ -968,8 +968,6 @@ class AssetApi { | ||||
|   /// | ||||
|   /// * [bool] isOffline: | ||||
|   /// | ||||
|   /// * [bool] isReadOnly: | ||||
|   /// | ||||
|   /// * [bool] isVisible: | ||||
|   /// | ||||
|   /// * [String] libraryId: | ||||
| @ -977,7 +975,7 @@ class AssetApi { | ||||
|   /// * [MultipartFile] livePhotoData: | ||||
|   /// | ||||
|   /// * [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 | ||||
|     final path = r'/asset/upload'; | ||||
| 
 | ||||
| @ -1037,10 +1035,6 @@ class AssetApi { | ||||
|       hasFields = true; | ||||
|       mp.fields[r'isOffline'] = parameterToString(isOffline); | ||||
|     } | ||||
|     if (isReadOnly != null) { | ||||
|       hasFields = true; | ||||
|       mp.fields[r'isReadOnly'] = parameterToString(isReadOnly); | ||||
|     } | ||||
|     if (isVisible != null) { | ||||
|       hasFields = true; | ||||
|       mp.fields[r'isVisible'] = parameterToString(isVisible); | ||||
| @ -1099,8 +1093,6 @@ class AssetApi { | ||||
|   /// | ||||
|   /// * [bool] isOffline: | ||||
|   /// | ||||
|   /// * [bool] isReadOnly: | ||||
|   /// | ||||
|   /// * [bool] isVisible: | ||||
|   /// | ||||
|   /// * [String] libraryId: | ||||
| @ -1108,8 +1100,8 @@ class AssetApi { | ||||
|   /// * [MultipartFile] livePhotoData: | ||||
|   /// | ||||
|   /// * [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 { | ||||
|     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, ); | ||||
|   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, isVisible: isVisible, libraryId: libraryId, livePhotoData: livePhotoData, sidecarData: sidecarData, ); | ||||
|     if (response.statusCode >= HttpStatus.badRequest) { | ||||
|       throw ApiException(response.statusCode, await _decodeBodyBytes(response)); | ||||
|     } | ||||
|  | ||||
							
								
								
									
										40
									
								
								mobile/openapi/lib/model/asset_response_dto.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										40
									
								
								mobile/openapi/lib/model/asset_response_dto.dart
									
									
									
										generated
									
									
									
								
							| @ -23,10 +23,10 @@ class AssetResponseDto { | ||||
|     required this.hasMetadata, | ||||
|     required this.id, | ||||
|     required this.isArchived, | ||||
|     required this.isExternal, | ||||
|     this.isExternal, | ||||
|     required this.isFavorite, | ||||
|     required this.isOffline, | ||||
|     required this.isReadOnly, | ||||
|     this.isReadOnly, | ||||
|     required this.isTrashed, | ||||
|     required this.libraryId, | ||||
|     this.livePhotoVideoId, | ||||
| @ -74,13 +74,27 @@ class AssetResponseDto { | ||||
| 
 | ||||
|   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 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; | ||||
| 
 | ||||
| @ -178,10 +192,10 @@ class AssetResponseDto { | ||||
|     (hasMetadata.hashCode) + | ||||
|     (id.hashCode) + | ||||
|     (isArchived.hashCode) + | ||||
|     (isExternal.hashCode) + | ||||
|     (isExternal == null ? 0 : isExternal!.hashCode) + | ||||
|     (isFavorite.hashCode) + | ||||
|     (isOffline.hashCode) + | ||||
|     (isReadOnly.hashCode) + | ||||
|     (isReadOnly == null ? 0 : isReadOnly!.hashCode) + | ||||
|     (isTrashed.hashCode) + | ||||
|     (libraryId.hashCode) + | ||||
|     (livePhotoVideoId == null ? 0 : livePhotoVideoId!.hashCode) + | ||||
| @ -220,10 +234,18 @@ class AssetResponseDto { | ||||
|       json[r'hasMetadata'] = this.hasMetadata; | ||||
|       json[r'id'] = this.id; | ||||
|       json[r'isArchived'] = this.isArchived; | ||||
|     if (this.isExternal != null) { | ||||
|       json[r'isExternal'] = this.isExternal; | ||||
|     } else { | ||||
|     //  json[r'isExternal'] = null; | ||||
|     } | ||||
|       json[r'isFavorite'] = this.isFavorite; | ||||
|       json[r'isOffline'] = this.isOffline; | ||||
|     if (this.isReadOnly != null) { | ||||
|       json[r'isReadOnly'] = this.isReadOnly; | ||||
|     } else { | ||||
|     //  json[r'isReadOnly'] = null; | ||||
|     } | ||||
|       json[r'isTrashed'] = this.isTrashed; | ||||
|       json[r'libraryId'] = this.libraryId; | ||||
|     if (this.livePhotoVideoId != null) { | ||||
| @ -287,10 +309,10 @@ class AssetResponseDto { | ||||
|         hasMetadata: mapValueOfType<bool>(json, r'hasMetadata')!, | ||||
|         id: mapValueOfType<String>(json, r'id')!, | ||||
|         isArchived: mapValueOfType<bool>(json, r'isArchived')!, | ||||
|         isExternal: mapValueOfType<bool>(json, r'isExternal')!, | ||||
|         isExternal: mapValueOfType<bool>(json, r'isExternal'), | ||||
|         isFavorite: mapValueOfType<bool>(json, r'isFavorite')!, | ||||
|         isOffline: mapValueOfType<bool>(json, r'isOffline')!, | ||||
|         isReadOnly: mapValueOfType<bool>(json, r'isReadOnly')!, | ||||
|         isReadOnly: mapValueOfType<bool>(json, r'isReadOnly'), | ||||
|         isTrashed: mapValueOfType<bool>(json, r'isTrashed')!, | ||||
|         libraryId: mapValueOfType<String>(json, r'libraryId')!, | ||||
|         livePhotoVideoId: mapValueOfType<String>(json, r'livePhotoVideoId'), | ||||
| @ -365,10 +387,8 @@ class AssetResponseDto { | ||||
|     'hasMetadata', | ||||
|     'id', | ||||
|     'isArchived', | ||||
|     'isExternal', | ||||
|     'isFavorite', | ||||
|     'isOffline', | ||||
|     'isReadOnly', | ||||
|     'isTrashed', | ||||
|     'libraryId', | ||||
|     'localDateTime', | ||||
|  | ||||
							
								
								
									
										36
									
								
								mobile/openapi/lib/model/metadata_search_dto.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										36
									
								
								mobile/openapi/lib/model/metadata_search_dto.dart
									
									
									
										generated
									
									
									
								
							| @ -24,12 +24,10 @@ class MetadataSearchDto { | ||||
|     this.id, | ||||
|     this.isArchived, | ||||
|     this.isEncoded, | ||||
|     this.isExternal, | ||||
|     this.isFavorite, | ||||
|     this.isMotion, | ||||
|     this.isNotInAlbum, | ||||
|     this.isOffline, | ||||
|     this.isReadOnly, | ||||
|     this.isVisible, | ||||
|     this.lensModel, | ||||
|     this.libraryId, | ||||
| @ -148,14 +146,6 @@ class MetadataSearchDto { | ||||
|   /// | ||||
|   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 | ||||
|   /// does not include a default value (using the "default:" property), however, the generated | ||||
| @ -188,14 +178,6 @@ class MetadataSearchDto { | ||||
|   /// | ||||
|   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 | ||||
|   /// does not include a default value (using the "default:" property), however, the generated | ||||
| @ -426,12 +408,10 @@ class MetadataSearchDto { | ||||
|     other.id == id && | ||||
|     other.isArchived == isArchived && | ||||
|     other.isEncoded == isEncoded && | ||||
|     other.isExternal == isExternal && | ||||
|     other.isFavorite == isFavorite && | ||||
|     other.isMotion == isMotion && | ||||
|     other.isNotInAlbum == isNotInAlbum && | ||||
|     other.isOffline == isOffline && | ||||
|     other.isReadOnly == isReadOnly && | ||||
|     other.isVisible == isVisible && | ||||
|     other.lensModel == lensModel && | ||||
|     other.libraryId == libraryId && | ||||
| @ -475,12 +455,10 @@ class MetadataSearchDto { | ||||
|     (id == null ? 0 : id!.hashCode) + | ||||
|     (isArchived == null ? 0 : isArchived!.hashCode) + | ||||
|     (isEncoded == null ? 0 : isEncoded!.hashCode) + | ||||
|     (isExternal == null ? 0 : isExternal!.hashCode) + | ||||
|     (isFavorite == null ? 0 : isFavorite!.hashCode) + | ||||
|     (isMotion == null ? 0 : isMotion!.hashCode) + | ||||
|     (isNotInAlbum == null ? 0 : isNotInAlbum!.hashCode) + | ||||
|     (isOffline == null ? 0 : isOffline!.hashCode) + | ||||
|     (isReadOnly == null ? 0 : isReadOnly!.hashCode) + | ||||
|     (isVisible == null ? 0 : isVisible!.hashCode) + | ||||
|     (lensModel == null ? 0 : lensModel!.hashCode) + | ||||
|     (libraryId == null ? 0 : libraryId!.hashCode) + | ||||
| @ -511,7 +489,7 @@ class MetadataSearchDto { | ||||
|     (withStacked == null ? 0 : withStacked!.hashCode); | ||||
| 
 | ||||
|   @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() { | ||||
|     final json = <String, dynamic>{}; | ||||
| @ -570,11 +548,6 @@ class MetadataSearchDto { | ||||
|     } else { | ||||
|     //  json[r'isEncoded'] = null; | ||||
|     } | ||||
|     if (this.isExternal != null) { | ||||
|       json[r'isExternal'] = this.isExternal; | ||||
|     } else { | ||||
|     //  json[r'isExternal'] = null; | ||||
|     } | ||||
|     if (this.isFavorite != null) { | ||||
|       json[r'isFavorite'] = this.isFavorite; | ||||
|     } else { | ||||
| @ -595,11 +568,6 @@ class MetadataSearchDto { | ||||
|     } else { | ||||
|     //  json[r'isOffline'] = null; | ||||
|     } | ||||
|     if (this.isReadOnly != null) { | ||||
|       json[r'isReadOnly'] = this.isReadOnly; | ||||
|     } else { | ||||
|     //  json[r'isReadOnly'] = null; | ||||
|     } | ||||
|     if (this.isVisible != null) { | ||||
|       json[r'isVisible'] = this.isVisible; | ||||
|     } else { | ||||
| @ -754,12 +722,10 @@ class MetadataSearchDto { | ||||
|         id: mapValueOfType<String>(json, r'id'), | ||||
|         isArchived: mapValueOfType<bool>(json, r'isArchived'), | ||||
|         isEncoded: mapValueOfType<bool>(json, r'isEncoded'), | ||||
|         isExternal: mapValueOfType<bool>(json, r'isExternal'), | ||||
|         isFavorite: mapValueOfType<bool>(json, r'isFavorite'), | ||||
|         isMotion: mapValueOfType<bool>(json, r'isMotion'), | ||||
|         isNotInAlbum: mapValueOfType<bool>(json, r'isNotInAlbum'), | ||||
|         isOffline: mapValueOfType<bool>(json, r'isOffline'), | ||||
|         isReadOnly: mapValueOfType<bool>(json, r'isReadOnly'), | ||||
|         isVisible: mapValueOfType<bool>(json, r'isVisible'), | ||||
|         lensModel: mapValueOfType<String>(json, r'lensModel'), | ||||
|         libraryId: mapValueOfType<String>(json, r'libraryId'), | ||||
|  | ||||
							
								
								
									
										36
									
								
								mobile/openapi/lib/model/smart_search_dto.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										36
									
								
								mobile/openapi/lib/model/smart_search_dto.dart
									
									
									
										generated
									
									
									
								
							| @ -20,12 +20,10 @@ class SmartSearchDto { | ||||
|     this.deviceId, | ||||
|     this.isArchived, | ||||
|     this.isEncoded, | ||||
|     this.isExternal, | ||||
|     this.isFavorite, | ||||
|     this.isMotion, | ||||
|     this.isNotInAlbum, | ||||
|     this.isOffline, | ||||
|     this.isReadOnly, | ||||
|     this.isVisible, | ||||
|     this.lensModel, | ||||
|     this.libraryId, | ||||
| @ -104,14 +102,6 @@ class SmartSearchDto { | ||||
|   /// | ||||
|   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 | ||||
|   /// does not include a default value (using the "default:" property), however, the generated | ||||
| @ -144,14 +134,6 @@ class SmartSearchDto { | ||||
|   /// | ||||
|   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 | ||||
|   /// does not include a default value (using the "default:" property), however, the generated | ||||
| @ -306,12 +288,10 @@ class SmartSearchDto { | ||||
|     other.deviceId == deviceId && | ||||
|     other.isArchived == isArchived && | ||||
|     other.isEncoded == isEncoded && | ||||
|     other.isExternal == isExternal && | ||||
|     other.isFavorite == isFavorite && | ||||
|     other.isMotion == isMotion && | ||||
|     other.isNotInAlbum == isNotInAlbum && | ||||
|     other.isOffline == isOffline && | ||||
|     other.isReadOnly == isReadOnly && | ||||
|     other.isVisible == isVisible && | ||||
|     other.lensModel == lensModel && | ||||
|     other.libraryId == libraryId && | ||||
| @ -343,12 +323,10 @@ class SmartSearchDto { | ||||
|     (deviceId == null ? 0 : deviceId!.hashCode) + | ||||
|     (isArchived == null ? 0 : isArchived!.hashCode) + | ||||
|     (isEncoded == null ? 0 : isEncoded!.hashCode) + | ||||
|     (isExternal == null ? 0 : isExternal!.hashCode) + | ||||
|     (isFavorite == null ? 0 : isFavorite!.hashCode) + | ||||
|     (isMotion == null ? 0 : isMotion!.hashCode) + | ||||
|     (isNotInAlbum == null ? 0 : isNotInAlbum!.hashCode) + | ||||
|     (isOffline == null ? 0 : isOffline!.hashCode) + | ||||
|     (isReadOnly == null ? 0 : isReadOnly!.hashCode) + | ||||
|     (isVisible == null ? 0 : isVisible!.hashCode) + | ||||
|     (lensModel == null ? 0 : lensModel!.hashCode) + | ||||
|     (libraryId == null ? 0 : libraryId!.hashCode) + | ||||
| @ -371,7 +349,7 @@ class SmartSearchDto { | ||||
|     (withExif == null ? 0 : withExif!.hashCode); | ||||
| 
 | ||||
|   @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() { | ||||
|     final json = <String, dynamic>{}; | ||||
| @ -410,11 +388,6 @@ class SmartSearchDto { | ||||
|     } else { | ||||
|     //  json[r'isEncoded'] = null; | ||||
|     } | ||||
|     if (this.isExternal != null) { | ||||
|       json[r'isExternal'] = this.isExternal; | ||||
|     } else { | ||||
|     //  json[r'isExternal'] = null; | ||||
|     } | ||||
|     if (this.isFavorite != null) { | ||||
|       json[r'isFavorite'] = this.isFavorite; | ||||
|     } else { | ||||
| @ -435,11 +408,6 @@ class SmartSearchDto { | ||||
|     } else { | ||||
|     //  json[r'isOffline'] = null; | ||||
|     } | ||||
|     if (this.isReadOnly != null) { | ||||
|       json[r'isReadOnly'] = this.isReadOnly; | ||||
|     } else { | ||||
|     //  json[r'isReadOnly'] = null; | ||||
|     } | ||||
|     if (this.isVisible != null) { | ||||
|       json[r'isVisible'] = this.isVisible; | ||||
|     } else { | ||||
| @ -546,12 +514,10 @@ class SmartSearchDto { | ||||
|         deviceId: mapValueOfType<String>(json, r'deviceId'), | ||||
|         isArchived: mapValueOfType<bool>(json, r'isArchived'), | ||||
|         isEncoded: mapValueOfType<bool>(json, r'isEncoded'), | ||||
|         isExternal: mapValueOfType<bool>(json, r'isExternal'), | ||||
|         isFavorite: mapValueOfType<bool>(json, r'isFavorite'), | ||||
|         isMotion: mapValueOfType<bool>(json, r'isMotion'), | ||||
|         isNotInAlbum: mapValueOfType<bool>(json, r'isNotInAlbum'), | ||||
|         isOffline: mapValueOfType<bool>(json, r'isOffline'), | ||||
|         isReadOnly: mapValueOfType<bool>(json, r'isReadOnly'), | ||||
|         isVisible: mapValueOfType<bool>(json, r'isVisible'), | ||||
|         lensModel: mapValueOfType<String>(json, r'lensModel'), | ||||
|         libraryId: mapValueOfType<String>(json, r'libraryId'), | ||||
|  | ||||
							
								
								
									
										2
									
								
								mobile/openapi/test/asset_api_test.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								mobile/openapi/test/asset_api_test.dart
									
									
									
										generated
									
									
									
								
							| @ -105,7 +105,7 @@ void main() { | ||||
|       // 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 { | ||||
|       // TODO | ||||
|     }); | ||||
|  | ||||
							
								
								
									
										2
									
								
								mobile/openapi/test/asset_response_dto_test.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								mobile/openapi/test/asset_response_dto_test.dart
									
									
									
										generated
									
									
									
								
							| @ -67,6 +67,7 @@ void main() { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     // This property was deprecated in v1.104.0 | ||||
|     // bool isExternal | ||||
|     test('to test the property `isExternal`', () async { | ||||
|       // TODO | ||||
| @ -82,6 +83,7 @@ void main() { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     // This property was deprecated in v1.104.0 | ||||
|     // bool isReadOnly | ||||
|     test('to test the property `isReadOnly`', () async { | ||||
|       // TODO | ||||
|  | ||||
							
								
								
									
										10
									
								
								mobile/openapi/test/metadata_search_dto_test.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										10
									
								
								mobile/openapi/test/metadata_search_dto_test.dart
									
									
									
										generated
									
									
									
								
							| @ -71,11 +71,6 @@ void main() { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     // bool isExternal | ||||
|     test('to test the property `isExternal`', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     // bool isFavorite | ||||
|     test('to test the property `isFavorite`', () async { | ||||
|       // TODO | ||||
| @ -96,11 +91,6 @@ void main() { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     // bool isReadOnly | ||||
|     test('to test the property `isReadOnly`', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     // bool isVisible | ||||
|     test('to test the property `isVisible`', () async { | ||||
|       // TODO | ||||
|  | ||||
							
								
								
									
										10
									
								
								mobile/openapi/test/smart_search_dto_test.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										10
									
								
								mobile/openapi/test/smart_search_dto_test.dart
									
									
									
										generated
									
									
									
								
							| @ -51,11 +51,6 @@ void main() { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     // bool isExternal | ||||
|     test('to test the property `isExternal`', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     // bool isFavorite | ||||
|     test('to test the property `isFavorite`', () async { | ||||
|       // TODO | ||||
| @ -76,11 +71,6 @@ void main() { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     // bool isReadOnly | ||||
|     test('to test the property `isReadOnly`', () async { | ||||
|       // TODO | ||||
|     }); | ||||
| 
 | ||||
|     // bool isVisible | ||||
|     test('to test the property `isVisible`', () async { | ||||
|       // TODO | ||||
|  | ||||
| @ -7297,6 +7297,8 @@ | ||||
|             "type": "boolean" | ||||
|           }, | ||||
|           "isExternal": { | ||||
|             "deprecated": true, | ||||
|             "description": "This property was deprecated in v1.104.0", | ||||
|             "type": "boolean" | ||||
|           }, | ||||
|           "isFavorite": { | ||||
| @ -7306,6 +7308,8 @@ | ||||
|             "type": "boolean" | ||||
|           }, | ||||
|           "isReadOnly": { | ||||
|             "deprecated": true, | ||||
|             "description": "This property was deprecated in v1.104.0", | ||||
|             "type": "boolean" | ||||
|           }, | ||||
|           "isTrashed": { | ||||
| @ -7388,10 +7392,8 @@ | ||||
|           "hasMetadata", | ||||
|           "id", | ||||
|           "isArchived", | ||||
|           "isExternal", | ||||
|           "isFavorite", | ||||
|           "isOffline", | ||||
|           "isReadOnly", | ||||
|           "isTrashed", | ||||
|           "libraryId", | ||||
|           "localDateTime", | ||||
| @ -7652,9 +7654,6 @@ | ||||
|           "isOffline": { | ||||
|             "type": "boolean" | ||||
|           }, | ||||
|           "isReadOnly": { | ||||
|             "type": "boolean" | ||||
|           }, | ||||
|           "isVisible": { | ||||
|             "type": "boolean" | ||||
|           }, | ||||
| @ -8599,9 +8598,6 @@ | ||||
|           "isEncoded": { | ||||
|             "type": "boolean" | ||||
|           }, | ||||
|           "isExternal": { | ||||
|             "type": "boolean" | ||||
|           }, | ||||
|           "isFavorite": { | ||||
|             "type": "boolean" | ||||
|           }, | ||||
| @ -8614,9 +8610,6 @@ | ||||
|           "isOffline": { | ||||
|             "type": "boolean" | ||||
|           }, | ||||
|           "isReadOnly": { | ||||
|             "type": "boolean" | ||||
|           }, | ||||
|           "isVisible": { | ||||
|             "type": "boolean" | ||||
|           }, | ||||
| @ -9821,9 +9814,6 @@ | ||||
|           "isEncoded": { | ||||
|             "type": "boolean" | ||||
|           }, | ||||
|           "isExternal": { | ||||
|             "type": "boolean" | ||||
|           }, | ||||
|           "isFavorite": { | ||||
|             "type": "boolean" | ||||
|           }, | ||||
| @ -9836,9 +9826,6 @@ | ||||
|           "isOffline": { | ||||
|             "type": "boolean" | ||||
|           }, | ||||
|           "isReadOnly": { | ||||
|             "type": "boolean" | ||||
|           }, | ||||
|           "isVisible": { | ||||
|             "type": "boolean" | ||||
|           }, | ||||
|  | ||||
| @ -122,10 +122,12 @@ export type AssetResponseDto = { | ||||
|     hasMetadata: boolean; | ||||
|     id: string; | ||||
|     isArchived: boolean; | ||||
|     isExternal: boolean; | ||||
|     /** This property was deprecated in v1.104.0 */ | ||||
|     isExternal?: boolean; | ||||
|     isFavorite: boolean; | ||||
|     isOffline: boolean; | ||||
|     isReadOnly: boolean; | ||||
|     /** This property was deprecated in v1.104.0 */ | ||||
|     isReadOnly?: boolean; | ||||
|     isTrashed: boolean; | ||||
|     libraryId: string; | ||||
|     livePhotoVideoId?: string | null; | ||||
| @ -296,7 +298,6 @@ export type CreateAssetDto = { | ||||
|     isArchived?: boolean; | ||||
|     isFavorite?: boolean; | ||||
|     isOffline?: boolean; | ||||
|     isReadOnly?: boolean; | ||||
|     isVisible?: boolean; | ||||
|     libraryId?: string; | ||||
|     livePhotoData?: Blob; | ||||
| @ -622,12 +623,10 @@ export type MetadataSearchDto = { | ||||
|     id?: string; | ||||
|     isArchived?: boolean; | ||||
|     isEncoded?: boolean; | ||||
|     isExternal?: boolean; | ||||
|     isFavorite?: boolean; | ||||
|     isMotion?: boolean; | ||||
|     isNotInAlbum?: boolean; | ||||
|     isOffline?: boolean; | ||||
|     isReadOnly?: boolean; | ||||
|     isVisible?: boolean; | ||||
|     lensModel?: string; | ||||
|     libraryId?: string; | ||||
| @ -699,12 +698,10 @@ export type SmartSearchDto = { | ||||
|     deviceId?: string; | ||||
|     isArchived?: boolean; | ||||
|     isEncoded?: boolean; | ||||
|     isExternal?: boolean; | ||||
|     isFavorite?: boolean; | ||||
|     isMotion?: boolean; | ||||
|     isNotInAlbum?: boolean; | ||||
|     isOffline?: boolean; | ||||
|     isReadOnly?: boolean; | ||||
|     isVisible?: boolean; | ||||
|     lensModel?: string; | ||||
|     libraryId?: string; | ||||
|  | ||||
| @ -113,6 +113,7 @@ export const DummyValue = { | ||||
|   PAGINATION: { take: 10, skip: 0 }, | ||||
|   EMAIL: 'user@immich.app', | ||||
|   STRING: 'abcdefghi', | ||||
|   NUMBER: 50, | ||||
|   BUFFER: Buffer.from('abcdefghi'), | ||||
|   DATE: new Date(), | ||||
|   TIME_BUCKET: '2024-01-01T00:00:00.000Z', | ||||
|  | ||||
| @ -36,8 +36,10 @@ export class AssetResponseDto extends SanitizedAssetResponseDto { | ||||
|   isArchived!: boolean; | ||||
|   isTrashed!: boolean; | ||||
|   isOffline!: boolean; | ||||
|   isExternal!: boolean; | ||||
|   isReadOnly!: boolean; | ||||
|   @PropertyLifecycle({ deprecatedAt: 'v1.104.0' }) | ||||
|   isExternal?: boolean; | ||||
|   @PropertyLifecycle({ deprecatedAt: 'v1.104.0' }) | ||||
|   isReadOnly?: boolean; | ||||
|   exifInfo?: ExifResponseDto; | ||||
|   smartInfo?: SmartInfoResponseDto; | ||||
|   tags?: TagResponseDto[]; | ||||
| @ -124,9 +126,9 @@ export function mapAsset(entity: AssetEntity, options: AssetMapOptions = {}): As | ||||
|           .map((a) => mapAsset(a, { stripMetadata, auth: options.auth })) | ||||
|       : undefined, | ||||
|     stackCount: entity.stack?.assets?.length ?? null, | ||||
|     isExternal: entity.isExternal, | ||||
|     isOffline: entity.isOffline, | ||||
|     isReadOnly: entity.isReadOnly, | ||||
|     isExternal: false, | ||||
|     isReadOnly: false, | ||||
|     hasMetadata: true, | ||||
|   }; | ||||
| } | ||||
|  | ||||
| @ -97,9 +97,6 @@ export class CreateAssetDto { | ||||
|   @ValidateBoolean({ optional: true }) | ||||
|   isOffline?: boolean; | ||||
| 
 | ||||
|   @ValidateBoolean({ optional: true }) | ||||
|   isReadOnly?: boolean; | ||||
| 
 | ||||
|   // The properties below are added to correctly generate the API docs
 | ||||
|   // and client SDKs. Validation should be handled in the controller.
 | ||||
|   @ApiProperty({ type: 'string', format: 'binary' }) | ||||
|  | ||||
| @ -33,9 +33,6 @@ class BaseSearchDto { | ||||
|   @ValidateBoolean({ optional: true }) | ||||
|   isEncoded?: boolean; | ||||
| 
 | ||||
|   @ValidateBoolean({ optional: true }) | ||||
|   isExternal?: boolean; | ||||
| 
 | ||||
|   @ValidateBoolean({ optional: true }) | ||||
|   isFavorite?: boolean; | ||||
| 
 | ||||
| @ -45,9 +42,6 @@ class BaseSearchDto { | ||||
|   @ValidateBoolean({ optional: true }) | ||||
|   isOffline?: boolean; | ||||
| 
 | ||||
|   @ValidateBoolean({ optional: true }) | ||||
|   isReadOnly?: boolean; | ||||
| 
 | ||||
|   @ValidateBoolean({ optional: true }) | ||||
|   isVisible?: boolean; | ||||
| 
 | ||||
|  | ||||
| @ -106,9 +106,6 @@ export class AssetEntity { | ||||
|   @Column({ type: 'boolean', default: false }) | ||||
|   isExternal!: boolean; | ||||
| 
 | ||||
|   @Column({ type: 'boolean', default: false }) | ||||
|   isReadOnly!: boolean; | ||||
| 
 | ||||
|   @Column({ type: 'boolean', default: false }) | ||||
|   isOffline!: boolean; | ||||
| 
 | ||||
|  | ||||
| @ -108,10 +108,6 @@ export interface IEntityJob extends IBaseJob { | ||||
|   source?: 'upload' | 'sidecar-write'; | ||||
| } | ||||
| 
 | ||||
| export interface IAssetDeletionJob extends IEntityJob { | ||||
|   fromExternal?: boolean; | ||||
| } | ||||
| 
 | ||||
| export interface ILibraryFileJob extends IEntityJob { | ||||
|   ownerId: string; | ||||
|   assetPath: string; | ||||
| @ -225,7 +221,7 @@ export type JobItem = | ||||
| 
 | ||||
|   // Asset Deletion
 | ||||
|   | { 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 } | ||||
| 
 | ||||
|   // Library Management
 | ||||
|  | ||||
| @ -56,11 +56,9 @@ export type SearchIdOptions = SearchAssetIDOptions & SearchUserIdOptions; | ||||
| export interface SearchStatusOptions { | ||||
|   isArchived?: boolean; | ||||
|   isEncoded?: boolean; | ||||
|   isExternal?: boolean; | ||||
|   isFavorite?: boolean; | ||||
|   isMotion?: boolean; | ||||
|   isOffline?: boolean; | ||||
|   isReadOnly?: boolean; | ||||
|   isVisible?: boolean; | ||||
|   isNotInAlbum?: boolean; | ||||
|   type?: AssetType; | ||||
|  | ||||
							
								
								
									
										14
									
								
								server/src/migrations/1714698592332-RemoveIsReadOnly.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								server/src/migrations/1714698592332-RemoveIsReadOnly.ts
									
									
									
									
									
										Normal 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`); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -22,7 +22,6 @@ SELECT | ||||
|   "entity"."isFavorite" AS "entity_isFavorite", | ||||
|   "entity"."isArchived" AS "entity_isArchived", | ||||
|   "entity"."isExternal" AS "entity_isExternal", | ||||
|   "entity"."isReadOnly" AS "entity_isReadOnly", | ||||
|   "entity"."isOffline" AS "entity_isOffline", | ||||
|   "entity"."checksum" AS "entity_checksum", | ||||
|   "entity"."duration" AS "entity_duration", | ||||
| @ -105,7 +104,6 @@ SELECT | ||||
|   "AssetEntity"."isFavorite" AS "AssetEntity_isFavorite", | ||||
|   "AssetEntity"."isArchived" AS "AssetEntity_isArchived", | ||||
|   "AssetEntity"."isExternal" AS "AssetEntity_isExternal", | ||||
|   "AssetEntity"."isReadOnly" AS "AssetEntity_isReadOnly", | ||||
|   "AssetEntity"."isOffline" AS "AssetEntity_isOffline", | ||||
|   "AssetEntity"."checksum" AS "AssetEntity_checksum", | ||||
|   "AssetEntity"."duration" AS "AssetEntity_duration", | ||||
| @ -141,7 +139,6 @@ SELECT | ||||
|   "AssetEntity"."isFavorite" AS "AssetEntity_isFavorite", | ||||
|   "AssetEntity"."isArchived" AS "AssetEntity_isArchived", | ||||
|   "AssetEntity"."isExternal" AS "AssetEntity_isExternal", | ||||
|   "AssetEntity"."isReadOnly" AS "AssetEntity_isReadOnly", | ||||
|   "AssetEntity"."isOffline" AS "AssetEntity_isOffline", | ||||
|   "AssetEntity"."checksum" AS "AssetEntity_checksum", | ||||
|   "AssetEntity"."duration" AS "AssetEntity_duration", | ||||
| @ -226,7 +223,6 @@ SELECT | ||||
|   "bd93d5747511a4dad4923546c51365bf1a803774"."isFavorite" AS "bd93d5747511a4dad4923546c51365bf1a803774_isFavorite", | ||||
|   "bd93d5747511a4dad4923546c51365bf1a803774"."isArchived" AS "bd93d5747511a4dad4923546c51365bf1a803774_isArchived", | ||||
|   "bd93d5747511a4dad4923546c51365bf1a803774"."isExternal" AS "bd93d5747511a4dad4923546c51365bf1a803774_isExternal", | ||||
|   "bd93d5747511a4dad4923546c51365bf1a803774"."isReadOnly" AS "bd93d5747511a4dad4923546c51365bf1a803774_isReadOnly", | ||||
|   "bd93d5747511a4dad4923546c51365bf1a803774"."isOffline" AS "bd93d5747511a4dad4923546c51365bf1a803774_isOffline", | ||||
|   "bd93d5747511a4dad4923546c51365bf1a803774"."checksum" AS "bd93d5747511a4dad4923546c51365bf1a803774_checksum", | ||||
|   "bd93d5747511a4dad4923546c51365bf1a803774"."duration" AS "bd93d5747511a4dad4923546c51365bf1a803774_duration", | ||||
| @ -308,7 +304,6 @@ FROM | ||||
|       "AssetEntity"."isFavorite" AS "AssetEntity_isFavorite", | ||||
|       "AssetEntity"."isArchived" AS "AssetEntity_isArchived", | ||||
|       "AssetEntity"."isExternal" AS "AssetEntity_isExternal", | ||||
|       "AssetEntity"."isReadOnly" AS "AssetEntity_isReadOnly", | ||||
|       "AssetEntity"."isOffline" AS "AssetEntity_isOffline", | ||||
|       "AssetEntity"."checksum" AS "AssetEntity_checksum", | ||||
|       "AssetEntity"."duration" AS "AssetEntity_duration", | ||||
| @ -405,7 +400,6 @@ SELECT | ||||
|   "AssetEntity"."isFavorite" AS "AssetEntity_isFavorite", | ||||
|   "AssetEntity"."isArchived" AS "AssetEntity_isArchived", | ||||
|   "AssetEntity"."isExternal" AS "AssetEntity_isExternal", | ||||
|   "AssetEntity"."isReadOnly" AS "AssetEntity_isReadOnly", | ||||
|   "AssetEntity"."isOffline" AS "AssetEntity_isOffline", | ||||
|   "AssetEntity"."checksum" AS "AssetEntity_checksum", | ||||
|   "AssetEntity"."duration" AS "AssetEntity_duration", | ||||
| @ -451,7 +445,6 @@ SELECT | ||||
|   "AssetEntity"."isFavorite" AS "AssetEntity_isFavorite", | ||||
|   "AssetEntity"."isArchived" AS "AssetEntity_isArchived", | ||||
|   "AssetEntity"."isExternal" AS "AssetEntity_isExternal", | ||||
|   "AssetEntity"."isReadOnly" AS "AssetEntity_isReadOnly", | ||||
|   "AssetEntity"."isOffline" AS "AssetEntity_isOffline", | ||||
|   "AssetEntity"."checksum" AS "AssetEntity_checksum", | ||||
|   "AssetEntity"."duration" AS "AssetEntity_duration", | ||||
| @ -519,7 +512,6 @@ SELECT | ||||
|   "AssetEntity"."isFavorite" AS "AssetEntity_isFavorite", | ||||
|   "AssetEntity"."isArchived" AS "AssetEntity_isArchived", | ||||
|   "AssetEntity"."isExternal" AS "AssetEntity_isExternal", | ||||
|   "AssetEntity"."isReadOnly" AS "AssetEntity_isReadOnly", | ||||
|   "AssetEntity"."isOffline" AS "AssetEntity_isOffline", | ||||
|   "AssetEntity"."checksum" AS "AssetEntity_checksum", | ||||
|   "AssetEntity"."duration" AS "AssetEntity_duration", | ||||
| @ -608,7 +600,6 @@ SELECT | ||||
|   "asset"."isFavorite" AS "asset_isFavorite", | ||||
|   "asset"."isArchived" AS "asset_isArchived", | ||||
|   "asset"."isExternal" AS "asset_isExternal", | ||||
|   "asset"."isReadOnly" AS "asset_isReadOnly", | ||||
|   "asset"."isOffline" AS "asset_isOffline", | ||||
|   "asset"."checksum" AS "asset_checksum", | ||||
|   "asset"."duration" AS "asset_duration", | ||||
| @ -667,7 +658,6 @@ SELECT | ||||
|   "stackedAssets"."isFavorite" AS "stackedAssets_isFavorite", | ||||
|   "stackedAssets"."isArchived" AS "stackedAssets_isArchived", | ||||
|   "stackedAssets"."isExternal" AS "stackedAssets_isExternal", | ||||
|   "stackedAssets"."isReadOnly" AS "stackedAssets_isReadOnly", | ||||
|   "stackedAssets"."isOffline" AS "stackedAssets_isOffline", | ||||
|   "stackedAssets"."checksum" AS "stackedAssets_checksum", | ||||
|   "stackedAssets"."duration" AS "stackedAssets_duration", | ||||
| @ -784,7 +774,6 @@ SELECT | ||||
|   "asset"."isFavorite" AS "asset_isFavorite", | ||||
|   "asset"."isArchived" AS "asset_isArchived", | ||||
|   "asset"."isExternal" AS "asset_isExternal", | ||||
|   "asset"."isReadOnly" AS "asset_isReadOnly", | ||||
|   "asset"."isOffline" AS "asset_isOffline", | ||||
|   "asset"."checksum" AS "asset_checksum", | ||||
|   "asset"."duration" AS "asset_duration", | ||||
| @ -843,7 +832,6 @@ SELECT | ||||
|   "stackedAssets"."isFavorite" AS "stackedAssets_isFavorite", | ||||
|   "stackedAssets"."isArchived" AS "stackedAssets_isArchived", | ||||
|   "stackedAssets"."isExternal" AS "stackedAssets_isExternal", | ||||
|   "stackedAssets"."isReadOnly" AS "stackedAssets_isReadOnly", | ||||
|   "stackedAssets"."isOffline" AS "stackedAssets_isOffline", | ||||
|   "stackedAssets"."checksum" AS "stackedAssets_checksum", | ||||
|   "stackedAssets"."duration" AS "stackedAssets_duration", | ||||
| @ -891,7 +879,6 @@ SELECT | ||||
|   "asset"."isFavorite" AS "asset_isFavorite", | ||||
|   "asset"."isArchived" AS "asset_isArchived", | ||||
|   "asset"."isExternal" AS "asset_isExternal", | ||||
|   "asset"."isReadOnly" AS "asset_isReadOnly", | ||||
|   "asset"."isOffline" AS "asset_isOffline", | ||||
|   "asset"."checksum" AS "asset_checksum", | ||||
|   "asset"."duration" AS "asset_duration", | ||||
| @ -950,7 +937,6 @@ SELECT | ||||
|   "stackedAssets"."isFavorite" AS "stackedAssets_isFavorite", | ||||
|   "stackedAssets"."isArchived" AS "stackedAssets_isArchived", | ||||
|   "stackedAssets"."isExternal" AS "stackedAssets_isExternal", | ||||
|   "stackedAssets"."isReadOnly" AS "stackedAssets_isReadOnly", | ||||
|   "stackedAssets"."isOffline" AS "stackedAssets_isOffline", | ||||
|   "stackedAssets"."checksum" AS "stackedAssets_checksum", | ||||
|   "stackedAssets"."duration" AS "stackedAssets_duration", | ||||
|  | ||||
| @ -165,7 +165,6 @@ FROM | ||||
|       "AssetFaceEntity__AssetFaceEntity_asset"."isFavorite" AS "AssetFaceEntity__AssetFaceEntity_asset_isFavorite", | ||||
|       "AssetFaceEntity__AssetFaceEntity_asset"."isArchived" AS "AssetFaceEntity__AssetFaceEntity_asset_isArchived", | ||||
|       "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"."checksum" AS "AssetFaceEntity__AssetFaceEntity_asset_checksum", | ||||
|       "AssetFaceEntity__AssetFaceEntity_asset"."duration" AS "AssetFaceEntity__AssetFaceEntity_asset_duration", | ||||
| @ -263,7 +262,6 @@ FROM | ||||
|       "AssetEntity"."isFavorite" AS "AssetEntity_isFavorite", | ||||
|       "AssetEntity"."isArchived" AS "AssetEntity_isArchived", | ||||
|       "AssetEntity"."isExternal" AS "AssetEntity_isExternal", | ||||
|       "AssetEntity"."isReadOnly" AS "AssetEntity_isReadOnly", | ||||
|       "AssetEntity"."isOffline" AS "AssetEntity_isOffline", | ||||
|       "AssetEntity"."checksum" AS "AssetEntity_checksum", | ||||
|       "AssetEntity"."duration" AS "AssetEntity_duration", | ||||
| @ -393,7 +391,6 @@ SELECT | ||||
|   "AssetFaceEntity__AssetFaceEntity_asset"."isFavorite" AS "AssetFaceEntity__AssetFaceEntity_asset_isFavorite", | ||||
|   "AssetFaceEntity__AssetFaceEntity_asset"."isArchived" AS "AssetFaceEntity__AssetFaceEntity_asset_isArchived", | ||||
|   "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"."checksum" AS "AssetFaceEntity__AssetFaceEntity_asset_checksum", | ||||
|   "AssetFaceEntity__AssetFaceEntity_asset"."duration" AS "AssetFaceEntity__AssetFaceEntity_asset_duration", | ||||
|  | ||||
| @ -27,7 +27,6 @@ FROM | ||||
|       "asset"."isFavorite" AS "asset_isFavorite", | ||||
|       "asset"."isArchived" AS "asset_isArchived", | ||||
|       "asset"."isExternal" AS "asset_isExternal", | ||||
|       "asset"."isReadOnly" AS "asset_isReadOnly", | ||||
|       "asset"."isOffline" AS "asset_isOffline", | ||||
|       "asset"."checksum" AS "asset_checksum", | ||||
|       "asset"."duration" AS "asset_duration", | ||||
| @ -58,7 +57,6 @@ FROM | ||||
|       "stackedAssets"."isFavorite" AS "stackedAssets_isFavorite", | ||||
|       "stackedAssets"."isArchived" AS "stackedAssets_isArchived", | ||||
|       "stackedAssets"."isExternal" AS "stackedAssets_isExternal", | ||||
|       "stackedAssets"."isReadOnly" AS "stackedAssets_isReadOnly", | ||||
|       "stackedAssets"."isOffline" AS "stackedAssets_isOffline", | ||||
|       "stackedAssets"."checksum" AS "stackedAssets_checksum", | ||||
|       "stackedAssets"."duration" AS "stackedAssets_duration", | ||||
| @ -123,7 +121,6 @@ SELECT | ||||
|   "asset"."isFavorite" AS "asset_isFavorite", | ||||
|   "asset"."isArchived" AS "asset_isArchived", | ||||
|   "asset"."isExternal" AS "asset_isExternal", | ||||
|   "asset"."isReadOnly" AS "asset_isReadOnly", | ||||
|   "asset"."isOffline" AS "asset_isOffline", | ||||
|   "asset"."checksum" AS "asset_checksum", | ||||
|   "asset"."duration" AS "asset_duration", | ||||
| @ -154,7 +151,6 @@ SELECT | ||||
|   "stackedAssets"."isFavorite" AS "stackedAssets_isFavorite", | ||||
|   "stackedAssets"."isArchived" AS "stackedAssets_isArchived", | ||||
|   "stackedAssets"."isExternal" AS "stackedAssets_isExternal", | ||||
|   "stackedAssets"."isReadOnly" AS "stackedAssets_isReadOnly", | ||||
|   "stackedAssets"."isOffline" AS "stackedAssets_isOffline", | ||||
|   "stackedAssets"."checksum" AS "stackedAssets_checksum", | ||||
|   "stackedAssets"."duration" AS "stackedAssets_duration", | ||||
| @ -333,7 +329,6 @@ SELECT | ||||
|   "asset"."isFavorite" AS "asset_isFavorite", | ||||
|   "asset"."isArchived" AS "asset_isArchived", | ||||
|   "asset"."isExternal" AS "asset_isExternal", | ||||
|   "asset"."isReadOnly" AS "asset_isReadOnly", | ||||
|   "asset"."isOffline" AS "asset_isOffline", | ||||
|   "asset"."checksum" AS "asset_checksum", | ||||
|   "asset"."duration" AS "asset_duration", | ||||
|  | ||||
| @ -41,7 +41,6 @@ FROM | ||||
|       "SharedLinkEntity__SharedLinkEntity_assets"."isFavorite" AS "SharedLinkEntity__SharedLinkEntity_assets_isFavorite", | ||||
|       "SharedLinkEntity__SharedLinkEntity_assets"."isArchived" AS "SharedLinkEntity__SharedLinkEntity_assets_isArchived", | ||||
|       "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"."checksum" AS "SharedLinkEntity__SharedLinkEntity_assets_checksum", | ||||
|       "SharedLinkEntity__SharedLinkEntity_assets"."duration" AS "SharedLinkEntity__SharedLinkEntity_assets_duration", | ||||
| @ -108,7 +107,6 @@ FROM | ||||
|       "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6"."isFavorite" AS "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6_isFavorite", | ||||
|       "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6"."isArchived" AS "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6_isArchived", | ||||
|       "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6"."isExternal" AS "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6_isExternal", | ||||
|       "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6"."isReadOnly" AS "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6_isReadOnly", | ||||
|       "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6"."isOffline" AS "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6_isOffline", | ||||
|       "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6"."checksum" AS "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6_checksum", | ||||
|       "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6"."duration" AS "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6_duration", | ||||
| @ -231,7 +229,6 @@ SELECT | ||||
|   "SharedLinkEntity__SharedLinkEntity_assets"."isFavorite" AS "SharedLinkEntity__SharedLinkEntity_assets_isFavorite", | ||||
|   "SharedLinkEntity__SharedLinkEntity_assets"."isArchived" AS "SharedLinkEntity__SharedLinkEntity_assets_isArchived", | ||||
|   "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"."checksum" AS "SharedLinkEntity__SharedLinkEntity_assets_checksum", | ||||
|   "SharedLinkEntity__SharedLinkEntity_assets"."duration" AS "SharedLinkEntity__SharedLinkEntity_assets_duration", | ||||
|  | ||||
| @ -151,6 +151,14 @@ GROUP BY | ||||
| ORDER BY | ||||
|   "users"."createdAt" ASC | ||||
| 
 | ||||
| -- UserRepository.updateUsage | ||||
| UPDATE "users" | ||||
| SET | ||||
|   "quotaUsageInBytes" = "quotaUsageInBytes" + 50, | ||||
|   "updatedAt" = CURRENT_TIMESTAMP | ||||
| WHERE | ||||
|   "id" = $1 | ||||
| 
 | ||||
| -- UserRepository.syncUsage | ||||
| UPDATE "users" | ||||
| SET | ||||
|  | ||||
| @ -253,7 +253,7 @@ export class AssetRepository implements IAssetRepository { | ||||
| 
 | ||||
|   @Chunked() | ||||
|   async softDeleteAll(ids: string[]): Promise<void> { | ||||
|     await this.repository.softDelete({ id: In(ids), isExternal: false }); | ||||
|     await this.repository.softDelete({ id: In(ids) }); | ||||
|   } | ||||
| 
 | ||||
|   @Chunked() | ||||
|  | ||||
| @ -112,6 +112,7 @@ export class UserRepository implements IUserRepository { | ||||
|     return stats; | ||||
|   } | ||||
| 
 | ||||
|   @GenerateSql({ params: [DummyValue.UUID, DummyValue.NUMBER] }) | ||||
|   async updateUsage(id: string, delta: number): Promise<void> { | ||||
|     await this.userRepository.increment({ id }, 'quotaUsageInBytes', delta); | ||||
|   } | ||||
|  | ||||
| @ -295,7 +295,6 @@ export class AssetServiceV1 { | ||||
|       livePhotoVideo: livePhotoAssetId === null ? null : ({ id: livePhotoAssetId } as AssetEntity), | ||||
|       originalFileName: file.originalName, | ||||
|       sidecarPath: sidecarPath || null, | ||||
|       isReadOnly: dto.isReadOnly ?? false, | ||||
|       isOffline: dto.isOffline ?? false, | ||||
|     }); | ||||
| 
 | ||||
|  | ||||
| @ -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 () => { | ||||
|       assetMock.getById.mockResolvedValue(assetStub.livePhotoStillAsset); | ||||
| 
 | ||||
|  | ||||
| @ -33,7 +33,7 @@ import { IAssetStackRepository } from 'src/interfaces/asset-stack.interface'; | ||||
| import { IAssetRepository } from 'src/interfaces/asset.interface'; | ||||
| import { ClientEvent, IEventRepository } from 'src/interfaces/event.interface'; | ||||
| import { | ||||
|   IAssetDeletionJob, | ||||
|   IEntityJob, | ||||
|   IJobRepository, | ||||
|   ISidecarWriteJob, | ||||
|   JOBS_ASSET_PAGINATION_SIZE, | ||||
| @ -371,8 +371,8 @@ export class AssetService { | ||||
|     return JobStatus.SUCCESS; | ||||
|   } | ||||
| 
 | ||||
|   async handleAssetDeletion(job: IAssetDeletionJob): Promise<JobStatus> { | ||||
|     const { id, fromExternal } = job; | ||||
|   async handleAssetDeletion(job: IEntityJob): Promise<JobStatus> { | ||||
|     const { id } = job; | ||||
| 
 | ||||
|     const asset = await this.assetRepository.getById(id, { | ||||
|       faces: { | ||||
| @ -387,11 +387,6 @@ export class AssetService { | ||||
|       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
 | ||||
|     if (asset.stack?.primaryAssetId === id) { | ||||
|       const stackAssetIds = asset.stack.assets.map((a) => a.id); | ||||
| @ -414,18 +409,15 @@ export class AssetService { | ||||
| 
 | ||||
|     // TODO refactor this to use cascades
 | ||||
|     if (asset.livePhotoVideoId) { | ||||
|       await this.jobRepository.queue({ | ||||
|         name: JobName.ASSET_DELETION, | ||||
|         data: { id: asset.livePhotoVideoId, fromExternal }, | ||||
|       }); | ||||
|       await this.jobRepository.queue({ name: JobName.ASSET_DELETION, data: { id: asset.livePhotoVideoId } }); | ||||
|     } | ||||
| 
 | ||||
|     const files = [asset.thumbnailPath, asset.previewPath, asset.encodedVideoPath]; | ||||
|     if (!(asset.isExternal || asset.isReadOnly)) { | ||||
|       files.push(asset.sidecarPath, asset.originalPath); | ||||
|     } | ||||
| 
 | ||||
|     await this.jobRepository.queue({ name: JobName.DELETE_FILES, data: { files } }); | ||||
|     await this.jobRepository.queue({ | ||||
|       name: JobName.DELETE_FILES, | ||||
|       data: { | ||||
|         files: [asset.thumbnailPath, asset.previewPath, asset.encodedVideoPath, asset.sidecarPath, asset.originalPath], | ||||
|       }, | ||||
|     }); | ||||
| 
 | ||||
|     return JobStatus.SUCCESS; | ||||
|   } | ||||
|  | ||||
| @ -368,7 +368,6 @@ describe(LibraryService.name, () => { | ||||
|             type: AssetType.IMAGE, | ||||
|             originalFileName: 'photo.jpg', | ||||
|             sidecarPath: null, | ||||
|             isReadOnly: true, | ||||
|             isExternal: true, | ||||
|           }, | ||||
|         ], | ||||
| @ -416,7 +415,6 @@ describe(LibraryService.name, () => { | ||||
|             type: AssetType.IMAGE, | ||||
|             originalFileName: 'photo.jpg', | ||||
|             sidecarPath: '/data/user1/photo.jpg.xmp', | ||||
|             isReadOnly: true, | ||||
|             isExternal: true, | ||||
|           }, | ||||
|         ], | ||||
| @ -463,7 +461,6 @@ describe(LibraryService.name, () => { | ||||
|             type: AssetType.VIDEO, | ||||
|             originalFileName: 'video.mp4', | ||||
|             sidecarPath: null, | ||||
|             isReadOnly: true, | ||||
|             isExternal: true, | ||||
|           }, | ||||
|         ], | ||||
| @ -1458,10 +1455,7 @@ describe(LibraryService.name, () => { | ||||
|       await expect(sut.handleOfflineRemoval({ id: libraryStub.externalLibrary1.id })).resolves.toBe(JobStatus.SUCCESS); | ||||
| 
 | ||||
|       expect(jobMock.queueAll).toHaveBeenCalledWith([ | ||||
|         { | ||||
|           name: JobName.ASSET_DELETION, | ||||
|           data: { id: assetStub.image1.id, fromExternal: true }, | ||||
|         }, | ||||
|         { name: JobName.ASSET_DELETION, data: { id: assetStub.image1.id } }, | ||||
|       ]); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
| @ -387,7 +387,7 @@ export class LibraryService { | ||||
|     const assetIds = await this.repository.getAssetIds(job.id, true); | ||||
|     this.logger.debug(`Will delete ${assetIds.length} asset(s) in library ${job.id}`); | ||||
|     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) { | ||||
| @ -503,7 +503,6 @@ export class LibraryService { | ||||
|         type: assetType, | ||||
|         originalFileName, | ||||
|         sidecarPath, | ||||
|         isReadOnly: true, | ||||
|         isExternal: true, | ||||
|       }); | ||||
|       assetId = addedAsset.id; | ||||
| @ -580,7 +579,7 @@ export class LibraryService { | ||||
|     for await (const assets of assetPagination) { | ||||
|       this.logger.debug(`Removing ${assets.length} offline assets`); | ||||
|       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 } })), | ||||
|       ); | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -440,7 +440,6 @@ export class MetadataService { | ||||
|           originalPath: motionPath, | ||||
|           originalFileName: asset.originalFileName, | ||||
|           isVisible: false, | ||||
|           isReadOnly: false, | ||||
|           deviceAssetId: 'NONE', | ||||
|           deviceId: 'NONE', | ||||
|         }); | ||||
|  | ||||
| @ -558,26 +558,5 @@ describe(StorageTemplateService.name, () => { | ||||
|       ); | ||||
|       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(); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
|  | ||||
| @ -170,7 +170,7 @@ export class StorageTemplateService { | ||||
|   } | ||||
| 
 | ||||
|   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
 | ||||
|       // TODO: shouldn't this only apply to external assets?
 | ||||
|       return; | ||||
|  | ||||
| @ -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 { | ||||
|     isArchived, | ||||
|     isEncoded, | ||||
|  | ||||
							
								
								
									
										19
									
								
								server/test/fixtures/asset.stub.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										19
									
								
								server/test/fixtures/asset.stub.ts
									
									
									
									
										vendored
									
									
								
							| @ -45,7 +45,6 @@ export const assetStub = { | ||||
|     sharedLinks: [], | ||||
|     faces: [], | ||||
|     sidecarPath: null, | ||||
|     isReadOnly: false, | ||||
|     deletedAt: null, | ||||
|     isOffline: false, | ||||
|     isExternal: false, | ||||
| @ -82,7 +81,6 @@ export const assetStub = { | ||||
|     originalFileName: 'IMG_456.jpg', | ||||
|     faces: [], | ||||
|     sidecarPath: null, | ||||
|     isReadOnly: false, | ||||
|     isOffline: false, | ||||
|     isExternal: false, | ||||
|     libraryId: 'library-id', | ||||
| @ -113,7 +111,6 @@ export const assetStub = { | ||||
|     localDateTime: new Date('2023-02-23T05:06:29.716Z'), | ||||
|     isFavorite: true, | ||||
|     isArchived: false, | ||||
|     isReadOnly: false, | ||||
|     isOffline: false, | ||||
|     libraryId: 'library-id', | ||||
|     library: libraryStub.uploadLibrary1, | ||||
| @ -150,7 +147,6 @@ export const assetStub = { | ||||
|     localDateTime: new Date('2023-02-23T05:06:29.716Z'), | ||||
|     isFavorite: true, | ||||
|     isArchived: false, | ||||
|     isReadOnly: false, | ||||
|     duration: null, | ||||
|     isVisible: true, | ||||
|     isExternal: false, | ||||
| @ -195,7 +191,6 @@ export const assetStub = { | ||||
|     localDateTime: new Date('2023-02-23T05:06:29.716Z'), | ||||
|     isFavorite: true, | ||||
|     isArchived: false, | ||||
|     isReadOnly: false, | ||||
|     duration: null, | ||||
|     isVisible: true, | ||||
|     isExternal: false, | ||||
| @ -235,7 +230,6 @@ export const assetStub = { | ||||
|     localDateTime: new Date('2023-02-23T05:06:29.716Z'), | ||||
|     isFavorite: true, | ||||
|     isArchived: false, | ||||
|     isReadOnly: false, | ||||
|     isExternal: true, | ||||
|     duration: null, | ||||
|     isVisible: true, | ||||
| @ -275,7 +269,6 @@ export const assetStub = { | ||||
|     localDateTime: new Date('2023-02-23T05:06:29.716Z'), | ||||
|     isFavorite: true, | ||||
|     isArchived: false, | ||||
|     isReadOnly: false, | ||||
|     isExternal: false, | ||||
|     duration: null, | ||||
|     isVisible: true, | ||||
| @ -315,7 +308,6 @@ export const assetStub = { | ||||
|     localDateTime: new Date('2023-02-23T05:06:29.716Z'), | ||||
|     isFavorite: true, | ||||
|     isArchived: false, | ||||
|     isReadOnly: false, | ||||
|     isExternal: true, | ||||
|     duration: null, | ||||
|     isVisible: true, | ||||
| @ -356,7 +348,6 @@ export const assetStub = { | ||||
|     localDateTime: new Date('2023-02-23T05:06:29.716Z'), | ||||
|     isFavorite: true, | ||||
|     isArchived: false, | ||||
|     isReadOnly: false, | ||||
|     duration: null, | ||||
|     isVisible: true, | ||||
|     livePhotoVideo: null, | ||||
| @ -396,7 +387,6 @@ export const assetStub = { | ||||
|     isFavorite: true, | ||||
|     isArchived: false, | ||||
|     isExternal: false, | ||||
|     isReadOnly: false, | ||||
|     isOffline: false, | ||||
|     libraryId: 'library-id', | ||||
|     library: libraryStub.uploadLibrary1, | ||||
| @ -436,7 +426,6 @@ export const assetStub = { | ||||
|     localDateTime: new Date('2023-02-23T05:06:29.716Z'), | ||||
|     isFavorite: true, | ||||
|     isArchived: false, | ||||
|     isReadOnly: false, | ||||
|     isExternal: false, | ||||
|     isOffline: false, | ||||
|     libraryId: 'library-id', | ||||
| @ -527,7 +516,6 @@ export const assetStub = { | ||||
|     localDateTime: new Date('2023-02-22T05:06:29.716Z'), | ||||
|     isFavorite: false, | ||||
|     isArchived: false, | ||||
|     isReadOnly: false, | ||||
|     isExternal: false, | ||||
|     isOffline: false, | ||||
|     libraryId: 'library-id', | ||||
| @ -570,7 +558,6 @@ export const assetStub = { | ||||
|     localDateTime: new Date('2023-02-23T05:06:29.716Z'), | ||||
|     isFavorite: true, | ||||
|     isArchived: false, | ||||
|     isReadOnly: false, | ||||
|     isExternal: false, | ||||
|     isOffline: false, | ||||
|     libraryId: 'library-id', | ||||
| @ -606,7 +593,6 @@ export const assetStub = { | ||||
|     localDateTime: new Date('2023-02-23T05:06:29.716Z'), | ||||
|     isFavorite: true, | ||||
|     isArchived: false, | ||||
|     isReadOnly: false, | ||||
|     isExternal: false, | ||||
|     isOffline: false, | ||||
|     libraryId: 'library-id', | ||||
| @ -643,7 +629,6 @@ export const assetStub = { | ||||
|     localDateTime: new Date('2023-02-23T05:06:29.716Z'), | ||||
|     isFavorite: true, | ||||
|     isArchived: false, | ||||
|     isReadOnly: true, | ||||
|     isExternal: false, | ||||
|     isOffline: false, | ||||
|     libraryId: 'library-id', | ||||
| @ -681,7 +666,6 @@ export const assetStub = { | ||||
|     localDateTime: new Date('2023-02-23T05:06:29.716Z'), | ||||
|     isFavorite: true, | ||||
|     isArchived: false, | ||||
|     isReadOnly: false, | ||||
|     isExternal: false, | ||||
|     isOffline: false, | ||||
|     libraryId: 'library-id', | ||||
| @ -719,7 +703,6 @@ export const assetStub = { | ||||
|     localDateTime: new Date('2023-02-23T05:06:29.716Z'), | ||||
|     isFavorite: true, | ||||
|     isArchived: false, | ||||
|     isReadOnly: false, | ||||
|     isExternal: true, | ||||
|     duration: null, | ||||
|     isVisible: true, | ||||
| @ -758,7 +741,6 @@ export const assetStub = { | ||||
|     localDateTime: new Date('2023-02-23T05:06:29.716Z'), | ||||
|     isFavorite: true, | ||||
|     isArchived: false, | ||||
|     isReadOnly: false, | ||||
|     isExternal: true, | ||||
|     duration: null, | ||||
|     isVisible: true, | ||||
| @ -797,7 +779,6 @@ export const assetStub = { | ||||
|     localDateTime: new Date('2023-02-23T05:06:29.716Z'), | ||||
|     isFavorite: true, | ||||
|     isArchived: false, | ||||
|     isReadOnly: false, | ||||
|     duration: null, | ||||
|     isVisible: true, | ||||
|     isExternal: false, | ||||
|  | ||||
							
								
								
									
										2
									
								
								server/test/fixtures/shared-link.stub.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								server/test/fixtures/shared-link.stub.ts
									
									
									
									
										vendored
									
									
								
							| @ -59,7 +59,6 @@ const assetResponse: AssetResponseDto = { | ||||
|   thumbhash: null, | ||||
|   fileModifiedAt: today, | ||||
|   isExternal: false, | ||||
|   isReadOnly: false, | ||||
|   isOffline: false, | ||||
|   fileCreatedAt: today, | ||||
|   localDateTime: today, | ||||
| @ -210,7 +209,6 @@ export const sharedLinkStub = { | ||||
|           isFavorite: false, | ||||
|           isArchived: false, | ||||
|           isExternal: false, | ||||
|           isReadOnly: false, | ||||
|           isOffline: false, | ||||
|           libraryId: 'library-id', | ||||
|           library: libraryStub.uploadLibrary1, | ||||
|  | ||||
| @ -193,9 +193,7 @@ | ||||
|     {/if} | ||||
| 
 | ||||
|     {#if isOwner} | ||||
|       {#if !asset.isReadOnly || !asset.isExternal} | ||||
|         <CircleIconButton color="opaque" icon={mdiDeleteOutline} on:click={() => dispatch('delete')} title="Delete" /> | ||||
|       {/if} | ||||
|       <CircleIconButton color="opaque" icon={mdiDeleteOutline} on:click={() => dispatch('delete')} title="Delete" /> | ||||
|       <div | ||||
|         use:clickOutside={{ | ||||
|           onOutclick: () => (isShowAssetOptions = false), | ||||
|  | ||||
| @ -308,23 +308,15 @@ | ||||
|   {/if} | ||||
| 
 | ||||
|   <div class="px-4 py-4"> | ||||
|     {#if !asset.exifInfo && !asset.isExternal} | ||||
|       <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} | ||||
|     {#if asset.exifInfo} | ||||
|       <div class="flex h-10 w-full items-center justify-between text-sm"> | ||||
|         <h2>DETAILS</h2> | ||||
|       </div> | ||||
|     {:else} | ||||
|       <p class="text-sm">NO EXIF INFO AVAILABLE</p> | ||||
|     {/if} | ||||
| 
 | ||||
|     {#if asset.exifInfo?.dateTimeOriginal && !asset.isReadOnly} | ||||
|     {#if asset.exifInfo?.dateTimeOriginal} | ||||
|       {@const assetDateTimeOriginal = DateTime.fromISO(asset.exifInfo.dateTimeOriginal, { | ||||
|         zone: asset.exifInfo.timeZone ?? undefined, | ||||
|       })} | ||||
| @ -374,7 +366,7 @@ | ||||
|           </div> | ||||
|         {/if} | ||||
|       </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 gap-4"> | ||||
|           <div> | ||||
| @ -385,43 +377,6 @@ | ||||
|           <Icon path={mdiPencil} size="20" /> | ||||
|         </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 isShowChangeDate} | ||||
| @ -501,7 +456,7 @@ | ||||
|       </div> | ||||
|     {/if} | ||||
| 
 | ||||
|     {#if asset.exifInfo?.city && !asset.isReadOnly} | ||||
|     {#if asset.exifInfo?.city} | ||||
|       <button | ||||
|         type="button" | ||||
|         class="flex w-full text-left justify-between place-items-start gap-4 py-4" | ||||
| @ -534,7 +489,7 @@ | ||||
|           </div> | ||||
|         {/if} | ||||
|       </button> | ||||
|     {:else if !asset.exifInfo?.city && !asset.isReadOnly && isOwner} | ||||
|     {:else if !asset.exifInfo?.city && isOwner} | ||||
|       <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" | ||||
| @ -552,26 +507,6 @@ | ||||
|           <Icon path={mdiPencil} size="20" /> | ||||
|         </div> | ||||
|       </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 isShowChangeLocation} | ||||
|       <ChangeLocation | ||||
|  | ||||
| @ -34,7 +34,7 @@ | ||||
| 
 | ||||
|   const handleDelete = async () => { | ||||
|     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); | ||||
|     clearSelect(); | ||||
|     isShowConfirmation = false; | ||||
|  | ||||
| @ -47,7 +47,7 @@ | ||||
| 
 | ||||
|   $: timelineY = element?.scrollTop || 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); | ||||
|  | ||||
| @ -258,9 +258,9 @@ export const getAssetType = (type: AssetTypeEnum) => { | ||||
| }; | ||||
| 
 | ||||
| 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) { | ||||
|     notificationController.show({ | ||||
|       message: `Can't change metadata of ${numberOfIssues} asset${numberOfIssues > 1 ? 's' : ''}`, | ||||
|  | ||||
| @ -22,9 +22,7 @@ export const assetFactory = Sync.makeFactory<AssetResponseDto>({ | ||||
|   isTrashed: Sync.each(() => faker.datatype.boolean()), | ||||
|   duration: '0:00:00.00000', | ||||
|   checksum: Sync.each(() => faker.string.alphanumeric(28)), | ||||
|   isExternal: Sync.each(() => faker.datatype.boolean()), | ||||
|   isOffline: Sync.each(() => faker.datatype.boolean()), | ||||
|   isReadOnly: Sync.each(() => faker.datatype.boolean()), | ||||
|   hasMetadata: Sync.each(() => faker.datatype.boolean()), | ||||
|   stackCount: null, | ||||
| }); | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user