Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Course bars: Download videos to device and Select download quality bars #239

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
5370d1d
chore: add videos downloading bar view
eyatsenkoperpetio Dec 15, 2023
9e16130
chore: add view and logic
eyatsenkoperpetio Dec 15, 2023
32ad2ea
chore: add progress to download data
eyatsenkoperpetio Dec 18, 2023
f88fe44
chore: downloads view
eyatsenkoperpetio Dec 19, 2023
82bb2ff
chore: add strings and fix delete video notif
eyatsenkoperpetio Dec 20, 2023
f1d9872
chore: add total progress for bar
eyatsenkoperpetio Dec 20, 2023
f191a36
chore: changes for no nested list flow
eyatsenkoperpetio Dec 20, 2023
fdca041
fix: showing all downloaded item if open from current course
eyatsenkoperpetio Dec 21, 2023
e6a448a
fix: bar progress
eyatsenkoperpetio Dec 21, 2023
d2258ef
chore: add large file alert and show all downloads in download view
eyatsenkoperpetio Dec 26, 2023
68b6e17
chore: change logic and remove extra code
eyatsenkoperpetio Dec 27, 2023
4030157
refactor: remove extra code
eyatsenkoperpetio Dec 27, 2023
ae7db32
chore: remove extra code
eyatsenkoperpetio Dec 27, 2023
02ccce8
chore: add course storage, refactor
eyatsenkoperpetio Dec 28, 2023
1f40b96
chore: add new video formats
eyatsenkoperpetio Dec 28, 2023
ebc81cd
chore: and video to course block
eyatsenkoperpetio Dec 29, 2023
0b1a83e
chore: add new core data models
eyatsenkoperpetio Dec 29, 2023
451ee45
chore: download manager to async await
eyatsenkoperpetio Dec 29, 2023
4b5ea44
chore: add calculate total size download bar
eyatsenkoperpetio Jan 2, 2024
0f8a930
chore: hide total if zero
eyatsenkoperpetio Jan 3, 2024
16b956f
chore: clean up and fix transparent downloads bar when scroll
eyatsenkoperpetio Jan 3, 2024
930b1c8
chore: add new logic check is downloadable video
eyatsenkoperpetio Jan 3, 2024
7e6c218
chore: add download quality view
eyatsenkoperpetio Jan 4, 2024
24983d2
chore: add select quality for download
eyatsenkoperpetio Jan 5, 2024
6d9e3cb
chore: show alert about change download quality when downloading all …
eyatsenkoperpetio Jan 5, 2024
a868a69
chore: remove extra and add strings
eyatsenkoperpetio Jan 8, 2024
fcdb31d
Merge branch 'feat/166_progress_downloads' into feat/video_download_q…
eyatsenkoperpetio Jan 8, 2024
348b1bc
chore: remove empty line
eyatsenkoperpetio Jan 8, 2024
5257f37
chore: add cancel all download when move to background
eyatsenkoperpetio Jan 8, 2024
23b01a4
Merge branch 'develop' into feat/course_download_quality_bars_dev
eyatsenkoperpetio Jan 9, 2024
269a084
Merge branch 'develop' into feat/course_download_quality_bars_dev
rnr Jan 9, 2024
a1f9f1d
chore: add resume downloading
eyatsenkoperpetio Jan 9, 2024
f288e68
fix: remaining count in bar
eyatsenkoperpetio Jan 9, 2024
d50e4d1
chore: add new logic get quality video
eyatsenkoperpetio Jan 15, 2024
acfc9c8
chore: remove extra code
eyatsenkoperpetio Jan 15, 2024
c1d65e4
chore: improve download quality names
eyatsenkoperpetio Jan 15, 2024
2f8709a
Merge branch 'feat/course_download_quality_bars' into feat/course_dow…
eyatsenkoperpetio Jan 15, 2024
b5761cc
chore: new logic
eyatsenkoperpetio Jan 15, 2024
bb69379
chore: update logic
eyatsenkoperpetio Jan 15, 2024
a3972ad
chore: remove extra code
eyatsenkoperpetio Jan 15, 2024
02a59d0
chore: remove extra
eyatsenkoperpetio Jan 15, 2024
71460e1
chore: change strings
eyatsenkoperpetio Jan 15, 2024
209c1a0
chore: add string
eyatsenkoperpetio Jan 15, 2024
6298260
chore: show remaining files in download bar
eyatsenkoperpetio Jan 15, 2024
475328e
chore: verticals blocks downloadable count nested list
eyatsenkoperpetio Jan 15, 2024
7b30bf0
chore: count of files
eyatsenkoperpetio Jan 15, 2024
8b7723b
chore: add disable download when offline
eyatsenkoperpetio Jan 16, 2024
86c9d48
chore: remove empty line
eyatsenkoperpetio Jan 16, 2024
5f698fe
chore: show delete file when offline
eyatsenkoperpetio Jan 16, 2024
e18a7f1
chore: add Untitled title to download cell view
eyatsenkoperpetio Jan 17, 2024
320daf6
Merge branch 'develop' into feat/course_download_quality_bars_dev_alp…
eyatsenkoperpetio Jan 17, 2024
63512f7
Merge branch 'develop' into feat/course-download-quality-bars
eyatsenkoperpetio Jan 17, 2024
0c3a656
fix: tests
eyatsenkoperpetio Jan 17, 2024
19c3564
chore: PR issues and add accessibility labels
eyatsenkoperpetio Jan 18, 2024
6125d9a
chore: add accessibility identifiers
eyatsenkoperpetio Jan 18, 2024
6b635c2
chore: move video download quality view and refactor
eyatsenkoperpetio Jan 19, 2024
0b16dc6
chore: add cancel all for course
eyatsenkoperpetio Jan 19, 2024
8dc472b
chore: show large alert
eyatsenkoperpetio Jan 19, 2024
50b1649
chore: resolve PR comments
eyatsenkoperpetio Jan 19, 2024
ab1792a
chore: resolve PR comments
eyatsenkoperpetio Jan 19, 2024
e7c3b61
chore: resolve PR comments
eyatsenkoperpetio Jan 19, 2024
ccca6a7
fix: tests
eyatsenkoperpetio Jan 20, 2024
fb20cae
chore: resolve PR comments
eyatsenkoperpetio Jan 29, 2024
d5f904b
chore: resolve PR commnets
eyatsenkoperpetio Jan 29, 2024
318b1ab
chore: rename DownloadData to DownloadDataTask
eyatsenkoperpetio Jan 29, 2024
d4c88cf
chore: use app alert and tests
eyatsenkoperpetio Jan 29, 2024
d67ef53
chore: add confirmation alert when deleting video
eyatsenkoperpetio Jan 30, 2024
d705e59
chore: remove empty line
eyatsenkoperpetio Jan 30, 2024
57aad7f
chore: add strings
eyatsenkoperpetio Jan 30, 2024
baca08c
fix: line length
eyatsenkoperpetio Jan 31, 2024
3c099bf
Merge branch 'develop' into feat/course-download-quality-bars
eyatsenkoperpetio Jan 31, 2024
376368d
Merge branch 'develop' into feat/course-download-quality-bars
eyatsenkoperpetio Jan 31, 2024
81a4512
fix: repeat download
eyatsenkoperpetio Jan 31, 2024
22fa31f
chore: add alert when disable downloading
eyatsenkoperpetio Jan 31, 2024
f7f1ad5
chore: resolve PR commnets
eyatsenkoperpetio Jan 31, 2024
a64de26
chore: update pod version
eyatsenkoperpetio Feb 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
289 changes: 221 additions & 68 deletions Authorization/AuthorizationTests/AuthorizationMock.generated.swift

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions Core/Core.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@
07E0939F2B308D2800F1E4B2 /* Data_Certificate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07E0939E2B308D2800F1E4B2 /* Data_Certificate.swift */; };
A53A32352B233DEC005FE38A /* ThemeConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = A53A32342B233DEC005FE38A /* ThemeConfig.swift */; };
BA30427F2B20B320009B64B7 /* SocialAuthError.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA30427D2B20B299009B64B7 /* SocialAuthError.swift */; };
BA4AFB422B5A7A0900A21367 /* VideoDownloadQualityView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA4AFB412B5A7A0900A21367 /* VideoDownloadQualityView.swift */; };
BA593F1C2AF8E498009ADB51 /* ScrollSlidingTabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA593F1B2AF8E498009ADB51 /* ScrollSlidingTabBar.swift */; };
BA593F1E2AF8E4A0009ADB51 /* FrameReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA593F1D2AF8E4A0009ADB51 /* FrameReader.swift */; };
BA76135C2B21BC7300B599B7 /* SocialAuthResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA76135B2B21BC7300B599B7 /* SocialAuthResponse.swift */; };
Expand Down Expand Up @@ -295,6 +296,7 @@
9D5B06CAA99EA5CD49CBE2BB /* Pods-App-Core.debugdev.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App-Core.debugdev.xcconfig"; path = "Target Support Files/Pods-App-Core/Pods-App-Core.debugdev.xcconfig"; sourceTree = "<group>"; };
A53A32342B233DEC005FE38A /* ThemeConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThemeConfig.swift; sourceTree = "<group>"; };
BA30427D2B20B299009B64B7 /* SocialAuthError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocialAuthError.swift; sourceTree = "<group>"; };
BA4AFB412B5A7A0900A21367 /* VideoDownloadQualityView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoDownloadQualityView.swift; sourceTree = "<group>"; };
BA593F1B2AF8E498009ADB51 /* ScrollSlidingTabBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScrollSlidingTabBar.swift; sourceTree = "<group>"; };
BA593F1D2AF8E4A0009ADB51 /* FrameReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FrameReader.swift; sourceTree = "<group>"; };
BA76135B2B21BC7300B599B7 /* SocialAuthResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocialAuthResponse.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -673,6 +675,7 @@
027BD3C42909707700392132 /* Shake.swift */,
023A1135291432B200D0D354 /* RegistrationTextField.swift */,
023A1137291432FD00D0D354 /* FieldConfiguration.swift */,
BA4AFB412B5A7A0900A21367 /* VideoDownloadQualityView.swift */,
BA593F1A2AF8E487009ADB51 /* ScrollSlidingTabBar */,
BAAD62C52AFCF00B000E6103 /* CustomDisclosureGroup.swift */,
BA8FA6672AD59A5700EA029A /* SocialAuthButton.swift */,
Expand Down Expand Up @@ -978,6 +981,7 @@
0241666B28F5A78B00082765 /* HTMLFormattedText.swift in Sources */,
02D800CC29348F460099CF16 /* ImagePicker.swift in Sources */,
027BD3B92909476200392132 /* KeyboardAvoidingModifier.swift in Sources */,
BA4AFB422B5A7A0900A21367 /* VideoDownloadQualityView.swift in Sources */,
0770DE2C28D092B3006D8A5D /* NetworkLogger.swift in Sources */,
064987972B4D69FF0071642A /* WebView.swift in Sources */,
0770DE2A28D0929E006D8A5D /* HTTPTask.swift in Sources */,
Expand Down
19 changes: 16 additions & 3 deletions Core/Core/Data/Model/UserSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,19 @@

