@capgo/capacitor-screen-recorder 1.2.0

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.
@@ -0,0 +1,17 @@
1
+ require 'json'
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = 'CapacitorScreenRecorder'
7
+ s.version = package['version']
8
+ s.summary = package['description']
9
+ s.license = package['license']
10
+ s.homepage = package['repository']['url']
11
+ s.author = package['author']
12
+ s.source = { :git => package['repository']['url'], :tag => s.version.to_s }
13
+ s.source_files = 'ios/Plugin/**/*.{swift,h,m,c,cc,mm,cpp}'
14
+ s.ios.deployment_target = '12.0'
15
+ s.dependency 'Capacitor'
16
+ s.swift_version = '5.1'
17
+ end
package/README.md ADDED
@@ -0,0 +1,53 @@
1
+ # capacitor-screen-recorder
2
+
3
+ Record device's screen
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @capgo/capacitor-screen-recorder
9
+ npx cap sync
10
+ ```
11
+
12
+ ## IOS
13
+
14
+ Nothing special
15
+
16
+ ## Android
17
+
18
+ increase project's minSdk version to at least 23.
19
+
20
+ ## API
21
+
22
+ <docgen-index>
23
+
24
+ * [`start()`](#start)
25
+ * [`stop()`](#stop)
26
+
27
+ </docgen-index>
28
+
29
+ <docgen-api>
30
+ <!--Update the source file JSDoc comments and rerun docgen to update the docs below-->
31
+
32
+ ### start()
33
+
34
+ ```typescript
35
+ start() => Promise<void>
36
+ ```
37
+
38
+ start the recording
39
+
40
+ --------------------
41
+
42
+
43
+ ### stop()
44
+
45
+ ```typescript
46
+ stop() => Promise<void>
47
+ ```
48
+
49
+ stop the recording
50
+
51
+ --------------------
52
+
53
+ </docgen-api>
@@ -0,0 +1,64 @@
1
+ ext {
2
+ junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.1'
3
+ androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.2.0'
4
+ androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.1.2'
5
+ androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.3.0'
6
+ }
7
+
8
+ buildscript {
9
+ ext.kotlin_version = '1.6.0-RC2'
10
+ repositories {
11
+ google()
12
+ jcenter()
13
+ }
14
+ dependencies {
15
+ classpath 'com.android.tools.build:gradle:4.2.1'
16
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
17
+ }
18
+ }
19
+
20
+ apply plugin: 'com.android.library'
21
+ apply plugin: 'kotlin-android'
22
+
23
+ android {
24
+ compileSdkVersion project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 30
25
+ defaultConfig {
26
+ minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 21
27
+ targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 30
28
+ versionCode 1
29
+ versionName "1.0"
30
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
31
+ }
32
+ buildTypes {
33
+ release {
34
+ minifyEnabled false
35
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
36
+ }
37
+ }
38
+ lintOptions {
39
+ abortOnError false
40
+ }
41
+ compileOptions {
42
+ sourceCompatibility JavaVersion.VERSION_1_8
43
+ targetCompatibility JavaVersion.VERSION_1_8
44
+ }
45
+ }
46
+
47
+ repositories {
48
+ google()
49
+ jcenter()
50
+ mavenCentral()
51
+ }
52
+
53
+
54
+ dependencies {
55
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
56
+ implementation project(':capacitor-android')
57
+ implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
58
+ testImplementation "junit:junit:$junitVersion"
59
+ androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
60
+ androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
61
+ implementation "androidx.core:core-ktx:+"
62
+ implementation ("dev.bmcreations:scrcast:0.3.0")
63
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
64
+ }
@@ -0,0 +1,3 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
2
+ package="ee.forgr.plugin.screenrecorder">
3
+ </manifest>
@@ -0,0 +1,33 @@
1
+ package ee.forgr.plugin.screenrecorder;
2
+
3
+ import com.getcapacitor.Plugin;
4
+ import com.getcapacitor.PluginCall;
5
+ import com.getcapacitor.PluginMethod;
6
+ import com.getcapacitor.annotation.CapacitorPlugin;
7
+ import dev.bmcreations.scrcast.ScrCast;
8
+ import dev.bmcreations.scrcast.config.Options;
9
+
10
+ @CapacitorPlugin(name = "ScreenRecorder")
11
+ public class ScreenRecorderPlugin extends Plugin {
12
+
13
+ private ScrCast recorder;
14
+
15
+ @Override
16
+ public void load() {
17
+ recorder = ScrCast.use(this.bridge.getActivity());
18
+ Options options = new Options();
19
+ recorder.updateOptions(options);
20
+ }
21
+
22
+ @PluginMethod
23
+ public void start(PluginCall call) {
24
+ recorder.record();
25
+ call.resolve();
26
+ }
27
+
28
+ @PluginMethod
29
+ public void stop(PluginCall call) {
30
+ recorder.stopRecording();
31
+ call.resolve();
32
+ }
33
+ }
File without changes
package/dist/docs.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "api": {
3
+ "name": "ScreenRecorderPlugin",
4
+ "slug": "screenrecorderplugin",
5
+ "docs": "",
6
+ "tags": [],
7
+ "methods": [
8
+ {
9
+ "name": "start",
10
+ "signature": "() => Promise<void>",
11
+ "parameters": [],
12
+ "returns": "Promise<void>",
13
+ "tags": [],
14
+ "docs": "start the recording",
15
+ "complexTypes": [],
16
+ "slug": "start"
17
+ },
18
+ {
19
+ "name": "stop",
20
+ "signature": "() => Promise<void>",
21
+ "parameters": [],
22
+ "returns": "Promise<void>",
23
+ "tags": [],
24
+ "docs": "stop the recording",
25
+ "complexTypes": [],
26
+ "slug": "stop"
27
+ }
28
+ ],
29
+ "properties": []
30
+ },
31
+ "interfaces": [],
32
+ "enums": []
33
+ }
@@ -0,0 +1,12 @@
1
+ export interface ScreenRecorderPlugin {
2
+ /**
3
+ * start the recording
4
+ *
5
+ */
6
+ start(): Promise<void>;
7
+ /**
8
+ * stop the recording
9
+ *
10
+ */
11
+ stop(): Promise<void>;
12
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=definitions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":""}
@@ -0,0 +1,4 @@
1
+ import type { ScreenRecorderPlugin } from './definitions';
2
+ declare const ScreenRecorder: ScreenRecorderPlugin;
3
+ export * from './definitions';
4
+ export { ScreenRecorder };
@@ -0,0 +1,7 @@
1
+ import { registerPlugin } from '@capacitor/core';
2
+ const ScreenRecorder = registerPlugin('ScreenRecorder', {
3
+ web: () => import('./web').then(m => new m.ScreenRecorderWeb()),
4
+ });
5
+ export * from './definitions';
6
+ export { ScreenRecorder };
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAIjD,MAAM,cAAc,GAAG,cAAc,CAAuB,gBAAgB,EAAE;IAC5E,GAAG,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,iBAAiB,EAAE,CAAC;CAChE,CAAC,CAAC;AAEH,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,cAAc,EAAE,CAAC"}
@@ -0,0 +1,6 @@
1
+ import { WebPlugin } from '@capacitor/core';
2
+ import type { ScreenRecorderPlugin } from './definitions';
3
+ export declare class ScreenRecorderWeb extends WebPlugin implements ScreenRecorderPlugin {
4
+ start(): Promise<void>;
5
+ stop(): Promise<void>;
6
+ }
@@ -0,0 +1,10 @@
1
+ import { WebPlugin } from '@capacitor/core';
2
+ export class ScreenRecorderWeb extends WebPlugin {
3
+ async start() {
4
+ throw new Error("Method not implemented.");
5
+ }
6
+ async stop() {
7
+ throw new Error("Method not implemented.");
8
+ }
9
+ }
10
+ //# sourceMappingURL=web.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAI5C,MAAM,OAAO,iBACX,SAAQ,SAAS;IAEjB,KAAK,CAAC,KAAK;QACP,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC/C,CAAC;IACD,KAAK,CAAC,IAAI;QACN,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC/C,CAAC;CACF"}
@@ -0,0 +1,26 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var core = require('@capacitor/core');
6
+
7
+ const ScreenRecorder = core.registerPlugin('ScreenRecorder', {
8
+ web: () => Promise.resolve().then(function () { return web; }).then(m => new m.ScreenRecorderWeb()),
9
+ });
10
+
11
+ class ScreenRecorderWeb extends core.WebPlugin {
12
+ async start() {
13
+ throw new Error("Method not implemented.");
14
+ }
15
+ async stop() {
16
+ throw new Error("Method not implemented.");
17
+ }
18
+ }
19
+
20
+ var web = /*#__PURE__*/Object.freeze({
21
+ __proto__: null,
22
+ ScreenRecorderWeb: ScreenRecorderWeb
23
+ });
24
+
25
+ exports.ScreenRecorder = ScreenRecorder;
26
+ //# sourceMappingURL=plugin.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.cjs.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst ScreenRecorder = registerPlugin('ScreenRecorder', {\n web: () => import('./web').then(m => new m.ScreenRecorderWeb()),\n});\nexport * from './definitions';\nexport { ScreenRecorder };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nexport class ScreenRecorderWeb extends WebPlugin {\n async start() {\n throw new Error(\"Method not implemented.\");\n }\n async stop() {\n throw new Error(\"Method not implemented.\");\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":";;;;;;AACK,MAAC,cAAc,GAAGA,mBAAc,CAAC,gBAAgB,EAAE;AACxD,IAAI,GAAG,EAAE,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,iBAAiB,EAAE,CAAC;AACnE,CAAC;;ACFM,MAAM,iBAAiB,SAASC,cAAS,CAAC;AACjD,IAAI,MAAM,KAAK,GAAG;AAClB,QAAQ,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;AACnD,KAAK;AACL,IAAI,MAAM,IAAI,GAAG;AACjB,QAAQ,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;AACnD,KAAK;AACL;;;;;;;;;"}
package/dist/plugin.js ADDED
@@ -0,0 +1,29 @@
1
+ var capacitorScreenRecorder = (function (exports, core) {
2
+ 'use strict';
3
+
4
+ const ScreenRecorder = core.registerPlugin('ScreenRecorder', {
5
+ web: () => Promise.resolve().then(function () { return web; }).then(m => new m.ScreenRecorderWeb()),
6
+ });
7
+
8
+ class ScreenRecorderWeb extends core.WebPlugin {
9
+ async start() {
10
+ throw new Error("Method not implemented.");
11
+ }
12
+ async stop() {
13
+ throw new Error("Method not implemented.");
14
+ }
15
+ }
16
+
17
+ var web = /*#__PURE__*/Object.freeze({
18
+ __proto__: null,
19
+ ScreenRecorderWeb: ScreenRecorderWeb
20
+ });
21
+
22
+ exports.ScreenRecorder = ScreenRecorder;
23
+
24
+ Object.defineProperty(exports, '__esModule', { value: true });
25
+
26
+ return exports;
27
+
28
+ })({}, capacitorExports);
29
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst ScreenRecorder = registerPlugin('ScreenRecorder', {\n web: () => import('./web').then(m => new m.ScreenRecorderWeb()),\n});\nexport * from './definitions';\nexport { ScreenRecorder };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nexport class ScreenRecorderWeb extends WebPlugin {\n async start() {\n throw new Error(\"Method not implemented.\");\n }\n async stop() {\n throw new Error(\"Method not implemented.\");\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":";;;AACK,UAAC,cAAc,GAAGA,mBAAc,CAAC,gBAAgB,EAAE;IACxD,IAAI,GAAG,EAAE,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,iBAAiB,EAAE,CAAC;IACnE,CAAC;;ICFM,MAAM,iBAAiB,SAASC,cAAS,CAAC;IACjD,IAAI,MAAM,KAAK,GAAG;IAClB,QAAQ,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IACnD,KAAK;IACL,IAAI,MAAM,IAAI,GAAG;IACjB,QAAQ,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IACnD,KAAK;IACL;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,24 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>CFBundleDevelopmentRegion</key>
6
+ <string>$(DEVELOPMENT_LANGUAGE)</string>
7
+ <key>CFBundleExecutable</key>
8
+ <string>$(EXECUTABLE_NAME)</string>
9
+ <key>CFBundleIdentifier</key>
10
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
11
+ <key>CFBundleInfoDictionaryVersion</key>
12
+ <string>6.0</string>
13
+ <key>CFBundleName</key>
14
+ <string>$(PRODUCT_NAME)</string>
15
+ <key>CFBundlePackageType</key>
16
+ <string>FMWK</string>
17
+ <key>CFBundleShortVersionString</key>
18
+ <string>1.0</string>
19
+ <key>CFBundleVersion</key>
20
+ <string>$(CURRENT_PROJECT_VERSION)</string>
21
+ <key>NSPrincipalClass</key>
22
+ <string></string>
23
+ </dict>
24
+ </plist>
@@ -0,0 +1,10 @@
1
+ #import <UIKit/UIKit.h>
2
+
3
+ //! Project version number for Plugin.
4
+ FOUNDATION_EXPORT double PluginVersionNumber;
5
+
6
+ //! Project version string for Plugin.
7
+ FOUNDATION_EXPORT const unsigned char PluginVersionString[];
8
+
9
+ // In this header, you should import all the public headers of your framework using statements like #import <Plugin/PublicHeader.h>
10
+
@@ -0,0 +1,9 @@
1
+ #import <Foundation/Foundation.h>
2
+ #import <Capacitor/Capacitor.h>
3
+
4
+ // Define the plugin using the CAP_PLUGIN Macro, and
5
+ // each method the plugin supports using the CAP_PLUGIN_METHOD macro.
6
+ CAP_PLUGIN(ScreenRecorderPlugin, "ScreenRecorder",
7
+ CAP_PLUGIN_METHOD(start, CAPPluginReturnPromise);
8
+ CAP_PLUGIN_METHOD(stop, CAPPluginReturnPromise);
9
+ )
@@ -0,0 +1,32 @@
1
+ import Foundation
2
+ import Capacitor
3
+
4
+ /**
5
+ * Please read the Capacitor iOS Plugin Development Guide
6
+ * here: https://capacitorjs.com/docs/plugins/ios
7
+ */
8
+ @objc(ScreenRecorderPlugin)
9
+ public class ScreenRecorderPlugin: CAPPlugin {
10
+ private let implementation = ScreenRecorder()
11
+
12
+ @objc func start(_ call: CAPPluginCall) {
13
+ implementation.startRecording(saveToCameraRoll: true, handler: { error in
14
+ if let error = error {
15
+ debugPrint("Error when start recording \(error)")
16
+ call.reject("Cannot start recording")
17
+ } else {
18
+ call.resolve()
19
+ }
20
+ })
21
+ }
22
+ @objc func stop(_ call: CAPPluginCall) {
23
+ implementation.stoprecording(handler: { error in
24
+ if let error = error {
25
+ debugPrint("Error when stop recording \(error)")
26
+ call.reject("Cannot stop recording")
27
+ } else {
28
+ call.resolve()
29
+ }
30
+ })
31
+ }
32
+ }
@@ -0,0 +1,206 @@
1
+ //
2
+ // ScreenRecorder.swift
3
+ // Wyler
4
+ //
5
+ // Created by Cesar Vargas on 10.04.20.
6
+ // Copyright © 2020 Cesar Vargas. All rights reserved.
7
+ //
8
+
9
+ import Foundation
10
+ import ReplayKit
11
+ import Photos
12
+
13
+ public enum ScreenRecorderError: Error {
14
+ case notAvailable
15
+ case photoLibraryAccessNotGranted
16
+ }
17
+
18
+ final public class ScreenRecorder {
19
+ private var videoOutputURL: URL?
20
+ private var videoWriter: AVAssetWriter?
21
+ private var videoWriterInput: AVAssetWriterInput?
22
+ private var micAudioWriterInput: AVAssetWriterInput?
23
+ private var appAudioWriterInput: AVAssetWriterInput?
24
+ private var saveToCameraRoll = false
25
+ let recorder = RPScreenRecorder.shared()
26
+
27
+ /**
28
+ Starts recording the content of the application screen. It works together with stopRecording
29
+
30
+ - Parameter outputURL: The output where the video will be saved. If nil, it saves it in the documents directory.
31
+ - Parameter size: The size of the video. If nil, it will use the app screen size.
32
+ - Parameter saveToCameraRoll: Whether to save it to camera roll. False by default.
33
+ - Parameter errorHandler: Called when an error is found
34
+ */
35
+ public func startRecording(to outputURL: URL? = nil,
36
+ size: CGSize? = nil,
37
+ saveToCameraRoll: Bool = false,
38
+ handler: @escaping (Error?) -> Void) {
39
+ recorder.isMicrophoneEnabled = true
40
+ do {
41
+ try createVideoWriter(in: outputURL)
42
+ addVideoWriterInput(size: size)
43
+ self.micAudioWriterInput = createAndAddAudioInput()
44
+ self.appAudioWriterInput = createAndAddAudioInput()
45
+ startCapture(handler: handler)
46
+ } catch let err {
47
+ handler(err)
48
+ }
49
+ }
50
+
51
+ private func checkPhotoLibraryAuthorizationStatus() {
52
+ let status = PHPhotoLibrary.authorizationStatus()
53
+ if status == .notDetermined {
54
+ PHPhotoLibrary.requestAuthorization({ _ in })
55
+ }
56
+ }
57
+
58
+ private func createVideoWriter(in outputURL: URL? = nil) throws {
59
+ let newVideoOutputURL: URL
60
+
61
+ if let passedVideoOutput = outputURL {
62
+ self.videoOutputURL = passedVideoOutput
63
+ newVideoOutputURL = passedVideoOutput
64
+ } else {
65
+ let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString
66
+ newVideoOutputURL = URL(fileURLWithPath: documentsPath.appendingPathComponent("WylerNewVideo.mp4"))
67
+ self.videoOutputURL = newVideoOutputURL
68
+ }
69
+
70
+ do {
71
+ try FileManager.default.removeItem(at: newVideoOutputURL)
72
+ } catch {}
73
+
74
+ do {
75
+ try videoWriter = AVAssetWriter(outputURL: newVideoOutputURL, fileType: AVFileType.mp4)
76
+ } catch let writerError as NSError {
77
+ videoWriter = nil
78
+ throw writerError
79
+ }
80
+ }
81
+
82
+ private func addVideoWriterInput(size: CGSize?) {
83
+ let passingSize: CGSize = size ?? UIScreen.main.bounds.size
84
+
85
+ let videoSettings: [String: Any] = [AVVideoCodecKey: AVVideoCodecType.h264,
86
+ AVVideoWidthKey: passingSize.width,
87
+ AVVideoHeightKey: passingSize.height]
88
+
89
+ let newVideoWriterInput = AVAssetWriterInput(mediaType: AVMediaType.video, outputSettings: videoSettings)
90
+ self.videoWriterInput = newVideoWriterInput
91
+ newVideoWriterInput.expectsMediaDataInRealTime = true
92
+ videoWriter?.add(newVideoWriterInput)
93
+ }
94
+
95
+ private func createAndAddAudioInput() -> AVAssetWriterInput {
96
+ let settings = [
97
+ AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
98
+ AVSampleRateKey: 12000,
99
+ AVNumberOfChannelsKey: 1,
100
+ AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
101
+ ]
102
+
103
+ let audioInput = AVAssetWriterInput(mediaType: .audio, outputSettings: settings)
104
+
105
+ audioInput.expectsMediaDataInRealTime = true
106
+ videoWriter?.add(audioInput)
107
+
108
+ return audioInput
109
+ }
110
+
111
+ private func startCapture(handler: @escaping (Error?) -> Void) {
112
+ guard recorder.isAvailable else {
113
+ return handler(ScreenRecorderError.notAvailable)
114
+ }
115
+ var sent = false
116
+ recorder.startCapture(handler: { (sampleBuffer, sampleType, passedError) in
117
+ if let passedError = passedError {
118
+ if (!sent) {
119
+ handler(passedError)
120
+ sent = true
121
+ }
122
+ }
123
+
124
+ switch sampleType {
125
+ case .video:
126
+ self.handleSampleBuffer(sampleBuffer: sampleBuffer)
127
+ case .audioApp:
128
+ self.add(sample: sampleBuffer, to: self.appAudioWriterInput)
129
+ case .audioMic:
130
+ self.add(sample: sampleBuffer, to: self.micAudioWriterInput)
131
+ default:
132
+ break
133
+ }
134
+ if (!sent) {
135
+ handler(nil)
136
+ sent = true
137
+ }
138
+ })
139
+ }
140
+
141
+ private func handleSampleBuffer(sampleBuffer: CMSampleBuffer) {
142
+ if self.videoWriter?.status == AVAssetWriter.Status.unknown {
143
+ self.videoWriter?.startWriting()
144
+ self.videoWriter?.startSession(atSourceTime: CMSampleBufferGetPresentationTimeStamp(sampleBuffer))
145
+ } else if self.videoWriter?.status == AVAssetWriter.Status.writing &&
146
+ self.videoWriterInput?.isReadyForMoreMediaData == true {
147
+ self.videoWriterInput?.append(sampleBuffer)
148
+ }
149
+ }
150
+
151
+ private func add(sample: CMSampleBuffer, to writerInput: AVAssetWriterInput?) {
152
+ if writerInput?.isReadyForMoreMediaData ?? false {
153
+ writerInput?.append(sample)
154
+ }
155
+ }
156
+
157
+ /**
158
+ Stops recording the content of the application screen, after calling startRecording
159
+
160
+ - Parameter errorHandler: Called when an error is found
161
+ */
162
+ public func stoprecording(handler: @escaping (Error?) -> Void) {
163
+ recorder.stopCapture( handler: { error in
164
+ if let error = error {
165
+ handler(error)
166
+ } else {
167
+ self.videoWriterInput?.markAsFinished()
168
+ self.micAudioWriterInput?.markAsFinished()
169
+ self.appAudioWriterInput?.markAsFinished()
170
+ self.videoWriter?.finishWriting {
171
+ self.saveVideoToCameraRollAfterAuthorized(handler: handler)
172
+ }
173
+ }
174
+ })
175
+ }
176
+
177
+ private func saveVideoToCameraRollAfterAuthorized(handler: @escaping (Error?) -> Void) {
178
+ if PHPhotoLibrary.authorizationStatus() == .authorized {
179
+ self.saveVideoToCameraRoll(handler: handler)
180
+ } else {
181
+ PHPhotoLibrary.requestAuthorization({ (status) in
182
+ if status == .authorized {
183
+ self.saveVideoToCameraRoll(handler: handler)
184
+ } else {
185
+ handler(ScreenRecorderError.photoLibraryAccessNotGranted)
186
+ }
187
+ })
188
+ }
189
+ }
190
+
191
+ private func saveVideoToCameraRoll(handler: @escaping (Error?) -> Void) {
192
+ guard let videoOutputURL = self.videoOutputURL else {
193
+ return handler(nil)
194
+ }
195
+
196
+ PHPhotoLibrary.shared().performChanges({
197
+ PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: videoOutputURL)
198
+ }, completionHandler: { _, error in
199
+ if let error = error {
200
+ handler(error)
201
+ } else {
202
+ handler(nil)
203
+ }
204
+ })
205
+ }
206
+ }
package/package.json ADDED
@@ -0,0 +1,78 @@
1
+ {
2
+ "name": "@capgo/capacitor-screen-recorder",
3
+ "version": "1.2.0",
4
+ "description": "Record device's screen",
5
+ "main": "dist/plugin.cjs.js",
6
+ "module": "dist/esm/index.js",
7
+ "types": "dist/esm/index.d.ts",
8
+ "unpkg": "dist/plugin.js",
9
+ "files": [
10
+ "android/src/main/",
11
+ "android/build.gradle",
12
+ "dist/",
13
+ "ios/Plugin/",
14
+ "CapacitorScreenRecorder.podspec"
15
+ ],
16
+ "author": "Martin Donadieu",
17
+ "license": "MIT",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/riderx/capacitor-screen-recorder.git"
21
+ },
22
+ "bugs": {
23
+ "url": "https://github.com/riderx/capacitor-screen-recorder/issues"
24
+ },
25
+ "keywords": [
26
+ "capacitor",
27
+ "plugin",
28
+ "native"
29
+ ],
30
+ "scripts": {
31
+ "verify": "npm run verify:ios && npm run verify:android && npm run verify:web",
32
+ "verify:ios": "cd ios && pod install && xcodebuild -workspace Plugin.xcworkspace -scheme Plugin && cd ..",
33
+ "verify:android": "cd android && ./gradlew clean build test && cd ..",
34
+ "verify:web": "npm run build",
35
+ "lint": "npm run eslint && npm run prettier -- --check && npm run swiftlint -- lint",
36
+ "fmt": "npm run eslint -- --fix && npm run prettier -- --write && npm run swiftlint -- --fix --format",
37
+ "eslint": "eslint . --ext ts",
38
+ "prettier": "prettier \"**/*.{css,html,ts,js,java}\"",
39
+ "swiftlint": "node-swiftlint",
40
+ "docgen": "docgen --api ScreenRecorderPlugin --output-readme README.md --output-json dist/docs.json",
41
+ "build": "npm run clean && npm run docgen && tsc && rollup -c rollup.config.js",
42
+ "clean": "rimraf ./dist",
43
+ "watch": "tsc --watch",
44
+ "prepublishOnly": "npm run build"
45
+ },
46
+ "devDependencies": {
47
+ "@capacitor/android": "^3.0.0",
48
+ "@capacitor/core": "^3.0.0",
49
+ "@capacitor/docgen": "^0.0.10",
50
+ "@capacitor/ios": "^3.0.0",
51
+ "@ionic/eslint-config": "^0.3.0",
52
+ "@ionic/prettier-config": "^1.0.1",
53
+ "@ionic/swiftlint-config": "^1.1.2",
54
+ "eslint": "^7.11.0",
55
+ "prettier": "~2.2.0",
56
+ "prettier-plugin-java": "~1.0.0",
57
+ "rimraf": "^3.0.2",
58
+ "rollup": "^2.32.0",
59
+ "swiftlint": "^1.0.1",
60
+ "typescript": "~4.0.3"
61
+ },
62
+ "peerDependencies": {
63
+ "@capacitor/core": "^3.0.0"
64
+ },
65
+ "prettier": "@ionic/prettier-config",
66
+ "swiftlint": "@ionic/swiftlint-config",
67
+ "eslintConfig": {
68
+ "extends": "@ionic/eslint-config/recommended"
69
+ },
70
+ "capacitor": {
71
+ "ios": {
72
+ "src": "ios"
73
+ },
74
+ "android": {
75
+ "src": "android"
76
+ }
77
+ }
78
+ }