diff --git a/Demo/Sources/Players/PlaybackView.swift b/Demo/Sources/Players/PlaybackView.swift index fd2803366..ef6d52987 100644 --- a/Demo/Sources/Players/PlaybackView.swift +++ b/Demo/Sources/Players/PlaybackView.swift @@ -571,14 +571,28 @@ private struct TimeSlider: View { return formatter }() + private static let timeFormatter: DateFormatter = { + let formatter = DateFormatter() + formatter.dateStyle = .none + formatter.timeStyle = .medium + return formatter + }() + @ObservedObject var player: Player @ObservedObject var progressTracker: ProgressTracker @ObservedObject var visibilityTracker: VisibilityTracker @State private var streamType: StreamType = .unknown private var formattedElapsedTime: String? { - guard streamType == .onDemand else { return nil } - return Self.formattedTime((progressTracker.time - progressTracker.timeRange.start), duration: progressTracker.timeRange.duration) + if streamType == .onDemand { + return Self.formattedTime((progressTracker.time - progressTracker.timeRange.start), duration: progressTracker.timeRange.duration) + } + else if let date = progressTracker.date() { + return Self.timeFormatter.string(from: date) + } + else { + return nil + } } private var formattedTotalTime: String? { diff --git a/README.md b/README.md index e2036b1ef..c55cb88fa 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ Pillarbox player provides all essential playback features you might expect: - Best-in-class Picture in Picture support. - The smoothest possible seek experience on Apple devices, with blazing-fast content navigation in streams enabled for trick play. - Playback speed controls. +- Looping playback. - Monitoring with Pillarbox Quality of Experience (QoE) and Quality of Service (QoS) platform. In addition Pillarbox provides the ability to play all SRG SSR content through a dedicated package. diff --git a/Sources/Player/Player+Navigation.swift b/Sources/Player/Player+Navigation.swift index 47700204d..e08c2c2cd 100644 --- a/Sources/Player/Player+Navigation.swift +++ b/Sources/Player/Player+Navigation.swift @@ -76,7 +76,8 @@ extension Player { private extension Player { func isAwayFromStartTime(withInterval interval: TimeInterval) -> Bool { - time.isValid && seekableTimeRange.isValid && (time - seekableTimeRange.start).seconds >= interval + let time = time() + return time.isValid && seekableTimeRange.isValid && (time - seekableTimeRange.start).seconds >= interval } func shouldSeekToStartTime() -> Bool { diff --git a/Sources/Player/Player+Seek.swift b/Sources/Player/Player+Seek.swift index 1246dab43..90fc7eca5 100644 --- a/Sources/Player/Player+Seek.swift +++ b/Sources/Player/Player+Seek.swift @@ -79,17 +79,28 @@ public extension Player { /// Performs an optimal seek to a given time, providing the best possible interactive user experience in all cases. /// - /// For the best result the player should be paused during the whole interaction. - /// /// - Parameters: /// - time: The time to reach. /// - completion: A completion called when seeking ends. The provided Boolean informs whether the seek could /// finish without being cancelled. + /// + /// If a user interaction is causing this seek method to be called several times in a row, the player should be paused + /// during the interaction to achieve the best possible result. func seek(to time: CMTime, completion: @escaping (Bool) -> Void = { _ in }) { let position = Self.optimalPosition(reaching: time, for: queuePlayer.currentItem) seek(position, smooth: true, completion: completion) } + /// Requests that the player seek to a specified date, and to notify you when the seek is complete. + /// + /// - Parameters: + /// - date: The date to reach. + /// - completion: A completion called when seeking ends. The provided Boolean informs whether the seek could + /// finish without being cancelled. + func seek(to date: Date, completion: @escaping (Bool) -> Void = { _ in }) { + queuePlayer.seek(to: date, completionHandler: completion) + } + /// Performs a precise seek to a chapter. /// /// - Parameters: diff --git a/Sources/Player/Player+Skip.swift b/Sources/Player/Player+Skip.swift index 2fb9f059b..722918efc 100644 --- a/Sources/Player/Player+Skip.swift +++ b/Sources/Player/Player+Skip.swift @@ -20,7 +20,7 @@ public extension Player { func canSkipForward() -> Bool { guard seekableTimeRange.isValidAndNotEmpty else { return false } if duration.isIndefinite { - let currentTime = queuePlayer.targetSeekTime ?? time + let currentTime = queuePlayer.targetSeekTime ?? time() return canSeek(to: currentTime + forwardSkipTime) } else { @@ -64,7 +64,7 @@ private extension Player { ) { assert(interval != .zero) let endTolerance = CMTime(value: 1, timescale: 1) - let currentTime = queuePlayer.targetSeekTime ?? time + let currentTime = queuePlayer.targetSeekTime ?? time() if interval < .zero || currentTime < seekableTimeRange.end - endTolerance { seek( to(currentTime + interval, toleranceBefore: toleranceBefore, toleranceAfter: toleranceAfter), diff --git a/Sources/Player/Player+SkipToDefault.swift b/Sources/Player/Player+SkipToDefault.swift index 4f73de4a9..63eb8d319 100644 --- a/Sources/Player/Player+SkipToDefault.swift +++ b/Sources/Player/Player+SkipToDefault.swift @@ -18,7 +18,7 @@ public extension Player { case .onDemand, .live: return true case .dvr where chunkDuration.isValid: - return time < seekableTimeRange.end - chunkDuration + return time() < seekableTimeRange.end - chunkDuration default: return false } diff --git a/Sources/Player/Player.docc/Articles/metrics/metrics-article.md b/Sources/Player/Player.docc/Articles/metrics/metrics-article.md index deca2c3ed..e3934ba40 100644 --- a/Sources/Player/Player.docc/Articles/metrics/metrics-article.md +++ b/Sources/Player/Player.docc/Articles/metrics/metrics-article.md @@ -21,7 +21,11 @@ To better understand how your playback experience is perceived by your users, yo Pillarbox ``Player`` provides extensive metrics which can help you provide answer to these questions. -## Obtain metrics periodically +### Receive metrics when needed + +You can receive current metrics at any time by calling the ``Player/metrics()`` player method. The same metrics are also available from ``PlayerProperties``. + +## Receive metrics periodically Subscribe to ``Player/periodicMetricsPublisher(forInterval:queue:limit:)`` to receive a stream of ``Metrics`` related to the item currently being played. diff --git a/Sources/Player/Player.docc/Extensions/player-extension.md b/Sources/Player/Player.docc/Extensions/player-extension.md index 6af8b02c6..b395d5921 100644 --- a/Sources/Player/Player.docc/Extensions/player-extension.md +++ b/Sources/Player/Player.docc/Extensions/player-extension.md @@ -33,8 +33,12 @@ - ``selectedMediaOption(for:)`` - ``setMediaSelection(preferredLanguages:for:)`` -#### Style +#### Behavior +- ``actionAtItemEnd`` +- ``configuration`` +- ``repeatMode`` +- ``shouldPlay`` - ``textStyleRules`` ### Navigation @@ -73,9 +77,14 @@ - ``propertiesPublisher`` - ``rate`` - ``systemPlayer`` -- ``time`` - ``version`` +### Current State + +- ``date()`` +- ``metrics()`` +- ``time()`` + ### Replay - ``canReplay()`` @@ -87,18 +96,13 @@ - ``seek(_:smooth:completion:)`` - ``seek(to:completion:)-9bknb`` - ``seek(to:completion:)-2ypz8`` +- ``seek(to:completion:)-1tbeq`` - ``after(_:)`` - ``at(_:)`` - ``before(_:)`` - ``near(_:)`` - ``to(_:toleranceBefore:toleranceAfter:)`` -### Setup - -- ``becomeActive()`` -- ``configuration`` -- ``resignActive()`` - ### Skip - ``canSkipBackward()`` @@ -118,6 +122,11 @@ - ``canReturnToPrevious()`` - ``returnToPrevious()`` +### System Integration + +- ``becomeActive()`` +- ``resignActive()`` + ### Time Publisher - ``boundaryTimePublisher(for:queue:)`` @@ -128,6 +137,11 @@ - ``isTrackingEnabled`` - ``currentSessionIdentifiers(trackedBy:)`` +### Metrics + +- ``metricEventsPublisher`` +- ``periodicMetricsPublisher(forInterval:queue:limit:)`` + ### User Interface - ``mediaSelectionMenu(characteristic:)`` diff --git a/Sources/Player/Player.swift b/Sources/Player/Player.swift index e8ab2fdca..f6f49eda8 100644 --- a/Sources/Player/Player.swift +++ b/Sources/Player/Player.swift @@ -140,13 +140,6 @@ public final class Player: ObservableObject, Equatable { } } - /// The current time. - /// - /// Returns `.invalid` when the time is unknown. - public var time: CMTime { - queuePlayer.currentTime().clamped(to: seekableTimeRange) - } - /// The low-level system player. /// /// Exposed for specific read-only needs like interfacing with `AVPlayer`-based 3rd party APIs. Mutating the state @@ -287,6 +280,33 @@ public final class Player: ObservableObject, Equatable { } } +public extension Player { + /// The current time. + /// + /// Returns `.invalid` when the time is unknown. + func time() -> CMTime { + properties.time() + } + + /// The current date. + /// + /// The date is `nil` when no date information is available from the stream. + func date() -> Date? { + properties.date() + } + + /// The current player metrics, if available. + /// + /// Each call to this function might return different results reflecting the most recent metrics available. The + /// included ``Metrics/increment`` collates data from the entire playback session and is therefore always equal + /// to ``Metrics/total``. + /// + /// > Important: Metrics are reset when toggling external playback. + func metrics() -> Metrics? { + properties.metrics() + } +} + private extension Player { func configurePublishedPropertyPublishers() { configurePropertiesPublisher() diff --git a/Sources/Player/Types/PlayerProperties.swift b/Sources/Player/Types/PlayerProperties.swift index 25d094138..803b9488d 100644 --- a/Sources/Player/Types/PlayerProperties.swift +++ b/Sources/Player/Types/PlayerProperties.swift @@ -117,14 +117,14 @@ public extension PlayerProperties { } public extension PlayerProperties { - /// The player time. + /// The current time. func time() -> CMTime { coreProperties.time() } - /// The date corresponding to the player time. + /// The current date. /// - /// This date is only returned when available from the stream. + /// The date is `nil` when no date information is available from the stream. func date() -> Date? { coreProperties.date() } diff --git a/Sources/Player/UserInterface/ProgressTracker.swift b/Sources/Player/UserInterface/ProgressTracker.swift index 9d9cb6440..4e1e10398 100644 --- a/Sources/Player/UserInterface/ProgressTracker.swift +++ b/Sources/Player/UserInterface/ProgressTracker.swift @@ -139,7 +139,7 @@ public final class ProgressTracker: ObservableObject { .map { time, seekableTimeRange in Self.progress(for: time, in: seekableTimeRange) } - .prepend(Self.progress(for: player.time, in: player.seekableTimeRange)) + .prepend(Self.progress(for: player.time(), in: player.seekableTimeRange)) .eraseToAnyPublisher() } .switchToLatest() @@ -177,6 +177,14 @@ public final class ProgressTracker: ObservableObject { return timeRange.start + CMTimeMultiplyByFloat64(timeRange.duration, multiplier: Float64(progress)) } + /// The date corresponding to the current progress. + /// + /// The date is `nil` when no date information is available from the stream. + public func date() -> Date? { + guard let player, let playerDate = player.date() else { return nil } + return playerDate.addingTimeInterval((time - player.time()).seconds) + } + private func seek(to progress: Float, optimal: Bool) { guard let player else { return } let time = Self.time(forProgress: progress, in: timeRange) diff --git a/Tests/AnalyticsTests/ComScore/ComScoreTrackerDvrPropertiesTests.swift b/Tests/AnalyticsTests/ComScore/ComScoreTrackerDvrPropertiesTests.swift index c4e3b0f80..b391dd75f 100644 --- a/Tests/AnalyticsTests/ComScore/ComScoreTrackerDvrPropertiesTests.swift +++ b/Tests/AnalyticsTests/ComScore/ComScoreTrackerDvrPropertiesTests.swift @@ -87,7 +87,7 @@ final class ComScoreTrackerDvrPropertiesTests: ComScoreTestCase { expect(labels.ns_st_ldw).to(equal(Stream.dvr.duration.seconds)) } ) { - player.seek(at(player.time - CMTime(value: 10, timescale: 1))) + player.seek(at(player.time() - CMTime(value: 10, timescale: 1))) } } } diff --git a/Tests/AnalyticsTests/ComScore/ComScoreTrackerTests.swift b/Tests/AnalyticsTests/ComScore/ComScoreTrackerTests.swift index 49c6979d0..a90827608 100644 --- a/Tests/AnalyticsTests/ComScore/ComScoreTrackerTests.swift +++ b/Tests/AnalyticsTests/ComScore/ComScoreTrackerTests.swift @@ -75,7 +75,7 @@ final class ComScoreTrackerTests: ComScoreTestCase { )) player.play() - expect(player.time.seconds).toEventually(beGreaterThan(1)) + expect(player.time().seconds).toEventually(beGreaterThan(1)) expectAtLeastHits( pause { labels in @@ -120,7 +120,7 @@ final class ComScoreTrackerTests: ComScoreTestCase { // See 2. at the top of this file. player?.play() // See 1. at the top of this file. - expect(player?.time.seconds).toEventually(beGreaterThan(5)) + expect(player?.time().seconds).toEventually(beGreaterThan(5)) player = nil } } @@ -148,7 +148,7 @@ final class ComScoreTrackerTests: ComScoreTestCase { // See 2. at the top of this file. player.play() // See 1. at the top of this file. - expect(player.time.seconds).toEventually(beGreaterThan(5)) + expect(player.time().seconds).toEventually(beGreaterThan(5)) player.isTrackingEnabled = false } } diff --git a/Tests/AnalyticsTests/CommandersAct/CommandersActTrackerDvrPropertiesTests.swift b/Tests/AnalyticsTests/CommandersAct/CommandersActTrackerDvrPropertiesTests.swift index 797def137..529456ec0 100644 --- a/Tests/AnalyticsTests/CommandersAct/CommandersActTrackerDvrPropertiesTests.swift +++ b/Tests/AnalyticsTests/CommandersAct/CommandersActTrackerDvrPropertiesTests.swift @@ -83,7 +83,7 @@ final class CommandersActTrackerDvrPropertiesTests: CommandersActTestCase { expect(labels.media_timeshift).to(beCloseTo(4, within: 2)) } ) { - player.seek(at(player.time - CMTime(value: 4, timescale: 1))) + player.seek(at(player.time() - CMTime(value: 4, timescale: 1))) } } diff --git a/Tests/AnalyticsTests/CommandersAct/CommandersActTrackerTests.swift b/Tests/AnalyticsTests/CommandersAct/CommandersActTrackerTests.swift index 11bf00ec7..b5e47067f 100644 --- a/Tests/AnalyticsTests/CommandersAct/CommandersActTrackerTests.swift +++ b/Tests/AnalyticsTests/CommandersAct/CommandersActTrackerTests.swift @@ -48,7 +48,7 @@ final class CommandersActTrackerTests: CommandersActTestCase { )) player.play() - expect(player.time.seconds).toEventually(beGreaterThan(1)) + expect(player.time().seconds).toEventually(beGreaterThan(1)) expectAtLeastHits( pause { labels in @@ -85,7 +85,7 @@ final class CommandersActTrackerTests: CommandersActTestCase { )) player?.play() - expect(player?.time.seconds).toEventually(beGreaterThan(5)) + expect(player?.time().seconds).toEventually(beGreaterThan(5)) expectAtLeastHits( stop { labels in @@ -106,7 +106,7 @@ final class CommandersActTrackerTests: CommandersActTestCase { player?.setDesiredPlaybackSpeed(2) player?.play() - expect(player?.time.seconds).toEventually(beGreaterThan(2)) + expect(player?.time().seconds).toEventually(beGreaterThan(2)) expectAtLeastHits( stop { labels in @@ -155,7 +155,7 @@ final class CommandersActTrackerTests: CommandersActTestCase { )) player.play() - expect(player.time.seconds).toEventually(beGreaterThan(5)) + expect(player.time().seconds).toEventually(beGreaterThan(5)) expectAtLeastHits(stop()) { player.isTrackingEnabled = false diff --git a/Tests/PlayerTests/Player/BlockedTimeRangeTests.swift b/Tests/PlayerTests/Player/BlockedTimeRangeTests.swift index dde7d28f9..460410b72 100644 --- a/Tests/PlayerTests/Player/BlockedTimeRangeTests.swift +++ b/Tests/PlayerTests/Player/BlockedTimeRangeTests.swift @@ -45,36 +45,36 @@ final class BlockedTimeRangeTests: TestCase { let player = Player(item: .simple(url: Stream.onDemand.url, metadata: MetadataWithBlockedTimeRange())) expect(player.streamType).toEventually(equal(.onDemand)) player.seek(at(.init(value: 30, timescale: 1))) - expect(kBlockedTimeRange.containsTime(player.time)).toNever(beTrue(), until: .seconds(2)) - expect(player.time).to(equal(kBlockedTimeRange.end)) + expect(kBlockedTimeRange.containsTime(player.time())).toNever(beTrue(), until: .seconds(2)) + expect(player.time()).to(equal(kBlockedTimeRange.end)) } func testSeekInOverlappingBlockedTimeRange() { let player = Player(item: .simple(url: Stream.onDemand.url, metadata: MetadataWithOverlappingBlockedTimeRanges())) expect(player.streamType).toEventually(equal(.onDemand)) player.seek(at(.init(value: 30, timescale: 1))) - expect(kOverlappingBlockedTimeRange.containsTime(player.time)).toNever(beTrue(), until: .seconds(2)) - expect(player.time).to(equal(kOverlappingBlockedTimeRange.end)) + expect(kOverlappingBlockedTimeRange.containsTime(player.time())).toNever(beTrue(), until: .seconds(2)) + expect(player.time()).to(equal(kOverlappingBlockedTimeRange.end)) } func testSeekInNestedBlockedTimeRange() { let player = Player(item: .simple(url: Stream.onDemand.url, metadata: MetadataWithNestedBlockedTimeRanges())) expect(player.streamType).toEventually(equal(.onDemand)) player.seek(at(.init(value: 40, timescale: 1))) - expect(kNestedBlockedTimeRange.containsTime(player.time)).toNever(beTrue(), until: .seconds(2)) - expect(player.time).to(equal(kBlockedTimeRange.end)) + expect(kNestedBlockedTimeRange.containsTime(player.time())).toNever(beTrue(), until: .seconds(2)) + expect(player.time()).to(equal(kBlockedTimeRange.end)) } func testBlockedTimeRangeTraversal() { let configuration = PlayerItemConfiguration(position: at(.init(value: 29, timescale: 1))) let player = Player(item: .simple(url: Stream.onDemand.url, metadata: MetadataWithBlockedTimeRange(), configuration: configuration)) player.play() - expect(player.time).toEventually(beGreaterThan(kBlockedTimeRange.end)) + expect(player.time()).toEventually(beGreaterThan(kBlockedTimeRange.end)) } func testOnDemandStartInBlockedTimeRange() { let configuration = PlayerItemConfiguration(position: at(.init(value: 30, timescale: 1))) let player = Player(item: .simple(url: Stream.onDemand.url, metadata: MetadataWithBlockedTimeRange(), configuration: configuration)) - expect(player.time).toEventually(equal(kBlockedTimeRange.end)) + expect(player.time()).toEventually(equal(kBlockedTimeRange.end)) } } diff --git a/Tests/PlayerTests/Player/PlayerTests.swift b/Tests/PlayerTests/Player/PlayerTests.swift index 8e20a3f4f..dd51a251b 100644 --- a/Tests/PlayerTests/Player/PlayerTests.swift +++ b/Tests/PlayerTests/Player/PlayerTests.swift @@ -25,24 +25,14 @@ final class PlayerTests: TestCase { func testTimesWhenEmpty() { let player = Player() - expect(player.time).toAlways(equal(.invalid), until: .seconds(1)) + expect(player.time()).toAlways(equal(.invalid), until: .seconds(1)) } func testTimesInEmptyRange() { let player = Player(item: .simple(url: Stream.live.url)) expect(player.seekableTimeRange).toEventuallyNot(equal(.invalid)) player.play() - expect(player.time).toNever(equal(.invalid), until: .seconds(1)) - } - - func testTimesStayInRange() { - let player = Player(item: .simple(url: Stream.dvr.url)) - expect(player.seekableTimeRange).toEventuallyNot(equal(.invalid)) - player.play() - expect { - player.seekableTimeRange.start <= player.time && player.time <= player.seekableTimeRange.end - } - .toAlways(beTrue(), until: .seconds(1)) + expect(player.time()).toNever(equal(.invalid), until: .seconds(1)) } func testMetadataUpdatesMustNotChangePlayerItem() { diff --git a/Tests/PlayerTests/Player/SeekTests.swift b/Tests/PlayerTests/Player/SeekTests.swift index 251d27122..1e2d04e57 100644 --- a/Tests/PlayerTests/Player/SeekTests.swift +++ b/Tests/PlayerTests/Player/SeekTests.swift @@ -80,7 +80,7 @@ final class SeekTests: TestCase { waitUntil { done in player.seek(near(CMTime(value: -10, timescale: 1))) { finished in expect(finished).to(beTrue()) - expect(player.time).to(equal(.zero)) + expect(player.time()).to(equal(.zero)) done() } } @@ -92,7 +92,7 @@ final class SeekTests: TestCase { waitUntil { done in player.seek(near(player.seekableTimeRange.end + CMTime(value: 10, timescale: 1))) { finished in expect(finished).to(beTrue()) - expect(player.time).to(equal(player.seekableTimeRange.end, by: beClose(within: 1))) + expect(player.time()).to(equal(player.seekableTimeRange.end, by: beClose(within: 1))) done() } } @@ -103,18 +103,18 @@ final class SeekTests: TestCase { expect(player.streamType).toEventually(equal(.onDemand)) player.play() player.seek(near(CMTime(value: -10, timescale: 1))) - expect(player.time).toAlways(beGreaterThanOrEqualTo(player.seekableTimeRange.start), until: .seconds(1)) + expect(player.time()).toAlways(beGreaterThanOrEqualTo(player.seekableTimeRange.start), until: .seconds(1)) } func testOnDemandStartAtTime() { let configuration = PlayerItemConfiguration(position: at(.init(value: 10, timescale: 1))) let player = Player(item: .simple(url: Stream.onDemand.url, configuration: configuration)) - expect(player.time.seconds).toEventually(equal(10)) + expect(player.time().seconds).toEventually(equal(10)) } func testDvrStartAtTime() { let configuration = PlayerItemConfiguration(position: at(.init(value: 10, timescale: 1))) let player = Player(item: .simple(url: Stream.dvr.url, configuration: configuration)) - expect(player.time.seconds).toEventually(equal(10)) + expect(player.time().seconds).toEventually(equal(10)) } } diff --git a/Tests/PlayerTests/Playlist/NavigationBackwardTests.swift b/Tests/PlayerTests/Playlist/NavigationBackwardTests.swift index c1faf52f7..2ae8d0271 100644 --- a/Tests/PlayerTests/Playlist/NavigationBackwardTests.swift +++ b/Tests/PlayerTests/Playlist/NavigationBackwardTests.swift @@ -36,7 +36,7 @@ final class NavigationBackwardTests: TestCase { player.returnToPrevious() expect(player.currentItem).to(equal(item)) - expect(player.time).toNever(equal(.zero), until: .seconds(3)) + expect(player.time()).toNever(equal(.zero), until: .seconds(3)) } func testReturnForOnDemandAtBeginningWithPreviousItem() { @@ -63,7 +63,7 @@ final class NavigationBackwardTests: TestCase { } player.returnToPrevious() expect(player.currentItem).to(equal(item1)) - expect(player.time).toEventually(equal(.zero)) + expect(player.time()).toEventually(equal(.zero)) } func testReturnForLiveWithPreviousItem() { diff --git a/Tests/PlayerTests/Playlist/NavigationSmartBackwardTests.swift b/Tests/PlayerTests/Playlist/NavigationSmartBackwardTests.swift index 015825641..222a445f1 100644 --- a/Tests/PlayerTests/Playlist/NavigationSmartBackwardTests.swift +++ b/Tests/PlayerTests/Playlist/NavigationSmartBackwardTests.swift @@ -32,7 +32,7 @@ final class NavigationSmartBackwardTests: TestCase { player.returnToPrevious() expect(player.currentItem).to(equal(item)) - expect(player.time).toEventually(equal(.zero)) + expect(player.time()).toEventually(equal(.zero)) } func testReturnForOnDemandAtBeginningWithPreviousItem() { @@ -59,7 +59,7 @@ final class NavigationSmartBackwardTests: TestCase { } player.returnToPrevious() expect(player.currentItem).to(equal(item2)) - expect(player.time).toEventually(equal(.zero)) + expect(player.time()).toEventually(equal(.zero)) } func testReturnForLiveWithPreviousItem() { diff --git a/Tests/PlayerTests/Skips/SkipBackwardTests.swift b/Tests/PlayerTests/Skips/SkipBackwardTests.swift index a24544297..d46555db3 100644 --- a/Tests/PlayerTests/Skips/SkipBackwardTests.swift +++ b/Tests/PlayerTests/Skips/SkipBackwardTests.swift @@ -25,11 +25,11 @@ final class SkipBackwardTests: TestCase { func testSkipForOnDemand() { let player = Player(item: .simple(url: Stream.onDemand.url)) expect(player.streamType).toEventually(equal(.onDemand)) - expect(player.time).to(equal(.zero)) + expect(player.time()).to(equal(.zero)) waitUntil { done in player.skipBackward { _ in - expect(player.time).to(equal(.zero)) + expect(player.time()).to(equal(.zero)) done() } } @@ -38,7 +38,7 @@ final class SkipBackwardTests: TestCase { func testMultipleSkipsForOnDemand() { let player = Player(item: .simple(url: Stream.onDemand.url)) expect(player.streamType).toEventually(equal(.onDemand)) - expect(player.time).to(equal(.zero)) + expect(player.time()).to(equal(.zero)) waitUntil { done in player.skipBackward { finished in @@ -46,7 +46,7 @@ final class SkipBackwardTests: TestCase { } player.skipBackward { finished in - expect(player.time).to(equal(.zero)) + expect(player.time()).to(equal(.zero)) expect(finished).to(beTrue()) done() } @@ -67,10 +67,10 @@ final class SkipBackwardTests: TestCase { func testSkipForDvr() { let player = Player(item: .simple(url: Stream.dvr.url)) expect(player.streamType).toEventually(equal(.dvr)) - let headTime = player.time + let headTime = player.time() waitUntil { done in player.skipBackward { finished in - expect(player.time).to(equal(headTime + player.backwardSkipTime, by: beClose(within: player.chunkDuration.seconds))) + expect(player.time()).to(equal(headTime + player.backwardSkipTime, by: beClose(within: player.chunkDuration.seconds))) expect(finished).to(beTrue()) done() } diff --git a/Tests/PlayerTests/Skips/SkipForwardTests.swift b/Tests/PlayerTests/Skips/SkipForwardTests.swift index 18ba5ff59..94b48028f 100644 --- a/Tests/PlayerTests/Skips/SkipForwardTests.swift +++ b/Tests/PlayerTests/Skips/SkipForwardTests.swift @@ -32,11 +32,11 @@ final class SkipForwardTests: TestCase { func testSkipForOnDemand() { let player = Player(item: .simple(url: Stream.onDemand.url)) expect(player.streamType).toEventually(equal(.onDemand)) - expect(player.time).to(equal(.zero)) + expect(player.time()).to(equal(.zero)) waitUntil { done in player.skipForward { _ in - expect(player.time).to(equal(player.forwardSkipTime, by: beClose(within: player.chunkDuration.seconds))) + expect(player.time()).to(equal(player.forwardSkipTime, by: beClose(within: player.chunkDuration.seconds))) done() } } @@ -45,7 +45,7 @@ final class SkipForwardTests: TestCase { func testMultipleSkipsForOnDemand() { let player = Player(item: .simple(url: Stream.onDemand.url)) expect(player.streamType).toEventually(equal(.onDemand)) - expect(player.time).to(equal(.zero)) + expect(player.time()).to(equal(.zero)) waitUntil { done in player.skipForward { finished in @@ -53,7 +53,7 @@ final class SkipForwardTests: TestCase { } player.skipForward { finished in - expect(player.time).to(equal(CMTimeMultiply(player.forwardSkipTime, multiplier: 2), by: beClose(within: player.chunkDuration.seconds))) + expect(player.time()).to(equal(CMTimeMultiply(player.forwardSkipTime, multiplier: 2), by: beClose(within: player.chunkDuration.seconds))) expect(finished).to(beTrue()) done() } @@ -74,10 +74,10 @@ final class SkipForwardTests: TestCase { func testSkipForDvr() { let player = Player(item: .simple(url: Stream.dvr.url)) expect(player.streamType).toEventually(equal(.dvr)) - let headTime = player.time + let headTime = player.time() waitUntil { done in player.skipForward { finished in - expect(player.time).to(equal(headTime, by: beClose(within: player.chunkDuration.seconds))) + expect(player.time()).to(equal(headTime, by: beClose(within: player.chunkDuration.seconds))) expect(finished).to(beTrue()) done() } @@ -87,7 +87,7 @@ final class SkipForwardTests: TestCase { func testSkipNearEndDoesNotSeekAnymore() { let player = Player(item: .simple(url: Stream.onDemand.url)) expect(player.streamType).toEventually(equal(.onDemand)) - expect(player.time).to(equal(.zero)) + expect(player.time()).to(equal(.zero)) let seekTo = Stream.onDemand.duration - CMTime(value: 1, timescale: 1) waitUntil { done in @@ -105,7 +105,7 @@ final class SkipForwardTests: TestCase { func testSkipNearEndCompletion() { let player = Player(item: .simple(url: Stream.onDemand.url)) expect(player.streamType).toEventually(equal(.onDemand)) - expect(player.time).to(equal(.zero)) + expect(player.time()).to(equal(.zero)) let seekTo = Stream.onDemand.duration - CMTime(value: 1, timescale: 1) waitUntil { done in @@ -118,7 +118,7 @@ final class SkipForwardTests: TestCase { waitUntil { done in player.skipForward { finished in expect(finished).to(beTrue()) - expect(player.time).to(equal(seekTo, by: beClose(within: 0.5))) + expect(player.time()).to(equal(seekTo, by: beClose(within: 0.5))) done() } } diff --git a/Tests/PlayerTests/Skips/SkipToDefaultTests.swift b/Tests/PlayerTests/Skips/SkipToDefaultTests.swift index a6445c133..225a9c2ae 100644 --- a/Tests/PlayerTests/Skips/SkipToDefaultTests.swift +++ b/Tests/PlayerTests/Skips/SkipToDefaultTests.swift @@ -16,7 +16,7 @@ final class SkipToDefaultTests: TestCase { waitUntil { done in player.skipToDefault { finished in expect(finished).to(beTrue()) - expect(player.time).to(equal(.invalid)) + expect(player.time()).to(equal(.invalid)) done() } } @@ -28,7 +28,7 @@ final class SkipToDefaultTests: TestCase { waitUntil { done in player.skipToDefault { finished in expect(finished).to(beTrue()) - expect(player.time).to(equal(.invalid)) + expect(player.time()).to(equal(.zero)) done() } } @@ -40,7 +40,7 @@ final class SkipToDefaultTests: TestCase { waitUntil { done in player.skipToDefault { finished in expect(finished).to(beTrue()) - expect(player.time).to(equal(.zero)) + expect(player.time()).to(equal(.zero)) done() } }