#894 fixed motion photo detection for xml variant of google container item

This commit is contained in:
Thibault Deckers 2024-02-04 19:52:56 +01:00
parent b4a5513fe1
commit e57484d912
5 changed files with 40 additions and 11 deletions

View file

@ -274,7 +274,7 @@ object MultiPage {
var foundXmp = false
fun processXmp(xmpMeta: XMPMeta) {
offsetFromEnd = GoogleXMP.getTrailingVideoOffsetFromEnd(xmpMeta)
offsetFromEnd = offsetFromEnd ?: GoogleXMP.getTrailingVideoOffsetFromEnd(xmpMeta)
}
try {

View file

@ -109,10 +109,11 @@ object GoogleXMP {
var hasImage = false
var hasVideo = false
for (i in 1 until count + 1) {
val mime = meta.getSafeStructField(listOf(GCONTAINER_DIRECTORY_PROP_NAME, i, GCONTAINER_ITEM_PROP_NAME, GCONTAINER_ITEM_MIME_PROP_NAME))?.value
val length = meta.getSafeStructField(listOf(GCONTAINER_DIRECTORY_PROP_NAME, i, GCONTAINER_ITEM_PROP_NAME, GCONTAINER_ITEM_LENGTH_PROP_NAME))?.value
hasImage = hasImage || MimeTypes.isImage(mime) && length != null
hasVideo = hasVideo || MimeTypes.isVideo(mime) && length != null
val mime = getContainerItemAttribute(meta, i, GCONTAINER_ITEM_MIME_PROP_NAME)
val length = getContainerItemAttribute(meta, i, GCONTAINER_ITEM_LENGTH_PROP_NAME)
// `length` is not always provided for the image item
hasImage = hasImage || MimeTypes.isImage(mime)
hasVideo = hasVideo || (MimeTypes.isVideo(mime) && length != null)
}
if (hasImage && hasVideo) return true
}
@ -128,6 +129,13 @@ object GoogleXMP {
return false
}
private fun getContainerItemAttribute(meta: XMPMeta, i: Int, attribute: XMPPropName): String? {
// variant of `Container:Item` with `<rdf:li rdf:parseType="Resource">`
val mime = meta.getSafeStructField(listOf(GCONTAINER_DIRECTORY_PROP_NAME, i, GCONTAINER_ITEM_PROP_NAME, attribute))?.value
// variant of `Container:Item` with `<rdf:li>`
return mime ?: meta.getSafeStructField(listOf(GCONTAINER_DIRECTORY_PROP_NAME, i, attribute))?.value
}
fun isPanorama(meta: XMPMeta): Boolean {
try {
if (gpanoRequiredProps.all { meta.doesPropExist(it) }) return true
@ -166,10 +174,9 @@ object GoogleXMP {
// `Container` motion photo
val count = meta.countPropArrayItems(GCONTAINER_DIRECTORY_PROP_NAME)
for (i in 1 until count + 1) {
val mime = meta.getSafeStructField(listOf(GCONTAINER_DIRECTORY_PROP_NAME, i, GCONTAINER_ITEM_PROP_NAME, GCONTAINER_ITEM_MIME_PROP_NAME))?.value
val length = meta.getSafeStructField(listOf(GCONTAINER_DIRECTORY_PROP_NAME, i, GCONTAINER_ITEM_PROP_NAME, GCONTAINER_ITEM_LENGTH_PROP_NAME))?.value
if (MimeTypes.isVideo(mime) && length != null) {
offsetFromEnd = length.toLong()
val mime = getContainerItemAttribute(meta, i, GCONTAINER_ITEM_MIME_PROP_NAME)
if (MimeTypes.isVideo(mime)) {
getContainerItemAttribute(meta, i, GCONTAINER_ITEM_LENGTH_PROP_NAME)?.let { offsetFromEnd = it.toLong() }
}
}
}

View file

@ -24,6 +24,7 @@ class XmpNamespaces {
static const gAudio = 'http://ns.google.com/photos/1.0/audio/';
static const gCamera = 'http://ns.google.com/photos/1.0/camera/';
static const gContainer = 'http://ns.google.com/photos/1.0/container/';
static const gContainerItem = 'http://ns.google.com/photos/1.0/container/item/';
static const gCreations = 'http://ns.google.com/photos/1.0/creations/';
static const gDepth = 'http://ns.google.com/photos/1.0/depthmap/';
static const gDevice = 'http://ns.google.com/photos/dd/1.0/device/';

View file

@ -90,7 +90,11 @@ class XmpNamespace extends Equatable {
List<Widget> buildNamespaceSection(BuildContext context) {
final props = rawProps.entries
.map((kv) {
final prop = XmpProp(kv.key, kv.value);
final key = kv.key;
if (skippedProps.any((pattern) => pattern.allMatches(key).isNotEmpty)) {
return null;
}
final prop = XmpProp(key, kv.value);
var extracted = false;
cards.forEach((card) => extracted |= card.extract(prop));
return extracted ? null : prop;
@ -134,6 +138,8 @@ class XmpNamespace extends Equatable {
: [];
}
Set<RegExp> get skippedProps => {};
List<XmpCardData> get cards => [];
String formatValue(XmpProp prop) => prop.value;

View file

@ -73,11 +73,26 @@ class XmpGCameraNamespace extends XmpGoogleNamespace {
}
class XmpGContainer extends XmpNamespace {
XmpGContainer({required super.schemaRegistryPrefixes, required super.rawProps}) : super(nsUri: XmpNamespaces.gContainer);
late final String _gContainerItemNsPrefix;
late final String _rdfNsPrefix;
XmpGContainer({required super.schemaRegistryPrefixes, required super.rawProps}) : super(nsUri: XmpNamespaces.gContainer) {
_gContainerItemNsPrefix = XmpNamespace.prefixForUri(schemaRegistryPrefixes, XmpNamespaces.gContainerItem);
_rdfNsPrefix = XmpNamespace.prefixForUri(schemaRegistryPrefixes, XmpNamespaces.rdf);
}
@override
late final Set<RegExp> skippedProps = {
// variant of `Container:Item` with `<rdf:li>`
RegExp(nsPrefix + r'Directory\[(\d+)\]/' + _rdfNsPrefix + r'type'),
};
@override
late final List<XmpCardData> cards = [
// variant of `Container:Item` with `<rdf:li rdf:parseType="Resource">`
XmpCardData(RegExp(nsPrefix + r'Directory\[(\d+)\]/' + nsPrefix + r'Item/(.*)'), title: 'Directory Item'),
// variant of `Container:Item` with `<rdf:li>`
XmpCardData(RegExp(nsPrefix + r'Directory\[(\d+)\]/(' + _gContainerItemNsPrefix + r'.*)'), title: 'Directory Item'),
];
}