Skip to content

Commit

Permalink
[video_player] Add optional web options [web] (flutter#4551)
Browse files Browse the repository at this point in the history
Web PR for Video Player Web Options (flutter/packages#3259).
  • Loading branch information
defuncart authored Sep 7, 2023
1 parent 85d9ce3 commit 8bf9abb
Show file tree
Hide file tree
Showing 6 changed files with 258 additions and 2 deletions.
4 changes: 4 additions & 0 deletions packages/video_player/video_player_web/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 2.1.0

* Adds web options to customize the control list and context menu display.

## 2.0.18

* Migrates to `dart:ui_web` APIs.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,5 +213,199 @@ void main() {
expect(events[0].duration, equals(jsCompatibleTimeUnset));
});
});

group('VideoPlayerWebOptions', () {
late VideoPlayer player;

setUp(() {
video = html.VideoElement();
player = VideoPlayer(videoElement: video)..initialize();
});

group('VideoPlayerWebOptionsControls', () {
testWidgets('when disabled expect no controls',
(WidgetTester tester) async {
await player.setOptions(
const VideoPlayerWebOptions(
// ignore: avoid_redundant_argument_values
controls: VideoPlayerWebOptionsControls.disabled(),
),
);

expect(video.controls, isFalse);
expect(video.controlsList, isNotNull);
expect(video.controlsList?.length, isZero);
});

group('when enabled', () {
testWidgets('expect controls', (WidgetTester tester) async {
await player.setOptions(
const VideoPlayerWebOptions(
controls: VideoPlayerWebOptionsControls.enabled(),
),
);

expect(video.controls, isTrue);
expect(video.controlsList, isNotNull);
expect(video.controlsList?.length, isZero);
expect(video.controlsList?.contains('nodownload'), isFalse);
expect(video.controlsList?.contains('nofullscreen'), isFalse);
expect(video.controlsList?.contains('noplaybackrate'), isFalse);
expect(video.getAttribute('disablePictureInPicture'), isNull);
});

testWidgets('and no download expect correct controls',
(WidgetTester tester) async {
await player.setOptions(
const VideoPlayerWebOptions(
controls: VideoPlayerWebOptionsControls.enabled(
allowDownload: false,
),
),
);

expect(video.controls, isTrue);
expect(video.controlsList, isNotNull);
expect(video.controlsList?.length, 1);
expect(video.controlsList?.contains('nodownload'), isTrue);
expect(video.controlsList?.contains('nofullscreen'), isFalse);
expect(video.controlsList?.contains('noplaybackrate'), isFalse);
expect(video.getAttribute('disablePictureInPicture'), isNull);
});

testWidgets('and no fullscreen expect correct controls',
(WidgetTester tester) async {
await player.setOptions(
const VideoPlayerWebOptions(
controls: VideoPlayerWebOptionsControls.enabled(
allowFullscreen: false,
),
),
);

expect(video.controls, isTrue);
expect(video.controlsList, isNotNull);
expect(video.controlsList?.length, 1);
expect(video.controlsList?.contains('nodownload'), isFalse);
expect(video.controlsList?.contains('nofullscreen'), isTrue);
expect(video.controlsList?.contains('noplaybackrate'), isFalse);
expect(video.getAttribute('disablePictureInPicture'), isNull);
});

testWidgets('and no playback rate expect correct controls',
(WidgetTester tester) async {
await player.setOptions(
const VideoPlayerWebOptions(
controls: VideoPlayerWebOptionsControls.enabled(
allowPlaybackRate: false,
),
),
);

expect(video.controls, isTrue);
expect(video.controlsList, isNotNull);
expect(video.controlsList?.length, 1);
expect(video.controlsList?.contains('nodownload'), isFalse);
expect(video.controlsList?.contains('nofullscreen'), isFalse);
expect(video.controlsList?.contains('noplaybackrate'), isTrue);
expect(video.getAttribute('disablePictureInPicture'), isNull);
});

testWidgets('and no picture in picture expect correct controls',
(WidgetTester tester) async {
await player.setOptions(
const VideoPlayerWebOptions(
controls: VideoPlayerWebOptionsControls.enabled(
allowPictureInPicture: false,
),
),
);

expect(video.controls, isTrue);
expect(video.controlsList, isNotNull);
expect(video.controlsList?.length, 0);
expect(video.controlsList?.contains('nodownload'), isFalse);
expect(video.controlsList?.contains('nofullscreen'), isFalse);
expect(video.controlsList?.contains('noplaybackrate'), isFalse);
expect(video.getAttribute('disablePictureInPicture'), 'true');
});
});
});

group('allowRemotePlayback', () {
testWidgets('when enabled expect no attribute',
(WidgetTester tester) async {
await player.setOptions(
const VideoPlayerWebOptions(
// ignore: avoid_redundant_argument_values
allowRemotePlayback: true,
),
);

expect(video.getAttribute('disableRemotePlayback'), isNull);
});

testWidgets('when disabled expect attribute',
(WidgetTester tester) async {
await player.setOptions(
const VideoPlayerWebOptions(
allowRemotePlayback: false,
),
);

expect(video.getAttribute('disableRemotePlayback'), 'true');
});
});

group('when called first time', () {
testWidgets('expect correct options', (WidgetTester tester) async {
await player.setOptions(
const VideoPlayerWebOptions(
controls: VideoPlayerWebOptionsControls.enabled(
allowDownload: false,
allowFullscreen: false,
allowPlaybackRate: false,
allowPictureInPicture: false,
),
allowContextMenu: false,
allowRemotePlayback: false,
),
);

expect(video.controls, isTrue);
expect(video.controlsList, isNotNull);
expect(video.controlsList?.length, 3);
expect(video.controlsList?.contains('nodownload'), isTrue);
expect(video.controlsList?.contains('nofullscreen'), isTrue);
expect(video.controlsList?.contains('noplaybackrate'), isTrue);
expect(video.getAttribute('disablePictureInPicture'), 'true');
expect(video.getAttribute('disableRemotePlayback'), 'true');
});

group('when called once more', () {
testWidgets('expect correct options', (WidgetTester tester) async {
await player.setOptions(
const VideoPlayerWebOptions(
// ignore: avoid_redundant_argument_values
controls: VideoPlayerWebOptionsControls.disabled(),
// ignore: avoid_redundant_argument_values
allowContextMenu: true,
// ignore: avoid_redundant_argument_values
allowRemotePlayback: true,
),
);

expect(video.controls, isFalse);
expect(video.controlsList, isNotNull);
expect(video.controlsList?.length, 0);
expect(video.controlsList?.contains('nodownload'), isFalse);
expect(video.controlsList?.contains('nofullscreen'), isFalse);
expect(video.controlsList?.contains('noplaybackrate'), isFalse);
expect(video.getAttribute('disablePictureInPicture'), isNull);
expect(video.getAttribute('disableRemotePlayback'), isNull);
});
});
});
});
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -239,5 +239,15 @@ void main() {
VideoEventType.bufferingEnd,
]));
});

