8000 Extending options for populating Taken Date from file-metadata by AndyKilmory · Pull Request #4457 · guardian/grid · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Extending options for populating Taken Date from file-metadata #4457

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ object ImageMetadataConverter extends GridLogging {

// keep positive years (ie AD) with a couple of days spare room to avoid accidentally flipping over into BC
val earliestSensibleDate: DateTime = DateTime.parse("0001-01-03T00:00:00.000Z")
// offset hours representing possible range of + timezones ahead of UTC
private val utcHoursOffset: Int = 14

private def extractSubjects(fileMetadata: FileMetadata): Option[List[String]] = {
val supplementalCategories = fileMetadata.iptc
Expand Down Expand Up @@ -54,10 +56,19 @@ object ImageMetadataConverter extends GridLogging {

def fromFileMetadata(fileMetadata: FileMetadata, latestAllowedDateTime: Option[DateTime] = None, earliestAllowedDateTime: Option[DateTime] = Some(earliestSensibleDate)): ImageMetadata = {
def parseDate(dateString: String): Option[DateTime] = parseRandomDate(dateString, maxDate = latestAllowedDateTime, minDate = earliestAllowedDateTime)

// setting of taken date
val exifTaken: Option[String] = fileMetadata.exifSub.get("Date/Time Original Composite")
val iptcTaken: Option[String] = fileMetadata.iptc.get("Date Time Created Composite")
val xmpTaken: Option[String] = fileMetadata.readXmpHeadStringProp("photoshop:DateCreated")
val iptcDate: Option[String] = fileMetadata.iptc.get("Date Created")
val dateTaken: Option[DateTime] = exifTaken.flatMap(parseDate) orElse
iptcTaken.flatMap(parseDate) orElse
xmpTaken.flatMap(parseDate) orElse
iptcDate.flatMap(parseDate)

ImageMetadata(
dateTaken = (fileMetadata.exifSub.get("Date/Time Original Composite") flatMap parseDate) orElse
(fileMetadata.iptc.get("Date Time Created Composite") flatMap parseDate) orElse
(fileMetadata.readXmpHeadStringProp("photoshop:DateCreated") flatMap parseDate),
dateTaken = dateTaken,
description = fileMetadata.readXmpHeadStringProp("dc:description") orElse
fileMetadata.iptc.get("Caption/Abstract") orElse
fileMetadata.exif.get("Image Description"),
Expand Down Expand Up @@ -138,21 +149,25 @@ object ImageMetadataConverter extends GridLogging {
DateTimeFormat.forPattern("yyyyddMM"),
DateTimeFormat.forPattern("yyyy"),
DateTimeFormat.forPattern("yyyy-MM"),
DateTimeFormat.forPattern("yyyy:MM:dd"),
DateTimeFormat.forPattern("yyyy-MM-dd"),

// 2014-12-16 - Maybe it's just a date
// no timezone provided so force UTC rather than use the machine's timezone
ISODateTimeFormat.date.withZoneUTC
)

private[metadata] def parseRandomDate(str: String, maxDate: Option[DateTime] = None, minDate: Option[DateTime] = None): Option[DateTime] = {
// account for images sent with local timezone info being interpreted as UTC
val feasibleMaxDate: Option[DateTime] = maxDate.map(_.plusHours(utcHoursOffset))
dateTimeFormatters.foldLeft[Option[DateTime]](None){
case (successfulDate@Some(_), _) => successfulDate
// NB We refuse parse results which result in future dates, if a max date is provided.
// eg If we get a pic today (22nd January 2021) with a date string of 20211201 we can be pretty sure
// that it should be parsed as (eg) US (12th Jan 2021), not EU (1st Dec 2021).
// So we refuse the (apparently successful) EU parse result.
case (None, formatter) => safeParsing(formatter.parseDateTime(str))
.filter(d => maxDate.forall(d.isBefore) && minDate.forall(d.isAfter))
.filter(d => feasibleMaxDate.forall(d.isBefore) && minDate.forall(d.isAfter))
}.map(_.withZone(DateTimeZone.UTC))
}

Expand Down
Loading
0