Compare commits

...

4 Commits

Author SHA1 Message Date
Bilal
928ef05f2e fix after rebase 2020-09-01 20:59:53 +03:00
Bilal
8cbe368e24 open broadcast page in director mode if available 2020-09-01 18:45:14 +03:00
Senad Uka
f611382e9e Upstream sync 2020-09-01 17:15:16 +02:00
Senad Uka
95a14ab2f6 Upstream sync 2020-08-31 18:19:00 +02:00
54 changed files with 613 additions and 423 deletions

View File

@@ -24,6 +24,8 @@ $(document).on "turbolinks:load", ->
stream_selected = $("#broadcast_video").data('videoType') == 'stream'; stream_selected = $("#broadcast_video").data('videoType') == 'stream';
if data.streamer_status == 'recording' && data.status == 'active' && stream_selected if data.streamer_status == 'recording' && data.status == 'active' && stream_selected
$("#broadcast_video").html data.video_content $("#broadcast_video").html data.video_content
$("#live_take").html data.live_take_content
new (Clappr.Player)( new (Clappr.Player)(
<%= "baseUrl: 'http://cdn.clappr.io/latest'," if Rails.env.test? %> <%= "baseUrl: 'http://cdn.clappr.io/latest'," if Rails.env.test? %>
parentId: '#broadcast_video' parentId: '#broadcast_video'
@@ -35,6 +37,7 @@ $(document).on "turbolinks:load", ->
hlsMinimumDvrSize: 1) hlsMinimumDvrSize: 1)
if data.streamer_status == "idle" && data.status == "idle" if data.streamer_status == "idle" && data.status == "idle"
$("#broadcast_video").html data.video_content $("#broadcast_video").html data.video_content
$("#live_take").html data.live_take_content
showBroadcastRecordings: (data) -> showBroadcastRecordings: (data) ->
$(".flash-message").html data.flash_content $(".flash-message").html data.flash_content

View File

@@ -0,0 +1,4 @@
$(document).on("click", "#director_mode_switch", function() {
// JQuery click event trigger was not working.
document.getElementById("director_mode_link").click();
});

View File

@@ -412,6 +412,16 @@ a[data-behavior=seekable-timecode] {
background-color: rgba($black, 0.05); background-color: rgba($black, 0.05);
} }
// Black background
.bg-black {
background-color: $black;
}
// White background
.bg-white {
background-color: $white;
}
// Custom width // Custom width
.w-65 { .w-65 {
width: 65% !important; width: 65% !important;
@@ -422,8 +432,32 @@ a[data-behavior=seekable-timecode] {
max-height: 30rem; max-height: 30rem;
} }
// Max-width 75%
.max-w-75 {
max-width: 75%;
}
// Fix height and width // Fix height and width
.fix-h-and-w { .fix-h-and-w {
width: 308px; width: 308px;
height:308px; height:308px;
} }
// Dropdown shown state overrride
.override-dropdown-show-state {
color: $white !important;
background-color: $black !important;
border-color: transparent !important;
}
// Override custom switch color
.override-custom-control-label::before {
background-color: #000000;
}
// Override nav tab design
.override-nav-tabs .nav-item.show .nav-link, .nav-tabs .nav-link.active {
background: transparent;
border-color: transparent;
border-bottom: 3px solid #ff0000;
}

View File

@@ -11,6 +11,7 @@ class BroadcastsChannel < ApplicationCable::Channel
def self.broadcast_stream_updates(broadcast) def self.broadcast_stream_updates(broadcast)
status_content = ApplicationController.render partial: "broadcasts/broadcast_status", locals: { broadcast: broadcast } status_content = ApplicationController.render partial: "broadcasts/broadcast_status", locals: { broadcast: broadcast }
video_content = ApplicationController.render partial: "broadcasts/video", locals: { broadcast: broadcast } video_content = ApplicationController.render partial: "broadcasts/video", locals: { broadcast: broadcast }
live_take = ApplicationController.render partial: "broadcasts/live_take", locals: { broadcast: broadcast }
broadcast_to broadcast, { broadcast_to broadcast, {
event: :broadcast_stream_update, event: :broadcast_stream_update,
@@ -18,6 +19,7 @@ class BroadcastsChannel < ApplicationCable::Channel
playback_url: broadcast.stream_playback_url, playback_url: broadcast.stream_playback_url,
full_live_stream_playback_url: broadcast.full_live_stream_playback_url, full_live_stream_playback_url: broadcast.full_live_stream_playback_url,
video_content: video_content, video_content: video_content,
live_take_content: live_take,
status_content: status_content, status_content: status_content,
streamer_status: broadcast.streamer_status streamer_status: broadcast.streamer_status
} }

View File

@@ -4,6 +4,7 @@ require './lib/knock_monkeypatch'
class Api::UserTokenController < Knock::AuthTokenController class Api::UserTokenController < Knock::AuthTokenController
include Oath::ControllerHelpers include Oath::ControllerHelpers
include RememberMe::Controller
skip_before_action :verify_authenticity_token skip_before_action :verify_authenticity_token
before_action :sign_in_user before_action :sign_in_user

View File

@@ -0,0 +1,34 @@
class BroadcastRecordingStarringsController < ApplicationController
layout "project"
before_action :set_project
before_action :set_broadcast
before_action :set_recording
def create
@recording.toggle_star
set_recordings
end
private
def broadcast_recording_params
params.require(:broadcast_recording).permit(:name, :description)
end
def set_project
@project = policy_scope(Project).find(params[:project_id])
end
def set_broadcast
@broadcast = authorize policy_scope(@project.broadcasts).find(params[:broadcast_id])
end
def set_recording
@recording = authorize policy_scope(@broadcast.broadcast_recordings).find(params[:broadcast_recording_id])
end
def set_recordings
@recordings = @broadcast.broadcast_recordings.visible.order_by_recent.paginate(page: params[:page])
end
end

View File

@@ -5,13 +5,25 @@ class BroadcastRecordingsController < ApplicationController
before_action :set_broadcast before_action :set_broadcast
before_action :set_recording before_action :set_recording
def edit
end
def update
@recording.update(broadcast_recording_params)
set_recordings
end
def destroy def destroy
@recording.update(hidden: true) @recording.update(hidden: true)
@recordings = @broadcast.broadcast_recordings.visible.order_by_recent.paginate(page: params[:page]) set_recordings
end end
private private
def broadcast_recording_params
params.require(:broadcast_recording).permit(:name, :description)
end
def set_project def set_project
@project = policy_scope(Project).find(params[:project_id]) @project = policy_scope(Project).find(params[:project_id])
end end
@@ -23,4 +35,8 @@ class BroadcastRecordingsController < ApplicationController
def set_recording def set_recording
@recording = authorize policy_scope(@broadcast.broadcast_recordings).find(params[:id]) @recording = authorize policy_scope(@broadcast.broadcast_recordings).find(params[:id])
end end
def set_recordings
@recordings = @broadcast.broadcast_recordings.visible.order_by_recent.paginate(page: params[:page])
end
end end

View File

@@ -32,6 +32,7 @@ class BroadcastsController < ApplicationController
@conference_url = conference_url_for(@broadcast) @conference_url = conference_url_for(@broadcast)
@recordings = @broadcast.broadcast_recordings.visible.order_by_recent.paginate(page: params[:page]) @recordings = @broadcast.broadcast_recordings.visible.order_by_recent.paginate(page: params[:page])
@files = @broadcast.files.order("created_at DESC").paginate(page: params[:files_page]) @files = @broadcast.files.order("created_at DESC").paginate(page: params[:files_page])
render layout: 'application' render layout: 'application'
end end
@@ -113,7 +114,7 @@ class BroadcastsController < ApplicationController
end end
def conference_url_for(broadcast) def conference_url_for(broadcast)
broadcast.video_conference_url_override || url_for([broadcast.project, broadcast, :zoom_meeting]) broadcast.video_conference_url_override.presence || url_for([broadcast.project, broadcast, :zoom_meeting])
end end
def log_create_analytics def log_create_analytics

View File

@@ -106,7 +106,7 @@ class MaterialReleasesController < ApplicationController
:term_id, :term_text, :term_id, :term_text,
:restriction_id, :restriction_text, :restriction_id, :restriction_text,
:description, :description,
:contract, { photos: [] } :contract, files: []
) )
end end

View File

@@ -44,7 +44,7 @@ class Public::BroadcastsController < Public::BaseController
end end
def conference_url_for(broadcast) def conference_url_for(broadcast)
broadcast.video_conference_url_override || broadcast_zoom_meeting_url(broadcast.token) broadcast.video_conference_url_override.presence || broadcast_zoom_meeting_url(broadcast.token)
end end
class MultiViewBroadcast class MultiViewBroadcast

View File

@@ -92,7 +92,7 @@ class Public::MaterialReleasesController < Public::BaseController
params.require(:material_release).permit(person_params, guardian_params, second_guardian_params, :minor, params.require(:material_release).permit(person_params, guardian_params, second_guardian_params, :minor,
:name, :address_street1, :address_street2, :address_city, :address_state, :address_zip, :address_country, :name, :address_street1, :address_street2, :address_city, :address_state, :address_zip, :address_country,
:signature_base64, :signature_base64,
:locale, :contract_template, :description, photos: [] :locale, :contract_template, :description, files: []
) )
end end

View File

