Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide captions support on A/V items #393

Merged
merged 14 commits into from
Aug 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .env.test
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
# Not a real endpoint
SLACK_NOTIFY_URL=https://hooks.slack.com/triggers/mdl-unit-test-endpoint
KALTURA_SESSION=
2 changes: 0 additions & 2 deletions .github/workflows/validate-pull-request.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,6 @@ jobs:
run: yarn install
- name: Setup DB
run: bin/rails db:schema:load
- name: Prep UV
run: ./prep_uv.sh
- name: Run Backend Tests
run: bundle exec rspec --tag ~skip_ci
- name: Upload Capybara Saved Pages
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ When you update certain frontend elements of the app, you'll need to rebuild the

5) Remove the images

`docker rm $IMAGE_ID`
`docker rmi $IMAGE_ID`

6) Build the new image (assuming you're done making changes for now)

Expand Down
6 changes: 3 additions & 3 deletions app/assets/javascripts/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,22 @@
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery
//= require 'blacklight_advanced_search'

//= require jquery3
//= require rails-ujs
//= require popper
//= require twitter/typeahead
//= require bootstrap
//= require chosen-jquery

// Required by Blacklight
//= require blacklight_advanced_search
//= require blacklight/blacklight

//= require_tree .


// For blacklight_range_limit built-in JS, if you don't want it you don't need
// this:
//= require 'blacklight_range_limit'
//= require blacklight_range_limit

9 changes: 7 additions & 2 deletions app/assets/stylesheets/mdl.scss
Original file line number Diff line number Diff line change
Expand Up @@ -1505,8 +1505,13 @@ a.view-text:hover {
}

.iiif-av-component .controls-container {
position: relative;
z-index: 10;
.volume {
float: left !important;
}

.captions-button {
padding-top: 10px;
}
}
/* End item page styles */

Expand Down
23 changes: 14 additions & 9 deletions app/controllers/catalog_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ class CatalogController < ApplicationController
before_action :permit_search_parameters, only: [:index, :range_limit, :oai]
before_action :manage_pagination, only: :index

rescue_from(Blacklight::Exceptions::RecordNotFound, with: :invalid_document_id_error)

##
# Determine whether to render the bookmarks control
def render_bookmarks_control?
Expand Down Expand Up @@ -191,15 +193,6 @@ def parse_referrer_pagination_params(query_string)
## Model that maps search index responses to the blacklight response model
# config.response_model = Blacklight::Solr::Response

## Default parameters to send to solr for all search-like requests. See also SearchBuilder#processed_parameters
config.default_solr_params = {
rows: 20,
fl: '*',
hl: 'on',
'hl.fl': '*',
'hl.fragsize': 0
}

config.default_per_page = 25

config.autocomplete_enabled = false
Expand Down Expand Up @@ -383,6 +376,18 @@ def parse_referrer_pagination_params(query_string)
config.add_index_field 'type_tesi', label: 'Type', highlight: true
config.add_index_field 'physical_format_tesi', label: 'Format', highlight: true

field_list = 'id,title_tesi,timestamp,identifier_ssim,' +
SolrDocument::OAI_FIELDS.join(',') +
',' +
config.index_fields.keys.join(',')
# Default parameters to send to solr for all search-like requests.
config.default_solr_params = {
rows: 20,
fl: field_list,
hl: 'on',
'hl.fl': field_list,
'hl.fragsize': 0
}

# solr fields to be displayed in the show (single result) view
# The ordering of the field names is the order of the display
Expand Down
39 changes: 37 additions & 2 deletions app/controllers/iiif_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,50 @@ class IiifController < ApplicationController
render json: { status: :internal_server_error }, status: :internal_server_error
end

FIELD_LIST = MDL::CiteDetails
.field_configs
.map(&:key)
.to_set
.merge(Set[
'id',
'title_ssi',
'rights_uri_ssi',
'contact_information_ssi',
'iiif_manifest_ss',
'format_tesi',
'kaltura_audio_playlist_entry_data_ts',
'dimensions_ssi',
'kaltura_audio_playlist_ssi',
'kaltura_audio_ssi',
'rights_statement_ssi',
'kaltura_video_playlist_ssi',
'kaltura_audio_playlist_ssi',
'kaltura_video_playlist_entry_data_ts',
'kaltura_video_ssi'
])
.join(',')
.freeze
private_constant :FIELD_LIST

include Blacklight::Catalog

configure_blacklight do |config|
config.default_document_solr_params = {
qt: 'document',
fl: FIELD_LIST,
rows: 1
}
end

def manifest
_response, document = search_service.fetch(params[:id])
if document.key?('iiif_manifest_ss')
render body: document['iiif_manifest_ss'], content_type: 'application/json'
else
doc = MDL::BorealisDocument.new(document: document)
manifest = IiifManifest.new(doc)
base_uri = URI::HTTPS.build(host: request.host)
doc = MDL::BorealisDocument.new(document:)
manifest = IiifManifest.new(doc, base_url: base_uri.to_s)
headers['access-control-allow-origin'] = '*'
render json: manifest
end
end
Expand Down
41 changes: 41 additions & 0 deletions app/controllers/tracks_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
class TracksController < ApplicationController
before_action :set_cors_headers

def entry
respond_to do |format|
format.vtt do
content = fetch_vtt_content
head :not_found and return unless content.present?

vtt_content = JSON.parse(content).dig(params[:entry_id])
head :not_found and return unless vtt_content.present?

render plain: "WEBVTT\n\n#{vtt_content}", content_type: 'text/vtt'
end
end
end

private

# TODO: remove
def set_cors_headers
headers['access-control-allow-origin'] = '*'
headers['access-control-allow-methods'] = 'GET'
headers['access-control-allow-headers'] = 'Accept, Accept-Language, Content-Type, Authorization'
end

def fetch_vtt_content
client = Blacklight.default_index.connection
response = client.get('select', params: {
defType: 'edismax',
fq: "id:\"#{RSolr.solr_escape(params[:id])}\"",
fl: 'captions_ts',
qt: 'search',
rows: 1,
q: '*:*',
wt: 'json'
})
docs = Array(response.dig('response', 'docs'))
docs.first&.[]('captions_ts')
end
end
18 changes: 0 additions & 18 deletions app/controllers/uvconfig_controller.rb

This file was deleted.

3 changes: 0 additions & 3 deletions app/javascript/packs/universalviewer.js

This file was deleted.

1 change: 0 additions & 1 deletion app/javascript/packs/universalviewer_lib.js

This file was deleted.

80 changes: 63 additions & 17 deletions app/models/iiif_manifest.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,34 @@ class IiifManifest
format: 'image/jpeg'
}
}.freeze
private_constant :ANNOTATION_DATA_BY_TYPE
RENDERABLE_ASSET_CLASSES = ANNOTATION_DATA_BY_TYPE.keys.freeze
private_constant :RENDERABLE_ASSET_CLASSES
AV_ASSETS = [MDL::BorealisVideo, MDL::BorealisAudio].freeze
private_constant :AV_ASSETS

