diff --git a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift index a87109541..f5abd3f0c 100644 --- a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift @@ -438,6 +438,11 @@ class AudioPlayerMock: AudioPlayerProtocol { } var underlyingActions: AnyPublisher! var mediaSource: MediaSourceProxy? + var duration: TimeInterval { + get { return underlyingDuration } + set(value) { underlyingDuration = value } + } + var underlyingDuration: TimeInterval! var currentTime: TimeInterval { get { return underlyingCurrentTime } set(value) { underlyingCurrentTime = value } @@ -1082,6 +1087,11 @@ class KeychainControllerMock: KeychainControllerProtocol { } class MediaPlayerMock: MediaPlayerProtocol { var mediaSource: MediaSourceProxy? + var duration: TimeInterval { + get { return underlyingDuration } + set(value) { underlyingDuration = value } + } + var underlyingDuration: TimeInterval! var currentTime: TimeInterval { get { return underlyingCurrentTime } set(value) { underlyingCurrentTime = value } diff --git a/ElementX/Sources/Services/Audio/Player/AudioPlayerState.swift b/ElementX/Sources/Services/Audio/Player/AudioPlayerState.swift index 33c975780..e851c6c5e 100644 --- a/ElementX/Sources/Services/Audio/Player/AudioPlayerState.swift +++ b/ElementX/Sources/Services/Audio/Player/AudioPlayerState.swift @@ -34,7 +34,7 @@ enum AudioPlayerStateIdentifier { @MainActor class AudioPlayerState: ObservableObject, Identifiable { let id: AudioPlayerStateIdentifier - let duration: Double + private(set) var duration: Double let waveform: EstimatedWaveform @Published private(set) var playbackState: AudioPlayerPlaybackState /// It's similar to `playbackState`, with the a difference: `.loading` @@ -134,8 +134,12 @@ class AudioPlayerState: ObservableObject, Identifiable { case .didStartLoading: playbackState = .loading case .didFinishLoading: - playbackState = .readyToPlay + if let audioPlayerDuration = audioPlayer?.duration, audioPlayerDuration != duration { + MXLog.info("updating duration: \(duration) -> \(audioPlayerDuration)") + duration = audioPlayerDuration + } fileURL = audioPlayer?.url + playbackState = .readyToPlay case .didStartPlaying: if let audioPlayer { await restoreAudioPlayerState(audioPlayer: audioPlayer) diff --git a/ElementX/Sources/Services/MediaPlayer/MediaPlayerProtocol.swift b/ElementX/Sources/Services/MediaPlayer/MediaPlayerProtocol.swift index 7af5e4e22..2666d19af 100644 --- a/ElementX/Sources/Services/MediaPlayer/MediaPlayerProtocol.swift +++ b/ElementX/Sources/Services/MediaPlayer/MediaPlayerProtocol.swift @@ -26,7 +26,7 @@ enum MediaPlayerState { protocol MediaPlayerProtocol: AnyObject { var mediaSource: MediaSourceProxy? { get } - + var duration: TimeInterval { get } var currentTime: TimeInterval { get } var url: URL? { get } var state: MediaPlayerState { get } diff --git a/ElementX/Sources/Services/VoiceMessage/VoiceMessageMediaManager.swift b/ElementX/Sources/Services/VoiceMessage/VoiceMessageMediaManager.swift index 62f6241e5..49aa29eab 100644 --- a/ElementX/Sources/Services/VoiceMessage/VoiceMessageMediaManager.swift +++ b/ElementX/Sources/Services/VoiceMessage/VoiceMessageMediaManager.swift @@ -56,7 +56,7 @@ class VoiceMessageMediaManager: VoiceMessageMediaManagerProtocol { let loadFileBgTask = await backgroundTaskService?.startBackgroundTask(withName: "LoadFile: \(source.url.hashValue)") defer { loadFileBgTask?.stop() } - guard let mimeType = source.mimeType, mimeType == supportedVoiceMessageMimeType else { + guard let mimeType = source.mimeType, mimeType.starts(with: supportedVoiceMessageMimeType) else { throw VoiceMessageMediaManagerError.unsupportedMimeTye } diff --git a/UnitTests/Sources/AudioPlayerStateTests.swift b/UnitTests/Sources/AudioPlayerStateTests.swift index c977456be..8131deedd 100644 --- a/UnitTests/Sources/AudioPlayerStateTests.swift +++ b/UnitTests/Sources/AudioPlayerStateTests.swift @@ -171,8 +171,9 @@ class AudioPlayerStateTests: XCTestCase { } func testHandlingAudioPlayerActionDidFinishLoading() async throws { - let originalStateProgress = 0.4 - await audioPlayerState.updateState(progress: originalStateProgress) + audioPlayerMock.duration = 10.0 + + audioPlayerState = AudioPlayerState(id: .timelineItemIdentifier(.random), duration: 0) audioPlayerState.attachAudioPlayer(audioPlayerMock) let deferred = deferFulfillment(audioPlayerState.$playbackState) { action in @@ -189,6 +190,8 @@ class AudioPlayerStateTests: XCTestCase { // The state is expected to be .readyToPlay XCTAssertEqual(audioPlayerState.playbackState, .readyToPlay) + // The duration should have been updated with the player's duration + XCTAssertEqual(audioPlayerState.duration, audioPlayerMock.duration) } func testHandlingAudioPlayerActionDidStartPlaying() async throws { diff --git a/UnitTests/Sources/VoiceMessageMediaManagerTests.swift b/UnitTests/Sources/VoiceMessageMediaManagerTests.swift index 8c20942b5..590fe8396 100644 --- a/UnitTests/Sources/VoiceMessageMediaManagerTests.swift +++ b/UnitTests/Sources/VoiceMessageMediaManagerTests.swift @@ -52,6 +52,29 @@ class VoiceMessageMediaManagerTests: XCTestCase { } } + func testLoadVoiceMessageFromSourceMimeTypeWithParameters() async throws { + // URL representing the file loaded by the media provider + let loadedFile = URL("/some/url/loaded_file.ogg") + // URL representing the final cached file + let cachedConvertedFileURL = URL("/some/url/cached_converted_file.m4a") + + voiceMessageCache.fileURLForReturnValue = nil + let mediaSource = MediaSourceProxy(url: someURL, mimeType: "audio/ogg; codecs=opus") + mediaProvider.loadFileFromSourceReturnValue = MediaFileHandleProxy.unmanaged(url: loadedFile) + voiceMessageCache.cacheMediaSourceUsingMoveReturnValue = .success(cachedConvertedFileURL) + + voiceMessageMediaManager = VoiceMessageMediaManager(mediaProvider: mediaProvider, + voiceMessageCache: voiceMessageCache, + audioConverter: AudioConverterMock(), + backgroundTaskService: MockBackgroundTaskService()) + + do { + _ = try await voiceMessageMediaManager.loadVoiceMessageFromSource(mediaSource, body: nil) + } catch { + XCTFail("An unexpected error has occured: \(error)") + } + } + func testLoadVoiceMessageFromSourceAlreadyCached() async throws { // Check if the file is already present in cache voiceMessageCache.fileURLForReturnValue = URL("/converted_file/url") diff --git a/changelog.d/2006.bugfix b/changelog.d/2006.bugfix new file mode 100644 index 000000000..5a02c611b --- /dev/null +++ b/changelog.d/2006.bugfix @@ -0,0 +1 @@ +Fixed some issues with voice messages when sent from a bridge.