From 7c608116a47833a112bbf12d75a161b2f0c110e3 Mon Sep 17 00:00:00 2001 From: Vladyslav Anokhin Date: Thu, 31 Jan 2019 19:21:12 +0200 Subject: [PATCH] Ad engine request detector --- PlayerCore | 2 +- .../project.pbxproj | 20 +++++ sources/advertisements/AdMetrics.swift | 10 +++ .../vrm/AdEngineRequestDetector.swift | 47 +++++++++++ .../vrm/AdEngineRequestDetectorTest.swift | 83 +++++++++++++++++++ .../detectors/vrm/VRMRequestDetector.swift | 28 +++++++ .../vrm/VRMRequestDetectorTest.swift | 30 +++++++ .../TrackingPixelsConnector.swift | 3 + .../TrackingPixelsConnector_Ad.swift | 19 +++++ 9 files changed, 241 insertions(+), 1 deletion(-) create mode 100644 sources/metrics/detectors/vrm/AdEngineRequestDetector.swift create mode 100644 sources/metrics/detectors/vrm/AdEngineRequestDetectorTest.swift create mode 100644 sources/metrics/detectors/vrm/VRMRequestDetector.swift create mode 100644 sources/metrics/detectors/vrm/VRMRequestDetectorTest.swift diff --git a/PlayerCore b/PlayerCore index 96fda65..b615075 160000 --- a/PlayerCore +++ b/PlayerCore @@ -1 +1 @@ -Subproject commit 96fda65c64819b8ecfd4c87011c9fba56c928920 +Subproject commit b615075873dc1699ced4d5f4a974e53bc0441297 diff --git a/VerizonVideoPartnerSDK.xcodeproj/project.pbxproj b/VerizonVideoPartnerSDK.xcodeproj/project.pbxproj index 0227505..d83b34c 100644 --- a/VerizonVideoPartnerSDK.xcodeproj/project.pbxproj +++ b/VerizonVideoPartnerSDK.xcodeproj/project.pbxproj @@ -293,6 +293,9 @@ E241041C21C4078E00036290 /* StartVRMGroupProcessingControllerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = E241041B21C4078E00036290 /* StartVRMGroupProcessingControllerTest.swift */; }; E257A9F521C927FF0016D35A /* VRMItemController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E257A9F421C927FF0016D35A /* VRMItemController.swift */; }; E257A9FB21C93F4A0016D35A /* VRMItemControllerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = E257A9FA21C93F4A0016D35A /* VRMItemControllerTest.swift */; }; + E2604391220379740034FC48 /* VRMRequestDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2604390220379740034FC48 /* VRMRequestDetector.swift */; }; + E2604392220379740034FC48 /* VRMRequestDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2604390220379740034FC48 /* VRMRequestDetector.swift */; }; + E2604397220379F10034FC48 /* VRMRequestDetectorTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2604396220379F10034FC48 /* VRMRequestDetectorTest.swift */; }; E2629D07211228FA006ED117 /* TelemetryMetricsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2629D06211228FA006ED117 /* TelemetryMetricsTest.swift */; }; E2629D3C21147ECE006ED117 /* NoVpaidUrlFixture.json in Resources */ = {isa = PBXBuildFile; fileRef = E2629D3521147ECE006ED117 /* NoVpaidUrlFixture.json */; }; E287B08C21F884A900FC49AA /* VRMSelectFinalResultController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E287B08B21F884A900FC49AA /* VRMSelectFinalResultController.swift */; }; @@ -306,6 +309,9 @@ E2D649A8208A431D00152ADE /* VideoImpressionTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2D649A7208A431D00152ADE /* VideoImpressionTest.swift */; }; E2D64A5C2090E8F600152ADE /* VideoImpression.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2E4F7032089020E00182CDF /* VideoImpression.swift */; }; E2E4F7042089020E00182CDF /* VideoImpression.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2E4F7032089020E00182CDF /* VideoImpression.swift */; }; + E2FD5AEC2203413E007EBE86 /* AdEngineRequestDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FD5AEB2203413E007EBE86 /* AdEngineRequestDetector.swift */; }; + E2FD5AED2203413E007EBE86 /* AdEngineRequestDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FD5AEB2203413E007EBE86 /* AdEngineRequestDetector.swift */; }; + E2FD5AEF2203414D007EBE86 /* AdEngineRequestDetectorTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FD5AEE2203414D007EBE86 /* AdEngineRequestDetectorTest.swift */; }; E2FE09552200AC14005E91C0 /* MaxAdSearchTimeController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE09542200AC14005E91C0 /* MaxAdSearchTimeController.swift */; }; E2FE09562200AC14005E91C0 /* MaxAdSearchTimeController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE09542200AC14005E91C0 /* MaxAdSearchTimeController.swift */; }; E2FE095B2200AD01005E91C0 /* MaxAdSearchTimeControllerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE095A2200AD01005E91C0 /* MaxAdSearchTimeControllerTest.swift */; }; @@ -647,6 +653,8 @@ E241041B21C4078E00036290 /* StartVRMGroupProcessingControllerTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartVRMGroupProcessingControllerTest.swift; sourceTree = ""; }; E257A9F421C927FF0016D35A /* VRMItemController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VRMItemController.swift; sourceTree = ""; }; E257A9FA21C93F4A0016D35A /* VRMItemControllerTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VRMItemControllerTest.swift; sourceTree = ""; }; + E2604390220379740034FC48 /* VRMRequestDetector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VRMRequestDetector.swift; sourceTree = ""; }; + E2604396220379F10034FC48 /* VRMRequestDetectorTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VRMRequestDetectorTest.swift; sourceTree = ""; }; E2629D06211228FA006ED117 /* TelemetryMetricsTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = TelemetryMetricsTest.swift; path = sources/telemetry/TelemetryMetricsTest.swift; sourceTree = ""; }; E2629D3521147ECE006ED117 /* NoVpaidUrlFixture.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = NoVpaidUrlFixture.json; sourceTree = ""; }; E287B08B21F884A900FC49AA /* VRMSelectFinalResultController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VRMSelectFinalResultController.swift; sourceTree = ""; }; @@ -657,6 +665,8 @@ E2B0062521CD49EB00B72485 /* FetchVRMItemControllerTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FetchVRMItemControllerTest.swift; sourceTree = ""; }; E2D649A7208A431D00152ADE /* VideoImpressionTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoImpressionTest.swift; sourceTree = ""; }; E2E4F7032089020E00182CDF /* VideoImpression.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoImpression.swift; sourceTree = ""; }; + E2FD5AEB2203413E007EBE86 /* AdEngineRequestDetector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdEngineRequestDetector.swift; sourceTree = ""; }; + E2FD5AEE2203414D007EBE86 /* AdEngineRequestDetectorTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdEngineRequestDetectorTest.swift; sourceTree = ""; }; E2FE09542200AC14005E91C0 /* MaxAdSearchTimeController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaxAdSearchTimeController.swift; sourceTree = ""; }; E2FE095A2200AD01005E91C0 /* MaxAdSearchTimeControllerTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaxAdSearchTimeControllerTest.swift; sourceTree = ""; }; FE9409FF1F3DD56600A867CD /* IntentDetectorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IntentDetectorTests.swift; sourceTree = ""; }; @@ -980,6 +990,10 @@ children = ( 50C99D512010DAE90041B013 /* VRMDetector.swift */, 508A597D2010F97B00641398 /* VRMDetectorTests.swift */, + E2FD5AEB2203413E007EBE86 /* AdEngineRequestDetector.swift */, + E2FD5AEE2203414D007EBE86 /* AdEngineRequestDetectorTest.swift */, + E2604390220379740034FC48 /* VRMRequestDetector.swift */, + E2604396220379F10034FC48 /* VRMRequestDetectorTest.swift */, ); path = vrm; sourceTree = ""; @@ -1820,6 +1834,7 @@ 50A11DCE1D59D7E000F4A068 /* ContextStartedDetector.swift in Sources */, E241041A21C4075300036290 /* StartVRMGroupProcessingController.swift in Sources */, 50A11DD11D59D7E000F4A068 /* ExecuteOnce.swift in Sources */, + E2FD5AED2203413E007EBE86 /* AdEngineRequestDetector.swift in Sources */, 50A11DD31D59D7E000F4A068 /* VideoActionsDetector.swift in Sources */, 50A11DD41D59D7E000F4A068 /* XMLParser.swift in Sources */, E287B0BB21FA097100FC49AA /* VPAIDAdCreativeController.swift in Sources */, @@ -1829,6 +1844,7 @@ 50A11DD81D59D7E000F4A068 /* PlaylistStatisticDetector.swift in Sources */, 50A5A5E71E79726E00C15E12 /* 3secPlaybackDetector.swift in Sources */, 50A11DDC1D59D7E000F4A068 /* VideoProviderParsing.swift in Sources */, + E2604392220379740034FC48 /* VRMRequestDetector.swift in Sources */, 50A11DDD1D59D7E000F4A068 /* Dictionary+ParseJSON.swift in Sources */, E241041721C3FB3A00036290 /* VRMRequestController.swift in Sources */, E23168AC212C4C9F00F5AD5B /* MaxShowTimeController.swift in Sources */, @@ -1880,6 +1896,7 @@ E22B6A8521D14D1F00D480A0 /* ParseVRMItemController.swift in Sources */, 504CA7581C874A5D006D0ADF /* Player_VideoEvents.swift in Sources */, DEB112361E30F0CE007E65BD /* Context.swift in Sources */, + E2604391220379740034FC48 /* VRMRequestDetector.swift in Sources */, 507053591DAE9700003C023A /* Memoise.swift in Sources */, 50D747751CE6235F00CB91D4 /* VRMRequest.swift in Sources */, DE4C51E11C80D43200BFFB0B /* TrackingPixelsConnector.swift in Sources */, @@ -1912,6 +1929,7 @@ DEAE91D81CAA830800C736D0 /* ApplyDecorator.swift in Sources */, 501AB1DE2028934A00BAFA9C /* AdErrorDetector.swift in Sources */, 50E5ADE91EE08A76004104D6 /* PlayerTracer.swift in Sources */, + E2FD5AEC2203413E007EBE86 /* AdEngineRequestDetector.swift in Sources */, 06419E2120EF7950007FE2F0 /* VPAIDProps.swift in Sources */, DE4C51C51C8094FB00BFFB0B /* TrackingPixelsReporter.swift in Sources */, 501AB1E42029C69C00BAFA9C /* AdPlaybackCycleDetector.swift in Sources */, @@ -1981,6 +1999,7 @@ 067209EC21B6BF440086CDBE /* BufferingDetectorTests.swift in Sources */, DE1FB2971CE48FAC00B99941 /* VRMProviderTests.swift in Sources */, 50C4602B1C80B791002AB8AB /* PlaylistStatisticDetectorTests.swift in Sources */, + E2604397220379F10034FC48 /* VRMRequestDetectorTest.swift in Sources */, 501AB1DB202892E800BAFA9C /* AdClickDetectorTests.swift in Sources */, FEF072CB1F59A3D5006A0BDC /* PlayerDestructionTests.swift in Sources */, E2FE095B2200AD01005E91C0 /* MaxAdSearchTimeControllerTest.swift in Sources */, @@ -1993,6 +2012,7 @@ 9A1469E621CE9549007315BA /* ActionComparator.swift in Sources */, DE1685E71CE76A5D00492A90 /* VASTParserTests.swift in Sources */, E241041621C3FAF400036290 /* VRMRequestControllerTest.swift in Sources */, + E2FD5AEF2203414D007EBE86 /* AdEngineRequestDetectorTest.swift in Sources */, E231AA7721340100008C5B6A /* AdMaxShowTimeDetectorTest.swift in Sources */, E2B0062821CD4A2B00B72485 /* FetchVRMItemControllerTest.swift in Sources */, E22B6A8B21D14D2D00D480A0 /* ParseVRMItemControllerTest.swift in Sources */, diff --git a/sources/advertisements/AdMetrics.swift b/sources/advertisements/AdMetrics.swift index 545fb04..ecdbca6 100644 --- a/sources/advertisements/AdMetrics.swift +++ b/sources/advertisements/AdMetrics.swift @@ -1,6 +1,7 @@ // Copyright 2018, Oath Inc. // Licensed under the terms of the MIT License. See LICENSE.md file in project root for terms. import Foundation +import PlayerCore enum Ad { } @@ -74,4 +75,13 @@ extension Ad.Metrics.Info { self.name = metaInfo.name self.cpm = metaInfo.cpm } + + init(metaInfo: VRMCore.Item.MetaInfo) { + self.engineType = metaInfo.engineType + self.ruleId = metaInfo.ruleId + self.ruleCompanyId = metaInfo.ruleCompanyId + self.vendor = metaInfo.vendor + self.name = metaInfo.name + self.cpm = metaInfo.cpm + } } diff --git a/sources/metrics/detectors/vrm/AdEngineRequestDetector.swift b/sources/metrics/detectors/vrm/AdEngineRequestDetector.swift new file mode 100644 index 0000000..c30a859 --- /dev/null +++ b/sources/metrics/detectors/vrm/AdEngineRequestDetector.swift @@ -0,0 +1,47 @@ +// Copyright 2019, Oath Inc. +// Licensed under the terms of the MIT License. See LICENSE.md file in project root for terms. + +import Foundation +import PlayerCore + +extension Detectors { + + final class AdEngineRequestDetector { + + struct Result { + let adInfo: Ad.Metrics.Info + let transactionId: String? + } + + private var processedCandidates = Set() + + func process(state: PlayerCore.State) -> [Result] { + return process(transactionId: state.vrmResponse?.transactionId, + scheduledItems: state.vrmScheduledItems.items) + } + + func process(transactionId: String?, + scheduledItems: [VRMCore.Item: Set]) -> [Result] { + guard scheduledItems.isEmpty == false else { return [] } + + struct NormalizedScheduledItem { + let item: VRMCore.Item + let candidate: ScheduledVRMItems.Candidate + } + + return scheduledItems + .filter { _, candidatesSet in + candidatesSet.isSubset(of: processedCandidates) == false + }.reduce(into: []) { result, pair in + return pair.value.forEach { candidate in + result.append(NormalizedScheduledItem(item: pair.key, candidate: candidate)) + } + }.compactMap { normalized in + processedCandidates.insert(normalized.candidate) + return Result(adInfo: .init(metaInfo: normalized.item.metaInfo), transactionId: transactionId) + } + } + } + +} + diff --git a/sources/metrics/detectors/vrm/AdEngineRequestDetectorTest.swift b/sources/metrics/detectors/vrm/AdEngineRequestDetectorTest.swift new file mode 100644 index 0000000..51c22d0 --- /dev/null +++ b/sources/metrics/detectors/vrm/AdEngineRequestDetectorTest.swift @@ -0,0 +1,83 @@ +// Copyright 2018, Oath Inc. +// Licensed under the terms of the MIT License. See LICENSE.md file in project root for terms. + +import XCTest +@testable import VerizonVideoPartnerSDK +@testable import PlayerCore + +class AdEngineRequestDetectorTest: XCTestCase { + + let firstCandidate = ScheduledVRMItems.Candidate(source: .vast("")) + let secondCandidate = ScheduledVRMItems.Candidate(source: .vast("")) + + var firstMetainfo: VRMCore.Item.MetaInfo! + var secondMetainfo: VRMCore.Item.MetaInfo! + var firstItem: VRMCore.Item! + var secondItem: VRMCore.Item! + var detector: Detectors.AdEngineRequestDetector! + + override func setUp() { + super.setUp() + detector = Detectors.AdEngineRequestDetector() + firstMetainfo = VRMCore.Item.MetaInfo(engineType: "engineType1", + ruleId: "ruleId1", + ruleCompanyId: "ruleCompanyId1", + vendor: "vendor1", + name: "name1", + cpm: "cpm1") + secondMetainfo = VRMCore.Item.MetaInfo(engineType: "engineType2", + ruleId: "ruleId2", + ruleCompanyId: "ruleCompanyId2", + vendor: "vendor2", + name: "name2", + cpm: "cpm2") + firstItem = VRMCore.Item(source: .vast(""), metaInfo: firstMetainfo) + secondItem = VRMCore.Item(source: .vast(""), metaInfo: secondMetainfo) + } + + func testEmptyDetect() { + let result = detector.process(transactionId: "", scheduledItems: [:]) + XCTAssertTrue(result.isEmpty) + } + + func testCorrectDetection() { + let queue: [VRMCore.Item: Set] = [firstItem: Set([firstCandidate])] + var result = detector.process(transactionId: "", scheduledItems: queue) + + XCTAssertEqual(result.count, 1) + XCTAssertEqual(result[0].adInfo.engineType, firstMetainfo.engineType) + XCTAssertEqual(result[0].adInfo.ruleId, firstMetainfo.ruleId) + XCTAssertEqual(result[0].adInfo.ruleCompanyId, firstMetainfo.ruleCompanyId) + XCTAssertEqual(result[0].adInfo.vendor, firstMetainfo.vendor) + XCTAssertEqual(result[0].adInfo.name, firstMetainfo.name) + XCTAssertEqual(result[0].adInfo.cpm, firstMetainfo.cpm) + } + + func testDoubleDetectSameCandidate() { + let queue: [VRMCore.Item: Set] = [firstItem: Set([firstCandidate])] + var result = detector.process(transactionId: "", scheduledItems: queue) + XCTAssertEqual(result.count, 1) + + result = detector.process(transactionId: "", scheduledItems: queue) + XCTAssertTrue(result.isEmpty) + } + + func testDetectingTwoCandidates() { + let queue: [VRMCore.Item: Set] = [firstItem: Set([firstCandidate, secondCandidate])] + var result = detector.process(transactionId: "", scheduledItems: queue) + XCTAssertEqual(result.count, 2) + + result = detector.process(transactionId: "", scheduledItems: queue) + XCTAssertTrue(result.isEmpty) + } + + func testDetectingTwoItems() { + let queue: [VRMCore.Item: Set] = [firstItem: Set([firstCandidate]), + secondItem: Set([secondCandidate])] + var result = detector.process(transactionId: "", scheduledItems: queue) + XCTAssertEqual(result.count, 2) + + result = detector.process(transactionId: "", scheduledItems: queue) + XCTAssertTrue(result.isEmpty) + } +} diff --git a/sources/metrics/detectors/vrm/VRMRequestDetector.swift b/sources/metrics/detectors/vrm/VRMRequestDetector.swift new file mode 100644 index 0000000..ea915a7 --- /dev/null +++ b/sources/metrics/detectors/vrm/VRMRequestDetector.swift @@ -0,0 +1,28 @@ +// Copyright 2019, Oath Inc. +// Licensed under the terms of the MIT License. See LICENSE.md file in project root for terms. + +import Foundation +import PlayerCore + +extension Detectors { + final class VRMRequestDetector { + struct Result { + let transactionId: String? + } + + private var trackedRequests = Set() + + func process(with state: PlayerCore.State) -> Result? { + return process(with: state.vrmRequestStatus.request?.id, + transactionId: state.vrmResponse?.transactionId) + } + + func process(with requestId: UUID?, transactionId: String?) -> Result? { + guard let requestId = requestId, + trackedRequests.contains(requestId) == false else { return nil } + + trackedRequests.insert(requestId) + return Result(transactionId: transactionId) + } + } +} diff --git a/sources/metrics/detectors/vrm/VRMRequestDetectorTest.swift b/sources/metrics/detectors/vrm/VRMRequestDetectorTest.swift new file mode 100644 index 0000000..0a51f5f --- /dev/null +++ b/sources/metrics/detectors/vrm/VRMRequestDetectorTest.swift @@ -0,0 +1,30 @@ +// Copyright 2018, Oath Inc. +// Licensed under the terms of the MIT License. See LICENSE.md file in project root for terms. + +import XCTest +@testable import VerizonVideoPartnerSDK +@testable import PlayerCore + +class VRMRequestDetectorTest: XCTestCase { + + func testDetectSameRequest() { + let uuid = UUID() + let sut = Detectors.VRMRequestDetector() + + var result = sut.process(with: uuid, transactionId: "id") + XCTAssertEqual(result?.transactionId, "id") + + result = sut.process(with: uuid, transactionId: "id") + XCTAssertNil(result) + } + + func testDetectDiffRequests() { + let sut = Detectors.VRMRequestDetector() + + var result = sut.process(with: UUID(), transactionId: "id1") + XCTAssertEqual(result?.transactionId, "id1") + + result = sut.process(with: UUID(), transactionId: "id2") + XCTAssertEqual(result?.transactionId, "id2") + } +} diff --git a/sources/metrics/tracking pixels/TrackingPixelsConnector.swift b/sources/metrics/tracking pixels/TrackingPixelsConnector.swift index e778562..7910688 100644 --- a/sources/metrics/tracking pixels/TrackingPixelsConnector.swift +++ b/sources/metrics/tracking pixels/TrackingPixelsConnector.swift @@ -20,6 +20,9 @@ extension TrackingPixels { let videoImpressionDetector = Detectors.VideoImpression() let vrmDetector = Detectors.VRMDetector() + let adRequestDetector = Detectors.VRMRequestDetector() + let adEngineRequestDetector = Detectors.AdEngineRequestDetector() + let adVideoLoadingDetector = Detectors.VideoLoading() let adQuartileDetector = Detectors.Quartile() let adUserActionsDetector = Detectors.UserActions() diff --git a/sources/metrics/tracking pixels/TrackingPixelsConnector_Ad.swift b/sources/metrics/tracking pixels/TrackingPixelsConnector_Ad.swift index 7d8891a..15ac17b 100644 --- a/sources/metrics/tracking pixels/TrackingPixelsConnector_Ad.swift +++ b/sources/metrics/tracking pixels/TrackingPixelsConnector_Ad.swift @@ -26,6 +26,25 @@ extension TrackingPixels.Connector { } } + adRequestDetector.process(with: state).flatMap { result in + reporter.adVRMRequest(videoIndex: state.playlist.currentIndex, + type: adType, + sequenceNumber: state.vrmRequestStatus.requestsFired, + transactionId: result.transactionId, + videoViewUID: state.playbackSession.id.uuidString) + } + + adEngineRequestDetector.process(state: state).forEach { result in + reporter.adEngineRequest(videoIndex: state.playlist.currentIndex, + info: result.adInfo, + type: adType, + transactionId: result.transactionId, + videoViewUID: state.playbackSession.id.uuidString) + reporter.adServerRequest(info: result.adInfo, + videoIndex: state.playlist.currentIndex, + videoViewUID: state.playbackSession.id.uuidString) + } + vrmDetector.process(state: state.adVRMManager).forEach { result in switch result { case .completeRequest(let complete):