@capgo/capacitor-screen-recorder 8.3.7 → 8.3.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ import dev.bmcreations.scrcast.config.Options;
|
|
|
10
10
|
@CapacitorPlugin(name = "ScreenRecorder")
|
|
11
11
|
public class ScreenRecorderPlugin extends Plugin {
|
|
12
12
|
|
|
13
|
-
private final String pluginVersion = "8.3.
|
|
13
|
+
private final String pluginVersion = "8.3.9";
|
|
14
14
|
|
|
15
15
|
private CapgoScrCast videoRecorder;
|
|
16
16
|
private CapgoScrCast audioRecorder;
|
|
@@ -7,7 +7,7 @@ import Capacitor
|
|
|
7
7
|
*/
|
|
8
8
|
@objc(ScreenRecorderPlugin)
|
|
9
9
|
public class ScreenRecorderPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
10
|
-
private let pluginVersion: String = "8.3.
|
|
10
|
+
private let pluginVersion: String = "8.3.9"
|
|
11
11
|
public let identifier = "ScreenRecorderPlugin"
|
|
12
12
|
public let jsName = "ScreenRecorder"
|
|
13
13
|
public let pluginMethods: [CAPPluginMethod] = [
|
|
@@ -15,6 +15,7 @@ import UIKit
|
|
|
15
15
|
public enum ScreenRecorderError: Error {
|
|
16
16
|
case notAvailable
|
|
17
17
|
case photoLibraryAccessNotGranted
|
|
18
|
+
case alreadyRecording
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
public enum VideoContainerFormat {
|
|
@@ -51,6 +52,7 @@ public enum VideoContainerFormat {
|
|
|
51
52
|
|
|
52
53
|
public final class ScreenRecorder {
|
|
53
54
|
private var videoOutputURL: URL?
|
|
55
|
+
private var didCreateOutputFile = false
|
|
54
56
|
private var videoWriter: AVAssetWriter?
|
|
55
57
|
private var videoWriterInput: AVAssetWriterInput?
|
|
56
58
|
private var micAudioWriterInput: AVAssetWriterInput?
|
|
@@ -58,6 +60,7 @@ public final class ScreenRecorder {
|
|
|
58
60
|
private var saveToCameraRoll = false
|
|
59
61
|
private var recordAudio = false
|
|
60
62
|
private var videoFormat: VideoContainerFormat = .mp4
|
|
63
|
+
private var isRecording = false
|
|
61
64
|
let recorder = RPScreenRecorder.shared()
|
|
62
65
|
|
|
63
66
|
public func startRecording(to outputURL: URL? = nil,
|
|
@@ -66,6 +69,11 @@ public final class ScreenRecorder {
|
|
|
66
69
|
recordAudio: Bool = false,
|
|
67
70
|
videoFormat: VideoContainerFormat = .mp4,
|
|
68
71
|
handler: @escaping (Error?) -> Void) {
|
|
72
|
+
guard !isRecording else {
|
|
73
|
+
return handler(ScreenRecorderError.alreadyRecording)
|
|
74
|
+
}
|
|
75
|
+
isRecording = true
|
|
76
|
+
|
|
69
77
|
self.saveToCameraRoll = saveToCameraRoll
|
|
70
78
|
self.recordAudio = recordAudio
|
|
71
79
|
self.videoFormat = videoFormat
|
|
@@ -73,11 +81,16 @@ public final class ScreenRecorder {
|
|
|
73
81
|
|
|
74
82
|
recorder.isMicrophoneEnabled = recordAudio
|
|
75
83
|
|
|
84
|
+
var attemptOutputURL: URL?
|
|
85
|
+
var attemptDidCreate = false
|
|
86
|
+
|
|
76
87
|
do {
|
|
77
88
|
if recordAudio {
|
|
78
89
|
try configureAudioSession()
|
|
79
90
|
}
|
|
80
91
|
try createVideoWriter(in: outputURL)
|
|
92
|
+
attemptOutputURL = self.videoOutputURL
|
|
93
|
+
attemptDidCreate = self.didCreateOutputFile
|
|
81
94
|
addVideoWriterInput(size: size)
|
|
82
95
|
if recordAudio {
|
|
83
96
|
self.micAudioWriterInput = createAndAddAudioInput()
|
|
@@ -85,6 +98,12 @@ public final class ScreenRecorder {
|
|
|
85
98
|
}
|
|
86
99
|
startCapture(handler: handler)
|
|
87
100
|
} catch let err {
|
|
101
|
+
isRecording = false
|
|
102
|
+
if let cleanupURL = attemptOutputURL {
|
|
103
|
+
self.deleteOutputFileIfNeeded(outputURL: cleanupURL, didCreate: attemptDidCreate)
|
|
104
|
+
} else if self.didCreateOutputFile, let cleanupURL = self.videoOutputURL {
|
|
105
|
+
self.deleteOutputFileIfNeeded(outputURL: cleanupURL, didCreate: true)
|
|
106
|
+
}
|
|
88
107
|
handler(err)
|
|
89
108
|
}
|
|
90
109
|
}
|
|
@@ -94,6 +113,8 @@ public final class ScreenRecorder {
|
|
|
94
113
|
videoWriterInput = nil
|
|
95
114
|
micAudioWriterInput = nil
|
|
96
115
|
appAudioWriterInput = nil
|
|
116
|
+
videoOutputURL = nil
|
|
117
|
+
didCreateOutputFile = false
|
|
97
118
|
}
|
|
98
119
|
|
|
99
120
|
private func configureAudioSession() throws {
|
|
@@ -106,12 +127,14 @@ public final class ScreenRecorder {
|
|
|
106
127
|
let newVideoOutputURL: URL
|
|
107
128
|
|
|
108
129
|
if let passedVideoOutput = outputURL {
|
|
130
|
+
self.didCreateOutputFile = false
|
|
109
131
|
self.videoOutputURL = passedVideoOutput
|
|
110
132
|
newVideoOutputURL = passedVideoOutput
|
|
111
133
|
} else {
|
|
112
134
|
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString
|
|
113
|
-
let fileName = "WylerNewVideo.\(videoFormat.fileExtension)"
|
|
135
|
+
let fileName = "WylerNewVideo-\(UUID().uuidString).\(videoFormat.fileExtension)"
|
|
114
136
|
newVideoOutputURL = URL(fileURLWithPath: documentsPath.appendingPathComponent(fileName))
|
|
137
|
+
self.didCreateOutputFile = true
|
|
115
138
|
self.videoOutputURL = newVideoOutputURL
|
|
116
139
|
}
|
|
117
140
|
|
|
@@ -148,13 +171,26 @@ public final class ScreenRecorder {
|
|
|
148
171
|
}
|
|
149
172
|
|
|
150
173
|
private func startCapture(handler: @escaping (Error?) -> Void) {
|
|
174
|
+
let outputURL = self.videoOutputURL
|
|
175
|
+
let didCreate = self.didCreateOutputFile
|
|
176
|
+
|
|
151
177
|
guard recorder.isAvailable else {
|
|
178
|
+
self.deleteOutputFileIfNeeded(outputURL: outputURL, didCreate: didCreate)
|
|
179
|
+
self.isRecording = false
|
|
152
180
|
return handler(ScreenRecorderError.notAvailable)
|
|
153
181
|
}
|
|
154
182
|
var sent = false
|
|
155
183
|
recorder.startCapture(handler: { (sampleBuffer, sampleType, passedError) in
|
|
156
184
|
if let passedError = passedError {
|
|
185
|
+
self.videoWriterInput?.markAsFinished()
|
|
186
|
+
self.micAudioWriterInput?.markAsFinished()
|
|
187
|
+
self.appAudioWriterInput?.markAsFinished()
|
|
188
|
+
if self.videoWriter?.status == .writing {
|
|
189
|
+
self.videoWriter?.cancelWriting()
|
|
190
|
+
}
|
|
191
|
+
self.deleteOutputFileIfNeeded(outputURL: outputURL, didCreate: didCreate)
|
|
157
192
|
if !sent {
|
|
193
|
+
self.isRecording = false
|
|
158
194
|
handler(passedError)
|
|
159
195
|
sent = true
|
|
160
196
|
}
|
|
@@ -202,8 +238,23 @@ public final class ScreenRecorder {
|
|
|
202
238
|
|
|
203
239
|
public func stoprecording(handler: @escaping (Error?) -> Void) {
|
|
204
240
|
recorder.stopCapture(handler: { error in
|
|
241
|
+
let outputURL = self.videoOutputURL
|
|
242
|
+
let didCreate = self.didCreateOutputFile
|
|
243
|
+
let shouldSaveToCameraRoll = self.saveToCameraRoll
|
|
244
|
+
let complete: (Error?) -> Void = { stopError in
|
|
245
|
+
self.isRecording = false
|
|
246
|
+
handler(stopError)
|
|
247
|
+
}
|
|
248
|
+
|
|
205
249
|
if let error = error {
|
|
206
|
-
|
|
250
|
+
self.videoWriterInput?.markAsFinished()
|
|
251
|
+
self.micAudioWriterInput?.markAsFinished()
|
|
252
|
+
self.appAudioWriterInput?.markAsFinished()
|
|
253
|
+
if self.videoWriter?.status == .writing {
|
|
254
|
+
self.videoWriter?.cancelWriting()
|
|
255
|
+
}
|
|
256
|
+
self.deleteOutputFileIfNeeded(outputURL: outputURL, didCreate: didCreate)
|
|
257
|
+
complete(error)
|
|
207
258
|
return
|
|
208
259
|
}
|
|
209
260
|
|
|
@@ -212,61 +263,108 @@ public final class ScreenRecorder {
|
|
|
212
263
|
self.appAudioWriterInput?.markAsFinished()
|
|
213
264
|
|
|
214
265
|
guard let writer = self.videoWriter else {
|
|
215
|
-
|
|
266
|
+
complete(nil)
|
|
216
267
|
return
|
|
217
268
|
}
|
|
218
269
|
|
|
219
270
|
if writer.status == .writing {
|
|
220
271
|
writer.finishWriting {
|
|
221
272
|
if let finishError = writer.error {
|
|
222
|
-
|
|
273
|
+
self.deleteOutputFileIfNeeded(outputURL: outputURL, didCreate: didCreate)
|
|
274
|
+
complete(finishError)
|
|
223
275
|
return
|
|
224
276
|
}
|
|
225
|
-
if
|
|
226
|
-
self.saveVideoToCameraRollAfterAuthorized(
|
|
277
|
+
if shouldSaveToCameraRoll {
|
|
278
|
+
self.saveVideoToCameraRollAfterAuthorized(outputURL: outputURL,
|
|
279
|
+
didCreate: didCreate,
|
|
280
|
+
handler: complete)
|
|
227
281
|
} else {
|
|
228
|
-
|
|
282
|
+
self.deleteOutputFileIfNeeded(outputURL: outputURL, didCreate: didCreate)
|
|
283
|
+
complete(nil)
|
|
229
284
|
}
|
|
230
285
|
}
|
|
231
286
|
} else if writer.status == .failed {
|
|
232
|
-
|
|
287
|
+
self.deleteOutputFileIfNeeded(outputURL: outputURL, didCreate: didCreate)
|
|
288
|
+
complete(writer.error)
|
|
233
289
|
} else {
|
|
234
|
-
if
|
|
235
|
-
self.saveVideoToCameraRollAfterAuthorized(
|
|
290
|
+
if shouldSaveToCameraRoll {
|
|
291
|
+
self.saveVideoToCameraRollAfterAuthorized(outputURL: outputURL,
|
|
292
|
+
didCreate: didCreate,
|
|
293
|
+
handler: complete)
|
|
236
294
|
} else {
|
|
237
|
-
|
|
295
|
+
self.deleteOutputFileIfNeeded(outputURL: outputURL, didCreate: didCreate)
|
|
296
|
+
complete(nil)
|
|
238
297
|
}
|
|
239
298
|
}
|
|
240
299
|
})
|
|
241
300
|
}
|
|
242
301
|
|
|
243
|
-
private func
|
|
244
|
-
|
|
245
|
-
|
|
302
|
+
private func canSaveToPhotoLibrary(_ status: PHAuthorizationStatus) -> Bool {
|
|
303
|
+
switch status {
|
|
304
|
+
case .authorized, .limited:
|
|
305
|
+
return true
|
|
306
|
+
default:
|
|
307
|
+
return false
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
private func saveVideoToCameraRollAfterAuthorized(outputURL: URL? = nil,
|
|
312
|
+
didCreate: Bool? = nil,
|
|
313
|
+
handler: @escaping (Error?) -> Void) {
|
|
314
|
+
let capturedOutputURL = outputURL ?? self.videoOutputURL
|
|
315
|
+
let capturedDidCreate = didCreate ?? self.didCreateOutputFile
|
|
316
|
+
let status = PHPhotoLibrary.authorizationStatus(for: .addOnly)
|
|
317
|
+
|
|
318
|
+
if canSaveToPhotoLibrary(status) {
|
|
319
|
+
self.saveVideoToCameraRoll(outputURL: capturedOutputURL,
|
|
320
|
+
didCreate: capturedDidCreate,
|
|
321
|
+
handler: handler)
|
|
246
322
|
} else {
|
|
247
|
-
PHPhotoLibrary.requestAuthorization({ (status) in
|
|
248
|
-
if status
|
|
249
|
-
self.saveVideoToCameraRoll(
|
|
323
|
+
PHPhotoLibrary.requestAuthorization(for: .addOnly, handler: { (status) in
|
|
324
|
+
if self.canSaveToPhotoLibrary(status) {
|
|
325
|
+
self.saveVideoToCameraRoll(outputURL: capturedOutputURL,
|
|
326
|
+
didCreate: capturedDidCreate,
|
|
327
|
+
handler: handler)
|
|
250
328
|
} else {
|
|
329
|
+
self.deleteOutputFileIfNeeded(outputURL: capturedOutputURL, didCreate: capturedDidCreate)
|
|
251
330
|
handler(ScreenRecorderError.photoLibraryAccessNotGranted)
|
|
252
331
|
}
|
|
253
332
|
})
|
|
254
333
|
}
|
|
255
334
|
}
|
|
256
335
|
|
|
257
|
-
private func saveVideoToCameraRoll(
|
|
258
|
-
|
|
336
|
+
private func saveVideoToCameraRoll(outputURL: URL?,
|
|
337
|
+
didCreate: Bool,
|
|
338
|
+
handler: @escaping (Error?) -> Void) {
|
|
339
|
+
guard let videoOutputURL = outputURL else {
|
|
259
340
|
return handler(nil)
|
|
260
341
|
}
|
|
261
342
|
|
|
262
343
|
PHPhotoLibrary.shared().performChanges({
|
|
263
344
|
PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: videoOutputURL)
|
|
264
|
-
}, completionHandler: {
|
|
345
|
+
}, completionHandler: { success, error in
|
|
265
346
|
if let error = error {
|
|
347
|
+
self.deleteOutputFileIfNeeded(outputURL: videoOutputURL, didCreate: didCreate)
|
|
266
348
|
handler(error)
|
|
267
|
-
} else {
|
|
349
|
+
} else if success {
|
|
350
|
+
self.deleteOutputFileIfNeeded(outputURL: videoOutputURL, didCreate: didCreate)
|
|
268
351
|
handler(nil)
|
|
352
|
+
} else {
|
|
353
|
+
self.deleteOutputFileIfNeeded(outputURL: videoOutputURL, didCreate: didCreate)
|
|
354
|
+
handler(NSError(domain: "ScreenRecorder",
|
|
355
|
+
code: -1,
|
|
356
|
+
userInfo: [NSLocalizedDescriptionKey: "Failed to save video to photo library"]))
|
|
269
357
|
}
|
|
270
358
|
})
|
|
271
359
|
}
|
|
360
|
+
|
|
361
|
+
private func deleteOutputFileIfNeeded(outputURL: URL? = nil, didCreate: Bool? = nil) {
|
|
362
|
+
let shouldDelete = didCreate ?? didCreateOutputFile
|
|
363
|
+
guard shouldDelete, let fileURL = outputURL ?? self.videoOutputURL else { return }
|
|
364
|
+
do {
|
|
365
|
+
try FileManager.default.removeItem(at: fileURL)
|
|
366
|
+
} catch {
|
|
367
|
+
debugPrint("Failed to delete recording file \(fileURL): \(error)")
|
|
368
|
+
}
|
|
369
|
+
}
|
|
272
370
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@capgo/capacitor-screen-recorder",
|
|
3
|
-
"version": "8.3.
|
|
3
|
+
"version": "8.3.9",
|
|
4
4
|
"description": "Record device's screen",
|
|
5
5
|
"main": "dist/plugin.cjs.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"@ionic/eslint-config": "^0.4.0",
|
|
67
67
|
"@ionic/prettier-config": "^4.0.0",
|
|
68
68
|
"@ionic/swiftlint-config": "^2.0.0",
|
|
69
|
-
"@types/node": "^
|
|
69
|
+
"@types/node": "^26.0.0",
|
|
70
70
|
"eslint": "^8.57.1",
|
|
71
71
|
"eslint-plugin-import": "^2.31.0",
|
|
72
72
|
"prettier": "^3.6.2",
|