271 lines
7.2 KiB
Ruby
271 lines
7.2 KiB
Ruby
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
|