testWidgets('can set web options', (WidgetTester tester) async {
expect(
VideoPlayerPlatform.instance.setWebOptions(
await textureId,
const VideoPlayerWebOptions(),
),
completes,
);
});
});
}
43 changes: 43 additions & 0 deletions packages/video_player/video_player_web/lib/src/video_player.dart
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class VideoPlayer {

final StreamController<VideoEvent> _eventController;
final html.VideoElement _videoElement;
void Function(html.Event)? _onContextMenu;

bool _isInitialized = false;
bool _isBuffering = false;
Expand Down Expand Up @@ -202,9 +203,51 @@ class VideoPlayer {
return Duration(milliseconds: (_videoElement.currentTime * 1000).round());
}

/// Sets options
Future<void> setOptions(VideoPlayerWebOptions options) async {
// In case this method is called multiple times, reset options.
_resetOptions();

if (options.controls.enabled) {
_videoElement.controls = true;
final String controlsList = options.controls.controlsList;
if (controlsList.isNotEmpty) {
_videoElement.setAttribute('controlsList', controlsList);
}

if (!options.controls.allowPictureInPicture) {
_videoElement.setAttribute('disablePictureInPicture', true);
}
}

if (!options.allowContextMenu) {
_onContextMenu = (html.Event event) => event.preventDefault();
_videoElement.addEventListener('contextmenu', _onContextMenu);
}

if (!options.allowRemotePlayback) {
_videoElement.setAttribute('disableRemotePlayback', true);
}
}

void _resetOptions() {
_videoElement.controls = false;
_videoElement.removeAttribute('controlsList');
_videoElement.removeAttribute('disablePictureInPicture');
if (_onContextMenu != null) {
_videoElement.removeEventListener('contextmenu', _onContextMenu);
_onContextMenu = null;
}
_videoElement.removeAttribute('disableRemotePlayback');
}

/// Disposes of the current [html.VideoElement].
void dispose() {
_videoElement.removeAttribute('src');
if (_onContextMenu != null) {
_videoElement.removeEventListener('contextmenu', _onContextMenu);
_onContextMenu = null;
}
_videoElement.load();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,11 @@ class VideoPlayerPlugin extends VideoPlayerPlatform {
return _player(textureId).events;
}

@override
Future<void> setWebOptions(int textureId, VideoPlayerWebOptions options) {
return _player(textureId).setOptions(options);
}

// Retrieves a [VideoPlayer] by its internal `id`.
// It must have been created earlier from the [create] method.
VideoPlayer _player(int id) {
Expand Down
4 changes: 2 additions & 2 deletions packages/video_player/video_player_web/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: video_player_web
description: Web platform implementation of video_player.
repository: https://github.com/flutter/packages/tree/main/packages/video_player/video_player_web
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22
version: 2.0.18
version: 2.1.0

environment:
sdk: ">=3.1.0 <4.0.0"
Expand All @@ -21,7 +21,7 @@ dependencies:
sdk: flutter
flutter_web_plugins:
sdk: flutter
video_player_platform_interface: ">=6.1.0 <7.0.0"
video_player_platform_interface: ^6.2.0

dev_dependencies:
flutter_test:
Expand Down

0 comments on commit 8bf9abb

Please sign in to comment.