diff --git a/common/app/model/Formats.scala b/common/app/model/Formats.scala index 6addc07952f4..b92260d49e33 100644 --- a/common/app/model/Formats.scala +++ b/common/app/model/Formats.scala @@ -255,6 +255,7 @@ object PressedContentFormat { implicit val imageMediaFormat: OFormat[ImageMedia] = Json.format[ImageMedia] implicit val videoMediaFormat: OFormat[VideoMedia] = Json.format[VideoMedia] implicit val videoElementFormat: OFormat[VideoElement] = Json.format[VideoElement] + implicit val assetDimensionsFormat: OFormat[AssetDimensions] = Json.format[AssetDimensions] implicit val mediaAssetFormat: OFormat[MediaAsset] = Json.format[MediaAsset] implicit val mediaAtomFormat: OFormat[MediaAtom] = Json.format[MediaAtom] implicit val mediaTypeFormat: MediaTypeFormat.type = MediaTypeFormat diff --git a/common/app/model/content/Atom.scala b/common/app/model/content/Atom.scala index a8f4e5c43918..27e82db1ab3b 100644 --- a/common/app/model/content/Atom.scala +++ b/common/app/model/content/Atom.scala @@ -1,6 +1,10 @@ package model.content -import com.gu.contentatom.thrift.atom.media.{Asset => AtomApiMediaAsset, MediaAtom => AtomApiMediaAtom} +import com.gu.contentatom.thrift.atom.media.{ + Asset => AtomApiMediaAsset, + MediaAtom => AtomApiMediaAtom, + VideoPlayerFormat => AtomApiVideoPlayerFormat, +} import com.gu.contentatom.thrift.AtomDataAliases.{MediaAlias => MediaAtomData} import com.gu.contentatom.thrift.atom.timeline.{TimelineItem => TimelineApiItem} import com.gu.contentatom.thrift.{ @@ -16,7 +20,7 @@ import model.{ImageAsset, ImageMedia, ShareLinkMeta} import org.apache.commons.lang3.time.DurationFormatUtils import org.joda.time.format.DateTimeFormat import org.joda.time.{DateTime, DateTimeZone, Duration} -import play.api.libs.json.{JsError, JsSuccess, Json, OFormat} +import play.api.libs.json.{JsError, JsSuccess, Json, OFormat, Writes} import quiz._ import views.support.GoogleStructuredData @@ -168,6 +172,7 @@ final case class MediaAtom( activeVersion: Option[Long], channelId: Option[String], trailImage: Option[ImageMedia], + videoPlayerFormat: Option[VideoPlayerFormat], ) extends Atom { def activeAssets: Seq[MediaAsset] = @@ -190,14 +195,35 @@ final case class MediaAtom( } } +object AssetDimensions { + implicit val assetDimensionsWrites: Writes[AssetDimensions] = + Json.writes[AssetDimensions] +} +final case class AssetDimensions( + width: Int, + height: Int, +) + final case class MediaAsset( id: String, version: Long, platform: MediaAssetPlatform, mimeType: Option[String], assetType: MediaAssetType, + dimensions: Option[AssetDimensions], + aspectRatio: Option[String], ) +sealed trait VideoPlayerFormat extends EnumEntry + +object VideoPlayerFormat extends Enum[VideoPlayerFormat] with PlayJsonEnum[VideoPlayerFormat] { + val values = findValues + + case object Default extends VideoPlayerFormat + case object Loop extends VideoPlayerFormat + case object Cinemagraph extends VideoPlayerFormat +} + sealed trait MediaAssetType extends EnumEntry object MediaAssetType extends Enum[MediaAssetType] with PlayJsonEnum[MediaAssetType] { @@ -233,6 +259,15 @@ object MediaAtom extends common.GuLogging { activeVersion = mediaAtom.activeVersion, channelId = mediaAtom.metadata.flatMap(_.channelId), trailImage = mediaAtom.trailImage.map(imageMediaMake(_, mediaAtom.title)), + videoPlayerFormat = VideoPlayerFormat.withNameOption( + mediaAtom.metadata + .flatMap(_.selfHost) + .flatMap(_.videoPlayerFormat) + .getOrElse( + AtomApiVideoPlayerFormat.Default, + ) + .name, + ), ) } @@ -254,6 +289,15 @@ object MediaAtom extends common.GuLogging { activeVersion = mediaAtom.activeVersion, channelId = mediaAtom.metadata.flatMap(_.channelId), trailImage = mediaAtom.trailImage.map(imageMediaMake(_, mediaAtom.title)), + videoPlayerFormat = VideoPlayerFormat.withNameOption( + mediaAtom.metadata + .flatMap(_.selfHost) + .flatMap(_.videoPlayerFormat) + .getOrElse( + AtomApiVideoPlayerFormat.Default, + ) + .name, + ), ) } @@ -268,6 +312,8 @@ object MediaAtom extends common.GuLogging { platform = MediaAssetPlatform.withName(mediaAsset.platform.name), mimeType = mediaAsset.mimeType, assetType = MediaAssetType.withName(mediaAsset.assetType.name), + dimensions = mediaAsset.dimensions.map(dim => AssetDimensions(dim.width, dim.height)), + aspectRatio = mediaAsset.aspectRatio, ) } diff --git a/common/app/model/dotcomrendering/pageElements/PageElement.scala b/common/app/model/dotcomrendering/pageElements/PageElement.scala index de7ca89d5c25..5f603e7d9cce 100644 --- a/common/app/model/dotcomrendering/pageElements/PageElement.scala +++ b/common/app/model/dotcomrendering/pageElements/PageElement.scala @@ -434,12 +434,14 @@ object MapBlockElement { case class MediaAtomBlockElementMediaAsset( url: String, mimeType: Option[String], + dimensions: Option[AssetDimensions], + aspectRatio: Option[String], ) object MediaAtomBlockElementMediaAsset { implicit val MediaAtomBlockElementMediaAssetWrites: Writes[MediaAtomBlockElementMediaAsset] = Json.writes[MediaAtomBlockElementMediaAsset] def fromMediaAsset(asset: MediaAsset): MediaAtomBlockElementMediaAsset = { - MediaAtomBlockElementMediaAsset(asset.id, asset.mimeType) + MediaAtomBlockElementMediaAsset(asset.id, asset.mimeType, asset.dimensions, asset.aspectRatio) } } case class MediaAtomBlockElement( @@ -452,6 +454,7 @@ case class MediaAtomBlockElement( expired: Option[Boolean], activeVersion: Option[Long], channelId: Option[String], + videoPlayerFormat: Option[VideoPlayerFormat], ) extends PageElement object MediaAtomBlockElement { implicit val MediaAtomBlockElementWrites: Writes[MediaAtomBlockElement] = Json.writes[MediaAtomBlockElement] @@ -1313,6 +1316,7 @@ object PageElement { mediaAtom.expired, mediaAtom.activeVersion, mediaAtom.channelId, + mediaAtom.videoPlayerFormat, ), ) } diff --git a/common/test/model/ContentTest.scala b/common/test/model/ContentTest.scala index b12667475cc0..5a4df728e185 100644 --- a/common/test/model/ContentTest.scala +++ b/common/test/model/ContentTest.scala @@ -396,9 +396,10 @@ class ContentTest val contentNoByline = content("video", Nil).content val contentWithByline = content("video", Nil, byline).content - val mediaAtomWithSource = Some(MediaAtom("", "", Nil, "", None, atomSource, None, None, None, None, None)) - val mediaAtomWithNoSource = Some(MediaAtom("", "", Nil, "", None, None, None, None, None, None, None)) - val mediaAtomWithEmptySource = Some(MediaAtom("", "", Nil, "", None, emptySource, None, None, None, None, None)) + val mediaAtomWithSource = Some(MediaAtom("", "", Nil, "", None, atomSource, None, None, None, None, None, None)) + val mediaAtomWithNoSource = Some(MediaAtom("", "", Nil, "", None, None, None, None, None, None, None, None)) + val mediaAtomWithEmptySource = + Some(MediaAtom("", "", Nil, "", None, emptySource, None, None, None, None, None, None)) Video(contentNoByline, None, None).bylineWithSource should be(None) Video(contentNoByline, videoSource, None).bylineWithSource should be(videoSource.map(s => s"Source: $s")) diff --git a/common/test/views/fragments/atoms/YoutubeSpec.scala b/common/test/views/fragments/atoms/YoutubeSpec.scala index 65a50fe663e9..7fdf73fa1365 100644 --- a/common/test/views/fragments/atoms/YoutubeSpec.scala +++ b/common/test/views/fragments/atoms/YoutubeSpec.scala @@ -21,6 +21,8 @@ class YoutubeSpec extends MixedPlaySpec { platform = Youtube, mimeType = None, assetType = Video, + dimensions = None, + aspectRatio = None, ), ), title = "", @@ -31,6 +33,7 @@ class YoutubeSpec extends MixedPlaySpec { activeVersion = None, channelId = None, trailImage = None, + videoPlayerFormat = None, ) val displayCaption = false val view = views.html.fragments.atoms diff --git a/common/test/views/support/cleaner/AtomCleanerTest.scala b/common/test/views/support/cleaner/AtomCleanerTest.scala index 2b1916ca97d2..8e76d12915a7 100644 --- a/common/test/views/support/cleaner/AtomCleanerTest.scala +++ b/common/test/views/support/cleaner/AtomCleanerTest.scala @@ -34,6 +34,8 @@ class AtomCleanerTest extends AnyFlatSpec with Matchers with WithTestApplication platform = MediaAssetPlatform.Youtube, mimeType = None, assetType = Video, + dimensions = None, + aspectRatio = None, ) val youTubeAtom = Some( @@ -53,6 +55,7 @@ class AtomCleanerTest extends AnyFlatSpec with Matchers with WithTestApplication activeVersion = None, channelId = None, trailImage = Some(image), + videoPlayerFormat = None, ), ), interactives = Nil,