@@ -15,7 +15,8 @@ class AcquiredMediaRelease < ApplicationRecord
include SecondGuardianPhotoable include SecondGuardianPhotoable
include GuardianName include GuardianName
include SecondGuardianName include SecondGuardianName
include FilesFilterable
class << self class << self
def custom_csv_exportable_headers def custom_csv_exportable_headers
%i[name files_count owner_info] %i[name files_count owner_info]
@@ -106,16 +107,4 @@ class AcquiredMediaRelease < ApplicationRecord
def files_count def files_count
files.any? ? files.size : I18n.t('acquired_media_releases.acquired_media_release.no_media') files.any? ? files.size : I18n.t('acquired_media_releases.acquired_media_release.no_media')
end end
def image_files
files_blobs.where("content_type ILIKE ?", "%image%")
end
def video_files
files_blobs.where("content_type ILIKE ?", "%video%")
end
def other_files
files_blobs.where("NOT content_type ILIKE ANY (array[?])", ["%image%", "%video%"])
end
end end

View File

@@ -7,8 +7,10 @@ class BroadcastRecording < ApplicationRecord
scope :visible, -> { where(hidden: false) } scope :visible, -> { where(hidden: false) }
before_save :set_title_and_description
def download_url def download_url
"https://stream.mux.com/#{asset_playback_uid}/#{file_name}?download=#{download_file_name}" "https://stream.mux.com/#{asset_playback_uid}/#{file_name}?download=#{name}"
end end
def playback_url def playback_url
@@ -16,6 +18,21 @@ class BroadcastRecording < ApplicationRecord
end end
def download_file_name def download_file_name
"#{broadcast_name}_Date_#{created_at.in_time_zone(broadcast.shoot_location_time_zone).strftime("%Y-%m-%d")}_Time_#{created_at.in_time_zone(broadcast.shoot_location_time_zone).strftime("%T")}".parameterize "#{broadcast_name}_Date_#{Time.now.in_time_zone(broadcast.shoot_location_time_zone).strftime("%Y-%m-%d")}_Time_#{Time.now.in_time_zone(broadcast.shoot_location_time_zone).strftime("%T")}".parameterize
end
def toggle_star
toggle! :starred
end
def thumbnail_url(width = 300)
"https://image.mux.com/#{asset_playback_uid}/thumbnail.jpg?width=#{width}"
end
private
def set_title_and_description
self.name ||= download_file_name
self.description ||= "No description provided for this recording."
end end
end end

View File

@@ -0,0 +1,17 @@
module FilesFilterable
extend ActiveSupport::Concern
included do
def image_files
files_blobs.where("content_type ILIKE ?", "%image%")
end
def video_files
files_blobs.where("content_type ILIKE ?", "%video%")
end
def other_files
files_blobs.where("NOT content_type ILIKE ANY (array[?])", ["%image%", "%video%"])
end
end
end

View File

@@ -3,7 +3,7 @@ class MaterialRelease < ApplicationRecord
include Contractable include Contractable
include Exploitable include Exploitable
include Notable include Notable
include Photoable include Photoable # This association needs to be removed after changing the API. Removing it right now will cause failure in API specs.
include Releasable include Releasable
include Searchable include Searchable
include Signable include Signable
@@ -16,11 +16,11 @@ class MaterialRelease < ApplicationRecord
include SecondGuardianPhotoable include SecondGuardianPhotoable
include GuardianName include GuardianName
include SecondGuardianName include SecondGuardianName
include FilesFilterable
class << self class << self
def custom_csv_exportable_headers def custom_csv_exportable_headers
%i[name owner_info] %i[name owner_info files_count]
end end
end end
@@ -56,6 +56,8 @@ class MaterialRelease < ApplicationRecord
%w[guardian_2_address_zip zip], %w[guardian_2_address_zip zip],
%w[guardian_2_address_country country] %w[guardian_2_address_country country]
] ]
has_many_attached :files
# We don't care for the argument but method WILL receive option name # We don't care for the argument but method WILL receive option name
# when called from inside with_option block, hence * argument # when called from inside with_option block, hence * argument
@@ -92,4 +94,8 @@ class MaterialRelease < ApplicationRecord
def uses_edl? def uses_edl?
true true
end end
def files_count
files.any? ? files.size : I18n.t('material_releases.material_release.no_media')
end
end end

View File

@@ -1,9 +1,21 @@
class BroadcastRecordingPolicy < ApplicationPolicy class BroadcastRecordingPolicy < ApplicationPolicy
def create?
true
end
def destroy? def destroy?
if user.nil? || user.user.nil? if user.nil? || user.user.nil?
return false return false
end end
user.manager? || user.account_manager? user.manager? || user.account_manager?
end end
def edit?
destroy?
end
def update?
edit?
end
end end

View File

@@ -19,12 +19,16 @@ class MaterialReleasePolicy < ReleasePolicy
user.manager? || user.account_manager? user.manager? || user.account_manager?
end end
def edit_photos? def edit_files?
true true
end end
def update_files?
edit_files?
end
def update_photos? def update_photos?
edit_photos? edit_files?
end end
def tag_multiple? def tag_multiple?

View File

@@ -0,0 +1 @@
$("#broadcast_recordings").html("<%= j render(partial: 'broadcasts/broadcast_recordings', locals: { recordings: @recordings, broadcast: @broadcast }) %>");

View File

@@ -0,0 +1,24 @@
<%= content_tag :div, class: "modal modal-right", id: "edit_broadcast_recording_modal", aria: { labelledby: "modalLabel", hidden: true }, role: "dialog", tabindex: -1 do %>
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="modalLabel">Edit Broadcast Recording</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<%= bootstrap_form_with model: [broadcast.project, broadcast, recording], layout: :horizontal, label_col: "col-3", control_col: "col-9" do |form| %>
<div class="modal-body">
<div id="broadcast_recording_fields">
<%= form.text_field :name %>
<%= form.text_area :description %>
</div>
</div>
<div class="modal-footer">
<%= form.button "Close", class: "btn btn-secondary", data: { dismiss: "modal" } %>
<%= form.submit "Update Broadcast Recording", class: "btn btn-primary", data: { disable_with: t("shared.disable_with") } %>
</div>
<% end %>
</div>
</div>
<% end %>

View File

@@ -0,0 +1,11 @@
$('[data-id="<%= dom_id(@recording) %>"]').remove();
<% if @recordings.empty? %>
$("#broadcast_recordings_nav").append('<p class="dropdown-item text-muted">Recordings will appear here</p>')
<% end %>
$("#broadcast_recordings").html("<%= j render(partial: 'broadcasts/broadcast_recordings', locals: { recordings: @recordings, broadcast: @broadcast }) %>");
// Close and remove the modal
$("#edit_broadcast_recording_modal").on("hidden.bs.modal", function (e) {
$("#edit_broadcast_recording_modal").remove();
});
$("#edit_broadcast_recording_modal").modal("hide");

View File

@@ -1,6 +1 @@
var dom_id = "<%= dom_id(@recording) %>" <%= render("broadcast_recordings/refresh_recordings_list") %>
$('[data-id="' + dom_id + '"]').remove();
<% if @recordings.empty? %>
$("#broadcast_recordings_nav").append('<p class="dropdown-item text-muted">Recordings will appear here</p>')
<% end %>
$("#broadcast_recordings").html("<%= j render(partial: 'broadcasts/broadcast_recordings', locals: { recordings: @recordings, broadcast: @broadcast }) %>");

View File

@@ -0,0 +1,6 @@
<% # Remove the modal if it already exists %>
$("#edit_broadcast_recording_modal").remove();
<% # Create and show the modal %>
$("body").append("<%= j render(partial: 'edit_broadcast_recording_modal', locals: { project: @project, broadcast: @broadcast, recording: @recording }) %>");
$("#edit_broadcast_recording_modal").modal("toggle");

View File

@@ -0,0 +1 @@
<%= render("broadcast_recordings/refresh_recordings_list") %>

View File

@@ -1,3 +1 @@
<%= link_to broadcast_recording.download_file_name, "javascript:void(0);", class: "dropdown-item", data: { behavior: "play_recording", playback_url: broadcast_recording.playback_url, id: dom_id(broadcast_recording) } %> <%= link_to broadcast_recording.name, "javascript:void(0);", class: "dropdown-item", data: { behavior: "play_recording", playback_url: broadcast_recording.playback_url, id: dom_id(broadcast_recording) } %>

View File

@@ -1,18 +1,29 @@
<% if recordings.present? %> <% if recordings.present? %>
<p>Click below to download the recordings of the live stream.</p> <div class="list-group">
<ul class="mt-2">
<% recordings.each do |recording| %> <% recordings.each do |recording| %>
<li> <div class="list-group-item list-group-item-action">
<%= link_to(recording.download_file_name, recording.download_url, target: "_blank") %> <div class="d-flex align-items-start">
<% if (controller.class.module_parent.to_s != "Public" && policy(BroadcastRecording).destroy?) %> <% if (controller.class.module_parent.to_s != "Public" && policy(BroadcastRecording).update?) %>
<%= link_to "Hide", [broadcast.project, broadcast, recording], class: "btn-sm btn-primary ml-1 text-decoration-none", remote: true, method: :delete, data: { confirm: t('.confirm_hide') } %> <%= link_to fa_icon("#{recording.starred ? 'star' : 'star-o'} fw"), [broadcast.project, broadcast, recording, :broadcast_recording_starrings], method: :post, class: "text-warning mr-3", remote: true %>
<% end %> <% end %>
</li> <%= image_tag(recording.thumbnail_url, class: 'img-thumbnail img-fluid max-w-75') %>
<div class="ml-auto">
<% if (controller.class.module_parent.to_s != "Public" && policy(BroadcastRecording).edit?) %>
<%= link_to fa_icon('edit'), [:edit, broadcast.project, broadcast, recording], remote: true %>
<% end %>
<%= link_to(fa_icon('download'), recording.download_url, target: "_blank") %>
</div>
</div>
<div class="d-flex flex-column align-items-start justify-content-start p-4">
<h5><%= recording.name %></h5>
<p><%= recording.description %></p>
</div>
</div>
<% end %> <% end %>
</ul> </div>
<div id="recordings_pagination" class="row mt-5 justify-content-center"> <div id="recordings_pagination" class="row mt-5 justify-content-center">
<%= will_paginate(recordings, params: {controller: "broadcasts", action: "show", project_id: broadcast.project_id, id: broadcast.id, page: params[:page], active_tab: 'recordings'}) %> <%= will_paginate(recordings, params: {controller: "broadcasts", action: "show", project_id: broadcast.project_id, id: broadcast.id, page: params[:page], active_tab: 'recordings'}) %>
</div> </div>
<% else %> <% else %>
<p>Recording of the live stream will appear here.</p> <p class="mt-4">Recording of the live stream will appear here.</p>
<% end %> <% end %>

