defmodule Image.Xmp do @moduledoc """ Functions to extract and interpret image XMP data. """ import SweetXml def extract_xmp(xmp) do SweetXml.xpath(xmp, ~x"//x:xmpmeta", artist: ~x".//dc:creator/rdf:Seq/rdf:li/text()"s |> transform_by(&maybe_nil/1), keywords: ~x".//dc:subject/rdf:Bag/rdf:li/text()"ls, created_at: ~x".//@xmp:CreateDate"s |> transform_by(&to_date_time/1), rating: ~x".//@xmp:Rating"s |> transform_by(&to_integer/1), copyright: [ ~x".//dc:rights/rdf:Alt"l, text: ~x"./rdf:li/text()"ls, language: ~x"./rdf:li/@xml:lang"ls ], title: [ ~x".//dc:title/rdf:Alt"l, text: ~x"./rdf:li/text()"ls, language: ~x"./rdf:li/@xml:lang"ls ], description: [ ~x".//dc:description/rdf:Alt"l, text: ~x"./rdf:li/text()"ls, language: ~x"./rdf:li/@xml:lang"ls ], location: [ ~x".//rdf:Description", city: ~x"./@photoshop:City"s |> transform_by(&maybe_nil/1), state: ~x"./@photoshop:State"s |> transform_by(&maybe_nil/1), territory: ~x"./@photoshop:Country"s |> transform_by(&maybe_nil/1), territory_code: ~x"./@Iptc4xmpCore:CountryCode"s |> transform_by(&maybe_nil/1) ] ) |> zip(:description) |> zip(:title) |> zip(:copyright) end defp zip(map, key) do case Map.fetch(map, key) do {:ok, value} -> zipped = zip(value) Map.put(map, key, zipped) :error -> map end end defp zip([%{language: languages, text: texts}]) do languages |> Enum.zip(texts) |> Map.new() end defp zip([]) do nil end defp to_date_time("") do nil end defp to_date_time(string) do case NaiveDateTime.from_iso8601(string) do {:ok, date_time} -> date_time _other -> string end end defp to_integer(string) do case Integer.parse(string) do {integer, ""} -> integer _other -> nil end end defp maybe_nil(""), do: nil defp maybe_nil(other), do: other end