Skip to content

Commit

Permalink
Merge pull request #5315 from avalonmediasystem/blank_canvas
Browse files Browse the repository at this point in the history
Use blank canvas to represent inaccesible playlist items
  • Loading branch information
masaball authored Aug 18, 2023
2 parents a868aae + be05458 commit e587669
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 9 deletions.
10 changes: 7 additions & 3 deletions app/controllers/playlists_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -247,10 +247,14 @@ def manifest
authorize! :read, @playlist

canvas_presenters = @playlist.items.collect do |item|
next if item.clip.master_file.nil?
cannot_read_item = cannot? :read, @master_file
@master_file = item.clip.master_file
stream_info = secure_streams(@master_file.stream_details, @master_file.media_object_id)
IiifPlaylistCanvasPresenter.new(playlist_item: item, stream_info: stream_info)
stream_info = if @master_file.nil?
nil
else
secure_streams(@master_file.stream_details, @master_file.media_object_id)
end
IiifPlaylistCanvasPresenter.new(playlist_item: item, stream_info: stream_info, cannot_read_item: cannot_read_item)
end
presenter = IiifPlaylistManifestPresenter.new(playlist: @playlist, items: canvas_presenters)

Expand Down
31 changes: 27 additions & 4 deletions app/models/iiif_playlist_canvas_presenter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,26 @@
# --- END LICENSE_HEADER BLOCK ---

class IiifPlaylistCanvasPresenter
attr_reader :playlist_item, :stream_info
attr_reader :playlist_item, :stream_info, :cannot_read_item
attr_accessor :media_fragment

def initialize(playlist_item:, stream_info:, media_fragment: nil)
def initialize(playlist_item:, stream_info:, cannot_read_item: false, media_fragment: nil)
@playlist_item = playlist_item
@stream_info = stream_info
@cannot_read_item = cannot_read_item
@media_fragment = media_fragment
end

delegate :id, to: :playlist_item

def to_s
playlist_item.title
if cannot_read_item
"Restricted item"
elsif master_file.nil?
"Deleted item"
else
playlist_item.title
end
end

def master_file
Expand All @@ -45,15 +52,31 @@ def range

# @return [IIIFManifest::V3::DisplayContent] the display content required by the manifest builder.
def display_content
return if cannot_read_item || master_file.nil?
master_file.is_video? ? video_content : audio_content
end

def annotation_content
return if cannot_read_item || master_file.nil?
playlist_item.marker.collect { |m| marker_content(m) }
end

private
def placeholder_content
if cannot_read_item
IIIFManifest::V3::DisplayContent.new(nil,
label: 'You do not have permission to playback this item.',
type: 'Text',
format: 'text/plain')
elsif master_file.nil?
IIIFManifest::V3::DisplayContent.new(nil,
label: 'The source for this playlist item has been deleted.',
type: 'Text',
format: 'text/plain')
end
end

private

def video_content
# @see https://github.com/samvera-labs/iiif_manifest
stream_urls.collect { |quality, _url| video_display_content(quality) }
Expand Down
110 changes: 108 additions & 2 deletions spec/models/iiif_playlist_canvas_presenter_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,30 @@
let(:stream_info) { master_file.stream_details }
let(:presenter) { described_class.new(playlist_item: playlist_item, stream_info: stream_info) }

context 'auth_service' do
describe '#to_s' do
it 'returns the playlist_item label' do
expect(presenter.to_s).to eq playlist_item.title
end

context 'when a user does not have access to a restricted item' do
let(:presenter) { described_class.new(playlist_item: playlist_item, stream_info: stream_info, cannot_read_item: true) }

it 'return "Restricted item"' do
expect(presenter.to_s).to eq "Restricted item"
end
end

context 'when an item has been deleted' do
let(:master_file) { nil }
let(:stream_info) { nil }

it 'returns "Deleted item"' do
expect(presenter.to_s).to eq "Deleted item"
end
end
end

describe '#auth_service' do
subject { presenter.display_content.first.auth_service }

it 'provides a cookie auth service' do
Expand Down Expand Up @@ -73,11 +96,28 @@
expect(subject.url).to include("#t=#{playlist_item.start_time / 1000},#{playlist_item.end_time / 1000}")
end
end

context 'when a user does not have access to a restricted item' do
let(:presenter) { described_class.new(playlist_item: playlist_item, stream_info: stream_info, cannot_read_item: true) }

it 'does not serialize the content' do
expect(presenter.display_content).to be_nil
end
end

context 'when an item has been deleted' do
let(:master_file) { nil }
let(:stream_info) { nil }

it 'does not serialize the content' do
expect(presenter.display_content).to be_nil
end
end
end

describe '#annotation_content' do
let(:marker) { FactoryBot.create(:avalon_marker) }
let(:playlist_item) { FactoryBot.create(:playlist_item, marker: [marker]) }
let(:playlist_item) { FactoryBot.build(:playlist_item, clip: playlist_clip, marker: [marker]) }
subject { presenter.annotation_content }

it "serializes playlist markers as iiif annotations" do
Expand All @@ -95,6 +135,23 @@
it "includes 'highlighting' motivation" do
expect(subject.first.motivation).to eq 'highlighting'
end

context 'when a user does not have access to a restricted item' do
let(:presenter) { described_class.new(playlist_item: playlist_item, stream_info: stream_info, cannot_read_item: true) }

it 'does not serialize the content' do
expect(presenter.annotation_content).to be_nil
end
end

context 'when an item has been deleted' do
let(:master_file) { nil }
let(:stream_info) { nil }

it 'does not serialize the content' do
expect(presenter.annotation_content).to be_nil
end
end
end

describe '#range' do
Expand All @@ -115,4 +172,53 @@
expect(subject.any? { |po| po["@id"] =~ /media_objects\/#{media_object.id}\/manifest/ }).to eq true
end
end

describe '#placeholder_contetnt' do
subject { presenter.placeholder_content }

it 'does not generate a placeholder for non-restricted items' do
expect(subject).to be_nil
end

context 'when a user does not have access to a restricted item' do
let(:presenter) { described_class.new(playlist_item: playlist_item, stream_info: stream_info, cannot_read_item: true) }

it 'generates placeholder canvas' do
expect(subject).to be_present
end

it 'has format' do
expect(subject.format).to eq "text/plain"
end

it 'has type' do
expect(subject.type).to eq "Text"
end

it 'has label' do
expect(subject.label).to eq "You do not have permission to playback this item."
end
end

context 'when an item has been deleted' do
let(:master_file) { nil }
let(:stream_info) { nil }

it 'generated placeholder canvas' do
expect(subject).to be_present
end

it 'has format' do
expect(subject.format).to eq "text/plain"
end

it 'has type' do
expect(subject.type).to eq "Text"
end

it 'has label' do
expect(subject.label).to eq "The source for this playlist item has been deleted."
end
end
end
end

0 comments on commit e587669

Please sign in to comment.