Skip to content

Commit b16cd5b

Browse files
authored
Use fileCDN with video attachments (#624)
1 parent 78bf645 commit b16cd5b

File tree

5 files changed

+84
-35
lines changed

5 files changed

+84
-35
lines changed

Sources/StreamChatSwiftUI/ChatChannel/Gallery/GalleryView.swift

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -153,20 +153,40 @@ public struct GalleryView: View {
153153
}
154154

155155
struct StreamVideoPlayer: View {
156-
157-
@State var player: AVPlayer
158-
156+
157+
@Injected(\.utils) private var utils
158+
159+
private var fileCDN: FileCDN {
160+
utils.fileCDN
161+
}
162+
163+
let url: URL
164+
165+
@State var avPlayer: AVPlayer?
166+
@State var error: Error?
167+
159168
init(url: URL) {
160-
let player = AVPlayer(url: url)
161-
_player = State(wrappedValue: player)
169+
self.url = url
162170
}
163171

164172
var body: some View {
165-
VideoPlayer(player: player)
166-
.clipped()
167-
.onAppear {
168-
try? AVAudioSession.sharedInstance().setCategory(.playback, options: [])
169-
player.play()
173+
VStack {
174+
if let avPlayer {
175+
VideoPlayer(player: avPlayer)
176+
.clipped()
177+
}
178+
}
179+
.onAppear {
180+
fileCDN.adjustedURL(for: url) { result in
181+
switch result {
182+
case let .success(url):
183+
self.avPlayer = AVPlayer(url: url)
184+
try? AVAudioSession.sharedInstance().setCategory(.playback, options: [])
185+
self.avPlayer?.play()
186+
case let .failure(error):
187+
self.error = error
188+
}
170189
}
190+
}
171191
}
172192
}

Sources/StreamChatSwiftUI/ChatChannel/Gallery/VideoPlayerView.swift

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,18 @@ public struct VideoPlayerView: View {
1212

1313
@Injected(\.fonts) private var fonts
1414
@Injected(\.colors) private var colors
15+
@Injected(\.utils) private var utils
16+
17+
private var fileCDN: FileCDN {
18+
utils.fileCDN
19+
}
1520

1621
let attachment: ChatMessageVideoAttachment
1722
let author: ChatUser
1823
@Binding var isShown: Bool
1924

20-
private let avPlayer: AVPlayer
25+
@State private var avPlayer: AVPlayer?
26+
@State private var error: Error?
2127

2228
public init(
2329
attachment: ChatMessageVideoAttachment,
@@ -26,7 +32,6 @@ public struct VideoPlayerView: View {
2632
) {
2733
self.attachment = attachment
2834
self.author = author
29-
avPlayer = AVPlayer(url: attachment.payload.videoURL)
3035
_isShown = isShown
3136
}
3237

@@ -37,7 +42,9 @@ public struct VideoPlayerView: View {
3742
subtitle: author.onlineText,
3843
isShown: $isShown
3944
)
40-
VideoPlayer(player: avPlayer)
45+
if let avPlayer {
46+
VideoPlayer(player: avPlayer)
47+
}
4148
Spacer()
4249
HStack {
4350
ShareButtonView(content: [attachment.payload.videoURL])
@@ -48,11 +55,19 @@ public struct VideoPlayerView: View {
4855
.foregroundColor(Color(colors.text))
4956
}
5057
.onAppear {
51-
try? AVAudioSession.sharedInstance().setCategory(.playback, options: [])
52-
avPlayer.play()
58+
fileCDN.adjustedURL(for: attachment.payload.videoURL) { result in
59+
switch result {
60+
case let .success(url):
61+
self.avPlayer = AVPlayer(url: url)
62+
try? AVAudioSession.sharedInstance().setCategory(.playback, options: [])
63+
self.avPlayer?.play()
64+
case let .failure(error):
65+
self.error = error
66+
}
67+
}
5368
}
5469
.onDisappear {
55-
avPlayer.replaceCurrentItem(with: nil)
70+
avPlayer?.replaceCurrentItem(with: nil)
5671
}
5772
}
5873
}

Sources/StreamChatSwiftUI/ChatChannel/MessageList/FileAttachmentPreview.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public struct FileAttachmentPreview: View {
1818

1919
var url: URL
2020

21-
@State var adjustedUrl: URL?
21+
@State private var adjustedUrl: URL?
2222
@State private var isLoading = false
2323
@State private var title: String?
2424
@State private var error: Error?

Sources/StreamChatSwiftUI/Utils/Common/FileCDN.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import Foundation
66
import StreamChat
77

8-
/// A protocol the video preview uploader implementation must conform to.
8+
/// FileCDN provides a set of functions to improve handling of files & videos from CDN.
99
public protocol FileCDN: AnyObject {
1010
/// Prepare and return an adjusted or signed `URL` for the given file `URL`
1111
/// This function can be used to intercept an unsigned URL and return a valid signed URL

Sources/StreamChatSwiftUI/Utils/Common/VideoPreviewLoader.swift

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ public protocol VideoPreviewLoader: AnyObject {
1717

1818
/// The `VideoPreviewLoader` implemenation used by default.
1919
public final class DefaultVideoPreviewLoader: VideoPreviewLoader {
20+
@Injected(\.utils) var utils
21+
2022
private let cache: Cache<URL, UIImage>
2123

2224
public init(countLimit: Int = 50) {
@@ -39,26 +41,38 @@ public final class DefaultVideoPreviewLoader: VideoPreviewLoader {
3941
return call(completion, with: .success(cached))
4042
}
4143

42-
let asset = AVURLAsset(url: url)
43-
let imageGenerator = AVAssetImageGenerator(asset: asset)
44-
let frameTime = CMTime(seconds: 0.1, preferredTimescale: 600)
45-
46-
imageGenerator.appliesPreferredTrackTransform = true
47-
imageGenerator.generateCGImagesAsynchronously(forTimes: [.init(time: frameTime)]) { [weak self] _, image, _, _, error in
48-
guard let self = self else { return }
49-
50-
let result: Result<UIImage, Error>
51-
if let thumbnail = image {
52-
result = .success(.init(cgImage: thumbnail))
53-
} else if let error = error {
54-
result = .failure(error)
55-
} else {
56-
log.error("Both error and image are `nil`.")
44+
utils.fileCDN.adjustedURL(for: url) { result in
45+
46+
let adjustedUrl: URL
47+
switch result {
48+
case let .success(url):
49+
adjustedUrl = url
50+
case let .failure(error):
51+
self.call(completion, with: .failure(error))
5752
return
5853
}
5954

60-
self.cache[url] = try? result.get()
61-
self.call(completion, with: result)
55+
let asset = AVURLAsset(url: adjustedUrl)
56+
let imageGenerator = AVAssetImageGenerator(asset: asset)
57+
let frameTime = CMTime(seconds: 0.1, preferredTimescale: 600)
58+
59+
imageGenerator.appliesPreferredTrackTransform = true
60+
imageGenerator.generateCGImagesAsynchronously(forTimes: [.init(time: frameTime)]) { [weak self] _, image, _, _, error in
61+
guard let self = self else { return }
62+
63+
let result: Result<UIImage, Error>
64+
if let thumbnail = image {
65+
result = .success(.init(cgImage: thumbnail))
66+
} else if let error = error {
67+
result = .failure(error)
68+
} else {
69+
log.error("Both error and image are `nil`.")
70+
return
71+
}
72+
73+
self.cache[url] = try? result.get()
74+
self.call(completion, with: result)
75+
}
6276
}
6377
}
6478

0 commit comments

Comments
 (0)