Skip to content

Commit 11b6dd3

Browse files
committed
added idle command
1 parent e7562ef commit 11b6dd3

File tree

11 files changed

+111
-60
lines changed

11 files changed

+111
-60
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ Please note that using videos from URLs requires ensuring that you have the righ
6666

6767
| Command | Description |
6868
|-----------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------|
69+
| `idle` | Start without any actions. Any command passed during initialization will be executed. If you'd like to start without any actions based on settings values just setup command to `.idle` |
6970
| `play` | Command to play the video. |
7071
| `pause` | Command to pause the video. |
7172
| `seek(to: Double)` | Command to seek to a specific time in the video. The parameter is the target position in seconds. If the time is negative, the playback will move to the start of the video. If the time exceeds the video's duration, the playback will move to the end of the video. If the time is within the video’s duration, the playback will move to the specified time. |

Sources/swiftui-loop-videoplayer/enum/PlaybackCommand.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ import CoreImage
1313
/// An enumeration of possible playback commands.
1414
@available(iOS 14.0, macOS 11.0, tvOS 14.0, *)
1515
public enum PlaybackCommand: Equatable {
16+
17+
case idle
18+
1619
/// Command to play the video.
1720
case play
1821

@@ -88,7 +91,7 @@ public enum PlaybackCommand: Equatable {
8891

8992
public static func == (lhs: PlaybackCommand, rhs: PlaybackCommand) -> Bool {
9093
switch (lhs, rhs) {
91-
case (.play, .play), (.pause, .pause), (.begin, .begin), (.end, .end),
94+
case (.idle, .idle), (.play, .play), (.pause, .pause), (.begin, .begin), (.end, .end),
9295
(.mute, .mute), (.unmute, .unmute), (.loop, .loop), (.unloop, .unloop),
9396
(.removeAllFilters, .removeAllFilters), (.removeAllVectors, .removeAllVectors):
9497
return true

Sources/swiftui-loop-videoplayer/enum/Setting.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ public enum Setting: Equatable, SettingsConvertible{
2525
/// Mute video
2626
case mute
2727

28+
/// Don't auto play video after initialization
29+
case notAutoPlay
30+
2831
/// File name
2932
case name(String)
3033

Sources/swiftui-loop-videoplayer/protocol/player/AbstractPlayer.swift

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import CoreImage
1414
@MainActor @preconcurrency
1515
public protocol AbstractPlayer: AnyObject {
1616

17+
var currentSettings : VideoSettings? { get set }
18+
1719
/// The delegate to be notified about errors encountered by the player.
1820
var delegate: PlayerDelegateProtocol? { get set }
1921

@@ -99,7 +101,7 @@ public protocol AbstractPlayer: AnyObject {
99101
func applyVideoComposition()
100102

101103
/// Updates the current playback asset, settings, and initializes playback or a specific action when the asset is ready.
102-
func update(asset: AVURLAsset, loop: Bool, autoPlay: Bool, callback: ((AVPlayerItem.Status) -> Void)?)
104+
func update(asset: AVURLAsset, settings: VideoSettings, callback: ((AVPlayerItem.Status) -> Void)?)
103105
}
104106

105107
extension AbstractPlayer{
@@ -137,7 +139,7 @@ extension AbstractPlayer{
137139
/// Pauses the video playback.
138140
/// This method pauses the video if it is currently playing, allowing it to be resumed later from the same position.
139141
func pause() {
140-
player?.pause()
142+
player?.pause()
141143
}
142144

143145
/// Seeks the video to a specific time.
@@ -151,8 +153,8 @@ extension AbstractPlayer{
151153
}
152154

153155
guard currentItem?.status == .readyToPlay else{
154-
if let currentAsset{
155-
update(asset: currentAsset , loop: false, autoPlay: false){ [weak self] status in
156+
if let currentAsset, let currentSettings{
157+
update(asset: currentAsset, settings: currentSettings.GetWithNotLoopNotAutoplay){ [weak self] status in
156158
if status == .readyToPlay{
157159
self?.seek(to: time)
158160
}else {
@@ -188,7 +190,6 @@ extension AbstractPlayer{
188190
}
189191

190192
player.seek(to: seekTime){ [weak self] value in
191-
let currentTime = CMTimeGetSeconds(player.currentTime())
192193
self?.delegate?.didSeek(value: value, currentTime: seekTime.seconds)
193194
}
194195
}

Sources/swiftui-loop-videoplayer/protocol/player/LoopingPlayerProtocol.swift

Lines changed: 28 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,13 @@ public protocol LoopingPlayerProtocol: AbstractPlayer, LayerMakerProtocol{
4949
/// Declare a variable to hold the time observer token outside the if statement
5050
var timeObserver: Any? { get set }
5151

52-
/// Initializes a new player view with a video asset and specified configurations.
52+
/// Initializes a new player view with a video asset and custom settings.
5353
///
5454
/// - Parameters:
55-
/// - asset: The `AVURLAsset` for video playback.
56-
/// - gravity: The `AVLayerVideoGravity` determining the video's display within the layer bounds.
57-
/// - timePublishing: Optional `CMTime` for publishing or triggering an event at a specific time.
58-
/// - loop: A Boolean indicating if the video should loop at the end of playback.
59-
/// - mute: A Boolean indicating if the audio playback should be muted.
60-
init(asset: AVURLAsset, gravity: AVLayerVideoGravity, timePublishing: CMTime?, loop : Bool, mute: Bool)
55+
/// - asset: The `AVURLAsset` used for video playback.
56+
/// - settings: The `VideoSettings` struct that includes all necessary configurations like gravity, loop, and mute.
57+
/// - timePublishing: Optional `CMTime` that specifies a particular time for publishing or triggering an event.
58+
init(asset: AVURLAsset, settings: VideoSettings, timePublishing: CMTime?)
6159

6260
/// Sets up the necessary observers on the AVPlayerItem and AVQueuePlayer to monitor changes and errors.
6361
///
@@ -74,26 +72,24 @@ public protocol LoopingPlayerProtocol: AbstractPlayer, LayerMakerProtocol{
7472

7573
internal extension LoopingPlayerProtocol {
7674

77-
/// Sets up the player components with the specified media asset, display properties, and optional time publishing interval.
75+
/// Initializes a new player view with a video asset and custom settings.
7876
///
7977
/// - Parameters:
80-
/// - asset: The AVURLAsset representing the video content.
81-
/// - gravity: Determines how the video content is scaled or fit within the player view.
82-
/// - timePublishing: Optional interval for publishing the current playback time; nil disables this feature.
78+
/// - asset: The `AVURLAsset` used for video playback.
79+
/// - settings: The `VideoSettings` struct that includes all necessary configurations like gravity, loop, and mute.
80+
/// - timePublishing: Optional `CMTime` that specifies a particular time for publishing or triggering an event.
8381
func setupPlayerComponents(
8482
asset: AVURLAsset,
85-
gravity: AVLayerVideoGravity,
86-
timePublishing: CMTime?,
87-
loop: Bool,
88-
mute: Bool
83+
settings: VideoSettings,
84+
timePublishing: CMTime?
8985
) {
9086

9187
let player = AVQueuePlayer(items: [])
9288
self.player = player
9389

94-
update(asset: asset, loop: loop)
90+
configurePlayer(player, settings: settings, timePublishing: timePublishing)
9591

96-
configurePlayer(player, gravity: gravity, timePublishing: timePublishing, loop: loop, mute: mute)
92+
update(asset: asset, settings: settings)
9793

9894
setupObservers(for: player)
9995
}
@@ -102,18 +98,16 @@ internal extension LoopingPlayerProtocol {
10298
///
10399
/// - Parameters:
104100
/// - player: The AVQueuePlayer to be configured.
105-
/// - gravity: The AVLayerVideoGravity determining how the video content should be scaled or fit within the player layer.
101+
/// - settings: The `VideoSettings` struct that includes all necessary configurations like gravity, loop, and mute.
106102
/// - timePublishing: Optional interval for publishing the current playback time; nil disables this feature.
107103
func configurePlayer(
108104
_ player: AVQueuePlayer,
109-
gravity: AVLayerVideoGravity,
110-
timePublishing: CMTime?,
111-
loop : Bool,
112-
mute : Bool
105+
settings: VideoSettings,
106+
timePublishing: CMTime?
113107
) {
114-
player.isMuted = mute
108+
player.isMuted = settings.mute
115109
playerLayer.player = player
116-
playerLayer.videoGravity = gravity
110+
playerLayer.videoGravity = settings.gravity
117111
#if canImport(UIKit)
118112
playerLayer.backgroundColor = UIColor.clear.cgColor
119113
layer.addSublayer(playerLayer)
@@ -152,18 +146,15 @@ internal extension LoopingPlayerProtocol {
152146
///
153147
/// - Parameters:
154148
/// - asset: The AVURLAsset to be loaded into the player.
155-
/// - loop: A Boolean value indicating whether the video should loop.
156-
/// - autoPlay: A Boolean value indicating whether playback should start automatically. Default is true.
149+
/// - settings: The `VideoSettings` struct that includes all necessary configurations like gravity, loop, and mute.
157150
/// - callback: An optional closure to be called when the asset is ready to play.
158-
func update(asset: AVURLAsset, loop: Bool, autoPlay: Bool = true, callback: ((AVPlayerItem.Status) -> Void)? = nil) {
151+
func update(asset: AVURLAsset, settings: VideoSettings, callback: ((AVPlayerItem.Status) -> Void)? = nil) {
159152

160153
guard let player = player else { return }
161154

162-
let wasPlaying = player.rate != 0
163-
164-
if wasPlaying {
165-
pause()
166-
}
155+
currentSettings = settings
156+
157+
player.pause()
167158

168159
if !player.items().isEmpty {
169160
// Cleaning
@@ -175,15 +166,15 @@ internal extension LoopingPlayerProtocol {
175166
let newItem = AVPlayerItem(asset: asset)
176167
player.insert(newItem, after: nil)
177168

178-
if loop {
179-
self.loop()
169+
if settings.loop {
170+
loop()
180171
}
181172

182173
// Set up state item status observer
183174
setupStateItemStatusObserver(newItem: newItem, callback: callback)
184175

185-
if autoPlay{
186-
player.play()
176+
if !settings.notAutoPlay{
177+
play()
187178
}
188179
}
189180

@@ -338,6 +329,7 @@ internal extension LoopingPlayerProtocol {
338329
addVectorLayer(builder: builder, clear: clear)
339330
case .removeAllVectors:
340331
removeAllVectors()
332+
default : return
341333
}
342334
}
343335
}

Sources/swiftui-loop-videoplayer/protocol/view/LoopPlayerViewProtocol.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ public extension LoopPlayerViewProtocol{
9191
asset: AVURLAsset?) -> PlayerView? {
9292

9393
if let asset{
94-
let player = PlayerView(asset: asset, gravity: settings.gravity, timePublishing: settings.timePublishing, loop: settings.loop, mute: settings.mute)
94+
let player = PlayerView(asset: asset, settings: settings, timePublishing: settings.timePublishing)
9595
container.addSubview(player)
9696
activateFullScreenConstraints(for: player, in: container)
9797
return player
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//
2+
// NotAutoPlay.swift
3+
//
4+
//
5+
// Created by Igor on 10.09.24.
6+
//
7+
8+
import Foundation
9+
10+
@available(iOS 14.0, macOS 11.0, tvOS 14.0, *)
11+
public struct NotAutoPlay: SettingsConvertible{
12+
13+
// MARK: - Life circle
14+
15+
public init() {}
16+
17+
/// Fetch settings
18+
@_spi(Private)
19+
public func asSettings() -> [Setting] {
20+
[.notAutoPlay]
21+
}
22+
}

Sources/swiftui-loop-videoplayer/utils/VideoSettings.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ public struct VideoSettings: Equatable{
2525
/// Mute video
2626
public let mute: Bool
2727

28+
/// Don't auto play video after initialization
29+
public let notAutoPlay: Bool
30+
2831
/// A CMTime value representing the interval at which the player's current time should be published.
2932
/// If set, the player will publish periodic time updates based on this interval.
3033
public let timePublishing: CMTime?
@@ -52,6 +55,21 @@ public struct VideoSettings: Equatable{
5255
private let unique : Bool
5356

5457
// MARK: - Life circle
58+
59+
// initializer
60+
init(name: String, ext: String, loop: Bool, mute: Bool, notAutoPlay: Bool, timePublishing: CMTime?, gravity: AVLayerVideoGravity, errorColor: Color, errorFontSize: CGFloat, errorWidgetOff: Bool, unique: Bool) {
61+
self.name = name
62+
self.ext = ext
63+
self.loop = loop
64+
self.mute = mute
65+
self.notAutoPlay = notAutoPlay
66+
self.timePublishing = timePublishing
67+
self.gravity = gravity
68+
self.errorColor = errorColor
69+
self.errorFontSize = errorFontSize
70+
self.errorWidgetOff = errorWidgetOff
71+
self.unique = unique
72+
}
5573

5674
/// - Parameter builder: Block builder
5775
public init(@SettingsBuilder builder: () -> [Setting]){
@@ -75,10 +93,21 @@ public struct VideoSettings: Equatable{
7593

7694
mute = settings.contains(.mute)
7795

96+
notAutoPlay = settings.contains(.notAutoPlay)
97+
7898
errorWidgetOff = settings.contains(.errorWidgetOff)
7999
}
80100
}
81101

102+
@available(iOS 14.0, macOS 11.0, tvOS 14.0, *)
103+
public extension VideoSettings {
104+
105+
/// Returns a new instance of VideoSettings with loop set to false and notAutoPlay set to true, keeping other settings unchanged.
106+
var GetWithNotLoopNotAutoplay: VideoSettings {
107+
VideoSettings(name: self.name, ext: self.ext, loop: false, mute: self.mute, notAutoPlay: true, timePublishing: self.timePublishing, gravity: self.gravity, errorColor: self.errorColor, errorFontSize: self.errorFontSize, errorWidgetOff: self.errorWidgetOff, unique: self.unique)
108+
}
109+
}
110+
82111
/// Check if unique
83112
/// - Parameter settings: Passed array of settings flatted by block builder
84113
/// - Returns: True - unique False - not

Sources/swiftui-loop-videoplayer/view/main/LoopPlayerMultiPlatform.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,8 @@ extension LoopPlayerMultiPlatform: UIViewRepresentable{
116116
let player = uiView.findFirstSubview(ofType: PlayerView.self)
117117
if let player {
118118
if let asset = getAssetIfChanged(for: settings, and: player.currentAsset) {
119-
player.update(asset: asset, loop: settings.loop)
120-
}
119+
player.update(asset: asset, settings: settings)
120+
}
121121

122122
// Check if command changed before applying it
123123
if context.coordinator.getLastCommand != command {
@@ -164,7 +164,7 @@ extension LoopPlayerMultiPlatform: NSViewRepresentable{
164164
let player = nsView.findFirstSubview(ofType: PlayerView.self)
165165
if let player {
166166
if let asset = getAssetIfChanged(for: settings, and: player.currentAsset){
167-
player.update(asset: asset, loop: settings.loop)
167+
player.update(asset: asset, settings: settings)
168168
}
169169
// Check if command changed before applying it
170170
if context.coordinator.getLastCommand != command {

Sources/swiftui-loop-videoplayer/view/player/ios/LoopingPlayerUIView.swift

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ import UIKit
1616

1717
@available(iOS 14.0, tvOS 14.0, *)
1818
@MainActor @preconcurrency
19-
class LoopingPlayerUIView: UIView, LoopingPlayerProtocol {
19+
class LoopingPlayerUIView: UIView, LoopingPlayerProtocol {
20+
21+
internal var currentSettings : VideoSettings?
2022

2123
/// `filters` is an array that stores CIFilter objects used to apply different image processing effects
2224
internal var filters: [CIFilter] = []
@@ -63,18 +65,16 @@ class LoopingPlayerUIView: UIView, LoopingPlayerProtocol {
6365
/// The delegate to be notified about errors encountered by the player.
6466
weak var delegate: PlayerDelegateProtocol?
6567

66-
/// Initializes a new player view with a video asset and specified configurations.
68+
/// Initializes a new player view with a video asset and custom settings.
6769
///
6870
/// - Parameters:
69-
/// - asset: The `AVURLAsset` for video playback.
70-
/// - gravity: The `AVLayerVideoGravity` determining the video's display within the layer bounds.
71-
/// - timePublishing: Optional `CMTime` for publishing or triggering an event at a specific time.
72-
/// - loop: A Boolean indicating if the video should loop at the end of playback.
73-
/// - mute: A Boolean indicating if the audio playback should be muted.
74-
required init(asset: AVURLAsset, gravity: AVLayerVideoGravity, timePublishing: CMTime?, loop: Bool, mute: Bool){
71+
/// - asset: The `AVURLAsset` used for video playback.
72+
/// - settings: The `VideoSettings` struct that includes all necessary configurations like gravity, loop, and mute.
73+
/// - timePublishing: Optional `CMTime` that specifies a particular time for publishing or triggering an event.
74+
required init(asset: AVURLAsset, settings: VideoSettings, timePublishing: CMTime?){
7575
super.init(frame: .zero)
7676
setupPlayerComponents(
77-
asset: asset, gravity: gravity, timePublishing : timePublishing, loop: loop, mute: mute
77+
asset: asset, settings: settings, timePublishing : timePublishing
7878
)
7979
}
8080

Sources/swiftui-loop-videoplayer/view/player/mac/LoopingPlayerNSView.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import AppKit
2020
@MainActor @preconcurrency
2121
class LoopingPlayerNSView: NSView, LoopingPlayerProtocol {
2222

23+
internal var currentSettings : VideoSettings?
24+
2325
/// `filters` is an array that stores CIFilter objects used to apply different image processing effects
2426
internal var filters: [CIFilter] = []
2527

@@ -69,14 +71,12 @@ class LoopingPlayerNSView: NSView, LoopingPlayerProtocol {
6971
///
7072
/// - Parameters:
7173
/// - asset: The `AVURLAsset` for video playback.
72-
/// - gravity: The `AVLayerVideoGravity` determining the video's display within the layer bounds.
74+
/// - settings: The `VideoSettings` struct that includes all necessary configurations like gravity, loop, and mute.
7375
/// - timePublishing: Optional `CMTime` for publishing or triggering an event at a specific time.
74-
/// - loop: A Boolean indicating if the video should loop at the end of playback.
75-
/// - mute: A Boolean indicating if the audio playback should be muted.
76-
required init(asset: AVURLAsset, gravity: AVLayerVideoGravity, timePublishing: CMTime?, loop : Bool, mute: Bool) {
76+
required init(asset: AVURLAsset, settings: VideoSettings, timePublishing: CMTime?) {
7777
super.init(frame: .zero)
7878
setupPlayerComponents(
79-
asset: asset, gravity: gravity, timePublishing: timePublishing, loop: loop, mute: mute
79+
asset: asset, settings: settings, timePublishing: timePublishing
8080
)
8181
}
8282

0 commit comments

Comments
 (0)