Initial commit
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
module AudioConfirmations
|
||||
class AudioConfirmationPresenter
|
||||
def present(audio_confirmation)
|
||||
AudioConfirmationData.new(
|
||||
source_file_name: audio_confirmation.source_file_name,
|
||||
presented_source_file_name: audio_confirmation.presented_source_file_name,
|
||||
timecode_in: audio_confirmation.timecode_in,
|
||||
should_toggle_checkmark: toggle_checkmark?(audio_confirmation),
|
||||
is_valid: audio_confirmation.valid?,
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def toggle_checkmark?(audio_confirmation)
|
||||
audio_confirmation.source_file_name.present? && audio_confirmation.timecode_in.present?
|
||||
end
|
||||
|
||||
class AudioConfirmationData < Struct.new(
|
||||
:source_file_name,
|
||||
:presented_source_file_name,
|
||||
:timecode_in,
|
||||
:should_toggle_checkmark,
|
||||
:is_valid,
|
||||
keyword_init: true)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,20 @@
|
||||
module AudioConfirmations
|
||||
class AudioConfirmationsPresenter
|
||||
def present(audio_confirmations)
|
||||
AudioConfirmationsData.new(
|
||||
audio_confirmations: sort(audio_confirmations),
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def sort(audio_confirmations)
|
||||
audio_confirmations.sort_by(&:appears_at)
|
||||
end
|
||||
|
||||
class AudioConfirmationsData < Struct.new(
|
||||
:audio_confirmations,
|
||||
keyword_init: true)
|
||||
end
|
||||
end
|
||||
end
|
||||
30
app/presenters/edl_events_presenter.rb
Normal file
30
app/presenters/edl_events_presenter.rb
Normal file
@@ -0,0 +1,30 @@
|
||||
class EdlEventsPresenter
|
||||
def initialize(gateway)
|
||||
@gateway = gateway
|
||||
end
|
||||
|
||||
def present
|
||||
{
|
||||
edl_events: edl_events,
|
||||
edl_attributes: edl_attributes,
|
||||
info_message: info_message,
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :gateway
|
||||
|
||||
def edl_events
|
||||
gateway.edl_events
|
||||
end
|
||||
|
||||
def edl_attributes
|
||||
edl_event = edl_events.first || EdlEvent.new
|
||||
edl_event.public_attributes
|
||||
end
|
||||
|
||||
def info_message
|
||||
I18n.t("shared.info_message", count: edl_events.size)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,26 @@
|
||||
module GraphicsElements
|
||||
class GraphicsElementPresenter
|
||||
def present(graphics_element)
|
||||
GraphicsElementData.new(
|
||||
source_file_name: graphics_element.source_file_name,
|
||||
timecode_in: graphics_element.timecode_in,
|
||||
should_toggle_checkmark: toggle_checkmark?(graphics_element),
|
||||
is_valid: graphics_element.valid?,
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def toggle_checkmark?(graphics_element)
|
||||
graphics_element.source_file_name.present? && graphics_element.timecode_in.present?
|
||||
end
|
||||
|
||||
class GraphicsElementData < Struct.new(
|
||||
:source_file_name,
|
||||
:timecode_in,
|
||||
:should_toggle_checkmark,
|
||||
:is_valid,
|
||||
keyword_init: true)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,20 @@
|
||||
module GraphicsElements
|
||||
class GraphicsElementsPresenter
|
||||
def present(graphics_elements)
|
||||
GraphicsElementsData.new(
|
||||
graphics_elements: sort(graphics_elements),
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def sort(graphics_elements)
|
||||
graphics_elements.sort_by(&:appears_at)
|
||||
end
|
||||
|
||||
class GraphicsElementsData < Struct.new(
|
||||
:graphics_elements,
|
||||
keyword_init: true)
|
||||
end
|
||||
end
|
||||
end
|
||||
25
app/presenters/issues_and_concerns_report_presenter.rb
Normal file
25
app/presenters/issues_and_concerns_report_presenter.rb
Normal file
@@ -0,0 +1,25 @@
|
||||
class IssuesAndConcernsReportPresenter
|
||||
def initialize(video)
|
||||
@video = video
|
||||
end
|
||||
|
||||
def project_name
|
||||
video.project.name
|
||||
end
|
||||
|
||||
def video_name
|
||||
video.file.filename.to_s
|
||||
end
|
||||
|
||||
def date_of_report
|
||||
BigMediaTime.time_zone_now.strftime("%D")
|
||||
end
|
||||
|
||||
def unreleased_appearances
|
||||
video.unreleased_appearances.sort_by { |unreleased_appearance| unreleased_appearance.time_elapsed.to_f}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :video
|
||||
end
|
||||
270
app/presenters/matches_presenter.rb
Normal file
270
app/presenters/matches_presenter.rb
Normal file
@@ -0,0 +1,270 @@
|
||||
class MatchesPresenter
|
||||
def initialize(video, video_analysis, audio_analysis, edl_events, graphics_edl_events)
|
||||
@video = video
|
||||
@video_analysis = video_analysis
|
||||
@audio_analysis = audio_analysis
|
||||
@edl_events = edl_events
|
||||
@graphics_edl_events = graphics_edl_events
|
||||
end
|
||||
|
||||
def build_chronological_matches
|
||||
if video.analysis_success?
|
||||
sort_matches(first_appearances_with_images)
|
||||
else
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
def build_chronological_audio_matches
|
||||
if video.audio_analysis_success?
|
||||
sort_matches(audio_matches)
|
||||
else
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
def build_graphics_matches
|
||||
if video.analysis_success?
|
||||
confirmed_graphics = video.graphics_elements
|
||||
|
||||
sort_matches(
|
||||
graphics_edl_events.
|
||||
reject { |edl_event| edl_event.source_file_name.blank? }.
|
||||
map do |edl_event|
|
||||
GraphicsMatch.new(
|
||||
edl_event.source_file_name,
|
||||
edl_event.start_time,
|
||||
already_confirmed?(confirmed_graphics, edl_event),
|
||||
edl_event.timecode_in
|
||||
)
|
||||
end.
|
||||
compact
|
||||
)
|
||||
else
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
def all_tracks_edl_events
|
||||
edl_events
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def audio_events
|
||||
if ENV["DISABLE_EDL_CHANNEL_FILTER"]
|
||||
edl_events
|
||||
else
|
||||
edl_events.find_all { |event| event.channel.include? "A" }
|
||||
end
|
||||
end
|
||||
|
||||
def video_events
|
||||
if ENV["DISABLE_EDL_CHANNEL_FILTER"]
|
||||
edl_events
|
||||
else
|
||||
edl_events.find_all { |event| event.channel == "V" }
|
||||
end
|
||||
end
|
||||
|
||||
def already_confirmed?(confirmation, edl_event)
|
||||
confirmation.any? { |confirmed| confirmed.source_file_name == edl_event.source_file_name && confirmed.timecode_in == edl_event.timecode_in }
|
||||
end
|
||||
|
||||
attr_reader :video, :video_analysis, :audio_analysis, :edl_events, :graphics_edl_events
|
||||
|
||||
def sort_matches(matches)
|
||||
matches.
|
||||
sort_by(&:start_time).
|
||||
reverse
|
||||
end
|
||||
|
||||
def first_appearances_with_images
|
||||
video_analysis.first_appearances.
|
||||
reject { |appearance| appearance.external_image_id.blank? }.
|
||||
map { |appearance| SuggestedMatch.build_from_first_appearance(appearance, appearance.start_time) }
|
||||
end
|
||||
|
||||
def audio_matches
|
||||
audio_analysis.results.map do |result|
|
||||
# If it's a library match, use the provided composer and publisher info
|
||||
# If it's an original music match, use the composer and publisher info we have
|
||||
# If it's an acquired media match, don't provide any composer and publisher info
|
||||
if result.type == "acquired_media"
|
||||
composers = ""
|
||||
publishers = ""
|
||||
elsif result.type == "original_music"
|
||||
file_info = FileInfo.find(result.uid)
|
||||
composers = file_info.releasable.composers.map { |composer| "#{composer.name}, #{composer.affiliation}, #{composer.percentage}%" }.join("|")
|
||||
publishers = file_info.releasable.publishers.map { |publisher| "#{publisher.name}, #{publisher.affiliation}, #{publisher.percentage}%" }.join("|")
|
||||
else
|
||||
composers = result.audio_data.composers.map { |composer| "#{composer.name}, #{composer.affiliation}, #{composer.percentage}" }.join("|")
|
||||
publishers = result.audio_data.publishers.map { |publisher| "#{publisher.name}, #{publisher.affiliation}, #{publisher.percentage}" }.join("|")
|
||||
end
|
||||
|
||||
result.edl.map do |edl_event|
|
||||
|
||||
is_confirmed = video.audio_confirmations.any? do |confirmation|
|
||||
# TODO: Use EDL ID in the future to determine this
|
||||
confirmation.source_file_name == result.requested_filename &&
|
||||
confirmation.timecode_in == edl_event.timecode_in
|
||||
end
|
||||
|
||||
result_type = result.type == "library" ? "library_music" : result.type
|
||||
|
||||
AudioSuggestedMatch.new(
|
||||
AudioData.new(
|
||||
result.audio_data.filename,
|
||||
result.requested_filename,
|
||||
composers,
|
||||
publishers,
|
||||
result.audio_data.catalog,
|
||||
result.audio_data.title,
|
||||
),
|
||||
edl_event.start_time.to_i,
|
||||
is_confirmed,
|
||||
edl_event.timecode_in,
|
||||
result_type,
|
||||
video.audio_only_edl_file.attached?
|
||||
)
|
||||
end
|
||||
|
||||
end.flatten
|
||||
end
|
||||
|
||||
class GraphicsMatch
|
||||
attr_reader :appears_at_timecode, :start_time, :filename, :confirmed, :timecode_in
|
||||
|
||||
def initialize(filename, start_time, confirmed, timecode_in)
|
||||
@filename = filename
|
||||
@start_time = start_time
|
||||
@appears_at_timecode = Timecode.from_seconds(start_time.to_f / 1000)
|
||||
@confirmed = confirmed
|
||||
@timecode_in = timecode_in
|
||||
end
|
||||
|
||||
def elapsed_time_until_appearance
|
||||
appears_at_timecode.to_seconds
|
||||
end
|
||||
end
|
||||
|
||||
class AudioSuggestedMatch
|
||||
attr_reader :appears_at_timecode, :start_time, :confirmed, :timecode_in, :confirmation_type, :edl_type
|
||||
|
||||
def initialize(audio_data, start_time, confirmed, timecode_in, confirmation_type, audio_edl_attached)
|
||||
@audio_data = audio_data
|
||||
@start_time = start_time
|
||||
@confirmed = confirmed
|
||||
@appears_at_timecode = Timecode.from_seconds(start_time.to_f / 1000)
|
||||
@timecode_in = timecode_in
|
||||
@confirmation_type = confirmation_type
|
||||
@edl_type = audio_edl_attached ? "audio" : "all_tracks"
|
||||
end
|
||||
|
||||
def presented_filename
|
||||
if confirmation_type == "original_music"
|
||||
"(O) #{audio_data.requested_filename}"
|
||||
else
|
||||
"(L) #{audio_data.requested_filename}"
|
||||
end
|
||||
end
|
||||
|
||||
def filename
|
||||
audio_data.requested_filename
|
||||
end
|
||||
|
||||
def composer_info
|
||||
audio_data.composer
|
||||
end
|
||||
|
||||
def publisher_info
|
||||
audio_data.publisher
|
||||
end
|
||||
|
||||
def catalog
|
||||
audio_data.catalog
|
||||
end
|
||||
|
||||
def title
|
||||
audio_data.title
|
||||
end
|
||||
|
||||
def elapsed_time_until_appearance
|
||||
appears_at_timecode.to_seconds
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :audio_data
|
||||
end
|
||||
|
||||
class FileInfoSuggestedMatch < SimpleDelegator
|
||||
attr_reader :appears_at_timecode, :start_time, :file_info, :timecode_in
|
||||
|
||||
def self.build(file_info, start_time, timecode_in)
|
||||
release = file_info.releasable
|
||||
|
||||
new(release, file_info, start_time, timecode_in)
|
||||
end
|
||||
|
||||
def initialize(release, file_info, start_time, timecode_in)
|
||||
super(release)
|
||||
@file_info = file_info
|
||||
@start_time = start_time
|
||||
@appears_at_timecode = Timecode.from_seconds(start_time.to_f / 1000)
|
||||
@timecode_in = timecode_in
|
||||
end
|
||||
|
||||
def elapsed_time_until_appearance
|
||||
appears_at_timecode.to_seconds
|
||||
end
|
||||
end
|
||||
|
||||
class SuggestedMatch < SimpleDelegator
|
||||
attr_reader :appears_at_timecode, :start_time
|
||||
|
||||
def self.build_from_first_appearance(appearance, start_time)
|
||||
release = ExternalImage.new(appearance.external_image_id).release
|
||||
|
||||
new(release, start_time)
|
||||
end
|
||||
|
||||
def initialize(release, start_time)
|
||||
super(release)
|
||||
@start_time = start_time
|
||||
@appears_at_timecode = Timecode.from_seconds(start_time.to_f / 1000)
|
||||
end
|
||||
|
||||
def elapsed_time_until_appearance
|
||||
appears_at_timecode.to_seconds
|
||||
end
|
||||
end
|
||||
|
||||
class ExternalImage
|
||||
attr_reader :uid
|
||||
|
||||
def initialize(uid)
|
||||
@uid = uid
|
||||
end
|
||||
|
||||
def release
|
||||
release_klass.find(id)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def id
|
||||
uid.match(/.*_release_(?<id>\d*)/)
|
||||
Regexp.last_match(:id) || uid
|
||||
end
|
||||
|
||||
def release_klass
|
||||
release_name.classify.constantize
|
||||
end
|
||||
|
||||
def release_name
|
||||
uid.match(/(?<type>\w*_release)_\d/)
|
||||
Regexp.last_match(:type) || "appearance_release"
|
||||
end
|
||||
end
|
||||
end
|
||||
16
app/presenters/unreleased_appearances_presenter.rb
Normal file
16
app/presenters/unreleased_appearances_presenter.rb
Normal file
@@ -0,0 +1,16 @@
|
||||
class UnreleasedAppearancesPresenter
|
||||
def present(unreleased_appearances)
|
||||
UnreleasedData.new(
|
||||
unreleased_appearances: sort(unreleased_appearances),
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def sort(unreleased_appearances)
|
||||
unreleased_appearances.sort_by(&:appears_at)
|
||||
end
|
||||
|
||||
class UnreleasedData < Struct.new(:unreleased_appearances, keyword_init: true)
|
||||
end
|
||||
end
|
||||
56
app/presenters/video_analysis_presenter.rb
Normal file
56
app/presenters/video_analysis_presenter.rb
Normal file
@@ -0,0 +1,56 @@
|
||||
class VideoAnalysisPresenter
|
||||
def initialize(video, video_analysis, matches_presenter)
|
||||
@video = video
|
||||
@video_analysis = video_analysis
|
||||
@matches_presenter = matches_presenter
|
||||
end
|
||||
|
||||
def stale?
|
||||
return if video.analysis_started_at.nil?
|
||||
|
||||
latest_release_created_at = [
|
||||
video.appearance_releases.maximum(:created_at),
|
||||
video.talent_releases.maximum(:created_at),
|
||||
video.acquired_media_releases.maximum(:created_at)
|
||||
].compact.max
|
||||
latest_release_created_at > video.analysis_started_at
|
||||
end
|
||||
|
||||
def bookmarks
|
||||
video.bookmarks
|
||||
end
|
||||
|
||||
def unreleased_appearances
|
||||
video.unreleased_appearances
|
||||
end
|
||||
|
||||
def video_url
|
||||
return original_video_url if !video.analysis_success?
|
||||
|
||||
video_analysis.overlay_video_url || original_video_url
|
||||
end
|
||||
|
||||
def chronological_appearances
|
||||
@chronological_appearances ||= matches_presenter.build_chronological_matches
|
||||
end
|
||||
|
||||
def chronological_graphics_matches
|
||||
@chronological_graphics_matches ||= matches_presenter.build_graphics_matches
|
||||
end
|
||||
|
||||
def chronological_audio_matches
|
||||
@chronological_audio_matches ||= matches_presenter.build_chronological_audio_matches
|
||||
end
|
||||
|
||||
def all_tracks_edl_events
|
||||
@all_tracks_edl_events ||= matches_presenter.all_tracks_edl_events
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :video_analysis, :video, :matches_presenter
|
||||
|
||||
def original_video_url
|
||||
ApplicationController._routes.url_helpers.rails_blob_url(video.file, host: AppHost.new.domain_with_port)
|
||||
end
|
||||
end
|
||||
39
app/presenters/video_presenter.rb
Normal file
39
app/presenters/video_presenter.rb
Normal file
@@ -0,0 +1,39 @@
|
||||
class VideoPresenter
|
||||
def initialize(video)
|
||||
@video = video
|
||||
end
|
||||
|
||||
def edl_file_path
|
||||
return "" unless video.edl_file.attached?
|
||||
|
||||
ApplicationController._routes.url_helpers.rails_blob_path(video.edl_file, only_path: true, disposition: 'inline')
|
||||
end
|
||||
|
||||
def graphics_only_edl_file_path
|
||||
return "" unless video.graphics_only_edl_file.attached?
|
||||
|
||||
ApplicationController._routes.url_helpers.rails_blob_path(video.graphics_only_edl_file, only_path: true, disposition: 'inline')
|
||||
end
|
||||
|
||||
def audio_only_edl_file_path
|
||||
return "" unless video.audio_only_edl_file.attached?
|
||||
|
||||
ApplicationController._routes.url_helpers.rails_blob_path(video.audio_only_edl_file, only_path: true, disposition: 'inline')
|
||||
end
|
||||
|
||||
def edl_download_disabled?
|
||||
!video.edl_file.attached?
|
||||
end
|
||||
|
||||
def graphics_edl_download_disabled?
|
||||
!video.graphics_only_edl_file.attached?
|
||||
end
|
||||
|
||||
def audio_edl_download_disabled?
|
||||
!video.audio_only_edl_file.attached?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :video
|
||||
end
|
||||
Reference in New Issue
Block a user