Switch media to another player
This commit is contained in:
parent
8fc7d0b61e
commit
340e8569cc
@ -74,10 +74,37 @@ class MediaPlayerEntity extends Entity {
|
||||
|
||||
List<String> get soundModeList => getStringListAttributeValue("sound_mode_list");
|
||||
List<String> get sourceList => getStringListAttributeValue("source_list");
|
||||
DateTime get positionLastUpdated => DateTime.tryParse("${attributes["media_position_updated_at"]}")?.toLocal();
|
||||
int get durationSeconds => _getIntAttributeValue("media_duration");
|
||||
int get positionSeconds => _getIntAttributeValue("media_position");
|
||||
|
||||
@override
|
||||
Widget _buildAdditionalControlsForPage(BuildContext context) {
|
||||
return MediaPlayerControls();
|
||||
}
|
||||
|
||||
bool canCalculateActualPosition() {
|
||||
return positionLastUpdated != null && durationSeconds != null && positionSeconds != null;
|
||||
}
|
||||
|
||||
double getActualPosition() {
|
||||
double result = 0;
|
||||
if (canCalculateActualPosition()) {
|
||||
Duration durationD;
|
||||
Duration positionD;
|
||||
durationD = Duration(seconds: durationSeconds);
|
||||
positionD = Duration(
|
||||
seconds: positionSeconds);
|
||||
result = positionD.inSeconds.toDouble();
|
||||
int differenceInSeconds = DateTime
|
||||
.now()
|
||||
.difference(positionLastUpdated)
|
||||
.inSeconds;
|
||||
result = ((result + differenceInSeconds) <= durationD.inSeconds) ? (result + differenceInSeconds) : durationD.inSeconds.toDouble();
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -23,26 +23,10 @@ class _MediaPlayerProgressBarState extends State<MediaPlayerProgressBar> {
|
||||
final EntityModel entityModel = EntityModel.of(context);
|
||||
final MediaPlayerEntity entity = entityModel.entityWrapper.entity;
|
||||
double progress;
|
||||
DateTime lastUpdated = DateTime.tryParse("${entity.attributes["media_position_updated_at"]}")?.toLocal();
|
||||
Duration duration;
|
||||
Duration position;
|
||||
int durationInSeconds = entity._getIntAttributeValue("media_duration");
|
||||
if (durationInSeconds != null) {
|
||||
duration = Duration(seconds: durationInSeconds);
|
||||
}
|
||||
int positionInSeconds = entity._getIntAttributeValue("media_position");
|
||||
if (positionInSeconds != null) {
|
||||
position = Duration(
|
||||
seconds: positionInSeconds);
|
||||
}
|
||||
if (lastUpdated != null && duration != null && position != null) {
|
||||
int currentPosition = position.inSeconds;
|
||||
int differenceInSeconds = DateTime
|
||||
.now()
|
||||
.difference(lastUpdated)
|
||||
.inSeconds;
|
||||
currentPosition = currentPosition + differenceInSeconds;
|
||||
progress = (currentPosition <= duration.inSeconds) ? currentPosition / duration.inSeconds : 100;
|
||||
int currentPosition;
|
||||
if (entity.canCalculateActualPosition()) {
|
||||
currentPosition = entity.getActualPosition().toInt();
|
||||
progress = (currentPosition <= entity.durationSeconds) ? currentPosition / entity.durationSeconds : 100;
|
||||
} else {
|
||||
progress = 0;
|
||||
}
|
||||
|
@ -11,6 +11,8 @@ class _MediaPlayerSeekBarState extends State<MediaPlayerSeekBar> {
|
||||
bool _seekStarted = false;
|
||||
bool _changedHere = false;
|
||||
double _currentPosition = 0;
|
||||
int _savedPosition = 0;
|
||||
|
||||
final TextStyle _seekTextStyle = TextStyle(
|
||||
fontSize: 20,
|
||||
color: Colors.blue,
|
||||
@ -20,6 +22,10 @@ class _MediaPlayerSeekBarState extends State<MediaPlayerSeekBar> {
|
||||
@override
|
||||
initState() {
|
||||
super.initState();
|
||||
if (HomeAssistant().savedPlayerPosition != null) {
|
||||
_savedPosition = HomeAssistant().savedPlayerPosition;
|
||||
HomeAssistant().savedPlayerPosition = null;
|
||||
}
|
||||
_timer = Timer.periodic(Duration(seconds: 1), (_) {
|
||||
if (!_seekStarted && !_changedHere) {
|
||||
setState(() {});
|
||||
@ -27,35 +33,57 @@ class _MediaPlayerSeekBarState extends State<MediaPlayerSeekBar> {
|
||||
});
|
||||
}
|
||||
|
||||
void _sendTo(entity) {
|
||||
HomeAssistant().savedPlayerPosition = entity.getActualPosition().toInt();
|
||||
HomeAssistant().savedPlayerId = entity.entityId;
|
||||
Navigator.of(context).pushNamed("/play-media", arguments: {"url": entity.attributes["media_content_id"], "type": entity.attributes["media_content_type"]});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final EntityModel entityModel = EntityModel.of(context);
|
||||
final MediaPlayerEntity entity = entityModel.entityWrapper.entity;
|
||||
DateTime lastUpdated = DateTime.tryParse("${
|
||||
entity.attributes["media_position_updated_at"]}")?.toLocal();
|
||||
Duration duration;
|
||||
Duration position;
|
||||
int durationInSeconds = entity._getIntAttributeValue("media_duration");
|
||||
if (durationInSeconds != null) {
|
||||
duration = Duration(seconds: durationInSeconds);
|
||||
}
|
||||
int positionInSeconds = entity._getIntAttributeValue("media_position");
|
||||
if (positionInSeconds != null) {
|
||||
position = Duration(
|
||||
seconds: positionInSeconds);
|
||||
}
|
||||
if (lastUpdated != null && duration != null && position != null) {
|
||||
|
||||
if (entity.canCalculateActualPosition()) {
|
||||
if (HomeAssistant().savedPlayerId != entity.entityId && HomeAssistant().savedPlayerPosition != null) {
|
||||
_savedPosition = HomeAssistant().savedPlayerPosition;
|
||||
HomeAssistant().savedPlayerPosition = null;
|
||||
}
|
||||
if (entity.state == EntityState.playing && !_seekStarted &&
|
||||
!_changedHere) {
|
||||
_currentPosition = position.inSeconds.toDouble();
|
||||
int differenceInSeconds = DateTime
|
||||
.now()
|
||||
.difference(lastUpdated)
|
||||
.inSeconds;
|
||||
_currentPosition = ((_currentPosition + differenceInSeconds) <= duration.inSeconds) ? (_currentPosition + differenceInSeconds) : duration.inSeconds.toDouble();
|
||||
_currentPosition = entity.getActualPosition();
|
||||
} else if (_changedHere) {
|
||||
_changedHere = false;
|
||||
}
|
||||
List<Widget> buttons = [];
|
||||
if (_savedPosition > 0) {
|
||||
buttons.add(
|
||||
RaisedButton(
|
||||
child: Text("Jump to ${Duration(seconds: _savedPosition).toString().split('.')[0]}"),
|
||||
color: Colors.orange,
|
||||
focusColor: Colors.white,
|
||||
onPressed: () {
|
||||
eventBus.fire(ServiceCallEvent(
|
||||
"media_player",
|
||||
"media_seek",
|
||||
"${entity.entityId}",
|
||||
{"seek_position": _savedPosition}
|
||||
));
|
||||
setState(() {
|
||||
_savedPosition = 0;
|
||||
});
|
||||
},
|
||||
)
|
||||
);
|
||||
}
|
||||
buttons.add(
|
||||
RaisedButton(
|
||||
child: Text("Send to another player..."),
|
||||
color: Colors.blue,
|
||||
textColor: Colors.white,
|
||||
onPressed: () => _sendTo(entity),
|
||||
)
|
||||
);
|
||||
return Padding(
|
||||
padding: EdgeInsets.fromLTRB(Sizes.leftWidgetPadding, 20, Sizes.rightWidgetPadding, 0),
|
||||
child: Column(
|
||||
@ -68,7 +96,7 @@ class _MediaPlayerSeekBarState extends State<MediaPlayerSeekBar> {
|
||||
Expanded(
|
||||
child: Text("${Duration(seconds: _currentPosition.toInt()).toString().split(".")[0]}",textAlign: TextAlign.center, style: _seekTextStyle),
|
||||
),
|
||||
Text("${duration.toString().split(".")[0]}")
|
||||
Text("${Duration(seconds: entity.durationSeconds).toString().split(".")[0]}")
|
||||
],
|
||||
),
|
||||
Container(height: 10,),
|
||||
@ -76,7 +104,7 @@ class _MediaPlayerSeekBarState extends State<MediaPlayerSeekBar> {
|
||||
min: 0,
|
||||
activeColor: Colors.amber,
|
||||
inactiveColor: Colors.black26,
|
||||
max: duration.inSeconds.toDouble(),
|
||||
max: entity.durationSeconds.toDouble(),
|
||||
value: _currentPosition,
|
||||
onChangeStart: (val) {
|
||||
_seekStarted = true;
|
||||
@ -103,6 +131,9 @@ class _MediaPlayerSeekBarState extends State<MediaPlayerSeekBar> {
|
||||
}
|
||||
});
|
||||
},
|
||||
),
|
||||
ButtonBar(
|
||||
children: buttons,
|
||||
)
|
||||
],
|
||||
),
|
||||
|
@ -14,6 +14,8 @@ class HomeAssistant {
|
||||
Map services;
|
||||
String _userName;
|
||||
HSVColor savedColor;
|
||||
int savedPlayerPosition;
|
||||
String savedPlayerId;
|
||||
|
||||
String fcmToken;
|
||||
|
||||
|
@ -173,7 +173,10 @@ class HAClientApp extends StatelessWidget {
|
||||
"/": (context) => MainPage(title: 'HA Client'),
|
||||
"/connection-settings": (context) => ConnectionSettingsPage(title: "Settings"),
|
||||
"/putchase": (context) => PurchasePage(title: "Support app development"),
|
||||
"/play-media": (context) => PlayMediaPage(mediaUrl: "${ModalRoute.of(context).settings.arguments != null ? (ModalRoute.of(context).settings.arguments as Map)['url'] : ''}",),
|
||||
"/play-media": (context) => PlayMediaPage(
|
||||
mediaUrl: "${ModalRoute.of(context).settings.arguments != null ? (ModalRoute.of(context).settings.arguments as Map)['url'] : ''}",
|
||||
mediaType: "${ModalRoute.of(context).settings.arguments != null ? (ModalRoute.of(context).settings.arguments as Map)['type'] ?? '' : ''}",
|
||||
),
|
||||
"/log-view": (context) => LogViewPage(title: "Log"),
|
||||
"/login": (context) => WebviewScaffold(
|
||||
url: "${ConnectionManager().oauthUrl}",
|
||||
|
@ -349,6 +349,7 @@ class ConnectionManager {
|
||||
}
|
||||
|
||||
Future callService({String domain, String service, String entityId, Map additionalServiceData}) {
|
||||
Completer completer = Completer();
|
||||
Map serviceData = {};
|
||||
if (entityId != null) {
|
||||
serviceData["entity_id"] = entityId;
|
||||
@ -357,16 +358,17 @@ class ConnectionManager {
|
||||
serviceData.addAll(additionalServiceData);
|
||||
}
|
||||
if (serviceData.isNotEmpty)
|
||||
return sendHTTPPost(
|
||||
sendHTTPPost(
|
||||
endPoint: "/api/services/$domain/$service",
|
||||
data: json.encode(serviceData)
|
||||
);
|
||||
).then((data) => completer.complete(data)).catchError((e) => completer.completeError(HAError("${e["message"]}")));
|
||||
//return sendSocketMessage(type: "call_service", additionalData: {"domain": domain, "service": service, "service_data": serviceData});
|
||||
else
|
||||
return sendHTTPPost(
|
||||
sendHTTPPost(
|
||||
endPoint: "/api/services/$domain/$service"
|
||||
);
|
||||
).then((data) => completer.complete(data)).catchError((e) => completer.completeError(HAError("${e["message"]}")));;
|
||||
//return sendSocketMessage(type: "call_service", additionalData: {"domain": domain, "service": service});
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
Future<List> getHistory(String entityId) async {
|
||||
|
@ -3,8 +3,9 @@ part of '../main.dart';
|
||||
class PlayMediaPage extends StatefulWidget {
|
||||
|
||||
final String mediaUrl;
|
||||
final String mediaType;
|
||||
|
||||
PlayMediaPage({Key key, this.mediaUrl}) : super(key: key);
|
||||
PlayMediaPage({Key key, this.mediaUrl, this.mediaType}) : super(key: key);
|
||||
|
||||
@override
|
||||
_PlayMediaPageState createState() => new _PlayMediaPageState();
|
||||
@ -22,13 +23,22 @@ class _PlayMediaPageState extends State<PlayMediaPage> {
|
||||
bool _isMediaExtractorExist = false;
|
||||
StreamSubscription _stateSubscription;
|
||||
StreamSubscription _refreshDataSubscription;
|
||||
final List<String> _contentTypes = ["movie", "video", "music", "image", "image/jpg", "playlist"];
|
||||
List<String> _contentTypes = ["movie", "video", "music", "image", "image/jpg", "playlist"];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_mediaUrl = widget.mediaUrl;
|
||||
_contentType = _contentTypes[0];
|
||||
if (widget.mediaType.isNotEmpty) {
|
||||
if (!_contentTypes.contains(widget.mediaType)) {
|
||||
_contentTypes.insert(0, widget.mediaType);
|
||||
_contentType = _contentTypes[0];
|
||||
} else {
|
||||
_contentType = widget.mediaType;
|
||||
}
|
||||
} else {
|
||||
_contentType = _contentTypes[0];
|
||||
}
|
||||
_stateSubscription = eventBus.on<StateChangedEvent>().listen((event) {
|
||||
if (event.entityId.contains("media_player")) {
|
||||
Logger.d("State change event handled by play media page: ${event.entityId}");
|
||||
|
Reference in New Issue
Block a user