Skip to content

Commit

Permalink
Remove processing HTML in VideoPress upload processor
Browse files Browse the repository at this point in the history
  • Loading branch information
fluiddot committed Apr 19, 2023
1 parent f9a686c commit 1419183
Show file tree
Hide file tree
Showing 4 changed files with 11 additions and 249 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package org.wordpress.android.ui.posts.mediauploadcompletionprocessors;

import org.wordpress.android.util.UriEncoder;
import org.wordpress.android.util.helpers.MediaFile;

import java.util.HashMap;
Expand All @@ -18,7 +17,6 @@
class BlockProcessorFactory {
private final MediaUploadCompletionProcessor mMediaUploadCompletionProcessor;
private final Map<MediaBlockType, BlockProcessor> mMediaBlockTypeBlockProcessorMap;
private final UriEncoder mUriEncoder;

/**
* This factory initializes block processors for all media block types and provides a method to retrieve a block
Expand All @@ -27,7 +25,6 @@ class BlockProcessorFactory {
BlockProcessorFactory(MediaUploadCompletionProcessor mediaUploadCompletionProcessor) {
mMediaUploadCompletionProcessor = mediaUploadCompletionProcessor;
mMediaBlockTypeBlockProcessorMap = new HashMap<>();
mUriEncoder = new UriEncoder();
}

/**
Expand All @@ -38,7 +35,7 @@ class BlockProcessorFactory {
*/
BlockProcessorFactory init(String localId, MediaFile mediaFile, String siteUrl) {
mMediaBlockTypeBlockProcessorMap.put(IMAGE, new ImageBlockProcessor(localId, mediaFile));
mMediaBlockTypeBlockProcessorMap.put(VIDEOPRESS, new VideoPressBlockProcessor(localId, mediaFile, mUriEncoder));
mMediaBlockTypeBlockProcessorMap.put(VIDEOPRESS, new VideoPressBlockProcessor(localId, mediaFile));
mMediaBlockTypeBlockProcessorMap.put(VIDEO, new VideoBlockProcessor(localId, mediaFile));
mMediaBlockTypeBlockProcessorMap.put(MEDIA_TEXT, new MediaTextBlockProcessor(localId, mediaFile));
mMediaBlockTypeBlockProcessorMap.put(GALLERY, new GalleryBlockProcessor(localId, mediaFile, siteUrl,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,165 +7,9 @@ import org.wordpress.android.util.helpers.MediaFile

class VideoPressBlockProcessor(
localId: String?,
mediaFile: MediaFile?,
private val uriEncoder: UriEncoder = UriEncoder()
mediaFile: MediaFile?
) : BlockProcessor(localId, mediaFile) {
class VideoPressBlockSettings(
var autoplay: Boolean? = null,
var controls: Boolean? = null,
var loop: Boolean? = null,
var muted: Boolean? = null,
private var persistVolume: Boolean? = null,
var playsinline: Boolean? = null,
var poster: String? = null,
var preload: String? = null,
var seekbarColor: String? = null,
var seekbarPlayedColor: String? = null,
var seekbarLoadingColor: String? = null,
var useAverageColor: Boolean? = null
) {
constructor(jsonAttributes: JsonObject) : this() {
autoplay = jsonAttributes["autoplay"]?.asBoolean ?: false
controls = jsonAttributes["controls"]?.asBoolean ?: true
loop = jsonAttributes["loop"]?.asBoolean ?: false
jsonAttributes["muted"]?.asBoolean?.let { isMuted ->
muted = isMuted
persistVolume = !isMuted
}
playsinline = jsonAttributes["playsinline"]?.asBoolean ?: false
poster = jsonAttributes["poster"]?.toString()
preload = jsonAttributes["preload"]?.toString() ?: "metadata"
seekbarColor = jsonAttributes["seekbarColor"]?.toString()
seekbarPlayedColor = jsonAttributes["seekbarPlayedColor"]?.toString()
seekbarLoadingColor = jsonAttributes["seekbarLoadingColor"]?.toString()
useAverageColor = jsonAttributes["useAverageColor"]?.asBoolean ?: true
}
}

private var mBlockSettings = VideoPressBlockSettings()

/**
* Build VideoPress URL based on the values of the block's various settings.
* In order to have a cleaner URL, only the options differing from the default settings are added.
* Matches logic in Jetpack.
* Ref: https://github.com/Automattic/jetpack/blob/b1b826ab38690c5fad18789301ac81297a458878/projects/packages/videopress/src/client/lib/url/index.ts#L19-L67
*
*/
private fun getVideoPressURL(guid: String): String {
val queryArgs = getDefaultQueryArgs()
getBlockSettingsQueryArgs(queryArgs, mBlockSettings)

val encodedQueryArgs = queryArgs.entries.joinToString("&") {
val encodedValue = it.value.removeSurrounding("\"")
"${uriEncoder.encode(it.key)}=${uriEncoder.encode(encodedValue)}"
}

return "https://videopress.com/v/$guid?$encodedQueryArgs"
}

private fun getDefaultQueryArgs(): MutableMap<String, String> {
return mutableMapOf(
"resizeToParent" to "true",
"cover" to "true"
)
}

private fun getBlockSettingsQueryArgs(
queryArgs: MutableMap<String, String>,
blockSettings: VideoPressBlockSettings
) {
with(blockSettings) {
addAutoplayArg(queryArgs, autoplay)
addControlsArg(queryArgs, controls)
addLoopArg(queryArgs, loop)
addMutedAndPersistVolumeArgs(queryArgs, muted)
addPlaysinlineArg(queryArgs, playsinline)
addPosterArg(queryArgs, poster)
addPreloadArg(queryArgs, preload)
addSeekbarArgs(queryArgs, seekbarColor, seekbarPlayedColor, seekbarLoadingColor)
addUseAverageColorArg(queryArgs, useAverageColor)
}
}

// Adds AutoPlay option. Turned OFF by default.
private fun addAutoplayArg(queryArgs: MutableMap<String, String>, autoplay: Boolean?) {
if (autoplay == true) {
queryArgs["autoPlay"] = "true"
}
}

// Adds Controls option. Turned ON by default.
private fun addControlsArg(queryArgs: MutableMap<String, String>, controls: Boolean?) {
if (controls == false) {
queryArgs["controls"] = "false"
}
}

// Adds Loops option. Turned OFF by default.
private fun addLoopArg(queryArgs: MutableMap<String, String>, loop: Boolean?) {
if (loop == true) {
queryArgs["loop"] = "true"
}
}

// Adds Volume-related options. Muted: Turned OFF by default.
private fun addMutedAndPersistVolumeArgs(queryArgs: MutableMap<String, String>, muted: Boolean?) {
if (muted == true) {
queryArgs["muted"] = "true"
queryArgs["persistVolume"] = "false"
}
}

// Adds PlaysInline option. Turned OFF by default.
private fun addPlaysinlineArg(queryArgs: MutableMap<String, String>, playsinline: Boolean?) {
if (playsinline == true) {
queryArgs["playsinline"] = "true"
}
}

// Adds Poster option. No image by default.
private fun addPosterArg(queryArgs: MutableMap<String, String>, poster: String?) {
poster?.let { queryArgs["posterUrl"] = it }
}

// Adds Preload option. Metadata by default.
private fun addPreloadArg(queryArgs: MutableMap<String, String>, preload: String?) {
preload?.let { if (it.isNotEmpty()) queryArgs["preloadContent"] = it }
}

/**
* Adds Seekbar options.
* - SeekbarColor: No color by default.
* - SeekbarPlayerColor: No color by default.
* - SeekbarLoadingColor: No color by default.
*/
private fun addSeekbarArgs(
queryArgs: MutableMap<String, String>,
seekbarColor: String?,
seekbarPlayedColor: String?,
seekbarLoadingColor: String?
) {
seekbarColor?.let { if (it.isNotEmpty()) queryArgs["sbc"] = it }
seekbarPlayedColor?.let { if (it.isNotEmpty()) queryArgs["sbpc"] = it }
seekbarLoadingColor?.let { if (it.isNotEmpty()) queryArgs["sblc"] = it }
}

// Adds UseAverageColor option. Turned ON by default.
private fun addUseAverageColorArg(queryArgs: MutableMap<String, String>, useAverageColor: Boolean?) {
if (useAverageColor == true) {
queryArgs["useAverageColor"] = "true"
}
}

override fun processBlockContentDocument(document: Document?): Boolean {
val videoPressElements = document?.select("figure")

if (videoPressElements != null) {
val url = getVideoPressURL(mRemoteGuid)
videoPressElements.append("<div class='jetpack-videopress-player__wrapper'>$url</div>")
return true
}

return false
}

Expand All @@ -177,9 +21,6 @@ class VideoPressBlockProcessor(
addProperty(ID_ATTRIBUTE, Integer.parseInt(mRemoteId))
addProperty(GUID_ATTRIBUTE, mRemoteGuid)
}

mBlockSettings = VideoPressBlockSettings(jsonAttributes)

true
} else {
false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -660,21 +660,13 @@ $newRefactoredGalleryBlockInnerBlocks</figure>
<figure class="wp-block-audio"><audio controls src="$remoteAudioUrl"></audio></figure>
<!-- /wp:audio -->"""

const val oldVideoPressBlockWithDefaultAttrs = """<!-- wp:videopress/video {"id":${localMediaId}} -->
<figure class="wp-block-videopress-video wp-block-jetpack-videopress jetpack-videopress-player"></figure>
<!-- /wp:videopress/video -->"""
const val oldVideoPressBlockWithDefaultAttrs = """<!-- wp:videopress/video {"id":${localMediaId}} /-->"""

const val newVideoPressBlockWithDefaultAttrs = """<!-- wp:videopress/video {"id":${remoteMediaId},"guid":"${videoPressGuid}"} -->
<figure class="wp-block-videopress-video wp-block-jetpack-videopress jetpack-videopress-player"><div class="jetpack-videopress-player__wrapper">https://videopress.com/v/${videoPressGuid}?resizeToParent=true&amp;cover=true&amp;preloadContent=metadata&amp;useAverageColor=true</div></figure>
<!-- /wp:videopress/video -->"""
const val newVideoPressBlockWithDefaultAttrs = """<!-- wp:videopress/video {"id":${remoteMediaId},"guid":"${videoPressGuid}"} /-->"""

const val oldVideoPressBlockWithAttrs = """<!-- wp:videopress/video {"autoplay":true,"controls":false,"description":"","id":${localMediaId},"loop":true,"muted":true,"playsinline":true,"poster":"https://test.files.wordpress.com/2022/02/265-5000x5000-1.jpeg","preload":"none","seekbarColor":"#abb8c3","seekbarLoadingColor":"#cf2e2e","seekbarPlayedColor":"#9b51e0","title":"Demo title","useAverageColor":false} -->
<figure class="wp-block-videopress-video wp-block-jetpack-videopress jetpack-videopress-player"></figure>
<!-- /wp:videopress/video -->"""
const val oldVideoPressBlockWithAttrs = """<!-- wp:videopress/video {"autoplay":true,"controls":false,"description":"","id":${localMediaId},"loop":true,"muted":true,"playsinline":true,"poster":"https://test.files.wordpress.com/2022/02/265-5000x5000-1.jpeg","preload":"none","seekbarColor":"#abb8c3","seekbarLoadingColor":"#cf2e2e","seekbarPlayedColor":"#9b51e0","title":"Demo title","useAverageColor":false} /-->"""

const val newVideoPressBlockWithAttrs = """<!-- wp:videopress/video {"autoplay":true,"controls":false,"description":"","id":${remoteMediaId},"loop":true,"muted":true,"playsinline":true,"poster":"https://test.files.wordpress.com/2022/02/265-5000x5000-1.jpeg","preload":"none","seekbarColor":"#abb8c3","seekbarLoadingColor":"#cf2e2e","seekbarPlayedColor":"#9b51e0","title":"Demo title","useAverageColor":false,"guid":"${videoPressGuid}"} -->
<figure class="wp-block-videopress-video wp-block-jetpack-videopress jetpack-videopress-player"><div class="jetpack-videopress-player__wrapper">https://videopress.com/v/AbCdE?resizeToParent=true&amp;cover=true&amp;autoPlay=true&amp;controls=false&amp;loop=true&amp;muted=true&amp;persistVolume=false&amp;playsinline=true&amp;posterUrl=https%3A%2F%2Ftest.files.wordpress.com%2F2022%2F02%2F265-5000x5000-1.jpeg&amp;preloadContent=none&amp;sbc=%23abb8c3&amp;sbpc=%239b51e0&amp;sblc=%23cf2e2e</div></figure>
<!-- /wp:videopress/video -->"""
const val newVideoPressBlockWithAttrs = """<!-- wp:videopress/video {"autoplay":true,"controls":false,"description":"","id":${remoteMediaId},"loop":true,"muted":true,"playsinline":true,"poster":"https://test.files.wordpress.com/2022/02/265-5000x5000-1.jpeg","preload":"none","seekbarColor":"#abb8c3","seekbarLoadingColor":"#cf2e2e","seekbarPlayedColor":"#9b51e0","title":"Demo title","useAverageColor":false,"guid":"${videoPressGuid}"} /-->"""

const val oldPostImage = paragraphBlock + oldImageBlock + newVideoBlock + newMediaTextBlock + newGalleryBlock
const val newPostImage = paragraphBlock + newImageBlock + newVideoBlock + newMediaTextBlock + newGalleryBlock
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,93 +16,25 @@ import java.nio.charset.StandardCharsets
@RunWith(MockitoJUnitRunner::class)
class VideoPressBlockProcessorTest {
private val mediaFile: MediaFile = mock()
private val uriEncoder = Mockito.mock(UriEncoder::class.java)
private lateinit var processor: VideoPressBlockProcessor

// Keys for each query in VideoPress URL.
private val resizeToParentKey = "resizeToParent"
private val coverKey = "cover"
private val autoPlayKey = "autoPlay"
private val controlsKey = "controls"
private val loopKey = "loop"
private val mutedKey = "muted"
private val persistVolumeKey = "persistVolume"
private val playsinlineKey = "playsinline"
private val posterUrlKey = "posterUrl"
private val preloadContentKey = "preloadContent"
private val sbcKey = "sbc"
private val sbpcKey = "sbpc"
private val sblcKey = "sblc"
private val useAverageColorKey = "useAverageColor"

// Un-encoded values for each query in VideoPress URL.
private val falseVal = "false"
private val trueVal = "true"
private val posterUrlVal = "https://test.files.wordpress.com/2022/02/265-5000x5000-1.jpeg"
private val defaultPreloadContentVal = "metadata"
private val changedPreloadContentVal = "none"
private val sbcVal = "#abb8c3"
private val sbpcVal = "#9b51e0"
private val sblcVal = "#cf2e2e"

private val urlKeys = listOf(
resizeToParentKey,
coverKey,
autoPlayKey,
controlsKey,
loopKey,
mutedKey,
persistVolumeKey,
playsinlineKey,
posterUrlKey,
preloadContentKey,
sbcKey,
sbpcKey,
sblcKey,
useAverageColorKey
)
private val urlValues = listOf(
falseVal,
trueVal,
posterUrlVal,
defaultPreloadContentVal,
changedPreloadContentVal,
sbcVal,
sbpcVal,
sblcVal
)

@Before
fun before() {
whenever(mediaFile.mediaId).thenReturn(TestContent.remoteMediaId)
whenever(mediaFile.videoPressGuid).thenReturn(TestContent.videoPressGuid)

/*
* As Uri.encode is part of an Android class, it cannot run locally in unit tests by default.
* To workaround this, it has been replaced below with URLEncoder.encode, which works in a similar manner.
* Note, we cannot currently use URLEncoder.encode in the main app as it only runs with API 33 or later.
* We support a minimum of API 24.
*/
for (key in urlKeys) {
whenever(uriEncoder.encode(key)).thenReturn(URLEncoder.encode(key, StandardCharsets.UTF_8))
}

for (value in urlValues) {
whenever(uriEncoder.encode(value)).thenReturn(URLEncoder.encode(value, StandardCharsets.UTF_8))
}

processor = VideoPressBlockProcessor(TestContent.localMediaId, mediaFile, uriEncoder)
processor = VideoPressBlockProcessor(TestContent.localMediaId, mediaFile)
}

@Test
fun `processBlock replaces id and contents in VideoPress block with default attributes`() {
val processedBlock = processor.processBlock(TestContent.oldVideoPressBlockWithDefaultAttrs)
fun `processBlock replaces id in VideoPress block with default attributes`() {
val processedBlock = processor.processBlock(TestContent.oldVideoPressBlockWithDefaultAttrs, true)
Assertions.assertThat(processedBlock).isEqualTo(TestContent.newVideoPressBlockWithDefaultAttrs)
}

@Test
fun `processBlock replaces id and contents in VideoPress block with different attributes to the default`() {
val processedBlock = processor.processBlock(TestContent.oldVideoPressBlockWithAttrs)
fun `processBlock replaces id in VideoPress block with different attributes to the default`() {
val processedBlock = processor.processBlock(TestContent.oldVideoPressBlockWithAttrs, true)
Assertions.assertThat(processedBlock).isEqualTo(TestContent.newVideoPressBlockWithAttrs)
}

Expand Down

0 comments on commit 1419183

Please sign in to comment.