178 lines
4.8 KiB
Dart
178 lines
4.8 KiB
Dart
// lib/remote/remote_models.dart
|
|
import 'url_utils.dart';
|
|
|
|
class RemotePhotoItem {
|
|
final String id;
|
|
final String name;
|
|
final String path;
|
|
final String? thub1, thub2;
|
|
final String? mimeType;
|
|
final int? width, height, sizeBytes;
|
|
final int? rotation;
|
|
|
|
/// EXIF/metadata
|
|
final DateTime? takenAtUtc;
|
|
final String? dataExifLegacy;
|
|
|
|
/// GPS + location
|
|
final double? lat, lng, alt;
|
|
final RemoteLocation? location;
|
|
|
|
/// extra
|
|
final String? user;
|
|
final String? cartella; // ✅ utile (folder)
|
|
final String? fastHash; // ✅ utile per link locale<->remoto
|
|
final int? durationMillis;
|
|
|
|
/// ✅ Timestamp server-side per sync robusta
|
|
final DateTime? createdAtUtc;
|
|
final DateTime? updatedAtUtc;
|
|
final DateTime? deletedAtUtc; // soft delete
|
|
|
|
/// ✅ timestamp file (ms) dal server (per dateModifiedMillis DB)
|
|
final int? mtimeMs;
|
|
|
|
RemotePhotoItem({
|
|
required this.id,
|
|
required this.name,
|
|
required this.path,
|
|
this.thub1,
|
|
this.thub2,
|
|
this.mimeType,
|
|
this.width,
|
|
this.height,
|
|
this.rotation,
|
|
this.sizeBytes,
|
|
this.takenAtUtc,
|
|
this.dataExifLegacy,
|
|
this.lat,
|
|
this.lng,
|
|
this.alt,
|
|
this.user,
|
|
this.cartella,
|
|
this.fastHash,
|
|
this.durationMillis,
|
|
this.location,
|
|
this.createdAtUtc,
|
|
this.updatedAtUtc,
|
|
this.deletedAtUtc,
|
|
this.mtimeMs,
|
|
});
|
|
|
|
// Costruzione URL assoluto delegata a utility (in base alle impostazioni)
|
|
String absoluteUrl(String baseUrl) => buildAbsoluteUri(baseUrl, path).toString();
|
|
|
|
static DateTime? _tryParseIsoUtc(dynamic v) {
|
|
if (v == null) return null;
|
|
try {
|
|
return DateTime.parse(v.toString()).toUtc();
|
|
} catch (_) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
static double? _toDouble(dynamic v) {
|
|
if (v == null) return null;
|
|
if (v is num) return v.toDouble();
|
|
return double.tryParse(v.toString());
|
|
}
|
|
|
|
/// Converte secondi→ms se < 1000, altrimenti assume già millisecondi.
|
|
static int? _toMillis(dynamic v) {
|
|
if (v == null) return null;
|
|
final num? n = (v is num) ? v : num.tryParse(v.toString());
|
|
if (n == null) return null;
|
|
return n >= 1000 ? n.toInt() : (n * 1000).toInt();
|
|
}
|
|
|
|
static int? _toIntMs(dynamic v) {
|
|
if (v == null) return null;
|
|
final num? n = (v is num) ? v : num.tryParse(v.toString());
|
|
if (n == null) return null;
|
|
// mtimeMs nel tuo esempio è float: 1775724485418.3494
|
|
return n.round();
|
|
}
|
|
|
|
/// ✅ helper: soft delete?
|
|
bool get isSoftDeleted => deletedAtUtc != null;
|
|
|
|
factory RemotePhotoItem.fromJson(Map<String, dynamic> j) {
|
|
final gps = j['gps'] as Map<String, dynamic>?;
|
|
final loc = j['location'] is Map<String, dynamic>
|
|
? RemoteLocation.fromJson(j['location'] as Map<String, dynamic>)
|
|
: null;
|
|
|
|
return RemotePhotoItem(
|
|
id: (j['id'] ?? j['name']).toString(),
|
|
name: (j['name'] ?? '').toString(),
|
|
path: (j['path'] ?? '').toString(),
|
|
thub1: j['thub1']?.toString(),
|
|
thub2: j['thub2']?.toString(),
|
|
mimeType: j['mime_type']?.toString(),
|
|
width: (j['width'] as num?)?.toInt(),
|
|
height: (j['height'] as num?)?.toInt(),
|
|
rotation: (j['rotation'] as num?)?.toInt(),
|
|
sizeBytes: (j['size_bytes'] as num?)?.toInt(),
|
|
|
|
takenAtUtc: _tryParseIsoUtc(j['taken_at']),
|
|
dataExifLegacy: j['data']?.toString(),
|
|
|
|
lat: gps != null ? _toDouble(gps['lat']) : null,
|
|
lng: gps != null ? _toDouble(gps['lng']) : null,
|
|
alt: gps != null ? _toDouble(gps['alt']) : null,
|
|
|
|
user: j['user']?.toString(),
|
|
cartella: j['cartella']?.toString(),
|
|
fastHash: j['fast_hash']?.toString(),
|
|
|
|
// durata: dal server è duration_ms
|
|
durationMillis: _toMillis(j['duration_ms']),
|
|
|
|
location: loc,
|
|
|
|
// ✅ campi sync
|
|
createdAtUtc: _tryParseIsoUtc(j['created_at']),
|
|
updatedAtUtc: _tryParseIsoUtc(j['updated_at']),
|
|
deletedAtUtc: _tryParseIsoUtc(j['deleted_at']),
|
|
|
|
// ✅ per DB dateModifiedMillis
|
|
mtimeMs: _toIntMs(j['mtimeMs']),
|
|
);
|
|
}
|
|
}
|
|
|
|
class RemoteLocation {
|
|
final String? continent;
|
|
final String? country;
|
|
final String? region;
|
|
final String? postcode;
|
|
final String? city;
|
|
final String? countyCode;
|
|
final String? address;
|
|
final String? timezone;
|
|
final String? timeOffset;
|
|
|
|
RemoteLocation({
|
|
this.continent,
|
|
this.country,
|
|
this.region,
|
|
this.postcode,
|
|
this.city,
|
|
this.countyCode,
|
|
this.address,
|
|
this.timezone,
|
|
this.timeOffset,
|
|
});
|
|
|
|
factory RemoteLocation.fromJson(Map<String, dynamic> j) => RemoteLocation(
|
|
continent: j['continent']?.toString(),
|
|
country: j['country']?.toString(),
|
|
region: j['region']?.toString(),
|
|
postcode: j['postcode']?.toString(),
|
|
city: j['city']?.toString(),
|
|
countyCode: j['county_code']?.toString(),
|
|
address: j['address']?.toString(),
|
|
timezone: j['timezone']?.toString(),
|
|
timeOffset: j['time']?.toString(),
|
|
);
|
|
}
|