Switch media to another player

This commit is contained in:
estevez-dev 2019-09-15 17:29:49 +03:00
parent 8fc7d0b61e
commit 340e8569cc
7 changed files with 109 additions and 50 deletions

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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,
)
],
),

View File

@ -14,6 +14,8 @@ class HomeAssistant {
Map services;
String _userName;
HSVColor savedColor;
int savedPlayerPosition;
String savedPlayerId;
String fcmToken;

View File

@ -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}",

View File

@ -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 {

View File

@ -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}");