This commit is contained in:
Fabio Micheluz 2026-02-28 22:59:08 +01:00
parent 1980353a20
commit 6e97813a01
5 changed files with 417 additions and 63 deletions

View file

@ -21,7 +21,10 @@ run con
```
fvm flutter run -t lib/main_play.dart --flavor play
```
se vuoi salvare tutto l'output in un file e cmq vederlo
```
fvm flutter run -t lib/main_play.dart --flavor play 2>&1 | tee output.log
```
i files nuovi sono
```
@ -38,8 +41,97 @@ e questo modificato
```
lib/widgets/home/home_page.dart
```
salvare il DB
```
adb exec-out run-as deckers.thibault.aves.debug cat /data/data/deckers.thibault.aves.debug/databases/metadata.db > metadata.db
```
verifica che il db sia quello giusto
ci devono essere entry address metadata album
```
sqlite3 metadata.db ".tables"
address dateTaken favourites vaults
android_metadata dynamicAlbums metadata videoPlayback
covers entry trash
```
verifica delke colonne, i campi
```
sqlite3 metadata.db "PRAGMA table_info(entry);"
```
risposta
```
0|id|INTEGER|0||1
1|contentId|INTEGER|0||0
2|uri|TEXT|0||0
3|path|TEXT|0||0
4|sourceMimeType|TEXT|0||0
5|width|INTEGER|0||0
6|height|INTEGER|0||0
7|sourceRotationDegrees|INTEGER|0||0
8|sizeBytes|INTEGER|0||0
9|title|TEXT|0||0
10|dateAddedSecs|INTEGER|0|strftime('%s','now')|0
11|dateModifiedMillis|INTEGER|0||0
12|sourceDateTakenMillis|INTEGER|0||0
13|durationMillis|INTEGER|0||0
14|trashed|INTEGER|0|0|0
15|origin|INTEGER|0|0|0
16|provider|TEXT|0||0
17|remoteId|TEXT|0||0
18|remotePath|TEXT|0||0
19|remoteThumb1|TEXT|0||0
20|remoteThumb2|TEXT|0||0
21|latitude|REAL|0||0
22|longitude|REAL|0||0
23|altitude|REAL|0||0
```
Verifica 1 — Quante foto remote sono state salvate
```
sqlite3 metadata.db "SELECT COUNT(*) FROM entry WHERE origin=1;"
99
```
Verifica 2 — Controllare che le foto remote abbiano GPS e path corretti
```
sqlite3 metadata.db "
SELECT id, title, remoteId, remotePath, latitude, longitude, altitude
FROM entry
WHERE origin=1
LIMIT 20;
"
```
risposta
```
6974|IMG_0123.JPG|d9fb0263ed0bc9945d2d6fde3822377d63ed7a62df20e2a1339d77a058b0e5a0|photos/Fabio/original/2017Irlanda19-29ago/IMG_0123.JPG|53.3419416666667|-6.28671666666667|23.6626344086022
6975|IMG_0124.JPG|a48db6ef8efee410190ff59bcb223fece468837d0c39bb408cce911213e5e36c|photos/Fabio/original/2017Irlanda19-29ago/IMG_0124.JPG|53.341975|-6.28675277777778|23.8189509306261
```
cartelle possibili
```
sqlite3 metadata.db "SELECT remotePath FROM entry WHERE origin=1;" \
| awk -F'/' '{$NF=""; sub(/\/$/,""); print}' \
| sort -u
```
risposta
```
photos Fabio original 2017Irlanda19-29ago
```
controlla se ci sono tabelke album
```
sqlite3 metadata.db "
SELECT name FROM sqlite_master
WHERE type='table' AND name LIKE '%album%';
"
```
risposta
```
dynamicAlbums
```
controlla negli albums
```
sqlite3 metadata.db "SELECT * FROM dynamicAlbums LIMIT 20;"
```
## Features
Aves can handle all sorts of images and videos, including your typical JPEGs and MP4s, but also more exotic things like **multi-page TIFFs, SVGs, old AVIs and more**!

View file

@ -1,4 +1,4 @@
// lib/remote/remote_repository.dart
import 'package:flutter/foundation.dart' show debugPrint;
import 'package:sqflite/sqflite.dart';
import 'remote_models.dart';
@ -6,69 +6,143 @@ class RemoteRepository {
final Database db;
RemoteRepository(this.db);
Future<void> upsertAll(List<RemotePhotoItem> items) async {
await db.transaction((txn) async {
for (final it in items) {
// cerca se esiste già una entry per quel remoteId
final existing = await txn.query(
'entry',
columns: ['id'],
where: 'remoteId = ?',
whereArgs: [it.id],
limit: 1,
);
final int? existingId = existing.isNotEmpty ? (existing.first['id'] as int?) : null;
final row = <String, Object?>{
'id': existingId, // se esiste sostituiamo, altrimenti INSERT nuovo
'contentId': null,
'uri': null,
'path': null,
'sourceMimeType': it.mimeType,
'width': it.width,
'height': it.height,
'sourceRotationDegrees': null,
'sizeBytes': it.sizeBytes,
'title': it.name,
'dateAddedSecs': DateTime.now().millisecondsSinceEpoch ~/ 1000,
'dateModifiedMillis': null,
'sourceDateTakenMillis': it.takenAtUtc?.millisecondsSinceEpoch,
'durationMillis': it.durationMillis, // <-- ora valorizzato anche per i video
'trashed': 0,
'origin': 1,
'provider': 'json@patachina',
'remoteId': it.id,
'remotePath': it.path,
'remoteThumb1': it.thub1,
'remoteThumb2': it.thub2,
};
// INSERT OR REPLACE (se 'id' è valorizzato, sostituisce; se null, crea nuovo)
final newId = await txn.insert(
'entry',
row,
conflictAlgorithm: ConflictAlgorithm.replace,
);
// opzionale: salva indirizzo (se il backend lo fornisce)
if (it.location != null) {
final addr = <String, Object?>{
'id': newId,
'addressLine': it.location!.address,
'countryCode': null, // county_code != country code
'countryName': it.location!.country,
'adminArea': it.location!.region,
'locality': it.location!.city,
};
await txn.insert(
'address',
addr,
conflictAlgorithm: ConflictAlgorithm.replace,
);
/// Assicura che le colonne GPS esistano nella tabella `entry`.
/// Se mancano, prova ad aggiungerle con ALTER TABLE.
Future<void> _ensureGpsColumns(DatabaseExecutor dbExec) async {
try {
final rows = await dbExec.rawQuery('PRAGMA table_info(entry);');
final names = rows.map((r) => r['name'] as String).toSet();
final stmts = <String>[];
if (!names.contains('latitude')) stmts.add('ALTER TABLE entry ADD COLUMN latitude REAL;');
if (!names.contains('longitude')) stmts.add('ALTER TABLE entry ADD COLUMN longitude REAL;');
if (!names.contains('altitude')) stmts.add('ALTER TABLE entry ADD COLUMN altitude REAL;');
for (final s in stmts) {
try {
await dbExec.execute(s);
debugPrint('[RemoteRepository] executed: $s');
} catch (e, st) {
debugPrint('[RemoteRepository] failed to execute $s: $e\n$st');
}
}
});
} catch (e, st) {
debugPrint('[RemoteRepository] _ensureGpsColumns error: $e\n$st');
}
}
/// Inserisce o aggiorna tutti gli elementi remoti.
/// Difensivo: assicura colonne GPS, logga, e in caso di errore riprova senza i campi GPS.
Future<void> upsertAll(List<RemotePhotoItem> items) async {
debugPrint('RemoteRepository.upsertAll: items=${items.length}');
try {
await db.transaction((txn) async {
// Assicuriamoci che le colonne GPS esistano prima di inserire
await _ensureGpsColumns(txn);
for (final it in items) {
debugPrint('RemoteRepository: processing remoteId=${it.id} lat=${it.lat} lng=${it.lng}');
final existing = await txn.query(
'entry',
columns: ['id'],
where: 'remoteId = ?',
whereArgs: [it.id],
limit: 1,
);
final int? existingId = existing.isNotEmpty ? (existing.first['id'] as int?) : null;
final row = <String, Object?>{
'id': existingId,
'contentId': null,
'uri': null,
'path': it.path,
'sourceMimeType': it.mimeType,
'width': it.width,
'height': it.height,
'sourceRotationDegrees': null,
'sizeBytes': it.sizeBytes,
'title': it.name,
'dateAddedSecs': DateTime.now().millisecondsSinceEpoch ~/ 1000,
'dateModifiedMillis': null,
'sourceDateTakenMillis': it.takenAtUtc?.millisecondsSinceEpoch,
'durationMillis': it.durationMillis,
'trashed': 0,
'origin': 1,
'provider': 'json@patachina',
// campi GPS (possono essere null)
'latitude': it.lat,
'longitude': it.lng,
'altitude': it.alt,
// campi remoti
'remoteId': it.id,
'remotePath': it.path,
'remoteThumb1': it.thub1,
'remoteThumb2': it.thub2,
};
try {
final newId = await txn.insert(
'entry',
row,
conflictAlgorithm: ConflictAlgorithm.replace,
);
if (it.location != null) {
final addr = <String, Object?>{
'id': newId,
'addressLine': it.location!.address,
'countryCode': null,
'countryName': it.location!.country,
'adminArea': it.location!.region,
'locality': it.location!.city,
};
await txn.insert(
'address',
addr,
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
} on DatabaseException catch (e, st) {
debugPrint('[RemoteRepository] insert failed for remoteId=${it.id}: $e\n$st');
// Fallback: riprova senza i campi GPS (utile se ALTER TABLE non è riuscito)
final rowNoGps = Map<String, Object?>.from(row)
..remove('latitude')
..remove('longitude')
..remove('altitude');
try {
final newId = await txn.insert(
'entry',
rowNoGps,
conflictAlgorithm: ConflictAlgorithm.replace,
);
if (it.location != null) {
final addr = <String, Object?>{
'id': newId,
'addressLine': it.location!.address,
'countryCode': null,
'countryName': it.location!.country,
'adminArea': it.location!.region,
'locality': it.location!.city,
};
await txn.insert(
'address',
addr,
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
debugPrint('[RemoteRepository] insert succeeded without GPS for remoteId=${it.id}');
} catch (e2, st2) {
debugPrint('[RemoteRepository] retry without GPS failed for remoteId=${it.id}: $e2\n$st2');
rethrow;
}
}
}
});
} catch (e, st) {
debugPrint('[RemoteRepository] upsertAll ERROR: $e\n$st');
rethrow;
}
}
Future<int> countRemote() async {

View file

@ -0,0 +1,89 @@
import 'package:sqflite/sqflite.dart';
import 'remote_models.dart';
class RemoteRepository {
final Database db;
RemoteRepository(this.db);
Future<void> upsertAll(List<RemotePhotoItem> items) async {
await db.transaction((txn) async {
for (final it in items) {
// cerca se esiste già una entry per quel remoteId
final existing = await txn.query(
'entry',
columns: ['id'],
where: 'remoteId = ?',
whereArgs: [it.id],
limit: 1,
);
final int? existingId =
existing.isNotEmpty ? (existing.first['id'] as int?) : null;
final row = <String, Object?>{
// se esiste sostituiamo, altrimenti INSERT nuovo
'id': existingId,
'contentId': null,
'uri': null,
'path': null,
'sourceMimeType': it.mimeType,
'width': it.width,
'height': it.height,
'sourceRotationDegrees': null,
'sizeBytes': it.sizeBytes,
'title': it.name,
'dateAddedSecs':
DateTime.now().millisecondsSinceEpoch ~/ 1000,
'dateModifiedMillis': null,
'sourceDateTakenMillis':
it.takenAtUtc?.millisecondsSinceEpoch,
'durationMillis': it.durationMillis,
'trashed': 0,
'origin': 1,
'provider': 'json@patachina',
// AGGIUNTA: GPS direttamente nel DB di Aves
'latitude': it.lat,
'longitude': it.lng,
'altitude': it.alt,
'remoteId': it.id,
'remotePath': it.path,
'remoteThumb1': it.thub1,
'remoteThumb2': it.thub2,
};
// INSERT OR REPLACE (se 'id' è valorizzato, sostituisce; se null, crea nuovo)
final newId = await txn.insert(
'entry',
row,
conflictAlgorithm: ConflictAlgorithm.replace,
);
// opzionale: salva indirizzo (se il backend lo fornisce)
if (it.location != null) {
final addr = <String, Object?>{
'id': newId,
'addressLine': it.location!.address,
'countryCode': null, // county_code != country code
'countryName': it.location!.country,
'adminArea': it.location!.region,
'locality': it.location!.city,
};
await txn.insert(
'address',
addr,
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
}
});
}
Future<int> countRemote() async {
final rows = await db
.rawQuery('SELECT COUNT(1) AS c FROM entry WHERE origin=1');
return (rows.first['c'] as int?) ?? 0;
}
}

BIN
metadata.db Normal file

Binary file not shown.

99
remote_paths.txt Normal file
View file

@ -0,0 +1,99 @@
photos/Fabio/original/2017Irlanda19-29ago/IMG_0092.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0099.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0100.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0102.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0103.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0104.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0106.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0107.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0108.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0109.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0110.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0112.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0113.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0114.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0116.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0119.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0120.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0122.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0123.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0124.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0125.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0126.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0133.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0134.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0135.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0136.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0137.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0138.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0139.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0140.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0141.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0143.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0145.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0146.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0147.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0148.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0149.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0150.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0152.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0153.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0154.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0155.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0156.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0157.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0160.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0162.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0163.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0164.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0165.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0166.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0167.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0170.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0171.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0172.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0174.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0175.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0176.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0177.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0178.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0179.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0180.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0182.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0183.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0185.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0188.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0190.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0193.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0203.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0204.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0206.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0207.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0208.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0209.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0211.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0212.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0214.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0215.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0216.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0217.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0218.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0223.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0227.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0228.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0229.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0232.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0233.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0234.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0235.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0237.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0241.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0242.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0243.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0244.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0245.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0246.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0247.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0248.JPG
photos/Fabio/original/2017Irlanda19-29ago/IMG_0249.JPG
photos/Fabio/original/2017Irlanda19-29ago/VID_20260221_095917.mp4