import Foundation

public struct UserSettings: Codable {
public struct UserSettings: Codable, Hashable {
public var wifiOnly: Bool
public var streamingQuality: StreamingQuality

public init(wifiOnly: Bool, streamingQuality: StreamingQuality) {
public var downloadQuality: DownloadQuality

public init(
wifiOnly: Bool,
streamingQuality: StreamingQuality,
downloadQuality: DownloadQuality
) {
self.wifiOnly = wifiOnly
self.streamingQuality = streamingQuality
self.downloadQuality = downloadQuality
}
}

Expand All @@ -23,3 +29,10 @@ public enum StreamingQuality: Codable {
case medium
case high
}

public enum DownloadQuality: Codable, CaseIterable {
case auto
case low_360
case medium_540
case high_720
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="22222" systemVersion="22G91" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="22522" systemVersion="22G91" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<entity name="CDDownloadData" representedClassName="CDDownloadData" syncable="YES" codeGenerationType="class">
<attribute name="courseId" optional="YES" attributeType="String"/>
<attribute name="displayName" optional="YES" attributeType="String"/>
<attribute name="fileName" optional="YES" attributeType="String"/>
<attribute name="fileSize" optional="YES" attributeType="Integer 32" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="id" optional="YES" attributeType="String"/>
<attribute name="progress" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="resumeData" optional="YES" attributeType="Binary"/>
Expand Down
15 changes: 8 additions & 7 deletions Core/Core/Data/Persistence/CorePersistenceProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ import Combine

public protocol CorePersistenceProtocol {
func publisher() -> AnyPublisher<Int, Never>
func getAllDownloadData() -> [DownloadData]
func addToDownloadQueue(blocks: [CourseBlock])
func getNextBlockForDownloading() -> DownloadData?
func getDownloadsForCourse(_ courseId: String) -> [DownloadData]
func downloadData(by blockId: String) -> DownloadData?
func addToDownloadQueue(blocks: [CourseBlock], downloadQuality: DownloadQuality)
func getNextBlockForDownloading() -> DownloadDataTask?
func updateDownloadState(id: String, state: DownloadState, resumeData: Data?)
func deleteDownloadData(id: String) throws
func saveDownloadData(data: DownloadData)
func deleteDownloadDataTask(id: String) throws
func saveDownloadDataTask(data: DownloadDataTask)
func downloadDataTask(for blockId: String) -> DownloadDataTask?
func downloadDataTask(for blockId: String, completion: @escaping (DownloadDataTask?) -> Void)
func getDownloadDataTasks(completion: @escaping ([DownloadDataTask]) -> Void)
func getDownloadDataTasksForCourse(_ courseId: String, completion: @escaping ([DownloadDataTask]) -> Void)
}

public final class CoreBundle {
Expand Down
163 changes: 147 additions & 16 deletions Core/Core/Domain/Model/CourseBlockModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
import Foundation

public struct CourseStructure: Equatable {
public static func == (lhs: CourseStructure, rhs: CourseStructure) -> Bool {
return lhs.id == rhs.id
}

public let id: String
public let graded: Bool
public let completion: Double
Expand Down Expand Up @@ -42,11 +46,24 @@ public struct CourseStructure: Equatable {
self.media = media
self.certificate = certificate
}

public static func == (lhs: CourseStructure, rhs: CourseStructure) -> Bool {
return lhs.id == rhs.id

public func totalVideosSizeInBytes(downloadQuality: DownloadQuality) -> Int {
childs.flatMap {
$0.childs.flatMap { $0.childs.flatMap { $0.childs.compactMap { $0 } } }
}
.filter { $0.isDownloadable }
.compactMap { $0.encodedVideo?.video(downloadQuality: downloadQuality)?.fileSize }
.reduce(.zero) { $0 + $1 }
}


public func totalVideosSizeInMb(downloadQuality: DownloadQuality) -> Double {
Double(totalVideosSizeInBytes(downloadQuality: downloadQuality)) / 1024.0 / 1024.0
}

public func totalVideosSizeInGb(downloadQuality: DownloadQuality) -> Double {
Double(totalVideosSizeInBytes(downloadQuality: downloadQuality)) / 1024.0 / 1024.0 / 1024.0
}

}

public struct CourseChapter: Identifiable {
Expand Down Expand Up @@ -102,7 +119,11 @@ public struct CourseSequential: Identifiable {
}
}

public struct CourseVertical {
public struct CourseVertical: Identifiable, Hashable {
public func hash(into hasher: inout Hasher) {
hasher.combine(id)
}

public let blockId: String
public let id: String
public let courseId: String
Expand All @@ -114,7 +135,7 @@ public struct CourseVertical {
public var isDownloadable: Bool {
return childs.first(where: { $0.isDownloadable }) != nil
}

public init(
blockId: String,
id: String,
Expand Down Expand Up @@ -144,7 +165,17 @@ public struct SubtitleUrl: Equatable {
}
}

public struct CourseBlock: Equatable {
public struct CourseBlock: Hashable {
public static func == (lhs: CourseBlock, rhs: CourseBlock) -> Bool {
lhs.id == rhs.id &&
lhs.blockId == rhs.blockId &&
lhs.completion == rhs.completion
}

public func hash(into hasher: inout Hasher) {
hasher.combine(id)
}

public let blockId: String
public let id: String
public let courseId: String
Expand All @@ -155,13 +186,12 @@ public struct CourseBlock: Equatable {
public let displayName: String
public let studentUrl: String
public let subtitles: [SubtitleUrl]?
public let videoUrl: String?
public let youTubeUrl: String?

public let encodedVideo: CourseBlockEncodedVideo?

public var isDownloadable: Bool {
return videoUrl != nil
encodedVideo?.isDownloadable ?? false
}

public init(
blockId: String,
id: String,
Expand All @@ -173,8 +203,7 @@ public struct CourseBlock: Equatable {
displayName: String,
studentUrl: String,
subtitles: [SubtitleUrl]? = nil,
videoUrl: String? = nil,
youTubeUrl: String? = nil
encodedVideo: CourseBlockEncodedVideo?
) {
self.blockId = blockId
self.id = id
Expand All @@ -186,7 +215,109 @@ public struct CourseBlock: Equatable {
self.displayName = displayName
self.studentUrl = studentUrl
self.subtitles = subtitles
self.videoUrl = videoUrl
self.youTubeUrl = youTubeUrl
self.encodedVideo = encodedVideo
}
}

public struct CourseBlockEncodedVideo {

public let fallback: CourseBlockVideo?
public let desktopMP4: CourseBlockVideo?
public let mobileHigh: CourseBlockVideo?
public let mobileLow: CourseBlockVideo?
public let hls: CourseBlockVideo?
public let youtube: CourseBlockVideo?
Comment on lines +224 to +229
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to use something similar to CourseDetailEncodedVideoData here instead of passing all the encodings separately?


public init(
fallback: CourseBlockVideo?,
youtube: CourseBlockVideo?,
desktopMP4: CourseBlockVideo?,
mobileHigh: CourseBlockVideo?,
mobileLow: CourseBlockVideo?,
hls: CourseBlockVideo?
) {
self.fallback = fallback
self.youtube = youtube
self.desktopMP4 = desktopMP4
self.mobileHigh = mobileHigh
self.mobileLow = mobileLow
self.hls = hls
}

public var isDownloadable: Bool {
[hls, desktopMP4, mobileHigh, mobileLow, fallback]
.contains { $0?.isDownloadable == true }
}

public func video(downloadQuality: DownloadQuality) -> CourseBlockVideo? {
switch downloadQuality {
case .auto:
[mobileLow, mobileHigh, desktopMP4, fallback, hls]
.first(where: { $0?.isDownloadable == true })?
.flatMap { $0 }
case .high_720:
[desktopMP4, mobileHigh, mobileLow, fallback, hls]
.first(where: { $0?.isDownloadable == true })?
.flatMap { $0 }
case .medium_540:
[mobileHigh, mobileLow, desktopMP4, fallback, hls]
.first(where: { $0?.isDownloadable == true })?
.flatMap { $0 }
case .low_360:
[mobileLow, mobileHigh, desktopMP4, fallback, hls]
.first(where: { $0?.isDownloadable == true })?
.flatMap { $0 }
}
}

public func video(streamingQuality: StreamingQuality) -> CourseBlockVideo? {
switch streamingQuality {
case .auto:
[mobileLow, mobileHigh, desktopMP4, fallback, hls]
.compactMap { $0 }
.sorted(by: { ($0?.streamPriority ?? 0) < ($1?.streamPriority ?? 0) })
.first?
.flatMap { $0 }
case .high:
[desktopMP4, mobileHigh, mobileLow, fallback, hls]
.compactMap { $0 }
.first?
.flatMap { $0 }
case .medium:
[mobileHigh, mobileLow, desktopMP4, fallback, hls]
.compactMap { $0 }
.first?
.flatMap { $0 }
case .low:
[mobileLow, mobileHigh, desktopMP4, fallback, hls]
.compactMap { $0 }
.first(where: { $0?.isDownloadable == true })?
.flatMap { $0 }
}
}

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

}

public struct CourseBlockVideo: Equatable {
public let url: String?
public let fileSize: Int?
public let streamPriority: Int?

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

public var isVideoURL: Bool {
[".mp4", ".m3u8"].contains(where: { url?.contains($0) == true })
}

public var isDownloadable: Bool {
[".mp4"].contains(where: { url?.contains($0) == true })
}
}
Loading
Loading