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

Implements new download manager and HLS downloads (Closes #267) #713

Merged
merged 13 commits into from
Jun 4, 2024
Merged
1 change: 1 addition & 0 deletions .swiftlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ disabled_rules:
- implicit_getter
- for_where
- opening_brace
- vertical_parameter_alignment

opt_in_rules:
- redundant_nil_coalescing
Expand Down
15 changes: 14 additions & 1 deletion Packages/ConfCore/ConfCore/Session.swift
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ public class Session: Object, Decodable {
// MARK: - Decodable

private enum AssetCodingKeys: String, CodingKey {
case id, year, title, downloadHD, downloadSD, slides, hls, images, shelf, duration
case id, year, title, downloadHD, downloadSD, downloadHLS, slides, hls, images, shelf, duration
}

private enum SessionCodingKeys: String, CodingKey {
Expand Down Expand Up @@ -230,6 +230,19 @@ public class Session: Object, Decodable {

self.assets.append(slidesAsset)
}

if let downloadHLS = try assetContainer.decodeIfPresent(String.self, forKey: .downloadHLS) {
let downloadHLSVideo = SessionAsset()
downloadHLSVideo.rawAssetType = SessionAssetType.downloadHLSVideo.rawValue
downloadHLSVideo.remoteURL = downloadHLS
downloadHLSVideo.year = Int(eventYear) ?? -1
downloadHLSVideo.sessionId = downloadHLS

let filename = "\(title).movpkg"
downloadHLSVideo.relativeLocalURL = "\(eventYear)/\(filename)"

self.assets.append(downloadHLSVideo)
}
}

func decodeRelatedIfPresent() throws {
Expand Down
10 changes: 2 additions & 8 deletions Packages/ConfCore/ConfCore/SessionAsset.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public enum SessionAssetType: String {
case none
case hdVideo = "WWDCSessionAssetTypeHDVideo"
case sdVideo = "WWDCSessionAssetTypeSDVideo"
case downloadHLSVideo = "WWDCSessionAssetTypeDownloadHLSVideo"
case image = "WWDCSessionAssetTypeShelfImage"
case slides = "WWDCSessionAssetTypeSlidesPDF"
case streamingVideo = "WWDCSessionAssetTypeStreamingVideo"
Expand All @@ -23,14 +24,7 @@ public enum SessionAssetType: String {
/// Session assets are resources associated with sessions, like videos, PDFs and useful links
public class SessionAsset: Object, Decodable {

/// The type of asset:
///
/// - WWDCSessionAssetTypeHDVideo
/// - WWDCSessionAssetTypeSDVideo
/// - WWDCSessionAssetTypeShelfImage
/// - WWDCSessionAssetTypeSlidesPDF
/// - WWDCSessionAssetTypeStreamingVideo
/// - WWDCSessionAssetTypeWebpageURL
/// The type of asset.
@objc internal dynamic var rawAssetType = "" {
didSet {
identifier = generateIdentifier()
Expand Down
100 changes: 100 additions & 0 deletions WWDC.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

9 changes: 5 additions & 4 deletions WWDC/AppCommandsReceiver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import OSLog
final class AppCommandsReceiver: Logging {
static let log = makeLogger(subsystem: "io.wwdc.app")

@MainActor
// swiftlint:disable:next cyclomatic_complexity
func handle(_ command: WWDCAppCommand, storage: Storage) -> DeepLink? {
log.debug("\(#function, privacy: .public) \(String(describing: command))")
Expand Down Expand Up @@ -40,14 +41,14 @@ final class AppCommandsReceiver: Logging {
return nil
case .download:
guard let session = command.session(in: storage) else { return nil }
DownloadManager.shared.download([session])

MediaDownloadManager.shared.download([session])

return nil
case .cancelDownload:
guard let session = command.session(in: storage) else { return nil }

DownloadManager.shared.cancelDownloads([session])
MediaDownloadManager.shared.cancelDownload(for: [session])

return nil
case .revealVideo:
Expand Down
12 changes: 9 additions & 3 deletions WWDC/AppCoordinator+SessionActions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ import OSLog

extension AppCoordinator: SessionActionsViewControllerDelegate {

@MainActor
func sessionActionsDidSelectCancelDownload(_ sender: NSView?) {
guard let viewModel = activeTabSelectedSessionViewModel else { return }

DownloadManager.shared.cancelDownloads([viewModel.session])
MediaDownloadManager.shared.cancelDownload(for: [viewModel.session])
}

func sessionActionsDidSelectFavorite(_ sender: NSView?) {
Expand All @@ -37,10 +38,11 @@ extension AppCoordinator: SessionActionsViewControllerDelegate {
NSWorkspace.shared.open(url)
}

@MainActor
func sessionActionsDidSelectDownload(_ sender: NSView?) {
guard let viewModel = activeTabSelectedSessionViewModel else { return }

DownloadManager.shared.download([viewModel.session])
MediaDownloadManager.shared.download([viewModel.session])
}

func sessionActionsDidSelectDeleteDownload(_ sender: NSView?) {
Expand All @@ -63,7 +65,11 @@ extension AppCoordinator: SessionActionsViewControllerDelegate {

switch choice {
case .yes:
DownloadManager.shared.deleteDownloadedFile(for: viewModel.session)
do {
try MediaDownloadManager.shared.removeDownloadedMedia(for: viewModel.session)
} catch {
NSAlert(error: error).runModal()
}
case .no:
break
}
Expand Down
23 changes: 12 additions & 11 deletions WWDC/AppCoordinator+SessionTableViewContextMenuActions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ extension AppCoordinator: SessionsTableViewControllerDelegate {
storage.setFavorite(false, onSessionsWithIDs: viewModels.map({ $0.session.identifier }))
}

@MainActor
func sessionTableViewContextMenuActionDownload(viewModels: [SessionViewModel]) {
if viewModels.count > 5 {
// asking to download many videos, warn
Expand All @@ -56,27 +57,27 @@ extension AppCoordinator: SessionsTableViewControllerDelegate {
guard case .yes = choice else { return }
}

DownloadManager.shared.download(viewModels.map { $0.session })
MediaDownloadManager.shared.download(viewModels.map(\.session))
}

@MainActor
func sessionTableViewContextMenuActionCancelDownload(viewModels: [SessionViewModel]) {
viewModels.forEach { viewModel in

guard DownloadManager.shared.isDownloading(viewModel.session) else { return }

DownloadManager.shared.deleteDownloadedFile(for: viewModel.session)
}
let cancellableDownloads = viewModels.map(\.session).filter { MediaDownloadManager.shared.isDownloadingMedia(for: $0) }

MediaDownloadManager.shared.cancelDownload(for: cancellableDownloads)
}

@MainActor
func sessionTableViewContextMenuActionRemoveDownload(viewModels: [SessionViewModel]) {
viewModels.forEach { viewModel in
DownloadManager.shared.deleteDownloadedFile(for: viewModel.session)
}
let deletableDownloads = viewModels.map(\.session).filter { MediaDownloadManager.shared.hasDownloadedMedia(for: $0) }

MediaDownloadManager.shared.delete(deletableDownloads)
}

@MainActor
func sessionTableViewContextMenuActionRevealInFinder(viewModels: [SessionViewModel]) {
guard let firstSession = viewModels.first?.session else { return }
guard let localURL = DownloadManager.shared.downloadedFileURL(for: firstSession) else { return }
guard let localURL = MediaDownloadManager.shared.downloadedFileURL(for: firstSession) else { return }

NSWorkspace.shared.selectFile(localURL.path, inFileViewerRootedAtPath: localURL.deletingLastPathComponent().path)
}
Expand Down
17 changes: 12 additions & 5 deletions WWDC/AppCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ final class AppCoordinator: Logging, Signposting {
}
}

private lazy var downloadMonitor = DownloadedContentMonitor()

@MainActor
init(windowController: MainWindowController, storage: Storage, syncEngine: SyncEngine) {
let signpostState = Self.signposter.beginInterval("initialization", id: Self.signposter.makeSignpostID(), "begin init")
self.storage = storage
Expand All @@ -112,8 +115,6 @@ final class AppCoordinator: Logging, Signposting {
)
self.searchCoordinator = searchCoordinator

DownloadManager.shared.start(with: storage)

liveObserver = LiveObserver(dateProvider: today, storage: storage, syncEngine: syncEngine)

// Primary UI Initialization
Expand Down Expand Up @@ -178,7 +179,7 @@ final class AppCoordinator: Logging, Signposting {
NSApp.isAutomaticCustomizeTouchBarMenuItemEnabled = true

let buttonsController = TitleBarButtonsViewController(
downloadManager: DownloadManager.shared,
downloadManager: .shared,
storage: storage
)
windowController.titleBarViewController.statusViewController = buttonsController
Expand All @@ -187,6 +188,9 @@ final class AppCoordinator: Logging, Signposting {
DispatchQueue.main.async { self?.startSharePlay() }
}

MediaDownloadManager.shared.activate()
downloadMonitor.activate(with: storage)

startup()
Self.signposter.endInterval("initialization", signpostState, "end init")
}
Expand All @@ -208,8 +212,11 @@ final class AppCoordinator: Logging, Signposting {
self.preferredTranscriptLanguageDidChange($0)
}.store(in: &cancellables)
NotificationCenter.default.publisher(for: .SyncEngineDidSyncSessionsAndSchedule).receive(on: DispatchQueue.main).sink { [weak self] note in
guard self?.checkSyncEngineOperationSucceededAndShowError(note: note) == true else { return }
DownloadManager.shared.syncWithFileSystem()
guard let self else { return }

guard self.checkSyncEngineOperationSucceededAndShowError(note: note) == true else { return }

self.downloadMonitor.syncWithFileSystem()
}.store(in: &cancellables)
NotificationCenter.default.publisher(for: .WWDCEnvironmentDidChange).receive(on: DispatchQueue.main).sink { _ in
self.refresh(nil)
Expand Down
5 changes: 5 additions & 0 deletions WWDC/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, Logging {
private var storage: Storage?
private var syncEngine: SyncEngine?

@MainActor
private func startupUI(using storage: Storage, syncEngine: SyncEngine) {
self.storage = storage
self.syncEngine = syncEngine
Expand Down Expand Up @@ -157,6 +158,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, Logging {
coordinator?.receiveNotification(with: userInfo)
}

@MainActor
@objc func handleURLEvent(_ event: NSAppleEventDescriptor?, replyEvent: NSAppleEventDescriptor?) {
guard let event = event else { return }
guard let urlString = event.paramDescriptor(forKeyword: UInt32(keyDirectObject))?.stringValue else { return }
Expand All @@ -165,6 +167,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, Logging {
openURL(url)
}

@MainActor
private func openURL(_ url: URL) {
if let command = WWDCAppCommand(from: url) {
handle(command)
Expand Down Expand Up @@ -277,10 +280,12 @@ extension AppDelegate: SUUpdaterDelegate {
}

extension AppDelegate {
@MainActor
static func run(_ command: WWDCAppCommand) {
(NSApp.delegate as? Self)?.handle(command, assumeSafe: true)
}

@MainActor
func handle(_ command: WWDCAppCommand, assumeSafe: Bool = false) {
if command.isForeground {
DispatchQueue.main.async { NSApp.activate(ignoringOtherApps: true) }
Expand Down
2 changes: 2 additions & 0 deletions WWDC/Boot.swift
Original file line number Diff line number Diff line change
Expand Up @@ -237,3 +237,5 @@ extension NSApplication {
exit(0)
}
}

extension NSWorkspace: @unchecked Sendable { }
Loading