cloudinary-video-player
Version:
Cloudinary Video Player
317 lines (280 loc) • 12.3 kB
JavaScript
"use strict";
(self["cloudinaryVideoPlayerChunkLoading"] = self["cloudinaryVideoPlayerChunkLoading"] || []).push([["visual-search"],{
/***/ "./plugins/visual-search/components/SearchButton.js":
/*!**********************************************************!*\
!*** ./plugins/visual-search/components/SearchButton.js ***!
\**********************************************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ SearchButton: () => (/* binding */ SearchButton)
/* harmony export */ });
/* harmony import */ var video_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! video.js */ "../node_modules/video.js/dist/alt/video.core-exposed.js");
/* harmony import */ var video_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(video_js__WEBPACK_IMPORTED_MODULE_0__);
const SearchButton = onClick => {
const button = video_js__WEBPACK_IMPORTED_MODULE_0___default().dom.createEl('button', {
className: 'vjs-control vjs-button vjs-visual-search-button',
title: 'Search video content',
ariaLabel: 'Search video content'
});
const searchIcon = video_js__WEBPACK_IMPORTED_MODULE_0___default().dom.createEl('span', {
className: 'vjs-icon-search'
});
button.appendChild(searchIcon);
const spinnerIcon = video_js__WEBPACK_IMPORTED_MODULE_0___default().dom.createEl('span', {
className: 'vjs-loading-spinner'
});
button.appendChild(spinnerIcon);
button.addEventListener('click', onClick);
return button;
};
/***/ }),
/***/ "./plugins/visual-search/components/SearchInput.js":
/*!*********************************************************!*\
!*** ./plugins/visual-search/components/SearchInput.js ***!
\*********************************************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ SearchInput: () => (/* binding */ SearchInput)
/* harmony export */ });
/* harmony import */ var video_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! video.js */ "../node_modules/video.js/dist/alt/video.core-exposed.js");
/* harmony import */ var video_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(video_js__WEBPACK_IMPORTED_MODULE_0__);
const SearchInput = (onSearch, onClose) => {
const form = video_js__WEBPACK_IMPORTED_MODULE_0___default().dom.createEl('form', {
className: 'vjs-visual-search-form'
});
const input = video_js__WEBPACK_IMPORTED_MODULE_0___default().dom.createEl('input', {
className: 'vjs-visual-search-input',
type: 'text',
ariaLabel: 'Search input',
tabIndex: -1
});
const closeButton = video_js__WEBPACK_IMPORTED_MODULE_0___default().dom.createEl('button', {
className: 'vjs-control vjs-button vjs-visual-search-close',
type: 'button',
title: 'Close search',
ariaLabel: 'Close search',
tabIndex: -1
});
// Add close icon
const closeIcon = video_js__WEBPACK_IMPORTED_MODULE_0___default().dom.createEl('span', {
className: 'vjs-icon-close'
});
closeButton.appendChild(closeIcon);
form.appendChild(input);
form.appendChild(closeButton);
// Handle search submission
form.addEventListener('submit', e => {
e.preventDefault();
const query = input.value.trim();
if (query) {
onSearch(query);
}
});
// Handle close button
closeButton.addEventListener('click', e => {
e.preventDefault();
if (onClose) {
onClose();
}
});
return {
element: form,
input,
closeButton
};
};
/***/ }),
/***/ "./plugins/visual-search/components/SearchResults.js":
/*!***********************************************************!*\
!*** ./plugins/visual-search/components/SearchResults.js ***!
\***********************************************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ SearchResults: () => (/* binding */ SearchResults)
/* harmony export */ });
/* harmony import */ var video_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! video.js */ "../node_modules/video.js/dist/alt/video.core-exposed.js");
/* harmony import */ var video_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(video_js__WEBPACK_IMPORTED_MODULE_0__);
const SearchResults = player => {
const clearMarkers = () => {
player.$$('.vjs-visual-search-marker').forEach(el => el.remove());
player.$$('.vjs-visual-search-results-wrapper').forEach(el => el.remove());
// Remove the class that indicates search results are displayed
player.removeClass('vjs-visual-search-results-active');
};
const displayResults = results => {
// Clear existing markers
clearMarkers();
const total = player.duration();
const seekBar = player.controlBar.progressControl.seekBar;
// Create wrapper for search results
const wrapperEl = video_js__WEBPACK_IMPORTED_MODULE_0___default().dom.createEl('div', {
className: 'vjs-visual-search-results-wrapper',
role: 'presentation'
});
// Add markers for each result
results.forEach(result => {
const {
start_time,
end_time
} = result;
const position = start_time / total * 100;
const width = (end_time - start_time) / total * 100;
const time = `${Math.floor(start_time / 60)}:${Math.floor(start_time % 60).toString().padStart(2, '0')}`;
const markerEl = video_js__WEBPACK_IMPORTED_MODULE_0___default().dom.createEl('div', {
className: 'vjs-control vjs-visual-search-marker',
style: `left: ${position}%; width: ${width}%`,
tabIndex: 0,
role: 'button',
title: `Search result at ${time}`,
ariaLabel: `Search result at ${time}`
});
wrapperEl.appendChild(markerEl);
// Add click handler to jump to this time
markerEl.addEventListener('click', () => {
player.currentTime(start_time);
});
// Add keyboard support
markerEl.addEventListener('keydown', e => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
player.currentTime(start_time);
}
});
});
// Add wrapper to seek bar
seekBar.el().appendChild(wrapperEl);
// Add a class to indicate search results are displayed
if (results.length > 0) {
player.addClass('vjs-visual-search-results-active');
}
};
return {
displayResults,
clearMarkers
};
};
/***/ }),
/***/ "./plugins/visual-search/visual-search.js":
/*!************************************************!*\
!*** ./plugins/visual-search/visual-search.js ***!
\************************************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var video_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! video.js */ "../node_modules/video.js/dist/alt/video.core-exposed.js");
/* harmony import */ var video_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(video_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _components_SearchButton__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./components/SearchButton */ "./plugins/visual-search/components/SearchButton.js");
/* harmony import */ var _components_SearchInput__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./components/SearchInput */ "./plugins/visual-search/components/SearchInput.js");
/* harmony import */ var _components_SearchResults__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./components/SearchResults */ "./plugins/visual-search/components/SearchResults.js");
/* harmony import */ var _visual_search_scss__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./visual-search.scss */ "./plugins/visual-search/visual-search.scss");
const visualSearch = (options, player) => {
player.addClass('vjs-visual-search');
let isSearchActive = false;
const searchResults = (0,_components_SearchResults__WEBPACK_IMPORTED_MODULE_3__.SearchResults)(player);
const performSearch = async query => {
const searchButton = player.$('.vjs-visual-search-button');
searchButton.classList.add('vjs-waiting');
try {
const source = player.cloudinary.source();
const publicId = source.publicId();
const transformation = Object.assign({}, source.transformation());
transformation.flags = transformation.flags || [];
transformation.flags.push(`getinfo:search_b64_${btoa(query)}`);
const visualSearchSrc = source.config().url(`${publicId}`, {
transformation
});
const response = await fetch(visualSearchSrc, {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
});
if (!response.ok) {
throw new Error(`Search request failed with status: ${response.status}`);
}
const results = await response.json();
searchResults.displayResults(results.timestamps);
if (results && !player.hasStarted()) {
// Make sure the progress bar is visible
player.play().then(() => player.pause());
}
} catch (error) {
console.error('Error performing visual search:', error);
} finally {
searchButton.classList.remove('vjs-waiting');
}
};
const clearUI = () => {
isSearchActive = false;
searchResults.clearMarkers();
player.$('.vjs-visual-search-wrapper')?.remove();
};
const createSearchUI = () => {
clearUI();
const titleBar = player.$('.vjs-title-bar');
if (titleBar) {
titleBar.classList.remove('vjs-hidden');
}
const searchContainer = video_js__WEBPACK_IMPORTED_MODULE_0___default().dom.createEl('div', {
className: 'vjs-visual-search-wrapper'
});
// Handle the search icon click (expand or submit)
const handleSearchButtonClick = () => {
if (!isSearchActive) {
isSearchActive = true;
searchContainer.classList.add('vjs-visual-search-active');
searchInput.input.tabIndex = 0;
searchInput.closeButton.tabIndex = 0;
searchInput.input.focus();
} else {
const query = searchInput.input.value.trim();
if (query) {
performSearch(query);
}
}
};
const closeSearch = () => {
if (isSearchActive) {
isSearchActive = false;
searchContainer.classList.remove('vjs-visual-search-active');
searchInput.input.value = '';
searchInput.input.tabIndex = -1;
searchInput.closeButton.tabIndex = -1;
searchResults.clearMarkers();
}
};
const searchButton = (0,_components_SearchButton__WEBPACK_IMPORTED_MODULE_1__.SearchButton)(handleSearchButtonClick);
const searchInput = (0,_components_SearchInput__WEBPACK_IMPORTED_MODULE_2__.SearchInput)(performSearch, closeSearch);
searchContainer.appendChild(searchButton);
searchContainer.appendChild(searchInput.element);
titleBar.prepend(searchContainer);
player.on('keydown', e => {
if (e.key === 'Escape' && isSearchActive) {
closeSearch();
}
});
};
createSearchUI();
// Public methods
player.visualSearch = {
createSearchUI,
clearUI
};
};
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (visualSearch);
/***/ }),
/***/ "./plugins/visual-search/visual-search.scss":
/*!**************************************************!*\
!*** ./plugins/visual-search/visual-search.scss ***!
\**************************************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
// extracted by mini-css-extract-plugin
/***/ })
}]);
//# sourceMappingURL=visual-search.light.js.map