Skip to content

Commit

Permalink
Sync: video stream priority (openedx#561)
Browse files Browse the repository at this point in the history
* chore: improve the logic of video stream priority (#89)

* chore: set hls as priority quality for .auto case

* fix: fix failed test

---------

Co-authored-by: Saeed Bashir <[email protected]>
Co-authored-by: Anton Yarmolenko <[email protected]>
  • Loading branch information
3 people authored Jan 22, 2025
1 parent e903d39 commit 99f0dea
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 45 deletions.
26 changes: 16 additions & 10 deletions Core/Core/Domain/Model/CourseBlockModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ public struct CourseBlockEncodedVideo: Sendable {
public func video(downloadQuality: DownloadQuality) -> CourseBlockVideo? {
switch downloadQuality {
case .auto:
[mobileLow, mobileHigh, desktopMP4, fallback, hls]
[hls, mobileLow, mobileHigh, desktopMP4, fallback]
.first(where: { $0?.isDownloadable == true })?
.flatMap { $0 }
case .high:
Expand All @@ -400,44 +400,50 @@ public struct CourseBlockEncodedVideo: Sendable {
public func video(streamingQuality: StreamingQuality) -> CourseBlockVideo? {
switch streamingQuality {
case .auto:
[mobileLow, mobileHigh, desktopMP4, fallback, hls]
[hls, mobileLow, mobileHigh, desktopMP4, fallback, youtube]
.compactMap { $0 }
.sorted(by: { ($0?.streamPriority ?? 0) < ($1?.streamPriority ?? 0) })
.first?
.flatMap { $0 }
case .high:
[desktopMP4, mobileHigh, mobileLow, fallback, hls]
[desktopMP4, mobileHigh, mobileLow, hls, youtube, fallback]
.compactMap { $0 }
.first?
.flatMap { $0 }
case .medium:
[mobileHigh, mobileLow, desktopMP4, fallback, hls]
[mobileHigh, mobileLow, desktopMP4, hls, youtube, fallback]
.compactMap { $0 }
.first?
.flatMap { $0 }
case .low:
[mobileLow, mobileHigh, desktopMP4, fallback, hls]
[mobileLow, mobileHigh, desktopMP4, hls, youtube, fallback]
.compactMap { $0 }
.first(where: { $0?.isDownloadable == true })?
.flatMap { $0 }
}
}
}

public var youtubeVideoUrl: String? {
youtube?.url
}

public enum CourseBlockVideoEncoding: Sendable {
case mobileLow, mobileHigh, desktopMP4, fallback, hls, youtube
}

public struct CourseBlockVideo: Equatable, Sendable {
public let url: String?
public let fileSize: Int?
public let streamPriority: Int?
public let type: CourseBlockVideoEncoding?

public init(url: String?, fileSize: Int?, streamPriority: Int?) {
public init(
url: String?,
fileSize: Int?,
streamPriority: Int?,
type: CourseBlockVideoEncoding?
) {
self.url = url
self.fileSize = fileSize
self.streamPriority = streamPriority
self.type = type
}

public var isVideoURL: Bool {
Expand Down
3 changes: 2 additions & 1 deletion Core/CoreTests/DownloadManager/DownloadManagerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,8 @@ final class DownloadManagerTests: XCTestCase {
fallback: CourseBlockVideo(
url: "https://test.com/video.mp4",
fileSize: videoSize,
streamPriority: 1
streamPriority: 1,
type: .desktopMP4
),
youtube: nil,
desktopMP4: nil,
Expand Down
76 changes: 59 additions & 17 deletions Course/Course/Data/CourseRepository.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public actor CourseRepository: CourseRepositoryProtocol {
self.config = config
self.persistence = persistence
}

public func getCourseBlocks(courseID: String) async throws -> CourseStructure {
let course = try await api.requestData(
CourseEndpoint.getCourseBlocks(courseID: courseID, userName: coreStorage.user?.username ?? "")
Expand Down Expand Up @@ -245,26 +245,48 @@ public actor CourseRepository: CourseRepositoryProtocol {
webUrl: block.webUrl,
subtitles: subtitles,
encodedVideo: .init(
fallback: parseVideo(encodedVideo: block.userViewData?.encodedVideo?.fallback),
youtube: parseVideo(encodedVideo: block.userViewData?.encodedVideo?.youTube),
desktopMP4: parseVideo(encodedVideo: block.userViewData?.encodedVideo?.desktopMP4),
mobileHigh: parseVideo(encodedVideo: block.userViewData?.encodedVideo?.mobileHigh),
mobileLow: parseVideo(encodedVideo: block.userViewData?.encodedVideo?.mobileLow),
hls: parseVideo(encodedVideo: block.userViewData?.encodedVideo?.hls)
fallback: parseVideo(
encodedVideo: block.userViewData?.encodedVideo?.fallback,
type: .fallback
),
youtube: parseVideo(
encodedVideo: block.userViewData?.encodedVideo?.youTube,
type: .youtube
),
desktopMP4: parseVideo(
encodedVideo: block.userViewData?.encodedVideo?.desktopMP4,
type: .desktopMP4
),
mobileHigh: parseVideo(
encodedVideo: block.userViewData?.encodedVideo?.mobileHigh,
type: .mobileHigh
),
mobileLow: parseVideo(
encodedVideo: block.userViewData?.encodedVideo?.mobileLow,
type: .mobileLow
),
hls: parseVideo(
encodedVideo: block.userViewData?.encodedVideo?.hls,
type: .hls
)
),
multiDevice: block.multiDevice,
offlineDownload: offlineDownload
)
}

private func parseVideo(encodedVideo: DataLayer.EncodedVideoData?) -> CourseBlockVideo? {
private func parseVideo(
encodedVideo: DataLayer.EncodedVideoData?,
type: CourseBlockVideoEncoding
) -> CourseBlockVideo? {
guard let encodedVideo, encodedVideo.url?.isEmpty == false else {
return nil
}
return .init(
url: encodedVideo.url,
fileSize: encodedVideo.fileSize,
streamPriority: encodedVideo.streamPriority
streamPriority: encodedVideo.streamPriority,
type: type
)
}
}
Expand Down Expand Up @@ -482,26 +504,46 @@ And there are various ways of describing it-- call it oral poetry or
webUrl: block.webUrl,
subtitles: subtitles,
encodedVideo: .init(
fallback: parseVideo(encodedVideo: block.userViewData?.encodedVideo?.fallback),
youtube: parseVideo(encodedVideo: block.userViewData?.encodedVideo?.youTube),
desktopMP4: parseVideo(encodedVideo: block.userViewData?.encodedVideo?.desktopMP4),
mobileHigh: parseVideo(encodedVideo: block.userViewData?.encodedVideo?.mobileHigh),
mobileLow: parseVideo(encodedVideo: block.userViewData?.encodedVideo?.mobileLow),
hls: parseVideo(encodedVideo: block.userViewData?.encodedVideo?.hls)
fallback: parseVideo(
encodedVideo: block.userViewData?.encodedVideo?.fallback,
type: .fallback
),
youtube: parseVideo(
encodedVideo: block.userViewData?.encodedVideo?.youTube,
type: .youtube
),
desktopMP4: parseVideo(
encodedVideo: block.userViewData?.encodedVideo?.desktopMP4,
type: .desktopMP4
),
mobileHigh: parseVideo(
encodedVideo: block.userViewData?.encodedVideo?.mobileHigh,
type: .mobileHigh
),
mobileLow: parseVideo(
encodedVideo: block.userViewData?.encodedVideo?.mobileLow, type: .mobileLow),
hls: parseVideo(
encodedVideo: block.userViewData?.encodedVideo?.hls,
type: .hls
)
),
multiDevice: block.multiDevice,
offlineDownload: offlineDownload
)
}

private func parseVideo(encodedVideo: DataLayer.EncodedVideoData?) -> CourseBlockVideo? {
private func parseVideo(
encodedVideo: DataLayer.EncodedVideoData?,
type: CourseBlockVideoEncoding
) -> CourseBlockVideo? {
guard let encodedVideo else {
return nil
}
return .init(
url: encodedVideo.url,
fileSize: encodedVideo.fileSize,
streamPriority: encodedVideo.streamPriority
streamPriority: encodedVideo.streamPriority,
type: type
)
}
}
Expand Down
18 changes: 9 additions & 9 deletions Course/Course/Presentation/Unit/CourseUnitViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,15 @@ public enum LessonType: Equatable {
case .discussion:
return .discussion(block.topicId ?? "", block.id, block.displayName)
case .video:
if block.encodedVideo?.youtubeVideoUrl != nil,
let encodedVideo = block.encodedVideo?.video(streamingQuality: streamingQuality)?.url {
return .video(videoUrl: encodedVideo, blockId: block.id)
} else if let youtubeVideoUrl = block.encodedVideo?.youtubeVideoUrl {
return .youtube(youtubeVideoUrl: youtubeVideoUrl, blockId: block.id)
} else if let encodedVideo = block.encodedVideo?.video(streamingQuality: streamingQuality)?.url {
return .video(videoUrl: encodedVideo, blockId: block.id)
} else if let encodedVideo = block.encodedVideo?.video(downloadQuality: DownloadQuality.auto)?.url {
return .video(videoUrl: encodedVideo, blockId: block.id)
if let encodedVideo = block.encodedVideo?.video(streamingQuality: streamingQuality),
let videoURL = encodedVideo.url {
if encodedVideo.type == .youtube {
return .youtube(youtubeVideoUrl: videoURL, blockId: block.id)
} else if encodedVideo.isVideoURL {
return .video(videoUrl: videoURL, blockId: block.id)
} else {
return .unknown(block.studentUrl)
}
} else {
return .unknown(block.studentUrl)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,7 @@ final class CourseContainerViewModelTests: XCTestCase {
encodedVideo: .init(
fallback: nil,
youtube: nil,
desktopMP4: .init(url: "test.mp4", fileSize: 1000, streamPriority: 1),
desktopMP4: .init(url: "test.mp4", fileSize: 1000, streamPriority: 1, type: .desktopMP4),
mobileHigh: nil,
mobileLow: nil,
hls: nil
Expand Down Expand Up @@ -535,7 +535,7 @@ final class CourseContainerViewModelTests: XCTestCase {
encodedVideo: .init(
fallback: nil,
youtube: nil,
desktopMP4: .init(url: "test.mp4", fileSize: 1000, streamPriority: 1),
desktopMP4: .init(url: "test.mp4", fileSize: 1000, streamPriority: 1, type: .desktopMP4),
mobileHigh: nil,
mobileLow: nil,
hls: nil
Expand Down Expand Up @@ -662,7 +662,7 @@ final class CourseContainerViewModelTests: XCTestCase {
encodedVideo: .init(
fallback: nil,
youtube: nil,
desktopMP4: .init(url: "test.mp4", fileSize: 1000, streamPriority: 1),
desktopMP4: .init(url: "test.mp4", fileSize: 1000, streamPriority: 1, type: .desktopMP4),
mobileHigh: nil,
mobileLow: nil,
hls: nil
Expand Down Expand Up @@ -790,7 +790,7 @@ final class CourseContainerViewModelTests: XCTestCase {
encodedVideo: .init(
fallback: nil,
youtube: nil,
desktopMP4: .init(url: "test.mp4", fileSize: 1000, streamPriority: 1),
desktopMP4: .init(url: "test.mp4", fileSize: 1000, streamPriority: 1, type:.desktopMP4),
mobileHigh: nil,
mobileLow: nil,
hls: nil
Expand Down Expand Up @@ -911,7 +911,7 @@ final class CourseContainerViewModelTests: XCTestCase {
encodedVideo: .init(
fallback: nil,
youtube: nil,
desktopMP4: .init(url: "test.mp4", fileSize: 1000, streamPriority: 1),
desktopMP4: .init(url: "test.mp4", fileSize: 1000, streamPriority: 1, type:.desktopMP4),
mobileHigh: nil,
mobileLow: nil,
hls: nil
Expand Down Expand Up @@ -1044,7 +1044,7 @@ final class CourseContainerViewModelTests: XCTestCase {
encodedVideo: .init(
fallback: nil,
youtube: nil,
desktopMP4: .init(url: "test.mp4", fileSize: 1000, streamPriority: 1),
desktopMP4: .init(url: "test.mp4", fileSize: 1000, streamPriority: 1, type:.desktopMP4),
mobileHigh: nil,
mobileLow: nil,
hls: nil
Expand Down Expand Up @@ -1177,7 +1177,7 @@ final class CourseContainerViewModelTests: XCTestCase {
encodedVideo: .init(
fallback: nil,
youtube: nil,
desktopMP4: .init(url: "test.mp4", fileSize: 1000, streamPriority: 1),
desktopMP4: .init(url: "test.mp4", fileSize: 1000, streamPriority: 1, type:.desktopMP4),
mobileHigh: nil,
mobileLow: nil,
hls: nil
Expand All @@ -1200,7 +1200,7 @@ final class CourseContainerViewModelTests: XCTestCase {
encodedVideo: .init(
fallback: nil,
youtube: nil,
desktopMP4: .init(url: "test.mp4", fileSize: 1000, streamPriority: 1),
desktopMP4: .init(url: "test.mp4", fileSize: 1000, streamPriority: 1, type:.desktopMP4),
mobileHigh: nil,
mobileLow: nil,
hls: nil
Expand Down

0 comments on commit 99f0dea

Please sign in to comment.