/
ConversionViewController.swift
135 lines (107 loc) Β· 3.77 KB
/
ConversionViewController.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
import AppKit
final class ConversionViewController: NSViewController {
private lazy var circularProgress = with(CircularProgress(size: 160.0)) {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.color = .themeColor
}
private lazy var timeRemainingLabel = with(Label()) {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.isHidden = true
$0.textColor = .secondaryLabelColor
$0.font = NSFont.monospacedDigitSystemFont(ofSize: 12, weight: .regular)
}
private lazy var timeRemainingEstimator = TimeRemainingEstimator(label: timeRemainingLabel)
private var conversion: Gifski.Conversion!
private var progress: Progress?
private var isRunning = false
convenience init(conversion: Gifski.Conversion) {
self.init()
self.conversion = conversion
}
override func loadView() {
let wrapper = NSView(frame: Constants.defaultWindowSize.cgRect)
wrapper.translatesAutoresizingMaskIntoConstraints = false
wrapper.addSubview(circularProgress)
wrapper.addSubview(timeRemainingLabel)
circularProgress.constrain(to: CGSize(widthHeight: circularProgress.frame.width))
circularProgress.center(inView: wrapper)
NSLayoutConstraint.activate([
timeRemainingLabel.topAnchor.constraint(greaterThanOrEqualTo: circularProgress.bottomAnchor),
timeRemainingLabel.centerXAnchor.constraint(equalTo: circularProgress.centerXAnchor),
timeRemainingLabel.bottomAnchor.constraint(equalTo: wrapper.bottomAnchor, constant: -16.0)
])
view = wrapper
}
override func viewDidAppear() {
super.viewDidAppear()
view.window?.makeFirstResponder(self)
start(conversion: conversion)
}
/// Gets called when the Esc key is pressed.
override func cancelOperation(_ sender: Any?) {
cancelConversion()
}
// TODO: Remove this when we target macOS 10.14.
@objc
func cancel(_ sender: Any?) {
cancelConversion()
}
private func start(conversion: Gifski.Conversion) {
guard !isRunning else {
return
}
isRunning = true
progress = Progress(totalUnitCount: 1)
progress?.publish()
circularProgress.progressInstance = progress
DockProgress.progressInstance = progress
timeRemainingEstimator.progress = progress
timeRemainingEstimator.start()
progress?.performAsCurrent(withPendingUnitCount: 1) { [weak self] in
Gifski.run(conversion) { result in
guard let self = self else {
return
}
do {
let gifUrl = self.generateTempGifUrl(for: conversion.video)
try result.get().write(to: gifUrl, options: .atomic)
try? gifUrl.setMetadata(key: .itemCreator, value: "\(App.name) \(App.version)")
Defaults[.successfulConversionsCount] += 1
self.didComplete(conversion: conversion, gifUrl: gifUrl)
} catch Gifski.Error.cancelled {
self.cancelConversion()
} catch {
self.presentError(error, modalFor: self.view.window)
self.cancelConversion()
}
}
}
}
private func generateTempGifUrl(for videoUrl: URL) -> URL {
let tempDirectory = FileManager.default.temporaryDirectory
let tempName = "\(videoUrl.filenameWithoutExtension).\(FileType.gif.fileExtension)"
return tempDirectory.appendingPathComponent(tempName, isDirectory: false)
}
private func cancelConversion() {
if progress?.isCancelled == false {
progress?.cancel()
}
let videoDropController = VideoDropViewController()
stopConversion { [weak self] in
self?.push(viewController: videoDropController)
}
}
private func didComplete(conversion: Gifski.Conversion, gifUrl: URL) {
let conversionCompleted = ConversionCompletedViewController(conversion: conversion, gifUrl: gifUrl)
stopConversion { [weak self] in
self?.push(viewController: conversionCompleted)
}
}
private func stopConversion(_ completion: (() -> Void)? = nil) {
isRunning = false
DockProgress.resetProgress()
circularProgress.fadeOut(delay: 0.5) {
completion?()
}
}
}