Skip to content

Commit

Permalink
#2522: refactor video resizing to support ability to test resizing in…
Browse files Browse the repository at this point in the history
… cypress and improve accessibility
  • Loading branch information
sudo-may committed Jan 2, 2025
1 parent 8506d4d commit 0be7194
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 56 deletions.
126 changes: 77 additions & 49 deletions dashboard/src/common-components/video/VideoPlayer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
import { onBeforeUnmount, onMounted, onUnmounted, ref, computed } from 'vue'
import videojs from 'video.js';
import WatchedSegmentsUtil from '@/common-components/video/WatchedSegmentsUtil';
import {useStorage} from "@vueuse/core";
const props = defineProps({
options: Object,
Expand All @@ -39,46 +40,24 @@ const watchProgress = ref({
const playerContainer = { player: null }
const playerWidth = ref(null);
const playerHeight = ref(null);
const resolution = ref('');
const resolution = computed(() => {
if (!videoPlayerSizeInStorage.value) {
return ''
}
return `${videoPlayerSizeInStorage.value.width} x ${videoPlayerSizeInStorage.value.height}`
})
const isResizing = ref(false);
const resizeTimer = ref(null);
const isFirstLoad = ref(true);
const resizeObserver = new ResizeObserver((mutations) => {
if(resizeTimer.value) {
clearTimeout(resizeTimer.value);
resizeTimer.value = null;
}
isResizing.value = true;
resizeTimer.value = setTimeout(() => {
isResizing.value = false;
if(isFirstLoad.value) {
isFirstLoad.value = false;
}
}, 250);
const currentWidth = mutations[0].contentBoxSize[0].inlineSize;
const currentHeight = mutations[0].contentBoxSize[0].blockSize;
resolution.value = `${mutations[0].devicePixelContentBoxSize[0].inlineSize} x ${mutations[0].devicePixelContentBoxSize[0].blockSize}`;
if(!props.loadFromServer) {
if( currentHeight > 0 && currentWidth > 0 ) {
localStorage.setItem(`${vidPlayerId}-playerSize`, JSON.stringify({width: currentWidth, height: currentHeight}));
}
}
emit('on-resize', resolution.value, currentWidth, currentHeight);
})
const videoPlayerSizeInStorage = useStorage(`${vidPlayerId}-playerSize`, {})
onMounted(() => {
if(props.options.width && props.options.height) {
playerWidth.value = props.options.width + 22;
playerHeight.value = props.options.height + 22;
}
const existingSize = localStorage.getItem(`${vidPlayerId}-playerSize`);
if (existingSize && !props.loadFromServer) {
const sizeObject = JSON.parse(existingSize);
playerWidth.value = sizeObject.width + 22;
playerHeight.value = sizeObject.height + 22;
}
resizeObserver.observe(document.getElementById('videoPlayerContainer'));
const player = videojs(vidPlayerId, {
playbackRates: [0.5, 1, 1.5, 2],
enableSmoothSeeking: true,
Expand All @@ -96,6 +75,8 @@ onMounted(() => {
});
playerContainer.player = player
});
createResizeSupport()
})
onBeforeUnmount(() => {
if (playerContainer.player) {
Expand All @@ -110,34 +91,81 @@ const updateProgress = (currentTime) => {
emit('watched-progress', watchProgress.value)
}
const createResizeSupport = () => {
function makeResizableDiv() {
const resizableDiv = `#${vidPlayerId}Container`
const element = document.querySelector(resizableDiv);
const handle = document.querySelectorAll( `#${vidPlayerId}ResizeHandle`)[0]
handle.addEventListener('mousedown', function (e) {
e.preventDefault()
window.addEventListener('mousemove', resize)
window.addEventListener('mouseup', stopResize)
})
function resize(e) {
const clientRect = element.getBoundingClientRect()
const width = Math.trunc(clientRect.width)
const height = Math.trunc(clientRect.height)
videoPlayerSizeInStorage.value = { width, height }
element.style.width = e.pageX - clientRect.left + 'px'
emit('on-resize', width, height);
}
function stopResize() {
window.removeEventListener('mousemove', resize)
}
}
makeResizableDiv()
}
</script>

<template>
<div data-cy="videoPlayer" id="videoPlayerContainer" :style="playerWidth ? `width: ${playerWidth}px;` : ''">
<div class="absolute z-5 top-0 left-0 right-0 bottom-0 bg-gray-500 opacity-50 text-center flex align-items-center justify-content-center" v-if="isResizing && !isFirstLoad">
<div class="absolute z-6 top-0 text-center bg-white mt-4" style="width: 100px;">
{{ resolution }}
</div>
</div>
<video :id="vidPlayerId"
class="video-js vjs-fluid"
data-setup='{}'
responsive
controls>
<source :src="options.url" :type="options.videoType">
<track v-if="props.options.captionsUrl" :src="props.options.captionsUrl" kind="captions" srclang="en" label="English">
</video>
<div :id="`${vidPlayerId}Container`" data-cy="videoPlayer" :style="playerWidth ? `width: ${playerWidth}px;` : ''" class="videoPlayerContainer">
<div class="bg-white block" style="width: 3rem; height: 3rem">
<i class="fas fa-expand-alt fa-rotate-90 handle bg-white border-top-1"
:id="`${vidPlayerId}ResizeHandle`"
data-cy="videoResizeHandle"
aria-label="Resize video dimensions control"
tabindex="0"></i>
</div>
<div class="absolute z-5 top-0 left-0 right-0 bottom-0 bg-gray-500 opacity-50 text-center flex align-items-center justify-content-center " v-if="isResizing && !isFirstLoad">
<div class="absolute z-6 top-0 text-center bg-white mt-4" style="width: 100px;">
{{ resolution }}
</div>
</div>
<video :id="vidPlayerId"
class="video-js vjs-fluid"
data-setup='{}'
responsive
controls>
<source :src="options.url" :type="options.videoType">
<track v-if="props.options.captionsUrl" :src="props.options.captionsUrl" kind="captions" srclang="en" label="English">
</video>
</div>
</template>

<style scoped>
#videoPlayerContainer {
resize: horizontal;
overflow: auto;
padding: 10px;
.videoPlayerContainer {
//resize: horizontal;
overflow: hidden;
border: 1px solid gray;
max-width: 100%;
min-width: 222px;
position: relative;
}
</style>
.handle{
font-size: 1.1rem;
right: 0px;
bottom: 0px;
position: absolute;
z-index: 500;
}
</style>
11 changes: 4 additions & 7 deletions dashboard/src/components/video/VideoConfigPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ const showSavedMsg = ref(false);
const overallErrMsg = ref(null);
const showFileUpload = ref(true);
const savedAtLeastOnce = ref(false);
const configuredResolution = ref(null);
const configuredWidth = ref(null);
const configuredResolution = computed(() => configuredWidth.value && configuredHeight.value ? configuredWidth.value + " x " + configuredHeight.value : 'Not Configured')
const configuredHeight = ref(null);
const computedVideoConf = computed(() => {
const captionsUrl = videoConf.value.captions && videoConf.value.captions.trim().length > 0
Expand Down Expand Up @@ -272,7 +272,6 @@ const updateVideoSettings = (settingRes) => {
videoConf.value.hostedFileName = settingRes.internallyHostedFileName;
configuredWidth.value = settingRes.width
configuredHeight.value = settingRes.height
configuredResolution.value = settingRes.width + " x " + settingRes.height;
if (videoConf.value.url) {
showFileUpload.value = videoConf.value.isInternallyHosted;
savedAtLeastOnce.value = true;
Expand Down Expand Up @@ -386,11 +385,10 @@ const schema = yup.object().shape({
const { values, meta, handleSubmit, resetForm, validate, errors } = useForm({ validationSchema: schema, })
const hasBeenResized = ref(false);
const videoResized = (resolution, width, height) => {
const videoResized = (width, height) => {
if(width !== configuredWidth.value && height !== configuredHeight.value) {
hasBeenResized.value = true;
}
configuredResolution.value = resolution;
configuredWidth.value = width;
configuredHeight.value = height;
}
Expand Down Expand Up @@ -425,7 +423,6 @@ const videoResized = (resolution, width, height) => {
</Message>
</div>
<!-- <div data-cy="videoInputFields" class="flex flex-column md:flex-row flex-wrap gap-2 mb-4">-->
<div data-cy="videoInputFields" class="mb-4" :class="{'flex flex-column gap-2': responsive.md.value }">
<div class="flex flex-column md:flex-row gap-2 md:mb-2">
<div class="flex-1 align-content-end">
Expand Down Expand Up @@ -621,8 +618,8 @@ const videoResized = (resolution, width, height) => {
<div class="col"><span class="text-primary">{{ watchedProgress.currentPosition.toFixed(2) }}</span> <span class="font-italic">Seconds</span></div>
</div>
<div class="grid">
<div class="col-6 lg:col-3 xl:col-2">Video Size:</div>
<div class="col"><span class="text-primary">{{ configuredResolution }}</span></div>
<div class="col-6 lg:col-3 xl:col-2">Default Video Size:</div>
<div class="col"><span class="text-primary" data-cy="defaultVideoSize">{{ configuredResolution }}</span></div>
</div>
<div class="grid">
<div class="col-6 lg:col-3 xl:col-2">Watched Segments:</div>
Expand Down
24 changes: 24 additions & 0 deletions e2e-tests/cypress/e2e/video/configure_video_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -236,4 +236,28 @@ describe('Configure Video Tests', () => {
cy.get('[data-cy="showFileUploadBtn"]').should('not.exist')
});

it('resize video setting', () => {
cy.createProject(1)
cy.createSubject(1, 1);
cy.createSkill(1, 1, 1)
const vid = { file: 'create-subject.webm', captions: 'cool caption', transcript: 'great' }
cy.saveVideoAttrs(1, 1, vid)
cy.visitVideoConfPage();

cy.wait(1000)
cy.get('[data-cy="defaultVideoSize"]').contains('Not Configured')
cy.get('[data-cy="videoResizeHandle"]').should('be.visible')
.trigger('mousedown', )
.trigger('mousemove', )
.trigger('mouseup', { force: true })
cy.get('[data-cy="defaultVideoSize"]').contains('707 x 537')


cy.get('[data-cy="videoResizeHandle"]').should('be.visible')
.trigger('mousedown', )
.trigger('mousemove', )
.trigger('mouseup', { force: true })
cy.get('[data-cy="defaultVideoSize"]').contains('697 x 530')

});
});

0 comments on commit 0be7194

Please sign in to comment.