View File

@@ -1,21 +1,13 @@
<% if broadcast.streamer_connected? || (broadcast.streamer_recording? && !broadcast.active?) %> <% if broadcast.streamer_connected? || (broadcast.streamer_recording? && !broadcast.active?) %>
<div class="alert alert-info text-center text-md-left"> <p class="mb-1">Live stream has connected successfully and will be available soon.</p>
<%= fa_icon "info-circle" %> <div class="badge badge-pill badge-success mb-2 text-uppercase">Connected</div>
<strong>Live stream has connected successfully and will be available soon.</strong>
</div>
<% elsif broadcast.streamer_recording? && broadcast.active? %> <% elsif broadcast.streamer_recording? && broadcast.active? %>
<div class="alert alert-success text-center text-md-left"> <p class="mb-1">Live stream has begun, click play to watch it.</p>
<%= fa_icon "success" %> <div class="badge badge-pill badge-danger mb-2 text-uppercase">Live</div>
<strong>Live stream has begun, click play to watch it.</strong>
</div>
<% elsif broadcast.streamer_disconnected? %> <% elsif broadcast.streamer_disconnected? %>
<div class="alert alert-warning text-center text-md-left"> <p class="mb-1">Live stream got disconnected.</p>
<%= fa_icon "warning" %> <div class="badge badge-pill badge-warning mb-2 text-uppercase">Disconnected</div>
<strong>Live stream got disconnected.</strong>
</div>
<% elsif (broadcast.idle? && broadcast.streamer_idle?) || (broadcast.created? && broadcast.streamer_idle?) %> <% elsif (broadcast.idle? && broadcast.streamer_idle?) || (broadcast.created? && broadcast.streamer_idle?) %>
<div class="alert alert-info text-center text-md-left"> <p class="mb-1">Live stream is waiting to begin.</p>
<%= fa_icon "info-circle" %> <div class="badge badge-pill badge-primary mb-2 text-uppercase">Idle</div>
<strong>Live stream is waiting to begin.</strong> <% end %>
</div>
<% end %>

View File

@@ -0,0 +1,12 @@
<div class="list-group">
<div class="list-group-item list-group-item-action flex-column align-items-start">
<div class="d-flex w-100 justify-content-between mb-1">
<h5 class="mb-1"><%= broadcast.name %></h5>
<small>Created - <%= time_ago_in_words(broadcast.created_at) + " ago" %></small>
</div>
<div id="broadcast_updates">
<%= render partial: 'broadcasts/broadcast_status', locals: { broadcast: broadcast } %>
</div>
</div>
</div>
<hr/>

View File

@@ -1,5 +1,5 @@
<% if broadcast.director_mode_video_embed.present? && params[:director_mode].present? %> <% if broadcast.director_mode_video_embed.present? && params[:director_mode].nil? %>
<div class="embed-responsive-item" data-video-type="stream"> <div id="director_broadcast_video" class="embed-responsive-item" data-video-type="stream">
<%= raw broadcast.director_mode_video_embed %> <%= raw broadcast.director_mode_video_embed %>
</div> </div>
<% elsif broadcast.streamer_recording? && broadcast.active? %> <% elsif broadcast.streamer_recording? && broadcast.active? %>
@@ -9,7 +9,7 @@
<table class="w-100 h-100 bg-secondary"> <table class="w-100 h-100 bg-secondary">
<tbody> <tbody>
<tr> <tr>
<td class="text-center align-middle text-white"> <td class="text-center align-middle text-white bg-black">
Video player will appear here when the stream becomes available. Video player will appear here when the stream becomes available.
</td> </td>
</tr> </tr>

View File

