From efac829155ae10d675ea40d531d4f7356efe4499 Mon Sep 17 00:00:00 2001 From: ali2236 Date: Tue, 20 Aug 2024 19:35:37 +0330 Subject: [PATCH] Migrated to package:web --- audio_service/example/pubspec.yaml | 2 +- audio_service/example/web/index.html | 14 +- audio_service/pubspec.yaml | 4 +- audio_service_web/CHANGELOG.md | 2 + audio_service_web/lib/audio_service_web.dart | 112 ++++++++------ .../lib/js/media_session_web.dart | 139 +++++------------- audio_service_web/pubspec.yaml | 5 +- 7 files changed, 111 insertions(+), 167 deletions(-) diff --git a/audio_service/example/pubspec.yaml b/audio_service/example/pubspec.yaml index 743879a0..280d4fd5 100644 --- a/audio_service/example/pubspec.yaml +++ b/audio_service/example/pubspec.yaml @@ -3,7 +3,7 @@ description: Demonstrates how to use the audio_service plugin. publish_to: 'none' environment: - sdk: ">=2.14.0 <4.0.0" + sdk: ">=3.3.0 <4.0.0" flutter: ">=3.0.0" dependencies: diff --git a/audio_service/example/web/index.html b/audio_service/example/web/index.html index a0c14606..012a92a2 100644 --- a/audio_service/example/web/index.html +++ b/audio_service/example/web/index.html @@ -1,6 +1,8 @@ + + @@ -18,16 +20,6 @@ - - - + diff --git a/audio_service/pubspec.yaml b/audio_service/pubspec.yaml index 1b2b7830..2a3f7481 100644 --- a/audio_service/pubspec.yaml +++ b/audio_service/pubspec.yaml @@ -8,7 +8,7 @@ topics: - background environment: - sdk: ">=2.14.0 <4.0.0" + sdk: ">=3.3.0 <4.0.0" flutter: ">=3.0.0" dependencies: @@ -32,7 +32,7 @@ dependencies: # Use these deps when publishing. audio_service_platform_interface: ^0.1.1 - audio_service_web: ^0.1.3 + audio_service_web: ^0.2.0 audio_session: ^0.1.20 rxdart: '>=0.26.0 <0.29.0' diff --git a/audio_service_web/CHANGELOG.md b/audio_service_web/CHANGELOG.md index 2e2199d6..444da12c 100644 --- a/audio_service_web/CHANGELOG.md +++ b/audio_service_web/CHANGELOG.md @@ -1,5 +1,7 @@ ## 0.2.0 +* Migrated to package:web (@ali2236) + ## 0.1.3 * Support rxdart 0.28.x. diff --git a/audio_service_web/lib/audio_service_web.dart b/audio_service_web/lib/audio_service_web.dart index e509ea0b..82d562eb 100644 --- a/audio_service_web/lib/audio_service_web.dart +++ b/audio_service_web/lib/audio_service_web.dart @@ -1,11 +1,12 @@ import 'dart:async'; -import 'dart:html' as html; -import 'dart:js' as js; -import 'dart:js_util'; +import 'dart:js_interop' as js; +import 'dart:js_interop_unsafe'; import 'package:audio_service_platform_interface/audio_service_platform_interface.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; +import 'package:web/web.dart' as web; + import 'js/media_session_web.dart'; class AudioServiceWeb extends AudioServicePlatform { @@ -13,12 +14,16 @@ class AudioServiceWeb extends AudioServicePlatform { AudioServicePlatform.instance = AudioServiceWeb(); } + web.MediaSession get _mediaSession => web.window.navigator.mediaSession; + final _mediaSessionSupported = _SupportChecker( - () => js.context.hasProperty('MediaSession'), + () => js.globalContext.hasProperty('MediaSession'.toJS).toDart, "MediaSession is not supported in this browser, so plugin is no-op", ); final _setPositionStateSupported = _SupportChecker( - () => hasProperty(html.window.navigator.mediaSession!, 'setPositionState'), + () => web.window.navigator.mediaSession + .hasProperty('setPositionState'.toJS) + .toDart, "MediaSession.setPositionState is not supported in this browser", ); @@ -39,64 +44,75 @@ class AudioServiceWeb extends AudioServicePlatform { final state = request.state; if (state.processingState == AudioProcessingStateMessage.idle) { - MediaSession.playbackState = MediaSessionPlaybackState.none; + _mediaSession.playbackState = MediaSessionPlaybackState.none; } else { if (state.playing) { - MediaSession.playbackState = MediaSessionPlaybackState.playing; + _mediaSession.playbackState = MediaSessionPlaybackState.playing; } else { - MediaSession.playbackState = MediaSessionPlaybackState.paused; + _mediaSession.playbackState = MediaSessionPlaybackState.paused; } } for (final control in state.controls) { switch (control.action) { case MediaActionMessage.play: - MediaSession.setActionHandler( + _mediaSession.setActionHandler( MediaSessionActions.play, - (details) => handlerCallbacks?.play(const PlayRequest()), + ((MediaSessionActionDetails details) { + handlerCallbacks?.play(const PlayRequest()); + }).toJS, ); break; case MediaActionMessage.pause: - MediaSession.setActionHandler( + _mediaSession.setActionHandler( MediaSessionActions.pause, - (details) => handlerCallbacks?.pause(const PauseRequest()), + ((MediaSessionActionDetails details) { + handlerCallbacks?.pause(const PauseRequest()); + }).toJS, ); break; case MediaActionMessage.skipToPrevious: - MediaSession.setActionHandler( + _mediaSession.setActionHandler( MediaSessionActions.previoustrack, - (details) => - handlerCallbacks?.skipToPrevious(const SkipToPreviousRequest()), + ((MediaSessionActionDetails details) { + handlerCallbacks?.skipToPrevious(const SkipToPreviousRequest()); + }).toJS, ); break; case MediaActionMessage.skipToNext: - MediaSession.setActionHandler( + _mediaSession.setActionHandler( MediaSessionActions.nexttrack, - (details) => - handlerCallbacks?.skipToNext(const SkipToNextRequest()), + ((MediaSessionActionDetails details) { + handlerCallbacks?.skipToNext(const SkipToNextRequest()); + }).toJS, ); break; case MediaActionMessage.rewind: - MediaSession.setActionHandler( + _mediaSession.setActionHandler( MediaSessionActions.seekbackward, - (details) => handlerCallbacks?.rewind(const RewindRequest()), + ((MediaSessionActionDetails details) { + handlerCallbacks?.rewind(const RewindRequest()); + }).toJS, ); break; case MediaActionMessage.fastForward: - MediaSession.setActionHandler( + _mediaSession.setActionHandler( MediaSessionActions.seekforward, - (details) => - handlerCallbacks?.fastForward(const FastForwardRequest()), + ((MediaSessionActionDetails details) { + handlerCallbacks?.fastForward(const FastForwardRequest()); + }).toJS, ); break; case MediaActionMessage.stop: - MediaSession.setActionHandler( + _mediaSession.setActionHandler( MediaSessionActions.stop, - (details) => handlerCallbacks?.stop(const StopRequest()), + ((MediaSessionActionDetails details) { + handlerCallbacks?.stop(const StopRequest()); + }).toJS, ); break; default: - // no-op + // no-op break; } } @@ -104,17 +120,18 @@ class AudioServiceWeb extends AudioServicePlatform { for (final message in state.systemActions) { switch (message) { case MediaActionMessage.seek: - MediaSession.setActionHandler('seekto', - (MediaSessionActionDetails details) { - // Browsers use seconds - handlerCallbacks?.seek(SeekRequest( - position: + _mediaSession.setActionHandler( + 'seekto', + ((MediaSessionActionDetails details) { + // Browsers use seconds + handlerCallbacks?.seek(SeekRequest( + position: Duration(milliseconds: (details.seekTime * 1000).round()), - )); - }); + )); + }).toJS); break; default: - // no-op + // no-op break; } } @@ -128,7 +145,7 @@ class AudioServiceWeb extends AudioServicePlatform { final position = _minDuration(state.updatePosition, duration); // Browsers expect for seconds - MediaSession.setPositionState(MediaSessionPositionState( + _mediaSession.setPositionState(web.MediaPositionState( duration: duration.inMilliseconds / 1000, playbackRate: state.speed, position: position.inMilliseconds / 1000, @@ -151,17 +168,16 @@ class AudioServiceWeb extends AudioServicePlatform { final album = mediaItem!.album; final artUri = mediaItem!.artUri; - MediaSession.metadata = html.MediaMetadata({ - 'title': mediaItem!.title, - if (artist != null) 'artist': artist, - if (album != null) 'album': album, - 'artwork': [ - { - 'src': artUri, - 'sizes': '512x512', - } - ], - }); + _mediaSession.metadata = web.MediaMetadata( + web.MediaMetadataInit( + title: mediaItem!.title, + artist: artist.toString(), + album: album.toString(), + artwork: [ + web.MediaImage(src: artUri.toString(), sizes: '512x512'), + ].toJS, + ), + ); } @override @@ -169,8 +185,7 @@ class AudioServiceWeb extends AudioServicePlatform { if (!_mediaSessionSupported.check()) { return; } - final session = html.window.navigator.mediaSession!; - session.metadata = null; + _mediaSession.metadata = null; mediaItem = null; } @@ -193,6 +208,7 @@ class _SupportChecker { _SupportChecker(this._checkCallback, this._warningMessage); bool _logged = false; + bool check() { final result = _checkCallback(); if (!_logged && !result) { diff --git a/audio_service_web/lib/js/media_session_web.dart b/audio_service_web/lib/js/media_session_web.dart index a4f25a8a..91470e15 100644 --- a/audio_service_web/lib/js/media_session_web.dart +++ b/audio_service_web/lib/js/media_session_web.dart @@ -6,45 +6,8 @@ @JS() library media_session_web; -import 'dart:js' as js; -import 'dart:html' as html; -import 'package:js/js.dart'; - -/// The interface to the Media Session API which allows a web page -/// to provide custom behaviors for standard media playback interactions, -/// and to report metadata that can be sent by the user agent -/// to the device or operating system for presentation in standardized -/// user interface elements. -@JS('navigator.mediaSession') -class MediaSession { - /// Returns an instance of [html.MediaMetadata], which contains rich media - /// metadata for display in a platform UI. - @JS('metadata') - external static html.MediaMetadata metadata; - - /// Indicates whether the current media session is playing. - /// - /// See [MediaSessionPlaybackState] for possible values. - @JS('playbackState') - external static String playbackState; - - /// Sets an action handler for a media session action, such as play or pause. - static void setActionHandler( - String action, - MediaSessionActionHandler callback, - ) => - _setActionHandler(action, js.allowInterop(callback)); - - @JS('setActionHandler') - external static void _setActionHandler( - String action, - MediaSessionActionHandler callback, - ); - - /// Sets the current playback position and speed of the media currently being presented. - @JS('setPositionState') - external static void setPositionState(MediaSessionPositionState? state); -} +import 'dart:js_interop'; +import 'package:web/web.dart' as web; /// Media session playback state types. /// @@ -90,70 +53,42 @@ typedef MediaSessionActionHandler = dynamic Function(MediaSessionActionDetails); /// The dictionary parameter for [MediaSessionActionHandler] callback. @JS() @anonymous -class MediaSessionActionDetails { - /// An action type string taken from the [MediaActions], indicating which - /// type of action needs to be performed. - external String get action; - - /// Indicates whether or not to perform a "fast" seek. - /// - /// A `seekto` action may optionally include this property. - /// - /// A "fast" seek is a seek being performed in a rapid sequence, such as when - /// fast-forwarding or reversing through the media, rapidly skipping through it. - /// - /// This property can be used to indicate that you should use the shortest possible - /// method to seek the media. This property is not included on the final action in - /// the seek sequence in this situation. - external bool get fastSeek; - - /// If the action is either `seekforward` or `seekbackward` - /// and this property is present, it is a floating point value which indicates - /// the seek interval. - /// - /// If this property isn't present, those actions should choose a reasonable - /// default interval. - external double get seekOffset; +extension type MediaSessionActionDetails._(JSObject _) implements JSObject { +/// An action type string taken from the [MediaActions], indicating which +/// type of action needs to be performed. +external String get action; - /// If the action is `seekto`, this property is present and - /// indicates the absolute time within the media to move the playback position to. - /// - /// This property is not present for other action types. - external double get seekTime; - - /// Creates the details. - external factory MediaSessionActionDetails({ - String? action, - bool? fastSeek, - double? seekOffset, - double? seekTime, - }); -} - -/// A representation of the current playback. +/// Indicates whether or not to perform a "fast" seek. /// -/// The dictionary parameter for [MediaSession.setPositionState]. -@JS() -@anonymous -class MediaSessionPositionState { - /// Duration in seconds. - external double get duration; - - /// Playback rate. - /// - /// Can be positive to represent forward playback or negative to - /// represent backwards playback. - /// - /// Cannot be zero. - external double get playbackRate; - - /// Position in seconds. - external double get position; +/// A `seekto` action may optionally include this property. +/// +/// A "fast" seek is a seek being performed in a rapid sequence, such as when +/// fast-forwarding or reversing through the media, rapidly skipping through it. +/// +/// This property can be used to indicate that you should use the shortest possible +/// method to seek the media. This property is not included on the final action in +/// the seek sequence in this situation. +external bool get fastSeek; + +/// If the action is either `seekforward` or `seekbackward` +/// and this property is present, it is a floating point value which indicates +/// the seek interval. +/// +/// If this property isn't present, those actions should choose a reasonable +/// default interval. +external double get seekOffset; - /// Creates the position state. - external factory MediaSessionPositionState({ - double? duration, - double? playbackRate, - double? position, - }); +/// If the action is `seekto`, this property is present and +/// indicates the absolute time within the media to move the playback position to. +/// +/// This property is not present for other action types. +external double get seekTime; + +/// Creates the details. +external factory MediaSessionActionDetails({ +String? action, +bool? fastSeek, +double? seekOffset, +double? seekTime, +}); } diff --git a/audio_service_web/pubspec.yaml b/audio_service_web/pubspec.yaml index 6dbfb65e..b5c0994a 100644 --- a/audio_service_web/pubspec.yaml +++ b/audio_service_web/pubspec.yaml @@ -4,7 +4,7 @@ version: 0.2.0 homepage: https://github.com/ryanheise/audio_service/tree/master/audio_service_web environment: - sdk: ">=2.14.0 <4.0.0" + sdk: ">=3.3.0 <4.0.0" flutter: ">=3.0.0" dependencies: @@ -21,9 +21,8 @@ dependencies: # Use these deps when publishing. audio_service_platform_interface: ^0.1.1 - rxdart: '>=0.26.0 <0.29.0' - js: '>=0.6.3 <0.8.0' + web: '>=0.5.1 <2.0.0' flutter: sdk: flutter flutter_web_plugins: