2021-06-24 16:54:41 +02:00
|
|
|
import 'package:logging/logging.dart';
|
2021-10-23 20:13:06 +02:00
|
|
|
import 'package:nc_photos/account.dart';
|
2021-11-12 22:13:02 +01:00
|
|
|
import 'package:nc_photos/ci_string.dart';
|
2021-09-25 18:22:19 +02:00
|
|
|
import 'package:nc_photos/entity/exif.dart';
|
2021-10-31 14:07:05 +01:00
|
|
|
import 'package:nc_photos/entity/file.dart';
|
2021-09-25 18:22:19 +02:00
|
|
|
import 'package:nc_photos/iterable_extension.dart';
|
2021-08-06 19:11:00 +02:00
|
|
|
import 'package:nc_photos/type.dart';
|
2021-09-25 18:22:19 +02:00
|
|
|
import 'package:tuple/tuple.dart';
|
2021-06-24 16:54:41 +02:00
|
|
|
|
|
|
|
abstract class AlbumUpgrader {
|
2021-08-06 19:11:00 +02:00
|
|
|
JsonObj? call(JsonObj json);
|
2021-06-24 16:54:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Upgrade v1 Album to v2
|
|
|
|
class AlbumUpgraderV1 implements AlbumUpgrader {
|
|
|
|
AlbumUpgraderV1({
|
|
|
|
this.logFilePath,
|
|
|
|
});
|
|
|
|
|
2021-07-23 22:05:57 +02:00
|
|
|
@override
|
2021-08-06 19:11:00 +02:00
|
|
|
call(JsonObj json) {
|
2021-06-24 16:54:41 +02:00
|
|
|
// v1 album items are corrupted in one of the updates, drop it
|
|
|
|
_log.fine("[call] Upgrade v1 Album for file: $logFilePath");
|
2021-08-06 19:11:00 +02:00
|
|
|
final result = JsonObj.from(json);
|
2021-06-24 16:54:41 +02:00
|
|
|
result["items"] = [];
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// File path for logging only
|
2021-07-23 22:05:57 +02:00
|
|
|
final String? logFilePath;
|
2021-06-24 16:54:41 +02:00
|
|
|
|
|
|
|
static final _log = Logger("entity.album.upgrader.AlbumUpgraderV1");
|
|
|
|
}
|
2021-06-24 18:26:56 +02:00
|
|
|
|
|
|
|
/// Upgrade v2 Album to v3
|
|
|
|
class AlbumUpgraderV2 implements AlbumUpgrader {
|
|
|
|
AlbumUpgraderV2({
|
|
|
|
this.logFilePath,
|
|
|
|
});
|
|
|
|
|
2021-07-23 22:05:57 +02:00
|
|
|
@override
|
2021-08-06 19:11:00 +02:00
|
|
|
call(JsonObj json) {
|
2021-06-24 18:26:56 +02:00
|
|
|
// move v2 items to v3 provider
|
|
|
|
_log.fine("[call] Upgrade v2 Album for file: $logFilePath");
|
2021-08-06 19:11:00 +02:00
|
|
|
final result = JsonObj.from(json);
|
2021-06-24 18:26:56 +02:00
|
|
|
result["provider"] = <String, dynamic>{
|
|
|
|
"type": "static",
|
|
|
|
"content": <String, dynamic>{
|
|
|
|
"items": result["items"],
|
|
|
|
}
|
|
|
|
};
|
|
|
|
result.remove("items");
|
2021-06-26 13:51:13 +02:00
|
|
|
|
|
|
|
// add the auto cover provider
|
|
|
|
result["coverProvider"] = <String, dynamic>{
|
|
|
|
"type": "auto",
|
|
|
|
"content": {},
|
|
|
|
};
|
2021-06-24 18:26:56 +02:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// File path for logging only
|
2021-07-23 22:05:57 +02:00
|
|
|
final String? logFilePath;
|
2021-06-24 18:26:56 +02:00
|
|
|
|
|
|
|
static final _log = Logger("entity.album.upgrader.AlbumUpgraderV2");
|
|
|
|
}
|
2021-07-07 20:40:43 +02:00
|
|
|
|
|
|
|
/// Upgrade v3 Album to v4
|
|
|
|
class AlbumUpgraderV3 implements AlbumUpgrader {
|
|
|
|
AlbumUpgraderV3({
|
|
|
|
this.logFilePath,
|
|
|
|
});
|
|
|
|
|
2021-07-23 22:05:57 +02:00
|
|
|
@override
|
2021-08-06 19:11:00 +02:00
|
|
|
call(JsonObj json) {
|
2021-07-07 20:40:43 +02:00
|
|
|
// move v3 items to v4 provider
|
|
|
|
_log.fine("[call] Upgrade v3 Album for file: $logFilePath");
|
2021-08-06 19:11:00 +02:00
|
|
|
final result = JsonObj.from(json);
|
2021-07-07 20:40:43 +02:00
|
|
|
// add the descending time sort provider
|
|
|
|
result["sortProvider"] = <String, dynamic>{
|
|
|
|
"type": "time",
|
|
|
|
"content": {
|
|
|
|
"isAscending": false,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// File path for logging only
|
2021-07-23 22:05:57 +02:00
|
|
|
final String? logFilePath;
|
2021-07-07 20:40:43 +02:00
|
|
|
|
|
|
|
static final _log = Logger("entity.album.upgrader.AlbumUpgraderV3");
|
|
|
|
}
|
2021-09-25 18:22:19 +02:00
|
|
|
|
|
|
|
/// Upgrade v4 Album to v5
|
|
|
|
class AlbumUpgraderV4 implements AlbumUpgrader {
|
|
|
|
AlbumUpgraderV4({
|
|
|
|
this.logFilePath,
|
|
|
|
});
|
|
|
|
|
|
|
|
@override
|
|
|
|
call(JsonObj json) {
|
|
|
|
_log.fine("[call] Upgrade v4 Album for file: $logFilePath");
|
|
|
|
final result = JsonObj.from(json);
|
|
|
|
try {
|
|
|
|
if (result["provider"]["type"] != "static") {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
final latestItem = (result["provider"]["content"]["items"] as List)
|
|
|
|
.map((e) => e.cast<String, dynamic>())
|
|
|
|
.where((e) => e["type"] == "file")
|
|
|
|
.map((e) => e["content"]["file"] as JsonObj)
|
|
|
|
.map((e) {
|
|
|
|
final overrideDateTime = e["overrideDateTime"] == null
|
|
|
|
? null
|
|
|
|
: DateTime.parse(e["overrideDateTime"]);
|
|
|
|
final String? dateTimeOriginalStr =
|
|
|
|
e["metadata"]?["exif"]?["DateTimeOriginal"];
|
|
|
|
final dateTimeOriginal =
|
|
|
|
dateTimeOriginalStr == null || dateTimeOriginalStr.isEmpty
|
|
|
|
? null
|
|
|
|
: Exif.dateTimeFormat.parse(dateTimeOriginalStr).toUtc();
|
|
|
|
final lastModified = e["lastModified"] == null
|
|
|
|
? null
|
|
|
|
: DateTime.parse(e["lastModified"]);
|
|
|
|
final latestItemTime =
|
|
|
|
overrideDateTime ?? dateTimeOriginal ?? lastModified;
|
|
|
|
|
|
|
|
// remove metadata
|
|
|
|
e.remove("metadata");
|
|
|
|
if (latestItemTime != null) {
|
|
|
|
return Tuple2(latestItemTime, e);
|
|
|
|
} else {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.whereType<Tuple2<DateTime, JsonObj>>()
|
|
|
|
.sorted((a, b) => a.item1.compareTo(b.item1))
|
|
|
|
.lastOrNull;
|
|
|
|
if (latestItem != null) {
|
|
|
|
// save the latest item time
|
|
|
|
result["provider"]["content"]["latestItemTime"] =
|
|
|
|
latestItem.item1.toIso8601String();
|
|
|
|
if (result["coverProvider"]["type"] == "auto") {
|
|
|
|
// save the cover
|
|
|
|
result["coverProvider"]["content"]["coverFile"] =
|
|
|
|
Map.of(latestItem.item2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (e, stackTrace) {
|
|
|
|
// this upgrade is not a must, if it failed then just leave it and it'll
|
|
|
|
// be upgraded the next time the album is saved
|
|
|
|
_log.shout("[call] Failed while upgrade", e, stackTrace);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// File path for logging only
|
|
|
|
final String? logFilePath;
|
|
|
|
|
|
|
|
static final _log = Logger("entity.album.upgrader.AlbumUpgraderV4");
|
|
|
|
}
|
2021-10-23 20:13:06 +02:00
|
|
|
|
|
|
|
/// Upgrade v5 Album to v6
|
|
|
|
class AlbumUpgraderV5 implements AlbumUpgrader {
|
|
|
|
const AlbumUpgraderV5(
|
|
|
|
this.account, {
|
2021-10-31 14:07:05 +01:00
|
|
|
this.albumFile,
|
2021-10-23 20:13:06 +02:00
|
|
|
this.logFilePath,
|
|
|
|
});
|
|
|
|
|
|
|
|
@override
|
|
|
|
call(JsonObj json) {
|
|
|
|
_log.fine("[call] Upgrade v5 Album for file: $logFilePath");
|
|
|
|
final result = JsonObj.from(json);
|
|
|
|
try {
|
|
|
|
if (result["provider"]["type"] != "static") {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
for (final item in (result["provider"]["content"]["items"] as List)) {
|
2021-11-12 22:13:02 +01:00
|
|
|
final CiString addedBy;
|
2021-10-31 14:07:05 +01:00
|
|
|
if (result.containsKey("albumFile")) {
|
2021-11-12 22:13:02 +01:00
|
|
|
addedBy = result["albumFile"]["ownerId"] == null
|
|
|
|
? account.username
|
|
|
|
: CiString(result["albumFile"]["ownerId"]);
|
2021-10-31 14:07:05 +01:00
|
|
|
} else {
|
|
|
|
addedBy = albumFile?.ownerId ?? account.username;
|
|
|
|
}
|
2021-11-12 22:13:02 +01:00
|
|
|
item["addedBy"] = addedBy.toString();
|
2021-10-23 20:13:06 +02:00
|
|
|
item["addedAt"] = result["lastUpdated"];
|
|
|
|
}
|
|
|
|
} catch (e, stackTrace) {
|
|
|
|
// this upgrade is not a must, if it failed then just leave it and it'll
|
|
|
|
// be upgraded the next time the album is saved
|
|
|
|
_log.shout("[call] Failed while upgrade", e, stackTrace);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
final Account account;
|
2021-10-31 14:07:05 +01:00
|
|
|
final File? albumFile;
|
2021-10-23 20:13:06 +02:00
|
|
|
|
|
|
|
/// File path for logging only
|
|
|
|
final String? logFilePath;
|
|
|
|
|
|
|
|
static final _log = Logger("entity.album.upgrader.AlbumUpgraderV5");
|
|
|
|
}
|
2021-11-18 09:43:14 +01:00
|
|
|
|
2021-11-18 10:02:16 +01:00
|
|
|
/// Upgrade v6 Album to v7
|
|
|
|
class AlbumUpgraderV6 implements AlbumUpgrader {
|
|
|
|
const AlbumUpgraderV6({
|
|
|
|
this.logFilePath,
|
|
|
|
});
|
|
|
|
|
|
|
|
@override
|
|
|
|
call(JsonObj json) {
|
|
|
|
_log.fine("[call] Upgrade v6 Album for file: $logFilePath");
|
|
|
|
return json;
|
|
|
|
}
|
|
|
|
|
|
|
|
static final _log = Logger("entity.album.upgrader.AlbumUpgraderV6");
|
|
|
|
|
|
|
|
/// File path for logging only
|
|
|
|
final String? logFilePath;
|
|
|
|
}
|
|
|
|
|
2021-11-18 09:43:14 +01:00
|
|
|
abstract class AlbumUpgraderFactory {
|
|
|
|
const AlbumUpgraderFactory();
|
|
|
|
|
|
|
|
AlbumUpgraderV1? buildV1();
|
|
|
|
AlbumUpgraderV2? buildV2();
|
|
|
|
AlbumUpgraderV3? buildV3();
|
|
|
|
AlbumUpgraderV4? buildV4();
|
|
|
|
AlbumUpgraderV5? buildV5();
|
2021-11-18 10:02:16 +01:00
|
|
|
AlbumUpgraderV6? buildV6();
|
2021-11-18 09:43:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
class DefaultAlbumUpgraderFactory extends AlbumUpgraderFactory {
|
|
|
|
const DefaultAlbumUpgraderFactory({
|
|
|
|
required this.account,
|
|
|
|
this.albumFile,
|
|
|
|
this.logFilePath,
|
|
|
|
});
|
|
|
|
|
|
|
|
@override
|
|
|
|
buildV1() => AlbumUpgraderV1(logFilePath: logFilePath);
|
|
|
|
|
|
|
|
@override
|
|
|
|
buildV2() => AlbumUpgraderV2(logFilePath: logFilePath);
|
|
|
|
|
|
|
|
@override
|
|
|
|
buildV3() => AlbumUpgraderV3(logFilePath: logFilePath);
|
|
|
|
|
|
|
|
@override
|
|
|
|
buildV4() => AlbumUpgraderV4(logFilePath: logFilePath);
|
|
|
|
|
|
|
|
@override
|
|
|
|
buildV5() => AlbumUpgraderV5(
|
|
|
|
account,
|
|
|
|
albumFile: albumFile,
|
|
|
|
logFilePath: logFilePath,
|
|
|
|
);
|
|
|
|
|
2021-11-18 10:02:16 +01:00
|
|
|
@override
|
|
|
|
buildV6() => AlbumUpgraderV6(logFilePath: logFilePath);
|
|
|
|
|
2021-11-18 09:43:14 +01:00
|
|
|
final Account account;
|
|
|
|
final File? albumFile;
|
|
|
|
|
|
|
|
/// File path for logging only
|
|
|
|
final String? logFilePath;
|
|
|
|
}
|