Skip to content

Commit 4e1cda2

Browse files
committed
added new commands
1 parent b5ba5d7 commit 4e1cda2

File tree

4 files changed

+109
-40
lines changed

4 files changed

+109
-40
lines changed

README.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,21 @@ Please note that using videos from URLs requires ensuring that you have the righ
2525
| `init(fileName:ext:gravity:` <br> `eColor:eFontSize:command:)` | Constructor | Initializes the player with specific video parameters and playback command binding. |
2626
| `init(settings: () -> VideoSettings, command:)` | Constructor | Initializes the player with a declarative settings block and playback command binding. |
2727

28+
2829
### Playback Commands
2930

3031
| Command | Description |
3132
|-----------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------|
3233
| `play` | Command to play the video. |
3334
| `pause` | Command to pause the video. |
34-
| `seek(to: Double)` | Command to seek to a specific time in the video. The parameter is the target position in seconds. Note: Errors such as seeking out of bounds are not currently handled and will be silently ignored. Potential errors include: <br> - `.seekTimeOutOfBounds`<br> - `.invalidDuration` <br> - `.playerOrCurrentItemNil `. <br> Future versions may introduce error handling for these cases. |
35-
| `begin` | Command to position the video at the beginning. | |
36-
| `end` | Command to position the video at the end. |
37-
| `mute` | Command to mute the video. By default, the player is muted. | |
38-
| `unmute` | Command to unmute the video. |
35+
| `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. |
36+
| `begin` | Command to position the video at the beginning. |
37+
| `end` | Command to position the video at the end. |
38+
| `mute` | Command to mute the video. By default, the player is muted. |
39+
| `unmute` | Command to unmute the video. |
40+
| `volume(Float)` | Command to adjust the volume of the video playback. The `volume` parameter is a `Float` value between 0.0 (mute) and 1.0 (full volume). If a value outside this range is passed, it will be clamped to the nearest valid value (0.0 or 1.0). |
41+
| `subtitles(String?)` | Command to set subtitles to a specified language or turn them off. Pass a language code (e.g., "en" for English) to set subtitles, or `nil` to turn them off. |
42+
| `playbackSpeed(Float)` | Command to adjust the playback speed of the video. The `speed` parameter is a `Float` value representing the playback speed (e.g., 1.0 for normal speed, 0.5 for half speed, 2.0 for double speed). If a negative value is passed, it will be clamped to 0.0. |
3943

4044

4145
### Initializer Parameters Settings

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,19 @@ public enum PlaybackCommand: Equatable {
3232
/// Command to unmute the video.
3333
case unmute
3434

35+
/// Command to set the volume of the video playback.
36+
/// - Parameter volume: A `Float` value between 0.0 (mute) and 1.0 (full volume).
37+
case volume(Float)
38+
39+
/// Command to set subtitles for the video playback to a specified language or to turn them off.
40+
/// - Parameter language: The language code (e.g., "en" for English) for the desired subtitles.
41+
/// Pass `nil` to turn off subtitles.
42+
case subtitles(String?)
43+
44+
/// Command to set the playback speed of the video playback.
45+
/// - Parameter speed: A `Float` value representing the playback speed (e.g., 1.0 for normal speed, 0.5 for half speed, 2.0 for double speed).
46+
case playbackSpeed(Float)
47+
3548
public static func == (lhs: PlaybackCommand, rhs: PlaybackCommand) -> Bool {
3649
switch (lhs, rhs) {
3750
case (.play, .play):
@@ -48,6 +61,12 @@ public enum PlaybackCommand: Equatable {
4861
return true
4962
case (.unmute, .unmute):
5063
return true
64+
case (.volume(let lhsVolume), .volume(let rhsVolume)):
65+
return lhsVolume == rhsVolume
66+
case (.subtitles(let lhsLanguage), .subtitles(let rhsLanguage)):
67+
return lhsLanguage == rhsLanguage
68+
case (.playbackSpeed(let lhsSpeed), .playbackSpeed(let rhsSpeed)):
69+
return lhsSpeed == rhsSpeed
5170
default:
5271
return false
5372
}

Sources/swiftui-loop-videoplayer/fn/fn+.swift

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -84,26 +84,3 @@ func detectError(settings: VideoSettings, asset: AVURLAsset?) -> VPErrors? {
8484
return nil
8585
}
8686
}
87-
88-
import AVFoundation
89-
90-
/// Seeks to the specified time in the given player if the time is within the video's duration.
91-
/// - Parameters:
92-
/// - player: The `AVQueuePlayer` instance to seek in.
93-
/// - seekTimeInSeconds: The time to seek to, in seconds.
94-
///
95-
/// Note: Errors such as seeking out of the bounds of the video duration are not handled in this function.
96-
/// These issues will be silently ignored. Error handling may be introduced in a future version of the package.
97-
func seekToTime(player: AVQueuePlayer?, seekTimeInSeconds: Double) {
98-
guard let player = player, let currentItem = player.currentItem else {
99-
return
100-
}
101-
102-
let duration = currentItem.duration
103-
if CMTimeGetSeconds(duration) > 0 {
104-
let seekTime = CMTime(seconds: seekTimeInSeconds, preferredTimescale: 600)
105-
if CMTimeCompare(seekTime, duration) <= 0 && CMTimeCompare(seekTime, .zero) >= 0 {
106-
player.seek(to: seekTime)
107-
}
108-
}
109-
}

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

Lines changed: 81 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -63,23 +63,41 @@ extension AbstractPlayer{
6363

6464
/// Seeks the video to a specific time.
6565
/// This method moves the playback position to the specified time with precise accuracy.
66+
/// If the specified time is out of bounds, it will be clamped to the nearest valid time.
6667
/// - Parameter time: The target time to seek to in the video timeline.
6768
func seek(to time: Double) {
68-
seekToTime(player: player, seekTimeInSeconds: time)
69+
guard let player = player, let duration = player.currentItem?.duration else {
70+
return
71+
}
72+
73+
let endTime = CMTimeGetSeconds(duration)
74+
75+
if time < 0 {
76+
// If the time is negative, seek to the start of the video
77+
player.seek(to: .zero)
78+
} else if time > endTime {
79+
// If the time exceeds the video duration, seek to the end of the video
80+
let endCMTime = CMTime(seconds: endTime, preferredTimescale: duration.timescale)
81+
player.seek(to: endCMTime)
82+
} else {
83+
// Otherwise, seek to the specified time
84+
let seekCMTime = CMTime(seconds: time, preferredTimescale: duration.timescale)
85+
player.seek(to: seekCMTime)
86+
}
6987
}
7088

7189
/// Seeks to the start of the video.
7290
/// This method positions the playback at the beginning of the video.
7391
func seekToStart() {
74-
seekToTime(player: player, seekTimeInSeconds: 0)
92+
seek(to: 0)
7593
}
7694

7795
/// Seeks to the end of the video.
7896
/// This method positions the playback at the end of the video.
7997
func seekToEnd() {
8098
if let duration = player?.currentItem?.duration {
8199
let endTime = CMTimeGetSeconds(duration)
82-
seekToTime(player: player, seekTimeInSeconds: endTime)
100+
seek(to: endTime)
83101
}
84102
}
85103

@@ -95,15 +113,60 @@ extension AbstractPlayer{
95113
player?.isMuted = false
96114
}
97115

98-
/// Sets the playback command for the video player.
99-
/// - Parameter value: The `PlaybackCommand` to set. This can be one of the following:
100-
/// - `play`: Command to play the video.
101-
/// - `pause`: Command to pause the video.
102-
/// - `seek(to:)`: Command to seek to a specific time in the video.
103-
/// - `begin`: Command to position the video at the beginning.
104-
/// - `end`: Command to position the video at the end.
105-
/// - `mute`: Command to mute the video.
106-
/// - `unmute`: Command to unmute the video.
116+
/// Sets the volume for the video playback.
117+
/// - Parameter volume: A `Float` value between 0.0 (mute) and 1.0 (full volume).
118+
/// If the value is out of range, it will be clamped to the nearest valid value.
119+
func setVolume(_ volume: Float) {
120+
let clampedVolume = max(0.0, min(volume, 1.0)) // Clamp the value between 0.0 and 1.0
121+
player?.volume = clampedVolume
122+
}
123+
124+
/// Sets the playback speed for the video playback.
125+
/// - Parameter speed: A `Float` value representing the playback speed (e.g., 1.0 for normal speed, 0.5 for half speed, 2.0 for double speed).
126+
/// If the value is out of range (negative), it will be clamped to the nearest valid value.
127+
func setPlaybackSpeed(_ speed: Float) {
128+
let clampedSpeed = max(0.0, speed) // Clamp to non-negative values, or adjust the upper bound as needed
129+
player?.rate = clampedSpeed
130+
}
131+
132+
/// Sets the subtitles for the video playback to a specified language or turns them off.
133+
/// - Parameters:
134+
/// - language: The language code (e.g., "en" for English) for the desired subtitles.
135+
/// Pass `nil` to turn off subtitles.
136+
func setSubtitles(to language: String?) {
137+
guard let currentItem = player?.currentItem,
138+
let group = currentItem.asset.mediaSelectionGroup(forMediaCharacteristic: .legible) else {
139+
return
140+
}
141+
142+
if let language = language {
143+
// Filter the subtitle options based on the language code
144+
let options = group.options.filter { option in
145+
guard let locale = option.locale else { return false }
146+
return locale.languageCode == language
147+
}
148+
// Select the first matching subtitle option
149+
if let option = options.first {
150+
currentItem.select(option, in: group)
151+
}
152+
} else {
153+
// Turn off subtitles by deselecting any option in the legible media selection group
154+
currentItem.select(nil, in: group)
155+
}
156+
}
157+
158+
/// Sets the playback command for the video player.
159+
/// - Parameter value: The `PlaybackCommand` to set. This can be one of the following:
160+
/// - `play`: Command to play the video.
161+
/// - `pause`: Command to pause the video.
162+
/// - `seek(to:)`: Command to seek to a specific time in the video.
163+
/// - `begin`: Command to position the video at the beginning.
164+
/// - `end`: Command to position the video at the end.
165+
/// - `mute`: Command to mute the video.
166+
/// - `unmute`: Command to unmute the video.
167+
/// - `volume`: Command to adjust the volume of the video playback.
168+
/// - `subtitles`: Command to set subtitles to a specified language or turn them off.
169+
/// - `playbackSpeed`: Command to adjust the playback speed of the video.
107170
func setCommand(_ value: PlaybackCommand) {
108171
switch value {
109172
case .play:
@@ -120,6 +183,12 @@ extension AbstractPlayer{
120183
mute()
121184
case .unmute:
122185
unmute()
186+
case .volume(let volume):
187+
setVolume(volume)
188+
case .subtitles(let language):
189+
setSubtitles(to: language)
190+
case .playbackSpeed(let speed):
191+
setPlaybackSpeed(speed)
123192
}
124193
}
125194
}

0 commit comments

Comments
 (0)