#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 var foundXmp = false
fun processXmp(xmpMeta: XMPMeta) { fun processXmp(xmpMeta: XMPMeta) {
offsetFromEnd = GoogleXMP.getTrailingVideoOffsetFromEnd(xmpMeta) offsetFromEnd = offsetFromEnd ?: GoogleXMP.getTrailingVideoOffsetFromEnd(xmpMeta)
} }
try { try {

View file

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

View file

@ -24,6 +24,7 @@ class XmpNamespaces {
static const gAudio = 'http://ns.google.com/photos/1.0/audio/'; static const gAudio = 'http://ns.google.com/photos/1.0/audio/';
static const gCamera = 'http://ns.google.com/photos/1.0/camera/'; static const gCamera = 'http://ns.google.com/photos/1.0/camera/';
static const gContainer = 'http://ns.google.com/photos/1.0/container/'; 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 gCreations = 'http://ns.google.com/photos/1.0/creations/';
static const gDepth = 'http://ns.google.com/photos/1.0/depthmap/'; static const gDepth = 'http://ns.google.com/photos/1.0/depthmap/';
static const gDevice = 'http://ns.google.com/photos/dd/1.0/device/'; 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) { List<Widget> buildNamespaceSection(BuildContext context) {
final props = rawProps.entries final props = rawProps.entries
.map((kv) { .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; var extracted = false;
cards.forEach((card) => extracted |= card.extract(prop)); cards.forEach((card) => extracted |= card.extract(prop));
return extracted ? null : prop; return extracted ? null : prop;
@ -134,6 +138,8 @@ class XmpNamespace extends Equatable {
: []; : [];
} }
Set<RegExp> get skippedProps => {};
List<XmpCardData> get cards => []; List<XmpCardData> get cards => [];
String formatValue(XmpProp prop) => prop.value; String formatValue(XmpProp prop) => prop.value;

View file

@ -73,11 +73,26 @@ class XmpGCameraNamespace extends XmpGoogleNamespace {
} }
class XmpGContainer extends XmpNamespace { 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 @override
late final List<XmpCardData> cards = [ 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'), 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'),
]; ];
} }