@capgo/capacitor-video-player 8.2.14 → 8.3.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.
- package/README.md +68 -33
- package/android/src/main/java/com/capgo/videoplayer/FullscreenExoPlayerFragment.java +145 -53
- package/android/src/main/java/com/capgo/videoplayer/SubtitleOptionOverrides.java +84 -0
- package/android/src/main/java/com/capgo/videoplayer/VideoChapter.java +14 -0
- package/android/src/main/java/com/capgo/videoplayer/VideoPlayer.java +3 -0
- package/android/src/main/java/com/capgo/videoplayer/VideoPlayerPlugin.java +48 -2
- package/android/src/main/res/drawable/ic_chapters_white.xml +10 -0
- package/android/src/main/res/layout/exo_playback_control_view.xml +9 -0
- package/android/src/main/res/values/strings.xml +2 -0
- package/dist/docs.json +44 -5
- package/dist/esm/definitions.d.ts +58 -6
- package/dist/esm/definitions.js.map +1 -1
- package/ios/Sources/VideoPlayerPlugin/AVPlayerControlsVisibilityObserver.swift +13 -0
- package/ios/Sources/VideoPlayerPlugin/FullscreenVideoPlayer.swift +243 -33
- package/ios/Sources/VideoPlayerPlugin/SubtitleStyleOptions.swift +7 -0
- package/ios/Sources/VideoPlayerPlugin/VideoChapter.swift +45 -0
- package/ios/Sources/VideoPlayerPlugin/VideoPlayerAudioSessionCoordinator.swift +101 -0
- package/ios/Sources/VideoPlayerPlugin/VideoPlayerCastController.swift +23 -5
- package/ios/Sources/VideoPlayerPlugin/VideoPlayerPlugin.swift +49 -1
- package/ios/Tests/VideoPlayerPluginTests/VideoChapterTests.swift +30 -0
- package/ios/Tests/VideoPlayerPluginTests/VideoPlayerAudioSessionCategoryPolicyTests.swift +52 -0
- package/package.json +6 -4
|
@@ -8,7 +8,7 @@ import AVKit
|
|
|
8
8
|
*/
|
|
9
9
|
@objc(VideoPlayerPlugin)
|
|
10
10
|
public class VideoPlayerPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
11
|
-
private let pluginVersion: String = "8.
|
|
11
|
+
private let pluginVersion: String = "8.3.0"
|
|
12
12
|
public let identifier = "VideoPlayerPlugin"
|
|
13
13
|
public let jsName = "VideoPlayer"
|
|
14
14
|
public let pluginMethods: [CAPPluginMethod] = [
|
|
@@ -76,6 +76,7 @@ extension VideoPlayerPlugin {
|
|
|
76
76
|
let exitOnEnd = call.getBool("exitOnEnd") ?? true
|
|
77
77
|
let loopOnEnd = call.getBool("loopOnEnd") ?? false
|
|
78
78
|
let pipEnabled = call.getBool("pipEnabled") ?? true
|
|
79
|
+
let bkmodeEnabled = call.getBool("bkmodeEnabled") ?? true
|
|
79
80
|
let showControls = call.getBool("showControls") ?? true
|
|
80
81
|
let chromecast = call.getBool("chromecast") ?? true
|
|
81
82
|
let title = call.getString("title")
|
|
@@ -84,6 +85,8 @@ extension VideoPlayerPlugin {
|
|
|
84
85
|
let chromecastUrl = call.getString("chromecastUrl")
|
|
85
86
|
let audioCategory = call.getString("audioCategory")
|
|
86
87
|
let subtitleTracks = Self.parseSubtitleTracks(from: call)
|
|
88
|
+
let subtitleStyleOptions = Self.parseSubtitleStyleOptions(from: call)
|
|
89
|
+
let chapters = Self.parseChapters(from: call)
|
|
87
90
|
let drmOptions = Self.parseDrmOptions(from: call)
|
|
88
91
|
|
|
89
92
|
// Create video player
|
|
@@ -94,6 +97,7 @@ extension VideoPlayerPlugin {
|
|
|
94
97
|
exitOnEnd: exitOnEnd,
|
|
95
98
|
loopOnEnd: loopOnEnd,
|
|
96
99
|
pipEnabled: pipEnabled,
|
|
100
|
+
bkmodeEnabled: bkmodeEnabled,
|
|
97
101
|
showControls: showControls,
|
|
98
102
|
chromecast: chromecast,
|
|
99
103
|
chromecastUrl: chromecastUrl,
|
|
@@ -101,6 +105,8 @@ extension VideoPlayerPlugin {
|
|
|
101
105
|
smallTitle: smallTitle,
|
|
102
106
|
artwork: artwork,
|
|
103
107
|
subtitleTracks: subtitleTracks,
|
|
108
|
+
subtitleStyleOptions: subtitleStyleOptions,
|
|
109
|
+
chapters: chapters,
|
|
104
110
|
fairplayCertificateUrl: drmOptions.fairplayCertificateUrl,
|
|
105
111
|
fairplayContentKeySpcUrl: drmOptions.fairplayContentKeySpcUrl,
|
|
106
112
|
fairplayAssetId: drmOptions.fairplayAssetId,
|
|
@@ -137,6 +143,48 @@ extension VideoPlayerPlugin {
|
|
|
137
143
|
}
|
|
138
144
|
}
|
|
139
145
|
|
|
146
|
+
private static func parseSubtitleStyleOptions(from call: CAPPluginCall) -> SubtitleStyleOptions? {
|
|
147
|
+
guard let options = call.getObject("subtitleOptions") else {
|
|
148
|
+
return nil
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
let fontSize: Double?
|
|
152
|
+
if let number = options["fontSize"] as? NSNumber {
|
|
153
|
+
fontSize = number.doubleValue
|
|
154
|
+
} else {
|
|
155
|
+
fontSize = nil
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return SubtitleStyleOptions(
|
|
159
|
+
foregroundColor: options["foregroundColor"] as? String,
|
|
160
|
+
backgroundColor: options["backgroundColor"] as? String,
|
|
161
|
+
fontSize: fontSize
|
|
162
|
+
)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
private static func parseChapters(from call: CAPPluginCall) -> [VideoChapter] {
|
|
166
|
+
guard let entries = call.getArray("chapters") else {
|
|
167
|
+
return []
|
|
168
|
+
}
|
|
169
|
+
var parsed: [VideoChapter] = []
|
|
170
|
+
for (index, entry) in entries.enumerated() {
|
|
171
|
+
guard let jsObject = entry as? JSObject else {
|
|
172
|
+
print("VideoPlayerPlugin: skipping invalid chapter at index \(index)")
|
|
173
|
+
continue
|
|
174
|
+
}
|
|
175
|
+
var dictionary: [String: Any] = [:]
|
|
176
|
+
for (key, value) in jsObject {
|
|
177
|
+
dictionary[key] = value
|
|
178
|
+
}
|
|
179
|
+
if let chapter = VideoChapter(dictionary: dictionary) {
|
|
180
|
+
parsed.append(chapter)
|
|
181
|
+
} else {
|
|
182
|
+
print("VideoPlayerPlugin: skipping invalid chapter at index \(index)")
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return parsed.sorted { $0.startTime < $1.startTime }
|
|
186
|
+
}
|
|
187
|
+
|
|
140
188
|
private static func parseSubtitleTracks(from call: CAPPluginCall) -> [VideoSubtitleTrack] {
|
|
141
189
|
let subtitlesArray = call.getArray("subtitles", JSObject.self)?.map { entry -> [String: Any] in
|
|
142
190
|
var dict: [String: Any] = [:]
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import XCTest
|
|
2
|
+
@testable import VideoPlayerPlugin
|
|
3
|
+
|
|
4
|
+
class VideoChapterTests: XCTestCase {
|
|
5
|
+
func testRejectsBooleanStartTime() {
|
|
6
|
+
XCTAssertNil(VideoChapter(dictionary: [
|
|
7
|
+
"title": "Intro",
|
|
8
|
+
"startTime": NSNumber(value: true)
|
|
9
|
+
]))
|
|
10
|
+
XCTAssertNil(VideoChapter(dictionary: [
|
|
11
|
+
"title": "Intro",
|
|
12
|
+
"startTime": NSNumber(value: false)
|
|
13
|
+
]))
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
func testAcceptsNumericStartTime() {
|
|
17
|
+
let chapter = VideoChapter(dictionary: [
|
|
18
|
+
"title": "Intro",
|
|
19
|
+
"startTime": NSNumber(value: 12.5)
|
|
20
|
+
])
|
|
21
|
+
XCTAssertEqual(chapter?.startTime, 12.5)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
func testRejectsStartTimeAtOrAboveIntMaxDouble() {
|
|
25
|
+
XCTAssertNil(VideoChapter(dictionary: [
|
|
26
|
+
"title": "Edge",
|
|
27
|
+
"startTime": Double(Int.max)
|
|
28
|
+
]))
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import XCTest
|
|
2
|
+
@testable import VideoPlayerPlugin
|
|
3
|
+
|
|
4
|
+
final class VideoPlayerAudioSessionCategoryPolicyTests: XCTestCase {
|
|
5
|
+
func testPerPlayerCategoryUsesExplicitCategoryWhenSet() {
|
|
6
|
+
let needs = VideoPlayerAudioSessionNeeds(
|
|
7
|
+
explicitCategory: "moviePlayback",
|
|
8
|
+
needsBackgroundOrPipPlayback: false
|
|
9
|
+
)
|
|
10
|
+
XCTAssertEqual(VideoPlayerAudioSessionCategoryPolicy.category(for: needs), "moviePlayback")
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
func testPerPlayerCategoryUsesPlaybackForBackgroundOrPip() {
|
|
14
|
+
let needs = VideoPlayerAudioSessionNeeds(
|
|
15
|
+
explicitCategory: nil,
|
|
16
|
+
needsBackgroundOrPipPlayback: true
|
|
17
|
+
)
|
|
18
|
+
XCTAssertEqual(VideoPlayerAudioSessionCategoryPolicy.category(for: needs), "playback")
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
func testUnionCategoryStaysAmbientWhenNoPlayerNeedsPlayback() {
|
|
22
|
+
let ambient = VideoPlayerAudioSessionNeeds(
|
|
23
|
+
explicitCategory: nil,
|
|
24
|
+
needsBackgroundOrPipPlayback: false
|
|
25
|
+
)
|
|
26
|
+
XCTAssertEqual(VideoPlayerAudioSessionCategoryPolicy.unionCategory(for: [ambient, ambient]), "ambient")
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
func testUnionCategoryUpgradesToPlaybackWhenAnyPlayerNeedsBackgroundOrPip() {
|
|
30
|
+
let ambient = VideoPlayerAudioSessionNeeds(
|
|
31
|
+
explicitCategory: nil,
|
|
32
|
+
needsBackgroundOrPipPlayback: false
|
|
33
|
+
)
|
|
34
|
+
let pip = VideoPlayerAudioSessionNeeds(
|
|
35
|
+
explicitCategory: nil,
|
|
36
|
+
needsBackgroundOrPipPlayback: true
|
|
37
|
+
)
|
|
38
|
+
XCTAssertEqual(VideoPlayerAudioSessionCategoryPolicy.unionCategory(for: [ambient, pip]), "playback")
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
func testUnionCategoryPrefersMoviePlaybackOverPlayback() {
|
|
42
|
+
let movie = VideoPlayerAudioSessionNeeds(
|
|
43
|
+
explicitCategory: "moviePlayback",
|
|
44
|
+
needsBackgroundOrPipPlayback: false
|
|
45
|
+
)
|
|
46
|
+
let pip = VideoPlayerAudioSessionNeeds(
|
|
47
|
+
explicitCategory: nil,
|
|
48
|
+
needsBackgroundOrPipPlayback: true
|
|
49
|
+
)
|
|
50
|
+
XCTAssertEqual(VideoPlayerAudioSessionCategoryPolicy.unionCategory(for: [movie, pip]), "moviePlayback")
|
|
51
|
+
}
|
|
52
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@capgo/capacitor-video-player",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.3.0",
|
|
4
4
|
"description": "Capacitor plugin to play video in native player",
|
|
5
5
|
"main": "dist/plugin.cjs.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -53,7 +53,8 @@
|
|
|
53
53
|
"check:cap9-deprecated": "node scripts/check-cap9-deprecated.mjs",
|
|
54
54
|
"example:install": "cd example-app && bun install --frozen-lockfile",
|
|
55
55
|
"example:build": "bun run build && cd example-app && bun install --frozen-lockfile && bun run build",
|
|
56
|
-
"changelog:ai": "node scripts/generate-ai-changelog.mjs"
|
|
56
|
+
"changelog:ai": "node scripts/generate-ai-changelog.mjs",
|
|
57
|
+
"screenshots:pr-108": "node docs/pr-108-screenshots/capture.mjs"
|
|
57
58
|
},
|
|
58
59
|
"devDependencies": {
|
|
59
60
|
"@capacitor/android": "^8.0.0",
|
|
@@ -69,13 +70,14 @@
|
|
|
69
70
|
"eslint": "^10.0.0",
|
|
70
71
|
"eslint-plugin-import": "^2.31.0",
|
|
71
72
|
"hls.js": "^1.5.18",
|
|
73
|
+
"playwright": "1.49.1",
|
|
72
74
|
"prettier": "^3.6.2",
|
|
73
75
|
"prettier-plugin-java": "^2.7.7",
|
|
76
|
+
"prettier-pretty-check": "^0.2.0",
|
|
74
77
|
"rimraf": "^6.1.0",
|
|
75
78
|
"rollup": "^4.53.2",
|
|
76
79
|
"swiftlint": "^2.0.0",
|
|
77
|
-
"typescript": "^6.0.3"
|
|
78
|
-
"prettier-pretty-check": "^0.2.0"
|
|
80
|
+
"typescript": "^6.0.3"
|
|
79
81
|
},
|
|
80
82
|
"peerDependencies": {
|
|
81
83
|
"@capacitor/core": ">=8.0.0",
|