@@ -14,159 +14,135 @@
<% end %> <% end %>
<% content_for :header do %> <% content_for :header do %>
<header class="container-fluid py-3 border-bottom sticky-top bg-light"> <header></header>
<div class="row align-items-center justify-content-center">
<div class="col-4 text-center">
<%= product_wordmark(:direct_me, class: 'navbar-brand') %>
</div>
</div>
</header>
<% end %> <% end %>
<div class="row"> <div class="row no-gutters m-n3">
<div class="col-lg-8 col-md-12 mb-3"> <div class="col-lg-8 col-md-12 bg-black">
<div class="card shadow-sm"> <div class="d-flex justify-content-start align-items-center flex-row p-3 mb-5 bg-dark">
<div class="card-header"> <%= product_wordmark(:direct_me, class: 'navbar-brand text-white') %>
<div class="d-flex justify-content-between align-items-center"> <div class="ml-4 dropdown">
<h1 class="h3 m-0"><%= @broadcast.name %></h1> <%= link_to "#", class: "btn btn-light dropdown-toggle text-white bg-black border-0 override-dropdown-show-state", role: "button", id: "dropdownMenuLink", data: { toggle: "dropdown" }, aria: { haspopup: "true", expanded: "false" } do %>
<div class="dropdown"> <i class="fa fa-video-camera text-primary" aria-hidden="true"></i><span class="ml-2" id="selected_stream"><%= @broadcast.name %></span>
<%= link_to "Switch View", "#", class: "btn btn-light border dropdown-toggle", role: "button", id: "dropdownMenuLink", data: { toggle: "dropdown" }, aria: { haspopup: "true", expanded: "false" } %> <% end %>
<div class="dropdown-menu" aria-labelledby="dropdownMenuLink"> <div class="dropdown-menu bg-black" aria-labelledby="dropdownMenuLink">
<h5 class="dropdown-header">Live Streams</h5> <h5 class="dropdown-header">Live Streams</h5>
<%= link_to fa_icon("check", text: @broadcast.name.titleize), "#", data: { behavior: "play_stream"}, class: "dropdown-item active" %> <%= link_to fa_icon("check", text: @broadcast.name.titleize), "#", data: { behavior: "play_stream"}, class: "dropdown-item active" %>
<% @multi_view_broadcasts.each do |broadcast| %> <% @multi_view_broadcasts.each do |broadcast| %>
<% if broadcast.id != @broadcast.id %> <% if broadcast.id != @broadcast.id %>
<%= link_to broadcast.name.titleize, broadcast.url, data: { behavior: "play_stream"}, class: class_string("dropdown-item", "active" => @broadcast.id == broadcast.id) %> <% if params[:director_mode] %>
<% if controller.class.module_parent.to_s == "Public" %>
<%= link_to broadcast.name.titleize, url_for(params.permit!.merge(director_mode: true, token: broadcast.token)), data: { behavior: "play_stream"}, class: class_string("dropdown-item", "active" => @broadcast.id == broadcast.id) %>
<% else %>
<%= link_to broadcast.name.titleize, url_for(params.permit!.merge(director_mode: true, id: broadcast.id)), data: { behavior: "play_stream"}, class: class_string("dropdown-item", "active" => @broadcast.id == broadcast.id) %>
<% end %>
<% else %>
<% if controller.class.module_parent.to_s == "Public" %>
<%= link_to broadcast.name.titleize, url_for(params.permit!.merge(token: broadcast.token).except(:director_mode)), data: { behavior: "play_stream"}, class: class_string("dropdown-item", "active" => @broadcast.id == broadcast.id) %>
<% else %>
<%= link_to broadcast.name.titleize, url_for(params.permit!.merge(id: broadcast.id).except(:director_mode)), data: { behavior: "play_stream"}, class: class_string("dropdown-item", "active" => @broadcast.id == broadcast.id) %>
<% end %> <% end %>
<% end %> <% end %>
<% if @broadcast.director_mode_video_embed.present? %>
<h5 class="dropdown-header">Director Mode</h5>
<% unless params[:director_mode] %>
<%= link_to "Enable Director Mode", url_for(params.permit!.merge(director_mode: true)), class: "dropdown-item" %>
<% else %>
<%= link_to "Disable Director Mode", url_for(params.permit!.except(:director_mode)), class: "dropdown-item" %>
<% end %>
<% end %>
<h5 class="dropdown-header">Previous Sessions</h5>
<div id="broadcast_recordings_nav">
<% if @recordings.any? %>
<%= render partial: "broadcasts/broadcast_recording_nav", collection: @recordings, as: :broadcast_recording %>
<% else %>
<p class="dropdown-item text-muted">Recordings will appear here</p>
<% end %>
</div>
</div>
</div>
</div>
</div>
<div class="card-body p-0">
<div class="embed-responsive embed-responsive-16by9">
<%= render partial: 'broadcasts/video', locals: { broadcast: @broadcast } %>
<% if @broadcast.streamer_recording? && @broadcast.active? %>
<%= javascript_tag nonce: true do %>
new Clappr.Player({
parentId: '#broadcast_video',
source: "<%= @broadcast.stream_playback_url %>",
width: '100%',
height: '100%',
mute: true,
autoPlay: true,
hlsMinimumDvrSize: 1
});
<% end %> <% end %>
<% end %> <% end %>
</div> </div>
</div> </div>
<% if @broadcast.director_mode_video_embed.present? %>
<% unless params[:director_mode] %>
<div class="custom-control custom-switch ml-auto">
<input type="checkbox" name="director_mode" value="true" class="custom-control-input" id="director_mode_switch" checked="checked" />
<label class="custom-control-label text-white override-custom-control-label" for="director_mode_switch">Director Mode</label>
</div>
<%= link_to "Disable Director Mode", [@broadcast.project, @broadcast, director_mode: false], class: "d-none", id: "director_mode_link" %>
<% else %>
<div class="custom-control custom-switch ml-auto">
<input type="checkbox" name="director_mode" value="true" class="custom-control-input" id="director_mode_switch" />
<label class="custom-control-label text-white override-custom-control-label" for="director_mode_switch">Director Mode</label>
</div>
<%= link_to "Enable Director Mode", [@broadcast.project, @broadcast], class: "d-none", id: "director_mode_link" %>
<% end %>
<% end %>
</div>
<div class="embed-responsive embed-responsive-16by9" id="video_content">
<%= render partial: 'broadcasts/video', locals: { broadcast: @broadcast } %>
<% if @broadcast.streamer_recording? && @broadcast.active? %>
<%= javascript_tag nonce: true do %>
new Clappr.Player({
parentId: '#broadcast_video',
source: "<%= @broadcast.full_live_stream_playback_url %>",
width: '100%',
height: '100%',
mute: true,
autoPlay: true,
hlsMinimumDvrSize: 1
});
<% end %>
<% end %>
</div> </div>
</div> </div>
<div class="col-lg-4 col-md-12 mb-3"> <div class="col-lg-4 col-md-12 bg-white p-3 min-vh-100 overflow-auto">
<div class="card shadow-sm mb-3"> <% unless controller.class.module_parent.to_s == "Public" %>
<div class="card-header"> <% if @multi_view_broadcasts.present? %>
<ul class="nav nav-tabs card-header-tabs"> <% tokens = @multi_view_broadcasts.map(&:token) %>
<li class="nav-item"> <div class="btn-group">
<%= link_to "Home", "#home", class: class_string("nav-link", "active" => !params[:active_tab].present?), data: { toggle: "tab" } %> <button type="button" class="btn btn-primary" id="broadcast_share_url" data-behavior="clipboard" href="<%= broadcast_url(@broadcast.token, multi_view_tokens: tokens) %>">Share URL</button>
</li> <button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<li class="nav-item"> <span class="sr-only">Toggle Dropdown</span>
<%= link_to "Previous Sessions", "#recordings", class: class_string("nav-link", "active" => params[:active_tab] == "recordings"), data: { toggle: "tab" } %> </button>
</li> <div class="dropdown-menu">
</ul> <%= link_to t('.actions.reset_url'), [@project, @broadcast], method: :patch, class: "dropdown-item" %>
</div>
<div class="card-body p-3">
<div class="tab-content">
<div class="<%= class_string("tab-pane fade show", "active" => !params[:active_tab].present?) %>" id="home">
<div id="broadcast_updates">
<%= render partial: 'broadcasts/broadcast_status', locals: { broadcast: @broadcast } %>
</div>
<% unless controller.class.module_parent.to_s == "Public" %>
<hr>
<div class="form-group">
<label for="broadcast_share_url">To share the stream, copy the URL below. Anyone with the link can view the stream.</label>
<div class="input-group">
<% if @multi_view_broadcasts.present? %>
<% tokens = @multi_view_broadcasts.map(&:token) %>
<input type="text" class="form-control" value="<%= broadcast_url(@broadcast.token, multi_view_tokens: tokens) %>" readonly>
<div class="input-group-append">
<button type="button" id="broadcast_share_url" class="btn btn-success" data-behavior="clipboard" href="<%= broadcast_url(@broadcast.token, multi_view_tokens: tokens) %>">
<i class="fa fa-clipboard"></i>
Copy URL
</button>
<%= link_to t('.actions.reset_url'), [@project, @broadcast], method: :patch, class: "btn btn-danger" %>
</div>
<% else %>
<input type="text" class="form-control" value="<%= broadcast_url(@broadcast.token) %>" readonly>
<div class="input-group-append">
<button type="button" id="broadcast_share_url" class="btn btn-success" data-behavior="clipboard" href="<%= broadcast_url(@broadcast.token) %>">
<i class="fa fa-clipboard"></i>
Copy URL
</button>
<%= link_to t('.actions.reset_url'), [@project, @broadcast], method: :patch, class: "btn btn-danger" %>
</div>
<% end %>
</div>
</div>
<hr>
<% end %>
<p class="card-text">If you want to join the ZOOM meeting dedicated to this broadcast, follow the link below.</p>
<%= link_to 'Video Conference', @conference_url, class: 'btn btn-primary btn-block', target: '_blank' %>
</div>
<div class="<%= class_string("tab-pane fade show", "active" => params[:active_tab] == 'recordings') %>" id="recordings">
<div id="broadcast_recordings">
<%= render partial: 'broadcasts/broadcast_recordings', locals: { recordings: @recordings, broadcast: @broadcast } %>
</div>
</div> </div>
</div> </div>
<% else %>
<div class="btn-group">
<button type="button" class="btn btn-primary" id="broadcast_share_url" data-behavior="clipboard" href="<%= broadcast_url(@broadcast.token) %>">Share URL</button>
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="sr-only">Toggle Dropdown</span>
</button>
<div class="dropdown-menu">
<%= link_to t('.actions.reset_url'), [@project, @broadcast], method: :patch, class: "dropdown-item" %>
</div>
</div>
<% end %>
<% end %>
<%= link_to 'Video Conference', @conference_url, class: 'btn btn-primary', target: '_blank' %>
<hr/>
<ul class="nav nav-tabs override-nav-tabs mb-3">
<li class="nav-item">
<%= link_to "Takes", "#recordings", class: class_string("nav-link", "active" => !params[:active_tab].present?), data: { toggle: "tab" } %>
</li>
<li class="nav-item">
<%= link_to "Files", "#files", class: class_string("nav-link", "active" => params[:active_tab] == "files"), data: { toggle: "tab" } %>
</li>
</ul>
<div class="tab-content">
<div class="<%= class_string("tab-pane fade show", "active" => !params[:active_tab].present?) %>" id="recordings">
<div id="live_take">
<%= render partial: 'broadcasts/live_take', locals: { broadcast: @broadcast } %>
</div>
<div id="broadcast_recordings">
<%= render partial: 'broadcasts/broadcast_recordings', locals: { recordings: @recordings, broadcast: @broadcast } %>
</div>
</div> </div>
</div> <div class="<%= class_string("tab-pane fade show", "active" => params[:active_tab] == "files") %>" id="files">
<% if @multi_view_broadcasts.present? %>
<!-- files section --> <ul class="nav nav-tabs">
<div id="files" class="card shadow-sm mb-3">
<div class="card-header">
<h2 class="h5">Files</h2>
<% if @multi_view_broadcasts %>
<ul class="nav nav-tabs card-header-tabs">
<% @multi_view_broadcasts.each_with_index do |mvb, index| %> <% @multi_view_broadcasts.each_with_index do |mvb, index| %>
<li class="nav-item"> <li class="nav-item">
<%= link_to mvb.name, "#files_broadcast_#{mvb.token}", class: class_string("nav-link", "active" => (params[:active_files_tab] == mvb.token || (params[:active_files_tab].nil? && index == 0))), data: { toggle: "tab" } %> <%= link_to mvb.name, "#files_broadcast_#{mvb.token}", class: class_string("nav-link", "active" => (params[:active_files_tab] == mvb.token || (params[:active_files_tab].nil? && index == 0))), data: { toggle: "tab" } %>
</li> </li>
<% end %> <% end %>
</ul> </ul>
<% end %> <div class="tab-content pt-4">
</div>
<div class="card-body p-3">
<div class="tab-content">
<% if @multi_view_broadcasts.present? %>
<% @multi_view_broadcasts.each_with_index do |mvb, index| %> <% @multi_view_broadcasts.each_with_index do |mvb, index| %>
<div class="<%= class_string("tab-pane fade show", "active" => (params[:active_files_tab] == mvb.token || (params[:active_files_tab].nil? && index == 0))) %>" id="files_broadcast_<%= mvb.token %>"> <div class="<%= class_string("tab-pane fade show", "active" => (params[:active_files_tab] == mvb.token || (params[:active_files_tab].nil? && index == 0))) %>" id="files_broadcast_<%= mvb.token %>">
<%= render partial: 'broadcasts/files_section', locals: { broadcast: mvb, files: mvb.files } %> <%= render partial: 'broadcasts/files_section', locals: { broadcast: mvb, files: mvb.files } %>
</div> </div>
<% end %> <% end %>
<% else %> </div>
<div class="tab-pane fade show active" id="files_broadcast_<%= @broadcast.id %>"> <% else %>
<%= render partial: 'broadcasts/files_section', locals: { broadcast: @broadcast, files: @files } %> <%= render partial: 'broadcasts/files_section', locals: { broadcast: @broadcast, files: @files } %>
</div> <% end %>
<% end %>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -52,7 +52,7 @@
</div> </div>
<% end %> <% end %>
<% if releasable.class == AcquiredMediaRelease %> <% if releasable.respond_to?(:image_files) %>
<div class="page"> <div class="page">
<%= render "contracts/files", release: releasable, preview: preview %> <%= render "contracts/files", release: releasable, preview: preview %>
</div> </div>

View File

@@ -57,8 +57,8 @@
<hr> <hr>
<%= field_set_tag content_tag(:span, t(".photos.heading"), class: "h6 text-muted text-uppercase") do %> <%= field_set_tag content_tag(:span, t(".files.heading"), class: "h6 text-muted text-uppercase") do %>
<%= render "shared/photos_dropzone_fields", form: form, release: material_release %> <%= render "shared/releasable_files_dropzone", form: form, releasable: material_release %>
<div class="<%= class_string("collapse" => !material_release.minor?) %>" data-ujs-target="guardian-fields"> <div class="<%= class_string("collapse" => !material_release.minor?) %>" data-ujs-target="guardian-fields">
<br> <br>

