Skip to content
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
1a45f42
Revise native oembed_controller.js to handle Kaltura embeds
rshiggin Aug 26, 2026
264bd12
Refactored .js for video embed with better guards and fallbacks
rshiggin Aug 28, 2026
1a282c1
External link icon for Kaltura URL, border and spacing between sections
rshiggin Aug 28, 2026
47d92ed
Remove .al-show-sub-heading, it's too broad a target
rshiggin Sep 2, 2026
768593b
move ead2002 directory to root of project
gkostin1966 Aug 26, 2026
01ef8ad
keep empty data directory in project root
gkostin1966 Aug 26, 2026
ec0716c
changes to compose
gkostin1966 Aug 26, 2026
908b7ea
changes to read me
gkostin1966 Aug 26, 2026
a6511e7
use Rails development environment
gkostin1966 Aug 26, 2026
f80e469
changes
gkostin1966 Aug 26, 2026
4e0b24c
change to production environment
gkostin1966 Aug 26, 2026
3c4f0f5
Brief refactoring in response to Copilot/Claude code review
rshiggin Aug 25, 2026
a8f5de9
Add class and improved output for nil repo ID/slug error
rshiggin Aug 27, 2026
0c39bb8
Make /opt/app-data directory and specify permissions; mount source co…
ssciolla Aug 31, 2026
f0e4603
Remove source code mappings to fix assets pipeline
ssciolla Sep 1, 2026
248c8dd
Convert search in banner from two layers to one
rshiggin Sep 2, 2026
5c84f45
Select item button in leaf display toolbar, alongside Request and Cle…
rshiggin Aug 28, 2026
c559787
Remove unintended visible label from checkboxes, minor style changes
rshiggin Sep 2, 2026
ed0912e
Restrictions preview in component (leaf) display to match prod
rshiggin Sep 1, 2026
5533797
Resolve conflicts
rshiggin Sep 3, 2026
64bcdfd
Still resolving conflict with _contents.scss
rshiggin Sep 1, 2026
922221f
Use locale file rather than catalog controller to translate parent re…
rshiggin Sep 1, 2026
0f3531d
rubocop manual fixes
rshiggin Sep 1, 2026
30c9145
Corrections responding to code review: moved solr query out of compon…
rshiggin Sep 2, 2026
632e282
Clean up conflict markup
rshiggin Sep 2, 2026
da8061e
Ensure restrictions preview only appears on single items with restric…
rshiggin Sep 2, 2026
df20a16
Tighten up conmment
rshiggin Sep 2, 2026
b5fd900
rubocop correction
rshiggin Sep 2, 2026
2c37303
Resolve additional conflicts
rshiggin Sep 3, 2026
57a6fc1
Resolve conflicts
rshiggin Sep 3, 2026
e2737a0
Remove .al-show-sub-heading, it's too broad a target
rshiggin Sep 2, 2026
7893ab8
Merge branch 'main' into arc-173-oembed-video
rshiggin Sep 3, 2026
00c4cb5
Minor refactor of oembed js
rshiggin Sep 10, 2026
044c543
Refactoring pass
rshiggin Sep 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions app/assets/stylesheets/_contents.scss
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,24 @@ h3.document-title-heading {
margin-top: 0.3rem;
}

.al-digital-object a::after {
content: "";
display: inline-block;
width: 1em;
height: 1em;
margin-left: 0.25rem;
vertical-align: -0.125em;
background-color: currentColor;
mask-repeat: no-repeat;
mask-position: center;
mask-size: contain;
-webkit-mask-repeat: no-repeat;
-webkit-mask-position: center;
-webkit-mask-size: contain;
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M8.636 3.5a.5.5 0 0 0-.5-.5H1.5A1.5 1.5 0 0 0 0 4.5v10A1.5 1.5 0 0 0 1.5 16h10a1.5 1.5 0 0 0 1.5-1.5V7.864a.5.5 0 0 0-1 0V14.5a.5.5 0 0 1-.5.5h-10a.5.5 0 0 1-.5-.5v-10a.5.5 0 0 1 .5-.5h6.636a.5.5 0 0 0 .5-.5'/%3E%3Cpath fill-rule='evenodd' d='M16 .5a.5.5 0 0 0-.5-.5h-5a.5.5 0 0 0 0 1h3.793L6.146 9.146a.5.5 0 1 0 .708.708L15 1.707V5.5a.5.5 0 0 0 1 0z'/%3E%3C/svg%3E");
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M8.636 3.5a.5.5 0 0 0-.5-.5H1.5A1.5 1.5 0 0 0 0 4.5v10A1.5 1.5 0 0 0 1.5 16h10a1.5 1.5 0 0 0 1.5-1.5V7.864a.5.5 0 0 0-1 0V14.5a.5.5 0 0 1-.5.5h-10a.5.5 0 0 1-.5.5v-10a.5.5 0 0 1 .5-.5h6.636a.5.5 0 0 0 .5-.5'/%3E%3Cpath fill-rule='evenodd' d='M16 .5a.5.5 0 0 0-.5-.5h-5a.5.5 0 0 0 0 1h3.793L6.146 9.146a.5.5 0 1 0 .708.708L15 1.707V5.5a.5.5 0 0 0 1 0z'/%3E%3C/svg%3E");
}

