Move media file loading logic to the SDK. (#702)

* Adopt getMediaFile for media previewing.
This commit is contained in:
Doug
2023-03-20 14:51:33 +00:00
committed by GitHub
parent bbd64092e7
commit efdb47a98a
42 changed files with 427 additions and 519 deletions
@@ -17,7 +17,6 @@
import Combine
import Foundation
import UIKit
import UniformTypeIdentifiers
class RoomTimelineController: RoomTimelineControllerProtocol {
private let userId: String
@@ -105,44 +104,34 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
guard let timelineItem = timelineItems.first(where: { $0.id == itemID }) else {
return .none
}
var source: MediaSourceProxy?
var title: String
switch timelineItem {
case let item as ImageRoomTimelineItem:
await loadFileForImageTimelineItem(item)
guard let index = timelineItems.firstIndex(where: { $0.id == itemID }),
let item = timelineItems[index] as? ImageRoomTimelineItem,
let fileURL = item.cachedFileURL else {
return .none
}
return .displayFile(fileURL: fileURL, title: item.body)
source = item.source
title = item.body
case let item as VideoRoomTimelineItem:
await loadVideoForTimelineItem(item)
guard let index = timelineItems.firstIndex(where: { $0.id == itemID }),
let item = timelineItems[index] as? VideoRoomTimelineItem,
let videoURL = item.cachedVideoURL else {
return .none
}
return .displayVideo(videoURL: videoURL, title: item.body)
source = item.source
title = item.body
case let item as FileRoomTimelineItem:
await loadFileForTimelineItem(item)
guard let index = timelineItems.firstIndex(where: { $0.id == itemID }),
let item = timelineItems[index] as? FileRoomTimelineItem,
let fileURL = item.cachedFileURL else {
return .none
}
return .displayFile(fileURL: fileURL, title: item.body)
source = item.source
title = item.body
case let item as AudioRoomTimelineItem:
await loadAudioForTimelineItem(item)
guard let index = timelineItems.firstIndex(where: { $0.id == itemID }),
let item = timelineItems[index] as? AudioRoomTimelineItem,
let audioURL = item.cachedAudioURL else {
return .none
}
// For now we are just displaying audio messages with the File preview until we create a timeline player for them.
return .displayFile(fileURL: audioURL, title: item.body)
source = item.source
title = item.body
default:
return .none
}
guard let source else { return .none }
switch await mediaProvider.loadFileFromSource(source) {
case .success(let file):
return .displayMediaFile(file: file, title: title)
case .failure:
return .none
}
}
func sendMessage(_ message: String, inReplyTo itemID: String?) async {
@@ -305,134 +294,4 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
return false
}
private func loadVideoForTimelineItem(_ timelineItem: VideoRoomTimelineItem) async {
if timelineItem.cachedVideoURL != nil {
// already cached
return
}
guard let source = timelineItem.source else {
return
}
let fileExtension = movieFileExtension(for: timelineItem.body)
switch await mediaProvider.loadFileFromSource(source, fileExtension: fileExtension) {
case .success(let fileURL):
guard let index = timelineItems.firstIndex(where: { $0.id == timelineItem.id }),
var item = timelineItems[index] as? VideoRoomTimelineItem else {
return
}
item.cachedVideoURL = fileURL
timelineItems[index] = item
case .failure:
break
}
}
/// Temporary method that generates a file extension for a video file name
/// using `UTType.movie` and falls back to .mp4 if anything goes wrong.
///
/// Ideally Rust should be able to handle this for us, otherwise we should be
/// attempting to detect the file type from the data itself.
private func movieFileExtension(for text: String) -> String {
let fallbackExtension = "mp4"
// This is not great. We could better estimate file extension from the mimetype.
guard let fileExtensionComponent = text.split(separator: ".").last else { return fallbackExtension }
let fileExtension = String(fileExtensionComponent)
// We can't trust that the extension provided is an extension that AVFoundation will accept.
guard let fileType = UTType(filenameExtension: fileExtension),
fileType.isSubtype(of: .movie)
else { return fallbackExtension }
return fileExtension
}
private func loadFileForImageTimelineItem(_ timelineItem: ImageRoomTimelineItem) async {
if timelineItem.cachedFileURL != nil {
// already cached
return
}
guard let source = timelineItem.source else {
return
}
// This is not great. We could better estimate file extension from the mimetype.
guard let fileExtension = timelineItem.body.split(separator: ".").last else {
return
}
switch await mediaProvider.loadFileFromSource(source, fileExtension: String(fileExtension)) {
case .success(let fileURL):
guard let index = timelineItems.firstIndex(where: { $0.id == timelineItem.id }),
var item = timelineItems[index] as? ImageRoomTimelineItem else {
return
}
item.cachedFileURL = fileURL
timelineItems[index] = item
case .failure:
break
}
}
private func loadFileForTimelineItem(_ timelineItem: FileRoomTimelineItem) async {
if timelineItem.cachedFileURL != nil {
// already cached
return
}
guard let source = timelineItem.source else {
return
}
// This is not great. We could better estimate file extension from the mimetype.
guard let fileExtension = timelineItem.body.split(separator: ".").last else {
return
}
switch await mediaProvider.loadFileFromSource(source, fileExtension: String(fileExtension)) {
case .success(let fileURL):
guard let index = timelineItems.firstIndex(where: { $0.id == timelineItem.id }),
var item = timelineItems[index] as? FileRoomTimelineItem else {
return
}
item.cachedFileURL = fileURL
timelineItems[index] = item
case .failure:
break
}
}
private func loadAudioForTimelineItem(_ timelineItem: AudioRoomTimelineItem) async {
if timelineItem.cachedAudioURL != nil {
// already cached
return
}
guard let source = timelineItem.source else {
return
}
// This is not great. We could better estimate file extension from the mimetype.
guard let fileExtension = timelineItem.body.split(separator: ".").last else {
return
}
switch await mediaProvider.loadFileFromSource(source, fileExtension: String(fileExtension)) {
case .success(let audioURL):
guard let index = timelineItems.firstIndex(where: { $0.id == timelineItem.id }),
var item = timelineItems[index] as? AudioRoomTimelineItem else {
return
}
item.cachedAudioURL = audioURL
timelineItems[index] = item
case .failure:
break
}
}
}