View File

@@ -9,10 +9,10 @@
<% end %> <% end %>
</td> </td>
<td> <td>
<% if material_release.photo.attached? %> <% if material_release.files.any? %>
<%= image_tag medium_variant(material_release.photo), class: "img-fluid" %> <%= material_release.files.size %>
<% else %> <% else %>
<%= fa_icon("warning", text: t(".no_photos"), class: "text-danger") %> <%= fa_icon("warning", text: t(".no_media"), class: "text-danger") %>
<% end %> <% end %>
</td> </td>
<td> <td>
@@ -41,8 +41,8 @@
<% if policy(material_release.tags).new? %> <% if policy(material_release.tags).new? %>
<%= link_to fa_icon("tags fw", text: "Tags"), [:new, material_release, :acts_as_taggable_on_tag], class: "dropdown-item", remote: true %> <%= link_to fa_icon("tags fw", text: "Tags"), [:new, material_release, :acts_as_taggable_on_tag], class: "dropdown-item", remote: true %>
<% end %> <% end %>
<% if policy(material_release).edit_photos? %> <% if policy(material_release).edit_files? %>
<%= link_to fa_icon("picture-o fw", text: "Photos"), [:edit, material_release, :photos], class: "dropdown-item" %> <%= link_to fa_icon("file-o fw", text: "Add Media"), [:edit, material_release, :files], class: "dropdown-item" %>
<% end %> <% end %>
<% if policy(Contract).show? && (material_release.contract.attached? || material_release.contract_template.present?) %> <% if policy(Contract).show? && (material_release.contract.attached? || material_release.contract_template.present?) %>
<%= link_to fa_icon("download fw", text: "Download"), [material_release, :contracts, format: "pdf"], class: "dropdown-item", target: "_blank" %> <%= link_to fa_icon("download fw", text: "Download"), [material_release, :contracts, format: "pdf"], class: "dropdown-item", target: "_blank" %>

View File

@@ -27,7 +27,7 @@
<tr> <tr>
<th data-behavior="all-selectable"><%= check_box_tag "material_release_ids[]", false, false %></th> <th data-behavior="all-selectable"><%= check_box_tag "material_release_ids[]", false, false %></th>
<th><%= t '.table_headers.approved'%></th> <th><%= t '.table_headers.approved'%></th>
<th></th> <th><%= t(".table_headers.files_count") %></th>
<th><%= MaterialRelease.human_attribute_name(:name) %></th> <th><%= MaterialRelease.human_attribute_name(:name) %></th>
<th><%= t(".table_headers.owner_info") %> <th><%= t(".table_headers.owner_info") %>
<th><%= t(".table_headers.notes") %></th> <th><%= t(".table_headers.notes") %></th>

View File

@@ -45,8 +45,8 @@
<%= render "shared/address_fields", form: form, subject: "person", required: true %> <%= render "shared/address_fields", form: form, subject: "person", required: true %>
<% end %> <% end %>
<%= card_field_set_tag t(".photo.heading") do %> <%= card_field_set_tag t(".files.heading") do %>
<%= render "shared/photos_dropzone_fields", form: form, release: @material_release %> <%= render "shared/releasable_files_dropzone", form: form, releasable: @material_release %>
<% end %> <% end %>
<hr> <hr>

View File

@@ -0,0 +1 @@
Rails.application.config.session_store :cookie_store, key: '_easy_release_session', expire_after: 1.month

View File

@@ -224,6 +224,9 @@ en:
manage: Manage manage: Manage
empty_bookmarks: empty_bookmarks:
empty: Notes will appear here empty: Notes will appear here
broadcast_recordings:
edit:
heading: Edit Broadcast Recording
broadcasts: broadcasts:
broadcast: broadcast:
actions: actions:
@@ -901,6 +904,8 @@ en:
form: form:
contract_and_rights: contract_and_rights:
heading: 3 of 4 Contract & Exploitable Rights heading: 3 of 4 Contract & Exploitable Rights
files:
heading: Files
guardian_2_info: guardian_2_info:
heading: Second Guardian Information (if company requires) heading: Second Guardian Information (if company requires)
guardian_info: guardian_info:
@@ -908,7 +913,7 @@ en:
material_details: material_details:
heading: 1 of 3 Material Details heading: 1 of 3 Material Details
photos: photos:
dropzone_label: Tap to take a photo of Licensed Material (optional) dropzone_label: "To Add Files to the release:<br>Drag & Drop Files<br>or<br>Click or Tap here to browse files"
guardian_2_photo: guardian_2_photo:
heading: Second Guardian Photo heading: Second Guardian Photo
guardian_photo: guardian_photo:
@@ -923,6 +928,7 @@ en:
empty: Material Releases will appear here empty: Material Releases will appear here
table_headers: table_headers:
approved: Approved approved: Approved
files_count: No. of Files
name: Name name: Name
notes: Notes notes: Notes
owner_info: Owner Info owner_info: Owner Info
@@ -934,6 +940,7 @@ en:
review: Review review: Review
messages: messages:
approved_tooltip: Approved by %{user} on %{timestamp} approved_tooltip: Approved by %{user} on %{timestamp}
no_media: No Media
no_photos: Needs Photo no_photos: Needs Photo
new: new:
heading: Import Material Release (Products / Logos) heading: Import Material Release (Products / Logos)
@@ -1252,6 +1259,8 @@ en:
cancel: Cancel cancel: Cancel
contact_info: contact_info:
heading: Licensor/Owner Contact Information heading: Licensor/Owner Contact Information
files:
heading: Files
guardian_2_info: guardian_2_info:
heading: Second Guardian Information (if company requires) heading: Second Guardian Information (if company requires)
guardian_2_photo: guardian_2_photo:

View File

@@ -55,7 +55,7 @@ Rails.application.routes.draw do
resources :appearance_releases, except: [:show], concerns: [:contractable, :notable] resources :appearance_releases, except: [:show], concerns: [:contractable, :notable]
resources :appearance_release_imports, only: [:create] resources :appearance_release_imports, only: [:create]
resources :location_releases, except: [:show], concerns: [:contractable, :notable, :photoable] resources :location_releases, except: [:show], concerns: [:contractable, :notable, :photoable]
resources :material_releases, except: [:show], concerns: [:contractable, :notable, :photoable] resources :material_releases, except: [:show], concerns: [:contractable, :notable, :file_uploadable]
resources :music_releases, except: [:show], concerns: [:contractable, :notable] resources :music_releases, except: [:show], concerns: [:contractable, :notable]
resources :talent_releases, except: [:show], concerns: [:contractable, :notable, :photoable] resources :talent_releases, except: [:show], concerns: [:contractable, :notable, :photoable]
resources :medical_releases, except: [:show], concerns: [:contractable, :notable, :photoable] resources :medical_releases, except: [:show], concerns: [:contractable, :notable, :photoable]
@@ -101,7 +101,9 @@ Rails.application.routes.draw do
delete :destroy_file delete :destroy_file
end end
resource :zoom_meeting, only: [:show] resource :zoom_meeting, only: [:show]
resources :broadcast_recordings, only: :destroy resources :broadcast_recordings, only: [:destroy, :edit, :update] do
resources :broadcast_recording_starrings, only: :create
end
end end
resources :directories, except: [:index] do resources :directories, except: [:index] do
member do member do

View File

@@ -0,0 +1,9 @@
class MigrateMaterialPhotosToFiles < ActiveRecord::DataMigration
def up
photos = ActiveStorage::Attachment.where(name: "photos", record_type: "MaterialRelease")
photos.each do |photo|
photo.update(name: "files")
end
end
end

View File

@@ -0,0 +1,6 @@
class AddNameAndDescriptionToBroadcastRecordings < ActiveRecord::Migration[6.0]
def change
add_column :broadcast_recordings, :name, :string
add_column :broadcast_recordings, :description, :text
end
end

View File

@@ -0,0 +1,5 @@
class AddStarToBroadcastRecordings < ActiveRecord::Migration[6.0]
def change
add_column :broadcast_recordings, :starred, :boolean, default: false
end
end

View File

@@ -9,20 +9,6 @@ SET xmloption = content;
SET client_min_messages = warning; SET client_min_messages = warning;
SET row_security = off; SET row_security = off;
--
-- Name: plpgsql; Type: EXTENSION; Schema: -; Owner: -
--
CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog;
--
-- Name: EXTENSION plpgsql; Type: COMMENT; Schema: -; Owner: -
--
COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language';
-- --
-- Name: fuzzystrmatch; Type: EXTENSION; Schema: -; Owner: - -- Name: fuzzystrmatch; Type: EXTENSION; Schema: -; Owner: -
-- --
@@ -555,7 +541,10 @@ CREATE TABLE public.broadcast_recordings (
created_at timestamp(6) without time zone NOT NULL, created_at timestamp(6) without time zone NOT NULL,
updated_at timestamp(6) without time zone NOT NULL, updated_at timestamp(6) without time zone NOT NULL,
duration double precision, duration double precision,
hidden boolean DEFAULT false hidden boolean DEFAULT false,
starred boolean DEFAULT false,
name character varying,
description text
); );
@@ -4036,6 +4025,8 @@ INSERT INTO "schema_migrations" (version) VALUES
('20200807190607'), ('20200807190607'),
('20200811102720'), ('20200811102720'),
('20200812060406'), ('20200812060406'),
('20200819070738'); ('20200819070738'),
('20200820082501'),
('20200824171649');

View File

