require "rails_helper" RSpec.describe MatchesPresenter do let(:project) { build(:project) } let(:video_analysis) { instance_double(VideoAnalysis) } let(:audio_analysis) { instance_double(AudioAnalysis) } describe "#build_chronological_matches" do context "when video analysis success" do let(:video) { create(:video, project: project, analysis_status: :success) } it "returns suggested_matches sorted by most recent first" do appearance_release = create(:appearance_release, project: project) appearance = BrayniacAI::FacialRecognitionResult.new(external_image_id: "appearance_release_#{appearance_release.id}", start_time: 100) allow(video_analysis).to receive(:first_appearances).and_return([appearance]) edl_event_response = build(:edl_event, channel: "V", start_time: 0, timecode_in: "00:00:00:00", timecode_out: "00:00:01:00", matches: [1, 2], ) results = described_class.new(video, video_analysis, audio_analysis, [edl_event_response], []).build_chronological_matches expect(results.count).to eq 1 expect(results.first.elapsed_time_until_appearance).to eq 0.12 expect(results.first.photo.name).to eq "person_photo" expect(results.first.name).to eq "Jane Doe" expect(results.first.appears_at_timecode).to eq Timecode.new("00:00:00:03") end end context "when video analysis not success" do let(:video) { create(:video, project: project, analysis_status: :pending) } it "returns empty array" do expect(described_class.new(video, video_analysis, audio_analysis, [], []).build_chronological_matches).to eq [] end end end describe "#build_graphics_matches" do context "when video analysis success" do let(:video) do create(:video, project: project, analysis_status: :success, graphics_elements: build_list(:graphics_element, 1, source_file_name: "my_file.mp4", timecode_in: "00:00:00:00") ) end it "returns graphics elements matches sorted by most recent first" do earlier_edl_event_response = build(:edl_event, start_time: 0, timecode_in: "00:00:00:00", timecode_out: "00:00:01:00", source_file_name: "my_file.mp4", ) later_edl_event_response = build(:edl_event, start_time: 100, timecode_in: "00:00:10:00", timecode_out: "00:00:11:00", source_file_name: "another_file.mp4", ) results = described_class.new( video, video_analysis, audio_analysis, [], [earlier_edl_event_response, later_edl_event_response], ).build_graphics_matches expect(results.count).to eq 2 expect(results.first.start_time).to eq 100 expect(results.first.elapsed_time_until_appearance).to eq 0.12 expect(results.first.filename).to eq "another_file.mp4" expect(results.first.appears_at_timecode).to eq Timecode.new("00:00:00:03") expect(results.first.confirmed).to be false expect(results.first.timecode_in).to eq "00:00:10:00" expect(results.second.start_time).to eq 0 expect(results.second.elapsed_time_until_appearance).to eq 0.0 expect(results.second.appears_at_timecode).to eq Timecode.new("00:00:00:00") expect(results.second.filename).to eq "my_file.mp4" expect(results.second.confirmed).to be true expect(results.second.timecode_in).to eq "00:00:00:00" end it "does not return graphics elements matches that have empty source_file_name" do null_source_file_edl_event_response = build(:edl_event, source_file_name: nil, ) empty_source_file_edl_event_response = build(:edl_event, source_file_name: "", ) results = described_class.new( video, video_analysis, [], [ null_source_file_edl_event_response, empty_source_file_edl_event_response, ], [], ).build_graphics_matches expect(results).to eq [] end end context "when video analysis not success" do let(:video) { create(:video, project: project, analysis_status: :pending) } it "returns empty array" do expect(described_class.new(video, video_analysis, [], [], []).build_graphics_matches).to eq [] end end end describe "#build_chronological_audio_matches" do context "when audio analysis success" do let(:video) { create(:video, project: project, audio_analysis_status: :success) } it "returns audio_matches sorted by most recent first" do acquired_media_release = create(:acquired_media_release, project: project, file_infos: [ build(:file_info, id: 3, filename: "acquired_media_1") ] ) music_release = create(:music_release, project: project, file_infos: [ build(:file_info, id: 1, filename: "original_music_1"), build(:file_info, id: 2, filename: "original_music_2"), ] ) audio_confirmation = create(:audio_confirmation, video: video, source_file_name: "original_music_2", timecode_in: "00:00:00:00", ) audio_analysis = OpenStruct.new( results: [ OpenStruct.new( uid: "2", requested_filename: "original_music_2", type: "original_music", audio_data: OpenStruct.new( filename: "original_music_2", composers: [], publishers: [], catalog: "", title: "", ), edl: [ OpenStruct.new( start_time: 0, timecode_in: "00:00:00:00", ), ] ), OpenStruct.new( uid: "1", requested_filename: "original_music_1", type: "original_music", audio_data: OpenStruct.new( filename: "original_music_1", composers: [], publishers: [], catalog: "", title: "", ), edl: [ OpenStruct.new( start_time: 5000, timecode_in: "00:00:05:00", ), ] ), OpenStruct.new( uid: "3", requested_filename: "acquired_media_1", type: "acquired_media", audio_data: OpenStruct.new( filename: "acquired_media_1", composers: [], publishers: [], catalog: "", title: "", ), edl: [ OpenStruct.new( start_time: 1000, timecode_in: "00:00:01:00", ), ] ), OpenStruct.new( uid: nil, requested_filename: "library_music_1", type: "library_music", audio_data: OpenStruct.new( filename: "library_music_1", composers: [OpenStruct.new(name: "Beethoven", affiliation: "Affiliation", percentage: "100.0%")], publishers: [OpenStruct.new(name: "Jingle Punks", affiliation: "Affiliation", percentage: "100.0%")], catalog: "MIBE", title: "Library Music", ), edl: [ OpenStruct.new( start_time: 2000, timecode_in: "00:00:02:00", ), ] ), ] ) results = described_class.new( video, video_analysis, audio_analysis, [], [], ).build_chronological_audio_matches expect(results.count).to eq 4 expect(results.first).to have_attributes( elapsed_time_until_appearance: 5.0, filename: "original_music_1", appears_at_timecode: Timecode.new("00:00:05:00"), timecode_in: "00:00:05:00", confirmed: false, composer_info: "Beethoven, Affiliation, 100.0%", publisher_info: "Jingle Punks, Affiliation, 100.0%", catalog: "", title: "", confirmation_type: "original_music", edl_type: "all_tracks", ) expect(results.second).to have_attributes( elapsed_time_until_appearance: 2.0, filename: "library_music_1", appears_at_timecode: Timecode.new("00:00:02:00"), timecode_in: "00:00:02:00", confirmed: false, composer_info: "Beethoven, Affiliation, 100.0%", publisher_info: "Jingle Punks, Affiliation, 100.0%", catalog: "MIBE", title: "Library Music", confirmation_type: "library_music", edl_type: "all_tracks", ) expect(results.third).to have_attributes( elapsed_time_until_appearance: 1.0, filename: "acquired_media_1", appears_at_timecode: Timecode.new("00:00:01:00"), timecode_in: "00:00:01:00", confirmed: false, composer_info: "", publisher_info: "", catalog: "", title: "", confirmation_type: "acquired_media", edl_type: "all_tracks", ) expect(results.fourth).to have_attributes( elapsed_time_until_appearance: 0.0, filename: "original_music_2", appears_at_timecode: Timecode.new("00:00:00:00"), timecode_in: "00:00:00:00", confirmed: true, composer_info: "Beethoven, Affiliation, 100.0%", publisher_info: "Jingle Punks, Affiliation, 100.0%", catalog: "", title: "", confirmation_type: "original_music", edl_type: "all_tracks", ) end context "when audio_only_edl attached" do it "returns audio_matches with edl_type " do video = create(:video, :with_audio_only_edl_file, project: project, audio_analysis_status: :success) audio_analysis = OpenStruct.new( results: [ OpenStruct.new( uid: "2", requested_filename: "source_file_name", type: "acquired_media", audio_data: OpenStruct.new( filename: "source_file_name", composers: [], publishers: [], catalog: "", title: "", ), edl: [ OpenStruct.new( source_file_name: "source_file_name", start_time: 0, timecode_in: "00:00:00:00", ), ] ), ] ) results = described_class.new( video, video_analysis, audio_analysis, [], [], ).build_chronological_audio_matches expect(results.first).to have_attributes( edl_type: "audio", ) end end end context "when video analysis not success" do let(:video) { create(:video, project: project, audio_analysis_status: :pending) } it "returns empty array" do expect(described_class.new(video, video_analysis, audio_analysis, [], []).build_chronological_audio_matches).to eq [] end end end describe "#all_tracks_edl_events" do let(:video) { create(:video, project: project, analysis_status: :pending) } it "returns edl events sorted by start time ascending" do earlier_edl_event_response = build(:edl_event, channel: "V", start_time: 0, timecode_in: "00:00:00:00", timecode_out: "00:00:01:00", duration: 1, source_file_name: "my_file.mp4", clip_name: "my_clip.mp4", description: "This is a great clip", matches: [], ) later_edl_event_response = build(:edl_event, channel: "A1", start_time: 100, timecode_in: "00:00:10:00", timecode_out: "00:00:11:00", duration: 1, source_file_name: "another_file.mp4", clip_name: "another_clip.mp4", description: "This is a great clip", matches: [], ) results = described_class.new( video, video_analysis, audio_analysis, [earlier_edl_event_response, later_edl_event_response], [], ).all_tracks_edl_events expect(results).to eq [ EdlEvent.new( channel: "V", start_time: 0, timecode_in: "00:00:00:00", timecode_out: "00:00:01:00", duration: 1, source_file_name: "my_file.mp4", clip_name: "my_clip.mp4", description: "This is a great clip", matches: [], ), EdlEvent.new( channel: "A1", start_time: 100, timecode_in: "00:00:10:00", timecode_out: "00:00:11:00", duration: 1, source_file_name: "another_file.mp4", clip_name: "another_clip.mp4", description: "This is a great clip", matches: [], ), ] end end end