.restrictions-preview {
position: relative;
background-color: var.$color-maize-200;
Expand All @@ -87,6 +105,14 @@ h3.document-title-heading {
margin-bottom: 2rem;
}

.col .al-document-container {
margin-top: 0.3rem;
}

h3.document-title-heading {
margin-top: 0.3rem;
}

.restrictions-preview::before {
content: "";
position: absolute;
Expand All @@ -95,3 +121,4 @@ h3.document-title-heading {
right: 0;
border-top: 1px dotted var.$color-neutral-100;
}

119 changes: 119 additions & 0 deletions app/javascript/arclight/oembed_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { Controller } from '@hotwired/stimulus'

const PARTNER_ID = '1758271'
const UICONF_ID = '56928822'
const PLAYER_HOST = 'https://cdnapisec.kaltura.com'
const ENTRY_ID_PATTERN = /^\d_[a-z0-9]{8}$/i

// Kaltura-backed hosts (kaltura.com and MediaSpace instances)
const KALTURA_HOST_SUFFIXES = ['.kaltura.com', '.mivideo.it.umich.edu']

export default class OembedController extends Controller {
Comment thread
rshiggin marked this conversation as resolved.
static values = {
url: String
}

connect() {
if (this.element.getAttribute('loaded') === 'loaded') return

const entryId = this.findKalturaEntryId()
if (entryId) {
this.loadKalturaEmbed(entryId)
return
}

this.loadOEmbed()
}

findKalturaEntryId() {
let url

try {
url = new URL(this.urlValue, window.location.href)
} catch (error) {
console.warn(`Invalid oEmbed URL: ${this.urlValue}`, error)
return null
}

const isKalturaHost = url.hostname === 'kaltura.com'
Comment thread
rshiggin marked this conversation as resolved.
Outdated
|| KALTURA_HOST_SUFFIXES.some((suffix) => url.hostname.endsWith(suffix))
if (!isKalturaHost) return null

const queryEntryId = url.searchParams.get('entry_id')
if (queryEntryId && ENTRY_ID_PATTERN.test(queryEntryId)) {
return queryEntryId
}

return url.pathname
.match(/(?:^|\/)(\d_[a-z0-9]{8})(?:\/|$)/i)?.[1] || null
Comment thread
rshiggin marked this conversation as resolved.
Outdated
}

loadKalturaEmbed(entryId) {
const linkContainer = this.element.querySelector('.al-digital-object')
if (!linkContainer) {
console.warn('Could not find .al-digital-object for Kaltura embed')
return
}

const params = new URLSearchParams({
iframeembed: 'true',
entry_id: entryId
})

const iframe = document.createElement('iframe')
iframe.src = `${PLAYER_HOST}/p/${PARTNER_ID}/embedPlaykitJs/uiconf_id/${UICONF_ID}?${params}`
iframe.setAttribute('allowfullscreen', '')
iframe.setAttribute('allow', 'autoplay; fullscreen; encrypted-media')
iframe.setAttribute(
'title',
this.element.querySelector('a')?.textContent?.trim() || 'Media player'
)
iframe.style.cssText = 'width: 100%; aspect-ratio: 16 / 9; height: auto; border: 0;'

// Keep the original link as a fallback.
linkContainer.insertAdjacentElement('beforebegin', iframe)
this.element.setAttribute('loaded', 'loaded')
}

loadOEmbed() {
fetch(this.urlValue)
.then((response) => {
if (response.ok) return response.text()
throw new Error(`HTTP error, status = ${response.status}`)
})
.then((body) => {
const endpoint = this.findOEmbedEndpoint(body)
if (!endpoint) {
console.warn(`No oEmbed endpoint found in <head> at ${this.urlValue}`)
return null
}

return fetch(endpoint)
})
.then((response) => {
if (!response) return null
if (response.ok) return response.json()
throw new Error(`HTTP error, status = ${response.status}`)
})
.then((json) => {
if (!json?.html) return

this.element.innerHTML = json.html
this.element.setAttribute('loaded', 'loaded')
})
.catch((error) => {
console.error(error)
})
}

findOEmbedEndpoint(body) {
const doc = new DOMParser().parseFromString(body, 'text/html')
const endpoint = doc
.querySelector('link[rel="alternate"][type="application/json+oembed"]')
?.getAttribute('href')

if (!endpoint) return null

return new URL(endpoint, this.urlValue).toString()
}
}
4 changes: 3 additions & 1 deletion config/importmap.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
pin "bootstrap", to: "https://ga.jspm.io/npm:bootstrap@5.3.2/dist/js/bootstrap.js"
pin "arclight", to: "arclight/arclight.js"
# TODO: We may be able to move these to a single importmap for arclight.
pin "arclight/oembed_controller", to: "arclight/oembed_controller.js"
# pin "arclight/oembed_controller", to: "arclight/oembed_controller.js"
Comment thread
rshiggin marked this conversation as resolved.
# chart.js is dependency of blacklight-range-limit, currently is not working
# as vendored importmaps, but instead must be pinned to CDN. You may want to update
# versions perioidically.
Expand All @@ -20,3 +20,5 @@
pin "@kurkle/color", to: "https://ga.jspm.io/npm:@kurkle/color@0.3.2/dist/color.esm.js"
pin "aeonform"
pin "arclight/truncate_controller", to: "arclight/truncate_controller.js"
pin "arclight/oembed_controller", to: "arclight/oembed_controller.js"
pin "arclight/iiif_controller", to: "arclight/iiif_controller.js"
Comment thread
rshiggin marked this conversation as resolved.