@@ -17,6 +17,7 @@ RSpec.describe BroadcastsChannel, type: :channel do
it "broadcasts to the channel" do it "broadcasts to the channel" do
status_content = ApplicationController.render partial: "broadcasts/broadcast_status", locals: { broadcast: broadcast } status_content = ApplicationController.render partial: "broadcasts/broadcast_status", locals: { broadcast: broadcast }
video_content = ApplicationController.render partial: "broadcasts/video", locals: { broadcast: broadcast } video_content = ApplicationController.render partial: "broadcasts/video", locals: { broadcast: broadcast }
live_take = ApplicationController.render partial: "broadcasts/live_take", locals: { broadcast: broadcast }
expect { expect {
BroadcastsChannel.broadcast_stream_updates(broadcast) BroadcastsChannel.broadcast_stream_updates(broadcast)
@@ -27,6 +28,7 @@ RSpec.describe BroadcastsChannel, type: :channel do
full_live_stream_playback_url: broadcast.full_live_stream_playback_url, full_live_stream_playback_url: broadcast.full_live_stream_playback_url,
status_content: status_content, status_content: status_content,
video_content: video_content, video_content: video_content,
live_take_content: live_take,
streamer_status: broadcast.streamer_status streamer_status: broadcast.streamer_status
}) })
end end

View File

@@ -0,0 +1,48 @@
require 'rails_helper'
RSpec.describe BroadcastRecordingStarringsController, type: :controller do
render_views
let(:user) { create(:user) }
let(:account) { user.primary_account }
let(:project) { create(:project, account: user.primary_account) }
before do
sign_in user
stub_mux_live_stream
end
describe "#create" do
let(:broadcast) { create(:broadcast, project: project, name: "New Broadcast") }
let(:recordings) { create_list(:broadcast_recording, 5, :with_random_asset_uid, broadcast: broadcast) }
let(:starred_recordings) { create_list(:broadcast_recording, 5, :with_random_asset_uid, broadcast: broadcast, starred: true) }
it "sets star property to true when recording is starred" do
recordings.each do |recording|
expect(recording.starred).to be_falsey
end
post :create, params: { project_id: project, broadcast_id: broadcast, broadcast_recording_id: recordings.first.id }, xhr: true
expect(recordings.first.reload.starred).to eq true
recordings[1..5].each do |recording|
expect(recording.reload.starred).to be_falsey
end
end
it "sets star property to false when recording is unstarred" do
starred_recordings.each do |recording|
expect(recording.starred).to be_truthy
end
post :create, params: { project_id: project, broadcast_id: broadcast, broadcast_recording_id: starred_recordings.first.id }, xhr: true
expect(starred_recordings.first.reload.starred).to eq false
starred_recordings[1..5].each do |recording|
expect(recording.reload.starred).to eq true
end
end
end
end

View File

@@ -9,9 +9,10 @@ RSpec.describe BroadcastRecordingsController, type: :controller do
before do before do
sign_in user sign_in user
stub_mux_live_stream
end end
describe "#destroy" do describe "#edit" do
let(:broadcast) { create(:broadcast, project: project, name: "New Broadcast") } let(:broadcast) { create(:broadcast, project: project, name: "New Broadcast") }
let(:recording) { create(:broadcast_recording, broadcast: broadcast) } let(:recording) { create(:broadcast_recording, broadcast: broadcast) }
@@ -19,6 +20,55 @@ RSpec.describe BroadcastRecordingsController, type: :controller do
stub_mux_live_stream stub_mux_live_stream
end end
it "assigns project, broadcast, broadcast recording" do
get :edit, params: { project_id: project, broadcast_id: broadcast, id: recording }, xhr: true
expect(assigns(:project)).to have_attributes({
id: project.id,
name: project.name,
account_id: project.account_id
})
expect(assigns(:broadcast)).to have_attributes({
id: broadcast.id,
name: broadcast.name,
project_id: project.id
})
expect(assigns(:recording)).to have_attributes({
id: recording.id,
broadcast_id: broadcast.id,
file_name: "high.mp4"
})
end
end
describe "#update" do
let(:broadcast) { create(:broadcast, project: project, name: "New Broadcast") }
let(:recording) { create(:broadcast_recording, broadcast: broadcast) }
let(:recordings) { create_list(:broadcast_recording, 5, :with_random_asset_uid, broadcast: broadcast) }
let(:starred_recordings) { create_list(:broadcast_recording, 5, :with_random_asset_uid, broadcast: broadcast, starred: true) }
before do
stub_mux_live_stream
end
it "updates the recording's name and description" do
expect(recording.name).to eq(recording.download_file_name)
expect(recording.description).to eq("No description provided for this recording.")
patch :update, params: { project_id: project, broadcast_id: broadcast, id: recording, broadcast_recording: { name: "Just for fun", description: "I had fun while making this stream." } }, xhr: true
recording.reload
expect(recording.name).to eq("Just for fun")
expect(recording.description).to eq("I had fun while making this stream.")
end
end
describe "#destroy" do
let(:broadcast) { create(:broadcast, project: project, name: "New Broadcast") }
let(:recording) { create(:broadcast_recording, broadcast: broadcast) }
it "hides the broadcast recording" do it "hides the broadcast recording" do
expect(recording.hidden).to be false expect(recording.hidden).to be false

View File

@@ -150,13 +150,6 @@ RSpec.describe BroadcastsController, type: :controller do
expect(assigns(:broadcast)).to eq(broadcast) expect(assigns(:broadcast)).to eq(broadcast)
end end
it "renders readonly share url" do
get :show, params: { project_id: project.id, id: broadcast.id }
expect(response.body).to have_button "Copy URL"
expect(response.body).to have_xpath "//input[@readonly][@value='#{broadcast_url(broadcast.token)}']"
end
it "displays zoom meeting button" do it "displays zoom meeting button" do
get :show, params: { project_id: project.id, id: broadcast.id } get :show, params: { project_id: project.id, id: broadcast.id }
@@ -174,7 +167,7 @@ RSpec.describe BroadcastsController, type: :controller do
it "renders the view dropdown with just the current broadcast" do it "renders the view dropdown with just the current broadcast" do
get :show, params: { project_id: project, id: broadcast } get :show, params: { project_id: project, id: broadcast }
expect(response.body).to have_content "Switch View" expect(response.body).to have_content broadcast.name
expect(response.body).to have_selector(".dropdown-menu .dropdown-item.active", text: "Another Broadcast") expect(response.body).to have_selector(".dropdown-menu .dropdown-item.active", text: "Another Broadcast")
end end
end end
@@ -185,32 +178,12 @@ RSpec.describe BroadcastsController, type: :controller do
get :show, params: { project_id: project, id: broadcast, multi_view_ids: [broadcast.id, other_broadcast.id] } get :show, params: { project_id: project, id: broadcast, multi_view_ids: [broadcast.id, other_broadcast.id] }
expect(response.body).to have_content "Switch View" expect(response.body).to have_content broadcast.name
expect(response.body).to have_selector(".dropdown-menu .dropdown-item.active", text: "Another Broadcast") expect(response.body).to have_selector(".dropdown-menu .dropdown-item.active", text: "Another Broadcast")
expect(response.body).to have_selector(".dropdown-menu a.dropdown-item", text: "Some Other Broadcast") expect(response.body).to have_selector(".dropdown-menu a.dropdown-item", text: "Some Other Broadcast")
end end
end end
context "when there are no recordings for the current broadcast" do
it "renders the view dropdown with a message" do
get :show, params: { project_id: project, id: broadcast }
expect(response.body).to have_content "Switch View"
expect(response.body).to have_selector(".dropdown-menu .dropdown-item", text: "Recordings will appear here")
end
end
context "when there are recordings available" do
it "renders the view dropdown with the recordings" do
recording = create(:broadcast_recording, broadcast: broadcast)
get :show, params: { project_id: project, id: broadcast }
expect(response.body).to have_content "Switch View"
expect(response.body).to have_selector(".dropdown-menu a.dropdown-item", text: recording.download_file_name)
end
end
context "when virtual director video embed is available" do context "when virtual director video embed is available" do
let(:broadcast) { create(:broadcast, project: project, name: "Another Broadcast", let(:broadcast) { create(:broadcast, project: project, name: "Another Broadcast",
director_mode_video_embed: "<iframe>video player</iframe>") } director_mode_video_embed: "<iframe>video player</iframe>") }
@@ -218,24 +191,22 @@ RSpec.describe BroadcastsController, type: :controller do
it "renders the view dropdown with a director mode enable option" do it "renders the view dropdown with a director mode enable option" do
get :show, params: { project_id: project, id: broadcast } get :show, params: { project_id: project, id: broadcast }
expect(response.body).to have_content "Switch View" expect(response.body).to have_content broadcast.name
expect(response.body).to have_selector(".dropdown-menu h5.dropdown-header", text: "Director Mode") expect(response.body).to have_selector(".custom-control-label", text: "Director Mode")
expect(response.body).to have_selector(".dropdown-menu a.dropdown-item", text: "Enable Director Mode")
end end
context "when director mode is enabled" do context "when director mode is enabled" do
it "shows the video embed" do it "shows the video embed" do
get :show, params: { project_id: project, id: broadcast, director_mode: true } get :show, params: { project_id: project, id: broadcast }
expect(response.body).to have_selector("iframe", text: "video player") expect(response.body).to have_selector("iframe", text: "video player")
end end
it "renders the view dropdown with a director mode disable option" do it "renders the view dropdown with a director mode disable option" do
get :show, params: { project_id: project, id: broadcast, director_mode: true } get :show, params: { project_id: project, id: broadcast }
expect(response.body).to have_content "Switch View" expect(response.body).to have_content broadcast.name
expect(response.body).to have_selector(".dropdown-menu h5.dropdown-header", text: "Director Mode") expect(response.body).to have_selector(".custom-control-label", text: "Director Mode")
expect(response.body).to have_selector(".dropdown-menu a.dropdown-item", text: "Disable Director Mode")
end end
end end
end end