delegate :collection,
:id,
:title,
:assets,
to: :borealis_document

# @return [MDL::BorealisDocument]
attr_reader :borealis_document

def initialize(borealis_document)
# @return [String]
attr_reader :base_url

###
# @param borealis_document [MDL::BorealisDocument]
# @param base_url [String]
def initialize(borealis_document, base_url:)
@borealis_document = borealis_document
@base_url = base_url
end

###
# @return [Hash]
def as_json(*)
{
'@context' => 'http://iiif.io/api/presentation/3/context.json',
Expand All @@ -58,7 +73,7 @@ def as_json(*)
}
}
end,
'items' => canvasable_assets.map { |a| canvas(a) }
'items' => canvasable_assets.map(&method(:canvas))
}.tap do |hsh|
if borealis_document.document['rights_statement_ssi']
hsh['requiredStatement'] = {
Expand All @@ -67,7 +82,7 @@ def as_json(*)
}
end
if renderable_assets.any?
hsh['rendering'] = renderable_assets.map { |a| rendering(a) }.flatten
hsh['rendering'] = renderable_assets.flat_map(&method(:rendering))
end
if rangeable_assets.any?
hsh['structures'] = rangeable_assets
Expand All @@ -80,13 +95,13 @@ def as_json(*)
private

def details
MDL::CiteDetails.new(
solr_doc: borealis_document.document
).details
MDL::CiteDetails
.new(solr_doc: borealis_document.document)
.details
end

def base_identifier
"https://collection.mndigital.org/iiif/info/#{collection}/#{id}"
"#{base_url}/iiif/info/#{collection}/#{id}"
end

def canvas(asset)
Expand Down Expand Up @@ -115,6 +130,22 @@ def canvas(asset)
hsh['duration'] = asset.playlist_data.sum { |d| d['duration'] }
else
hsh['duration'] = borealis_document.duration
if !asset.playlist?
hsh['rendering'] = [
{
'id' => vtt_url(asset.entry_id),
'type' => 'Text',
'label' => { 'en' => ['English'] },
'format' => 'text/vtt'
},
{
'id' => asset.src,
'type' => annotation_body_type(asset),
'duration' => borealis_document.duration,
'format' => annotation_body_format(asset)
}
]
end
end
width, height = annotation_aspect(asset)

Expand All @@ -128,6 +159,12 @@ def canvas(asset)
end
end

# @param entry_id [String]
# @return [String]
def vtt_url(entry_id)
"#{base_url}/tracks/#{collection}:#{id}/entry/#{entry_id}.vtt"
end

def annotation_items(asset, canvas_id)
if asset.playlist?
playlist_annotation_items(asset, canvas_id)
Expand Down Expand Up @@ -175,7 +212,21 @@ def playlist_annotation_items(asset, canvas_id)
hsh['width'] = width if width
hsh['height'] = height if height
end,
'target' => target
'target' => target,
'rendering' => [
{
'id' => vtt_url(data['entry_id']),
'type' => 'Text',
'label' => { 'en' => ['English'] },
'format' => 'text/vtt'
},
{
'id' => asset.src(data['entry_id']),
'type' => type,
'duration' => data['duration'],
'format' => body_format
}
]
}
end
end
Expand All @@ -193,7 +244,7 @@ def rendering(asset)
}
end
else
{
[{
'id' => asset.src,
'type' => annotation_body_type(asset),
'label' => {
Expand All @@ -207,7 +258,7 @@ def rendering(asset)
'format' => 'image/jpeg'
}
]
}
}]
end
end

Expand Down Expand Up @@ -248,18 +299,13 @@ def canvas_id(asset)

def canvasable_assets
assets.select do |a|
[MDL::BorealisVideo, MDL::BorealisAudio].include?(a.class)
AV_ASSETS.include?(a.class)
end
end

def renderable_assets
assets.select do |a|
[
MDL::BorealisVideo,
MDL::BorealisAudio,
MDL::BorealisPdf,
MDL::BorealisImage
].include?(a.class)
RENDERABLE_ASSET_CLASSES.include?(a.class)
end
end

Expand Down
Loading
Loading