nostr images fetching from API
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
import 'package:dio/dio.dart';
|
||||
import '../local/local_storage_service.dart';
|
||||
import '../local/models/item.dart';
|
||||
@@ -127,6 +128,9 @@ class ImmichService {
|
||||
|
||||
/// Fetches a list of assets from Immich.
|
||||
///
|
||||
/// Based on official Immich API documentation: https://api.immich.app/endpoints/search/searchAssets
|
||||
/// Uses POST /api/search/metadata endpoint with search parameters.
|
||||
///
|
||||
/// [limit] - Maximum number of assets to fetch (default: 100).
|
||||
/// [skip] - Number of assets to skip (for pagination).
|
||||
///
|
||||
@@ -139,22 +143,41 @@ class ImmichService {
|
||||
int skip = 0,
|
||||
}) async {
|
||||
try {
|
||||
final response = await _dio.get(
|
||||
'/api/asset',
|
||||
queryParameters: {
|
||||
// Official endpoint: POST /api/search/metadata
|
||||
// Response structure: {"assets": {"items": [...], "total": N, "count": N, "nextPage": ...}}
|
||||
final response = await _dio.post(
|
||||
'/api/search/metadata',
|
||||
data: {
|
||||
'limit': limit,
|
||||
'skip': skip,
|
||||
// Empty search to get all assets
|
||||
},
|
||||
);
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
if (response.statusCode != 200 && response.statusCode != 201) {
|
||||
throw ImmichException(
|
||||
'Failed to fetch assets: ${response.statusMessage}',
|
||||
response.statusCode,
|
||||
);
|
||||
}
|
||||
|
||||
final List<dynamic> assetsJson = response.data;
|
||||
// Parse response structure: {"assets": {"items": [...], "total": N, "count": N}}
|
||||
final responseData = response.data as Map<String, dynamic>;
|
||||
|
||||
if (!responseData.containsKey('assets')) {
|
||||
throw ImmichException('Unexpected response format: missing "assets" field');
|
||||
}
|
||||
|
||||
final assetsData = responseData['assets'] as Map<String, dynamic>;
|
||||
|
||||
if (!assetsData.containsKey('items')) {
|
||||
throw ImmichException('Unexpected response format: missing "items" field in assets');
|
||||
}
|
||||
|
||||
final assetsJson = assetsData['items'] as List<dynamic>;
|
||||
final total = assetsData['total'] as int? ?? 0;
|
||||
final count = assetsData['count'] as int? ?? 0;
|
||||
|
||||
final List<ImmichAsset> assets = assetsJson
|
||||
.map((json) => ImmichAsset.fromJson(json as Map<String, dynamic>))
|
||||
.toList();
|
||||
@@ -166,25 +189,45 @@ class ImmichService {
|
||||
|
||||
return assets;
|
||||
} on DioException catch (e) {
|
||||
final statusCode = e.response?.statusCode;
|
||||
final errorData = e.response?.data;
|
||||
String errorMessage;
|
||||
|
||||
if (errorData is Map) {
|
||||
errorMessage = errorData['message']?.toString() ??
|
||||
errorData.toString();
|
||||
} else {
|
||||
errorMessage = errorData?.toString() ??
|
||||
e.message ??
|
||||
'Unknown error';
|
||||
}
|
||||
|
||||
throw ImmichException(
|
||||
'Failed to fetch assets: ${e.message ?? 'Unknown error'}',
|
||||
e.response?.statusCode,
|
||||
'Failed to fetch assets: $errorMessage',
|
||||
statusCode,
|
||||
);
|
||||
} catch (e) {
|
||||
if (e is ImmichException) {
|
||||
rethrow;
|
||||
}
|
||||
throw ImmichException('Failed to fetch assets: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Fetches a single asset by ID.
|
||||
///
|
||||
/// [assetId] - The unique identifier of the asset.
|
||||
/// Based on official Immich API documentation: https://api.immich.app/endpoints/assets
|
||||
/// Endpoint: GET /api/assets/{id}
|
||||
///
|
||||
/// [assetId] - The unique identifier (UUID) of the asset.
|
||||
///
|
||||
/// Returns [ImmichAsset] if found.
|
||||
///
|
||||
/// Throws [ImmichException] if fetch fails.
|
||||
Future<ImmichAsset> _getAssetById(String assetId) async {
|
||||
try {
|
||||
final response = await _dio.get('/api/asset/$assetId');
|
||||
// Official endpoint: GET /api/assets/{id}
|
||||
final response = await _dio.get('/api/assets/$assetId');
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
throw ImmichException(
|
||||
@@ -195,9 +238,22 @@ class ImmichService {
|
||||
|
||||
return ImmichAsset.fromJson(response.data as Map<String, dynamic>);
|
||||
} on DioException catch (e) {
|
||||
final statusCode = e.response?.statusCode;
|
||||
final errorData = e.response?.data;
|
||||
String errorMessage;
|
||||
|
||||
if (errorData is Map) {
|
||||
errorMessage = errorData['message']?.toString() ??
|
||||
errorData.toString();
|
||||
} else {
|
||||
errorMessage = errorData?.toString() ??
|
||||
e.message ??
|
||||
'Unknown error';
|
||||
}
|
||||
|
||||
throw ImmichException(
|
||||
'Failed to fetch asset: ${e.message ?? 'Unknown error'}',
|
||||
e.response?.statusCode,
|
||||
'Failed to fetch asset: $errorMessage',
|
||||
statusCode,
|
||||
);
|
||||
} catch (e) {
|
||||
throw ImmichException('Failed to fetch asset: $e');
|
||||
@@ -261,5 +317,134 @@ class ImmichService {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the thumbnail URL for an asset.
|
||||
///
|
||||
/// Uses GET /api/assets/{id}/thumbnail endpoint.
|
||||
///
|
||||
/// [assetId] - The unique identifier of the asset.
|
||||
///
|
||||
/// Returns the full URL to the thumbnail image.
|
||||
String getThumbnailUrl(String assetId) {
|
||||
return '$_baseUrl/api/assets/$assetId/thumbnail';
|
||||
}
|
||||
|
||||
/// Gets the full image URL for an asset.
|
||||
///
|
||||
/// Uses GET /api/assets/{id}/original endpoint.
|
||||
///
|
||||
/// [assetId] - The unique identifier of the asset.
|
||||
///
|
||||
/// Returns the full URL to the original image file.
|
||||
String getImageUrl(String assetId) {
|
||||
return '$_baseUrl/api/assets/$assetId/original';
|
||||
}
|
||||
|
||||
/// Gets the base URL for Immich API.
|
||||
String get baseUrl => _baseUrl;
|
||||
|
||||
/// Fetches image bytes for an asset.
|
||||
///
|
||||
/// Uses GET /api/assets/{id}/thumbnail for thumbnails or GET /api/assets/{id}/original for full images.
|
||||
///
|
||||
/// [assetId] - The unique identifier of the asset (from metadata response).
|
||||
/// [isThumbnail] - Whether to fetch thumbnail (true) or original image (false). Default: true.
|
||||
///
|
||||
/// Returns the image bytes as Uint8List.
|
||||
///
|
||||
/// Throws [ImmichException] if fetch fails.
|
||||
Future<Uint8List> fetchImageBytes(String assetId, {bool isThumbnail = true}) async {
|
||||
try {
|
||||
// Use correct endpoint based on thumbnail vs original
|
||||
final endpoint = isThumbnail
|
||||
? '/api/assets/$assetId/thumbnail'
|
||||
: '/api/assets/$assetId/original';
|
||||
|
||||
final response = await _dio.get<List<int>>(
|
||||
endpoint,
|
||||
options: Options(
|
||||
responseType: ResponseType.bytes,
|
||||
),
|
||||
);
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
throw ImmichException(
|
||||
'Failed to fetch image: ${response.statusMessage}',
|
||||
response.statusCode,
|
||||
);
|
||||
}
|
||||
|
||||
return Uint8List.fromList(response.data ?? []);
|
||||
} on DioException catch (e) {
|
||||
final statusCode = e.response?.statusCode;
|
||||
final errorData = e.response?.data;
|
||||
String errorMessage;
|
||||
|
||||
if (errorData is Map) {
|
||||
errorMessage = errorData['message']?.toString() ??
|
||||
errorData.toString();
|
||||
} else {
|
||||
errorMessage = errorData?.toString() ??
|
||||
e.message ??
|
||||
'Unknown error';
|
||||
}
|
||||
|
||||
throw ImmichException(
|
||||
'Failed to fetch image: $errorMessage',
|
||||
statusCode,
|
||||
);
|
||||
} catch (e) {
|
||||
throw ImmichException('Failed to fetch image: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the headers needed for authenticated image requests.
|
||||
///
|
||||
/// Returns a map of headers including the API key.
|
||||
Map<String, String> getImageHeaders() {
|
||||
return {
|
||||
'x-api-key': _apiKey,
|
||||
};
|
||||
}
|
||||
|
||||
/// Tests the connection to Immich server by calling the /api/server/about endpoint.
|
||||
///
|
||||
/// Returns server information including version and status.
|
||||
///
|
||||
/// Throws [ImmichException] if the request fails.
|
||||
Future<Map<String, dynamic>> getServerInfo() async {
|
||||
try {
|
||||
final response = await _dio.get('/api/server/about');
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
throw ImmichException(
|
||||
'Failed to get server info: ${response.statusMessage}',
|
||||
response.statusCode,
|
||||
);
|
||||
}
|
||||
|
||||
return response.data as Map<String, dynamic>;
|
||||
} on DioException catch (e) {
|
||||
final statusCode = e.response?.statusCode;
|
||||
final errorData = e.response?.data;
|
||||
String errorMessage;
|
||||
|
||||
if (errorData is Map) {
|
||||
errorMessage = errorData['message']?.toString() ??
|
||||
errorData.toString();
|
||||
} else {
|
||||
errorMessage = errorData?.toString() ??
|
||||
e.message ??
|
||||
'Unknown error';
|
||||
}
|
||||
|
||||
throw ImmichException(
|
||||
'Failed to get server info: $errorMessage',
|
||||
statusCode,
|
||||
);
|
||||
} catch (e) {
|
||||
throw ImmichException('Failed to get server info: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user