Skip to content

Commit

Permalink
#894 fixed motion photo detection for xml variant of google container…
Browse files Browse the repository at this point in the history
… item
  • Loading branch information
deckerst committed Feb 4, 2024
1 parent b4a5513 commit e57484d
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ object MultiPage {
var foundXmp = false

fun processXmp(xmpMeta: XMPMeta) {
offsetFromEnd = GoogleXMP.getTrailingVideoOffsetFromEnd(xmpMeta)
offsetFromEnd = offsetFromEnd ?: GoogleXMP.getTrailingVideoOffsetFromEnd(xmpMeta)
}

try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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
Expand Down Expand Up @@ -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() }
}
}
}
Expand Down
1 change: 1 addition & 0 deletions lib/ref/metadata/xmp.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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/';
Expand Down
8 changes: 7 additions & 1 deletion lib/widgets/viewer/info/metadata/xmp_namespaces.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -134,6 +138,8 @@ class XmpNamespace extends Equatable {
: [];
}

Set<RegExp> get skippedProps => {};

List<XmpCardData> get cards => [];

String formatValue(XmpProp prop) => prop.value;
Expand Down
17 changes: 16 additions & 1 deletion lib/widgets/viewer/info/metadata/xmp_ns/google.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
];
}

Expand Down

0 comments on commit e57484d

Please sign in to comment.