@@ -110,7 +110,7 @@ public protocol AbstractPlayer: AnyObject {
110
110
func applyVideoComposition( )
111
111
112
112
/// Updates the current playback asset, settings, and initializes playback or a specific action when the asset is ready.
113
- func update( settings: VideoSettings , asset : AVURLAsset ? )
113
+ func update( settings: VideoSettings , doUpdate : Bool )
114
114
}
115
115
116
116
extension AbstractPlayer {
@@ -188,24 +188,25 @@ extension AbstractPlayer{
188
188
/// the observer is invalidated, ensuring that the callback is called only once.
189
189
///
190
190
/// - Parameters:
191
- /// - newItem : The `AVPlayerItem` whose status is to be observed.
191
+ /// - item : The `AVPlayerItem` whose status is to be observed.
192
192
/// - callback: A closure that is called when the item's status changes to `.readyToPlay` or `.failed`.
193
- func setupStateItemStatusObserver ( newItem : AVPlayerItem , callback : @escaping ( AVPlayerItem . Status ) -> Void ) {
193
+ func setupStateStatusObserver ( for item : AVPlayerItem , callback : @escaping ( AVPlayerItem . Status ) -> Void ) {
194
194
195
195
clearStatusObserver ( )
196
196
197
- guard newItem . status == . unknown else {
198
- callback ( newItem . status)
197
+ guard item . status == . unknown else {
198
+ callback ( item . status)
199
199
return
200
200
}
201
201
202
- //.unknown: This state is essentially the default, indicating that the player item is new or has not yet attempted to load its assets.
203
- statusObserver = newItem . observe ( \ . status, options : [ . new , . old ] ) { [ weak self ] item , _ in
202
+ statusObserver = item . observe ( \ . status , options : [ . new , . initial ] ) { [ weak self ] item, change in
203
+ print ( item . status. rawValue , " status " )
204
204
guard item. status == . readyToPlay || item. status == . failed else {
205
205
return
206
206
}
207
207
208
208
callback ( item. status)
209
+
209
210
Task { @MainActor in
210
211
self ? . clearStatusObserver ( )
211
212
}
@@ -219,31 +220,52 @@ extension AbstractPlayer{
219
220
statusObserver = nil
220
221
}
221
222
}
223
+
224
+ /// Creates an `AVPlayerItem` with optional subtitle merging.
225
+ /// - Parameters:
226
+ /// - asset: The main video asset.
227
+ /// - settings: A `VideoSettings` object containing subtitle configuration.
228
+ /// - Returns: A new `AVPlayerItem` configured with the merged or original asset.
229
+ func createPlayerItem( with settings: VideoSettings ) -> AVPlayerItem ? {
230
+
231
+ guard let asset = assetFor ( settings) else {
232
+ delegate? . didReceiveError ( . sourceNotFound( settings. name) )
233
+ return nil
234
+ }
235
+
236
+ if let subtitleAsset = subtitlesAssetFor ( settings) ,
237
+ let mergedAsset = mergeAssetWithSubtitles ( videoAsset: asset, subtitleAsset: subtitleAsset) {
238
+ // Create and return a new `AVPlayerItem` using the merged asset
239
+ return AVPlayerItem ( asset: mergedAsset)
240
+ } else {
241
+ // Create and return a new `AVPlayerItem` using the original asset
242
+ return AVPlayerItem ( asset: asset)
243
+ }
244
+ }
222
245
223
246
/// Seeks the video to a specific time.
224
247
/// This method moves the playback position to the specified time with precise accuracy.
225
248
/// If the specified time is out of bounds, it will be clamped to the nearest valid time.
226
249
/// - Parameter time: The target time to seek to in the video timeline.
227
250
func seek( to time: Double , play: Bool = false ) {
228
251
guard let player = player, let duration = player. currentItem? . duration else {
229
- if let settings = currentSettings{
230
- let callback : ( AVPlayerItem . Status ) -> Void = { [ weak self] status in
231
- if status == . readyToPlay{
232
- self ? . seek ( to: time, play: play)
233
- } else {
234
- self ? . delegate? . didSeek ( value: false , currentTime: time)
235
- }
236
- }
237
- update ( settings: settings, asset: assetFor ( settings) )
238
- DispatchQueue . main. asyncAfter ( deadline: . now( ) + 0.5 ) {
239
- if let item = self . currentItem{
240
- self . setupStateItemStatusObserver ( newItem: item, callback: callback)
241
- }
242
- }
243
- } else {
252
+ guard let settings = currentSettings else {
244
253
delegate? . didSeek ( value: false , currentTime: time)
254
+ return
255
+ }
256
+ update ( settings: settings, doUpdate: true )
257
+ let callback : ( AVPlayerItem . Status ) -> Void = { [ weak self] status in
258
+ guard status == . readyToPlay else {
259
+ self ? . delegate? . didSeek ( value: false , currentTime: time)
260
+ return
261
+ }
262
+ self ? . seek ( to: time, play: play)
263
+ }
264
+ /// Need to refactor
265
+ DispatchQueue . main. asyncAfter ( deadline: . now( ) + 0.5 ) { [ weak self] in
266
+ guard let item = self ? . currentItem else { return }
267
+ self ? . setupStateStatusObserver ( for: item, callback: callback)
245
268
}
246
-
247
269
return
248
270
}
249
271
@@ -274,6 +296,8 @@ extension AbstractPlayer{
274
296
self ? . delegate? . didSeek ( value: value, currentTime: currentTime)
275
297
if play{
276
298
self ? . play ( )
299
+ } else {
300
+ self ? . pause ( )
277
301
}
278
302
}
279
303
}
0 commit comments