View File

@@ -68,13 +68,7 @@ RSpec.describe PhotosController, type: :controller do
it_behaves_like "a photoable releases controller" it_behaves_like "a photoable releases controller"
end end
context "for material releases" do
subject { create(:material_release, project: project) }
it_behaves_like "a photoable releases controller"
end
private private
def release_params def release_params

View File

@@ -45,7 +45,7 @@ RSpec.describe Public::BroadcastsController, type: :controller do
it "renders the view dropdown with just the current broadcast" do it "renders the view dropdown with just the current broadcast" do
get :show, params: { token: broadcast.token } get :show, params: { token: broadcast.token }
expect(response.body).to have_content "Switch View" expect(response.body).to have_content broadcast.name
expect(response.body).to have_selector(".dropdown-menu .dropdown-item.active", text: "Broadcast") expect(response.body).to have_selector(".dropdown-menu .dropdown-item.active", text: "Broadcast")
end end
end end
@@ -56,7 +56,7 @@ RSpec.describe Public::BroadcastsController, type: :controller do
get :show, params: { token: broadcast.token, multi_view_tokens: [broadcast.token, other_broadcast.token] } get :show, params: { token: broadcast.token, multi_view_tokens: [broadcast.token, other_broadcast.token] }
expect(response.body).to have_content "Switch View" expect(response.body).to have_content broadcast.name
expect(response.body).to have_selector(".dropdown-menu .dropdown-item.active", text: "Broadcast") expect(response.body).to have_selector(".dropdown-menu .dropdown-item.active", text: "Broadcast")
expect(response.body).to have_selector(".dropdown-menu a.dropdown-item", text: "Some Other Broadcast") expect(response.body).to have_selector(".dropdown-menu a.dropdown-item", text: "Some Other Broadcast")
end end
@@ -66,8 +66,7 @@ RSpec.describe Public::BroadcastsController, type: :controller do
it "renders the view dropdown with a message" do it "renders the view dropdown with a message" do
get :show, params: { token: broadcast.token } get :show, params: { token: broadcast.token }
expect(response.body).to have_content "Switch View" expect(response.body).to have_content broadcast.name
expect(response.body).to have_selector(".dropdown-menu .dropdown-item", text: "Recordings will appear here")
end end
end end
@@ -77,8 +76,7 @@ RSpec.describe Public::BroadcastsController, type: :controller do
get :show, params: { token: broadcast.token } get :show, params: { token: broadcast.token }
expect(response.body).to have_content "Switch View" expect(response.body).to have_content broadcast.name
expect(response.body).to have_selector(".dropdown-menu a.dropdown-item", text: recording.download_file_name)
end end
end end

View File

@@ -10,10 +10,10 @@ describe Public::MaterialReleasesController do
it "allows photos param" do it "allows photos param" do
contract_template = create(:contract_template, project: project) contract_template = create(:contract_template, project: project)
post :create, params: { account_id: user.primary_account.to_param, project_id: project, contract_template_id: contract_template, material_release: material_release_params_with_photos } post :create, params: { account_id: user.primary_account.to_param, project_id: project, contract_template_id: contract_template, material_release: material_release_params_with_files }
expect(response).to be_successful expect(response).to be_successful
expect(MaterialRelease.last.photos.attached?).to eq true expect(MaterialRelease.last.files.attached?).to eq true
end end
it "displays validation errors" do it "displays validation errors" do
@@ -63,8 +63,8 @@ describe Public::MaterialReleasesController do
attributes_for(:material_release, :native).except(:signature).merge(signature_param) attributes_for(:material_release, :native).except(:signature).merge(signature_param)
end end
def material_release_params_with_photos def material_release_params_with_files
attributes_for(:material_release, :native, :with_photo).except(:signature).merge(signature_param) attributes_for(:material_release, :native, :with_file).except(:signature).merge(signature_param)
end end

View File

@@ -5,5 +5,9 @@ FactoryBot.define do
asset_uid "asset_uid" asset_uid "asset_uid"
asset_playback_uid "asset_playback_uid" asset_playback_uid "asset_playback_uid"
hidden { false } hidden { false }
trait :with_random_asset_uid do
sequence(:asset_uid, 'a')
end
end end
end end

View File

@@ -42,6 +42,12 @@ FactoryBot.define do
end end
end end
trait :with_file do
files do
path = Rails.root.join("spec", "fixtures", "files", "material_photo.png")
[Rack::Test::UploadedFile.new(path, "image/png")]
end
end
trait :non_native do trait :non_native do
contract do contract do

View File

