diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 8e041b1aa6..19c78f1385 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -47,8 +47,4 @@ // include all of our vendored js //= require_tree ../../../vendor/assets/javascripts/. -// Require VideoJS and VideoJS quality selector for embedded player -//= require video.js/dist/video.min.js -//= require @silvermine/videojs-quality-selector/dist/js/silvermine-videojs-quality-selector.min.js - //= require_tree . diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 30153ba8ee..f4af972a20 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -55,6 +55,3 @@ @import 'avalon'; @import "datatables"; - -@import "video.js/dist/video-js.css"; -@import "@silvermine/videojs-quality-selector/dist/css/quality-selector.css"; diff --git a/app/assets/stylesheets/embed.scss b/app/assets/stylesheets/embed.scss deleted file mode 100644 index 1dfb32109b..0000000000 --- a/app/assets/stylesheets/embed.scss +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 2011-2024, The Trustees of Indiana University and Northwestern - * University. Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed - * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - * CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - * --- END LICENSE_HEADER BLOCK --- -*/ - -.video-container { - position: absolute; -} - -.video-js { - position: relative; - z-index: 0; -} - -.video-title { - position: absolute; - z-index: 1; - top: 5px; - left: 8px; -} - -.video-title .video-link { - color: white; - font-size:150%; -} - -.video-js.vjs-playing + .video-title { - display: none; -} - -// @silvermine/videojs-quality-selector -.vjs-menu { - li { - font-size: 1em; - } -} - -.video-js .vjs-control-bar { - /* Audio: Make the controlbar visible by default */ - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - /* Increase the control-bar icons/text size */ - font-size: 120%; -} - -/* Make player height minimum to the controls height so when we hide -video/poster area the controls are displayed correctly. */ -.video-js.vjs-audio { - min-height: 3.7em; -} - -.video-js .vjs-progress-control:hover .vjs-play-progress:after { - display: none; -} - -/* Show poster image when playback ends */ -.video-js.vjs-ended .vjs-poster { - display: block; -} - -.video-js .vjs-current-time { - display: block; -} - -/* Put playhead on top of markers */ -.video-js .vjs-play-progress:before { - z-index: 101; -} - -/* time-control elements */ -.video-js .vjs-time-control { - min-width: 0.5rem; - padding: 0 0.25rem; -} - -.vjs-time-divider { - display: block; -} - -.vjs-duration { - display: block !important; -} - -/* big-play button */ -.video-js .vjs-big-play-button { - margin-left: auto; - margin-right: auto; - height: 2.5rem; - width: 2.5rem; - line-height: 2.5rem; - border-radius: 50%; - scale: 2; - border: 0.1rem solid #fff; -} - -/* captions button selection */ -.captions-on { - border-bottom: 0.45rem ridge; -} - -.vjs-custom-external-link .vjs-icon-placeholder { - font-size: 120%; -} diff --git a/app/controllers/master_files_controller.rb b/app/controllers/master_files_controller.rb index 31f2011ffe..c1aa6c40b0 100644 --- a/app/controllers/master_files_controller.rb +++ b/app/controllers/master_files_controller.rb @@ -71,14 +71,6 @@ def show end def embed - if can? :read, @master_file - @stream_info = secure_streams(@master_file.stream_details, @master_file.media_object_id) - @stream_info['t'] = view_context.parse_media_fragment(params[:t]) # add MediaFragment from params - @stream_info['link_back_url'] = view_context.share_link_for(@master_file) - end - - @player_width = "100%" - @player_height = "100%" respond_to do |format| format.html do response.headers.delete "X-Frame-Options" diff --git a/app/javascript/components/MediaObjectRamp.jsx b/app/javascript/components/MediaObjectRamp.jsx index 8dd48fecb9..047ae20f8b 100644 --- a/app/javascript/components/MediaObjectRamp.jsx +++ b/app/javascript/components/MediaObjectRamp.jsx @@ -1,5 +1,5 @@ /* - * Copyright 2011-2023, The Trustees of Indiana University and Northwestern + * Copyright 2011-2024, The Trustees of Indiana University and Northwestern * University. Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * diff --git a/app/javascript/components/PlaylistRamp.jsx b/app/javascript/components/PlaylistRamp.jsx index 78fe26ebe6..7bbd1a2e09 100644 --- a/app/javascript/components/PlaylistRamp.jsx +++ b/app/javascript/components/PlaylistRamp.jsx @@ -1,5 +1,5 @@ /* - * Copyright 2011-2023, The Trustees of Indiana University and Northwestern + * Copyright 2011-2024, The Trustees of Indiana University and Northwestern * University. Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * diff --git a/app/javascript/components/embeds/EmbeddedRamp.jsx b/app/javascript/components/embeds/EmbeddedRamp.jsx new file mode 100644 index 0000000000..7846a0d998 --- /dev/null +++ b/app/javascript/components/embeds/EmbeddedRamp.jsx @@ -0,0 +1,119 @@ +/* + * Copyright 2011-2024, The Trustees of Indiana University and Northwestern + * University. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * --- END LICENSE_HEADER BLOCK --- +*/ + +import React from 'react'; +import { + IIIFPlayer, + MediaPlayer +} from '@samvera/ramp'; +import 'video.js/dist/video-js.css'; +import "@samvera/ramp/dist/ramp.css"; +import './Ramp.scss'; + +const Ramp = ({ + urls, + media_object_id +}) => { + const [manifestUrl, setManifestUrl] = React.useState(''); + const [startCanvasId, setStartCanvasId] = React.useState(); + const [startCanvasTime, setStartCanvasTime] = React.useState(); + let interval; + + React.useEffect(() => { + const { base_url, fullpath_url } = urls; + // Split the current path from the time fragment in the format .../:id?t=time + let [fullpath, start_time] = fullpath_url.split('?t='); + // Split the current path in the format /master_files/:mf_id/embed + let [_, __, mf_id, ___] = fullpath.split('/'); + // Build the manifest URL + let url = `${base_url}/media_objects/${media_object_id}/manifest.json`; + + // Set start Canvas ID and start time in the state for Ramp + setStartCanvasId( + mf_id && mf_id != undefined + ? `${base_url}/media_objects/${media_object_id}/manifest/canvas/${mf_id}` + : undefined + ); + setStartCanvasTime( + start_time && start_time != undefined + ? parseFloat(start_time) + : undefined + ); + setManifestUrl(url); + + interval = setInterval(addControls, 500); + + // Clear interval upon component unmounting + return () => clearInterval(interval); + }, []); + + const addControls = () => { + let player = document.getElementById('iiif-media-player'); + if (player && player.player) { + let embeddedPlayer = player.player + + // Player API handling + window.addEventListener('message', function(event) { + var command = event.data.command; + + if (command=='play') embeddedPlayer.play(); + else if (command=='pause') embeddedPlayer.pause(); + else if (command=='toggle_loop') { + embeddedPlayer.loop() ? embeddedPlayer.loop(false): embeddedPlayer.loop(true); + embeddedPlayer.autoplay() ? embeddedPlayer.autoplay(false) : embeddedPlayer.autoplay(true); + } + else if (command=='set_offset') embeddedPlayer.currentTime(event.data.offset); // time is in seconds + else if (command=='get_offset') event.source.postMessage({'command': 'currentTime','currentTime': embeddedPlayer.currentTime()}, event.origin); + }); + + /* + Quality selector extends outside iframe for audio items, so we need to disable that control + and rely on the quality automatically selected by the user's system. + */ + if (embeddedPlayer.isAudio()) { embeddedPlayer.controlBar.removeChild('qualitySelector'); } + + // Create button component for "View in Repository" and add to control bar + let repositoryUrl = Object.values(urls).join('/').replace('/embed', ''); + let position = embeddedPlayer.isAudio() ? embeddedPlayer.controlBar.children_.length : embeddedPlayer.controlBar.children_.length - 1; + var viewInRepoButton = embeddedPlayer.getChild('ControlBar').addChild('button', { + clickHandler: function(event) { + window.open(repositoryUrl, '_blank').focus(); + } + }, position); + + viewInRepoButton.addClass('vjs-custom-external-link'); + viewInRepoButton.el_.setAttribute('style', 'cursor: pointer;'); + viewInRepoButton.controlText('View in Repository'); + + // Add button icon + document.querySelector('.vjs-custom-external-link .vjs-icon-placeholder').innerHTML = '' + + // This function only needs to run once, so we clear the interval here + clearInterval(interval); + } + }; + + return ( + + + + ); +}; + +export default Ramp; \ No newline at end of file diff --git a/app/javascript/components/embeds/Ramp.scss b/app/javascript/components/embeds/Ramp.scss new file mode 100644 index 0000000000..6dc816a220 --- /dev/null +++ b/app/javascript/components/embeds/Ramp.scss @@ -0,0 +1,40 @@ +/* + * Copyright 2011-2024, The Trustees of Indiana University and Northwestern + * University. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * --- END LICENSE_HEADER BLOCK --- +*/ + +[class*="ramp--"] { + font-family: Arial, Helvetica, sans-serif; +} + +.ramp--media_player { + .video-js .vjs-big-play-button { + left: 55% !important; + } + @media (max-width: 585px) { + .video-js .vjs-big-play-button { + scale: 1.5; + } + + .video-js .vjs-control-bar { + font-size: 90% !important; + } + + // reduce player height to match with adjusted font-size + // for smaller screens + .video-js.vjs-audio { + min-height: 2.9em; + } + } +} diff --git a/app/javascript/packs/application.js b/app/javascript/packs/application.js index 37464816a0..f6046a0933 100644 --- a/app/javascript/packs/application.js +++ b/app/javascript/packs/application.js @@ -25,6 +25,11 @@ // console.log('Hello World from Webpacker') // Support component names relative to this directory: -var componentRequireContext = require.context("components", true) + +/* + * For some reason including the `embeds` directory in this `require.context` breaks + * the player. Filtering out the directory allows everything to operate as intended. + */ +var componentRequireContext = require.context("components", true, /^(?!embed)/) var ReactRailsUJS = require("react_ujs") ReactRailsUJS.useContext(componentRequireContext) diff --git a/app/javascript/packs/embed.js b/app/javascript/packs/embed.js new file mode 100644 index 0000000000..0a89e1c8ae --- /dev/null +++ b/app/javascript/packs/embed.js @@ -0,0 +1,18 @@ +/* + * Copyright 2011-2024, The Trustees of Indiana University and Northwestern + * University. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * --- END LICENSE_HEADER BLOCK --- +*/ +var embedRequireContext = require.context("components/embeds", false) +var ReactRailsUJS = require("react_ujs") +ReactRailsUJS.useContext(embedRequireContext) \ No newline at end of file diff --git a/app/views/layouts/embed.html.erb b/app/views/layouts/embed.html.erb index c1cc70d1ce..d2e7777297 100644 --- a/app/views/layouts/embed.html.erb +++ b/app/views/layouts/embed.html.erb @@ -13,10 +13,6 @@ Unless required by applicable law or agreed to in writing, software distributed specific language governing permissions and limitations under the License. --- END LICENSE_HEADER BLOCK --- %> -<% content_for :page_styles do %> - <%= stylesheet_link_tag 'embed' %> -<% end %> - @@ -26,8 +22,9 @@ Unless required by applicable law or agreed to in writing, software distributed <%= csrf_meta_tags %> - <%= favicon_link_tag %> - <%= stylesheet_link_tag "application", media: "all" %> + <%= favicon_link_tag %> + <%= stylesheet_pack_tag 'embed' %> + <%= javascript_pack_tag 'embed' %> <%= yield :page_styles %> <%= yield :additional_head_content %> <%= render "modules/google_analytics" %> @@ -35,7 +32,6 @@ Unless required by applicable law or agreed to in writing, software distributed <%= yield %> - <%= javascript_include_tag "application" %> <%= yield :page_scripts %> diff --git a/app/views/master_files/_player.html.erb b/app/views/master_files/_player.html.erb index 35d78c9349..d4ab0100e3 100644 --- a/app/views/master_files/_player.html.erb +++ b/app/views/master_files/_player.html.erb @@ -14,28 +14,9 @@ Unless required by applicable law or agreed to in writing, software distributed --- END LICENSE_HEADER BLOCK --- %> -<%= render partial: 'modules/player/section', locals: {section: @master_file, section_info: @stream_info, f_start: @f_start, f_end: @f_end} %> - -<% content_for :page_scripts do %> - - - - -<% end %> +<%= react_component("EmbeddedRamp", + { + urls: { base_url: request.protocol+request.host_with_port, fullpath_url: request.fullpath }, + media_object_id: @master_file.media_object_id + } +) %> diff --git a/app/views/modules/player/_section.html.erb b/app/views/modules/player/_section.html.erb deleted file mode 100644 index ccb5526d6e..0000000000 --- a/app/views/modules/player/_section.html.erb +++ /dev/null @@ -1,20 +0,0 @@ -<%# -Copyright 2011-2024, The Trustees of Indiana University and Northwestern - University. Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed - under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - CONDITIONS OF ANY KIND, either express or implied. See the License for the - specific language governing permissions and limitations under the License. ---- END LICENSE_HEADER BLOCK --- -%> -
- <% if section.present? && section.derivatives.present? && @master_file.present? %> - <%= render partial: 'modules/player/video_js_element', locals: {section: section, section_info: section_info, f_start: @f_start, f_end: @f_end} %> - <% end %> -
diff --git a/app/views/modules/player/_video_js_element.html.erb b/app/views/modules/player/_video_js_element.html.erb deleted file mode 100644 index 38e869a58b..0000000000 --- a/app/views/modules/player/_video_js_element.html.erb +++ /dev/null @@ -1,117 +0,0 @@ -<%# -Copyright 2011-2024, The Trustees of Indiana University and Northwestern - University. Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed - under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - CONDITIONS OF ANY KIND, either express or implied. See the License for the - specific language governing permissions and limitations under the License. ---- END LICENSE_HEADER BLOCK --- -%> - -<%= master_file_meta_properties(section) do %> - <% control_bar_options = if section_info[:is_video] - { - "children": [ - 'playToggle', - 'volumePanel', - 'progressControl', - 'currentTimeDisplay', - 'timeDivider', - 'durationDisplay', - 'subsCapsButton', - 'qualitySelector' - ], - fullscreenToggle: section_info[:is_video] ? true : false - } - else - { - "children": [ - 'playToggle', - 'volumePanel', - 'progressControl', - 'currentTimeDisplay', - 'timeDivider', - 'durationDisplay' - ] - } - end %> - - <% @videojs_options = { - "autoplay": false, - "width": @player_width || 480, - "height": @player_height || 270, - "bigPlayButton": section_info[:is_video] ? true : false, - "poster": section_info[:is_video] ? section_info[:poster_image] : false, - "preload": "auto", - "controlBar": control_bar_options, - "userActions": { - hotkeys: true - } - }.compact.to_json %> - -
- - <% section_info[:stream_hls].each do |hls| %> - - <% end %> - <% if section_info[:caption_paths].present? %> - <% section_info[:caption_paths].each do |c| %> - label="<%= c[:label] %>" <% end %> srclang="<%= c[:language] %>" kind="subtitles" type="<%= c[:mime_type] %>" src="<%= c[:path] %>"> - <% end %> - <% end %> - - <% if section_info[:is_video] %> - - <% end %> -
-<% end %> - -<% content_for :page_scripts do %> - -<% end %> diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb index e2b627dbdb..47360bd41e 100644 --- a/config/initializers/assets.rb +++ b/config/initializers/assets.rb @@ -13,8 +13,6 @@ # folder are already added. # Rails.application.config.assets.precompile += %w( admin.js admin.css ) -Rails.application.config.assets.precompile += %w( embed.css ) - # MediaElement 4 files Rails.application.config.assets.precompile += %w( select2.min.js select2.min.css )