@@ -56,14 +56,32 @@ feature 'User managing broadcasts' do
visit project_broadcast_path(project, broadcast) visit project_broadcast_path(project, broadcast)
expect(page).to have_content('Live stream is waiting to begin.') expect(page).to have_content('Live stream is waiting to begin.')
expect(page).to have_content('Copy URL') expect(page).to have_content('Share URL')
within '#files' do click_on "Files"
expect(page).to have_content('contract.pdf') expect(page).to have_content('contract.pdf')
click_on 'Takes'
expect(page).to have_content(recording.name)
end
context 'visit show page of active broadcast' do
scenario 'loads full live stream playback url if available' do
broadcast = create(:broadcast, :with_stream, :with_files, project: project, streamer_status: :recording, status: :active)
visit project_broadcast_path(project, broadcast)
expect(page.body).not_to match broadcast.stream_playback_url
expect(page.body).to match broadcast.full_live_stream_playback_url
end end
click_on 'Previous Sessions' scenario 'loads full broadcast asset url if available' do
expect(page).to have_content(recording.download_file_name) broadcast = create(:broadcast, :with_stream, :with_files, project: project, streamer_status: :recording, status: :active, full_live_stream_playback_uid: '')
visit project_broadcast_path(project, broadcast)
expect(page.body).to match broadcast.stream_playback_url
end
end end
scenario 'Clicking Reset URL regenerates broadcast token' do scenario 'Clicking Reset URL regenerates broadcast token' do
@@ -73,93 +91,19 @@ feature 'User managing broadcasts' do
visit project_broadcast_path(project, broadcast) visit project_broadcast_path(project, broadcast)
expect(page).to have_content reset_url expect(page).to have_content reset_url
expect(page).to have_xpath "//input[@readonly][@value='#{broadcast_url(old_token)}']"
click_link reset_url click_link reset_url
expect(Broadcast.last.token).not_to eq old_token expect(Broadcast.last.token).not_to eq old_token
expect(page).to have_xpath "//input[@readonly][@value='#{broadcast_url(Broadcast.last.token)}']"
expect(page).to have_content token_reset_notice expect(page).to have_content token_reset_notice
end end
scenario 'Player will not reload if stream is reactivated while user is watching previous recording', js: true do
broadcast = create(:broadcast, :with_stream, :with_files, project: project)
recording = create(:broadcast_recording, broadcast: broadcast)
visit project_broadcast_path(project, broadcast)
expect(page).to have_content stream_idle_message
broadcast.streamer_status = :recording
broadcast.status = :active
BroadcastsChannel.broadcast_stream_updates(broadcast)
expect(page).to have_content stream_begun_message
expect(page).to have_selector('div#broadcast_video', count: 2)
broadcast.streamer_status = :idle
broadcast.status = :idle
BroadcastsChannel.broadcast_stream_updates(broadcast)
click_on switch_view_dropdown
click_on recording.download_file_name
expect(page).to have_content stream_idle_message
expect(page).to have_selector('div#broadcast_video', count: 1)
broadcast.streamer_status = :recording
broadcast.status = :active
BroadcastsChannel.broadcast_stream_updates(broadcast)
expect(page).to have_content stream_begun_message
expect(page).to have_selector('div#broadcast_video', count: 1)
end
scenario 'user can go back and forth between live session and previous sessions', js: true do
broadcast = create(:broadcast, :with_stream, :with_files, project: project)
recording = create(:broadcast_recording, broadcast: broadcast)
visit project_broadcast_path(project, broadcast)
expect(page).to have_content broadcast.name.titleize, count: 1
expect(page).to have_content recording.download_file_name, count: 0
click_on switch_view_dropdown
expect(page).to have_content broadcast.name.titleize, count: 2
expect(page).to have_content recording.download_file_name, count: 1
live_stream_nav_item = page.find('.dropdown-item', text: broadcast.name.titleize)
recording_nav_item = page.find('.dropdown-item', text: recording.download_file_name)
expect(live_stream_nav_item[:class].include?('active')).to eq true
expect(recording_nav_item[:class].include?('active')).to eq false
click_on recording.download_file_name
expect(page).to have_content broadcast.name.titleize, count: 1
expect(page).to have_content recording.download_file_name, count: 0
expect(live_stream_nav_item[:class].include?('active')).to eq false
expect(recording_nav_item[:class].include?('active')).to eq true
click_on switch_view_dropdown
click_on broadcast.name.titleize
expect(page).to have_content broadcast.name.titleize, count: 1
expect(page).to have_content recording.download_file_name, count: 0
# Page is reloaded, we need to get dropdown items again
live_stream_nav_item = page.find('.dropdown-item', text: broadcast.name.titleize, visible: :all)
recording_nav_item = page.find('.dropdown-item', text: recording.download_file_name, visible: :all)
expect(live_stream_nav_item[:class].include?('active')).to eq true
expect(recording_nav_item[:class].include?('active')).to eq false
end
scenario 'form will not submit if user clicks Add files without selected files', js: true do scenario 'form will not submit if user clicks Add files without selected files', js: true do
broadcast = create(:broadcast, :with_stream, :with_files, project: project) broadcast = create(:broadcast, :with_stream, :with_files, project: project)
visit project_broadcast_path(project, broadcast) visit project_broadcast_path(project, broadcast)
expect(page).to have_content('Live stream is waiting to begin.') expect(page).to have_content('Live stream is waiting to begin.')
click_on "Files"
expect(page).to have_content add_file_button expect(page).to have_content add_file_button
click_on add_file_button click_on add_file_button
@@ -169,7 +113,7 @@ feature 'User managing broadcasts' do
broadcast = create(:broadcast, :with_stream, :with_files, project: project) broadcast = create(:broadcast, :with_stream, :with_files, project: project)
visit project_broadcast_path(project, broadcast) visit project_broadcast_path(project, broadcast)
click_on "Files"
expect(page).to have_content delete_file_button, count: 3 expect(page).to have_content delete_file_button, count: 3
accept_alert do accept_alert do
@@ -189,9 +133,11 @@ feature 'User managing broadcasts' do
new_window = window_opened_by { click_link 'Multi-View' } new_window = window_opened_by { click_link 'Multi-View' }
within_window new_window do within_window new_window do
expect(page).to have_content switch_view_dropdown click_on "Files"
expect(page).to have_content broadcast_one.name
click_on switch_view_dropdown expect(page).to have_content broadcast_two.name
click_on broadcast_one.name
expect(page).to have_link('Broadcast 1') expect(page).to have_link('Broadcast 1')
expect(page).to have_link('Broadcast 2') expect(page).to have_link('Broadcast 2')
@@ -205,22 +151,28 @@ feature 'User managing broadcasts' do
end end
end end
scenario 'project manager can hide broadcast recordings', js: true do scenario 'opening broadcast page starts in normal mode if director mode is not available' do
broadcast = create(:broadcast, :with_stream, :with_files, project: project) broadcast = create(:broadcast, :with_stream, :with_files, project: project)
recording = create(:broadcast_recording, broadcast: broadcast, asset_uid: "another_asset_uid") broadcast.director_mode_video_embed = nil
broadcast.save
visit project_broadcast_path(project, broadcast) visit project_broadcast_path(project, broadcast)
click_on 'Previous Sessions' expect(page).not_to have_selector('#director_broadcast_video')
expect(page).to have_content(recording.download_file_name) expect(page).to have_selector('#broadcast_video')
expect(page).to have_content('Hide')
accept_alert do
click_link "Hide"
end
expect(page).not_to have_content(recording.download_file_name) expect(page).not_to have_selector('#director_mode_switch')
expect(page).to have_content("Recording of the live stream will appear here") end
scenario 'opening broadcast page starts in director mode if available' do
broadcast = create(:broadcast, :with_stream, :with_files, project: project, director_mode_video_embed: 'director_mode')
visit project_broadcast_path(project, broadcast)
expect(page).not_to have_selector('#broadcast_video')
expect(page).to have_selector('#director_broadcast_video')
expect(page).to have_selector('#director_mode_switch')
end end
context 'When the user is associate' do context 'When the user is associate' do
@@ -240,17 +192,6 @@ feature 'User managing broadcasts' do
expect(page).to have_content delete_file_button, count: 0 expect(page).to have_content delete_file_button, count: 0
end end
scenario 'associate does not see hide button in front of recording' do
broadcast = create(:broadcast, :with_stream, :with_files, project: project)
recording = create(:broadcast_recording, broadcast: broadcast, asset_uid: "another_asset_uid")
visit project_broadcast_path(project, broadcast)
click_on 'Previous Sessions'
expect(page).to have_content(recording.download_file_name)
expect(page).not_to have_content('Hide')
end
end end
context 'When the user is account manager' do context 'When the user is account manager' do
@@ -267,7 +208,7 @@ feature 'User managing broadcasts' do
broadcast = create(:broadcast, :with_stream, :with_files, project: project) broadcast = create(:broadcast, :with_stream, :with_files, project: project)
visit project_broadcast_path(project, broadcast) visit project_broadcast_path(project, broadcast)
click_on "Files"
expect(page).to have_content delete_file_button, count: 3 expect(page).to have_content delete_file_button, count: 3
accept_alert do accept_alert do
@@ -277,26 +218,7 @@ feature 'User managing broadcasts' do
expect(page).to have_content delete_file_button, count: 2 expect(page).to have_content delete_file_button, count: 2
expect(Broadcast.find(broadcast.id).files.count).to eq 2 expect(Broadcast.find(broadcast.id).files.count).to eq 2
end end
scenario 'account manager can hide broadcast recordings', js: true do
broadcast = create(:broadcast, :with_stream, :with_files, project: project)
recording = create(:broadcast_recording, broadcast: broadcast, asset_uid: "another_asset_uid")
visit project_broadcast_path(project, broadcast)
click_on 'Previous Sessions'
expect(page).to have_content(recording.download_file_name)
expect(page).to have_content('Hide')
accept_alert do
click_link "Hide"
end
expect(page).not_to have_content(recording.download_file_name)
expect(page).to have_content("Recording of the live stream will appear here")
end
end end
end end
private private
@@ -320,10 +242,6 @@ feature 'User managing broadcasts' do
all('input[type="checkbox"]')[1].click all('input[type="checkbox"]')[1].click
end end
def switch_view_dropdown
'Switch View'
end
def schedule_demo def schedule_demo
t 'broadcasts.splash.actions.book_demo' t 'broadcasts.splash.actions.book_demo'
end end

View File

@@ -37,7 +37,7 @@ feature "User managing material releases" do
click_button submit_release_button click_button submit_release_button
expect(page).to have_content success_submit_message expect(page).to have_content success_submit_message
expect(MaterialRelease.last.photos.attached?).to eq true expect(MaterialRelease.last.files.attached?).to eq true
end end
scenario "creating a release for a minor - guardian fields are required when minor checkbox is checked", js: true do scenario "creating a release for a minor - guardian fields are required when minor checkbox is checked", js: true do
@@ -225,7 +225,7 @@ feature "User managing material releases" do
click_button create_release_button click_button create_release_button
expect(page).to have_content(create_release_notice) expect(page).to have_content(create_release_notice)
expect(page).to have_photo("material_photo.png", visible: :all) expect(page).to have_content("1")
click_on "Manage" click_on "Manage"
expect(page).to have_link("Download") expect(page).to have_link("Download")
@@ -364,19 +364,19 @@ feature "User managing material releases" do
visit project_material_releases_path(project) visit project_material_releases_path(project)
expect(page).to have_content("Needs Photo") expect(page).to have_content("No Media")
click_on "Manage" click_on "Manage"
click_on "Photos" click_on "Add Media"
expect(page).to have_content("Add Photos") expect(page).to have_content("Add Files")
expect(page).to have_content("Apple MacBook Air") expect(page).to have_content("Apple MacBook Air")
drop_file Rails.root.join(file_fixture("material_photo.png")), type: :dropzone drop_file Rails.root.join(file_fixture("material_photo.png")), type: :dropzone
click_on "Save Changes" click_on "Save Changes"
expect(page).to have_content("The release has been updated") expect(page).to have_content("Files added successfully to the release")
expect(page).to have_photo("material_photo.png", visible: :all) expect(page).to have_content("1")
end end
scenario "viewing the contract PDF" do scenario "viewing the contract PDF" do

View File

@@ -6,7 +6,8 @@ RSpec.describe BroadcastRecording, type: :model do
end end
describe "validations" do describe "validations" do
subject { described_class.new(asset_uid: "asset_uid", asset_playback_uid: "playback_uid", file_name: "medium.mp4") } let(:broadcast) { create(:broadcast, :with_stream, skip_create_callback: true, name: "My Broadcast") }
subject { described_class.new(asset_uid: "asset_uid", asset_playback_uid: "playback_uid", file_name: "medium.mp4", broadcast: broadcast) }
it { is_expected.to validate_uniqueness_of(:asset_uid) } it { is_expected.to validate_uniqueness_of(:asset_uid) }
end end
@@ -24,16 +25,20 @@ RSpec.describe BroadcastRecording, type: :model do
let(:broadcast_recording) { create(:broadcast_recording, broadcast: broadcast) } let(:broadcast_recording) { create(:broadcast_recording, broadcast: broadcast) }
it "should have a download url" do it "should have a download url" do
download_file_name = broadcast_recording.send(:download_file_name) name = broadcast_recording.name
expect(broadcast_recording.download_url).to eq("https://stream.mux.com/asset_playback_uid/high.mp4?download=#{download_file_name}") expect(broadcast_recording.download_url).to eq("https://stream.mux.com/asset_playback_uid/high.mp4?download=#{name}")
end end
end end
describe "#download_file_name" do describe "#download_file_name" do
before do
allow_any_instance_of(BroadcastRecording).to receive(:download_file_name).and_return("my-broadcast_date_2020-05-14_time_15-30-00")
end
it "includes the name of the live stream and the created datetime" do it "includes the name of the live stream and the created datetime" do
broadcast = create(:broadcast, skip_create_callback: true, name: "My Broadcast") broadcast = create(:broadcast, skip_create_callback: true, name: "My Broadcast")
recording = create(:broadcast_recording, broadcast: broadcast, created_at: DateTime.new(2020, 05, 14, 15, 30, 00)) recording = create(:broadcast_recording, broadcast: broadcast)
file_name = recording.download_file_name file_name = recording.name
expect(file_name).to eq "my-broadcast_date_2020-05-14_time_15-30-00" expect(file_name).to eq "my-broadcast_date_2020-05-14_time_15-30-00"
end end

View File

@@ -24,7 +24,11 @@ describe MaterialReleasePolicy do
end end
end end
permissions :edit_photos? do permissions :edit_files? do
it { is_expected.to permit(:edit_photos) }
end
permissions :update_files? do
it { is_expected.to permit(:edit_photos) } it { is_expected.to permit(:edit_photos) }
end end