Skip to content

Commit b6d5988

Browse files
committed
improved media-player widget, handling and events
1 parent 8e1c0f7 commit b6d5988

File tree

7 files changed

+250
-89
lines changed

7 files changed

+250
-89
lines changed

www/css/sepiaFW-cards.css

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,15 @@
173173
.sepiaFW-cards-contextMenu-single li {
174174
margin: 8px 4px 0px 4px !important;
175175
}
176+
.sepiaFW-cards-contextMenu-section {
177+
display: flex;
178+
flex-wrap: wrap;
179+
justify-content: space-evenly;
180+
align-items: center;
181+
}
182+
.sepiaFW-cards-contextMenu-section.full-width {
183+
width: 100%;
184+
}
176185
#sepiaFW-my-view .sepiaFW-cards-list-contextMenu .sepiaFW-cards-list-moveBtn {
177186
display: none;
178187
}

www/scripts/sepiaFW.audio.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -395,8 +395,7 @@ function sepiaFW_build_audio(){
395395
audioTitle = document.getElementById('sepiaFW-audio-ctrls-title');
396396
audioStartBtn = document.getElementById('sepiaFW-audio-ctrls-start');
397397
$(audioStartBtn).off().on('click', function(){
398-
//test: player.src = "sounds/coin.mp3";
399-
//player.play();
398+
//NOTE: for now this is "internal" player only (as volume control)
400399
if (!AudioPlayer.initAudio(function(){ AudioPlayer.playURL('', player); })){
401400
AudioPlayer.playURL('', player);
402401
}

www/scripts/sepiaFW.client.controls.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -194,11 +194,10 @@ function sepiaFW_build_client_controls(sepiaSessionId){
194194
}
195195
//Player and platform specific additional STOP methods
196196
var sentAdditionalEvent = false;
197-
if (emp && (emp.isPlaying() || emp.isOnHold())){
197+
if (emp){ //&& (emp.isPlaying() || emp.isOnHold())
198198
//Embedded media player
199-
emp.pause();
200-
sentAdditionalEvent = true;
201-
SepiaFW.debug.info("Client controls - Media: stopping embedded media player");
199+
sentAdditionalEvent = SepiaFW.ui.cards.embed.stopAllMediaPlayers();
200+
SepiaFW.debug.info("Client controls - Media: stopping embedded media player(s)");
202201
}else if (SepiaFW.ui.isAndroid){
203202
//we do this only if we have a recent Android media event - otherwhise it will activate all music apps
204203
var requireMediaAppPackage = true;

www/scripts/sepiaFW.local.js

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -179,9 +179,10 @@ function sepiaFW_build_strings(){
179179
StringsDE.broadcast_to_device_or_chat = "Broadcast an eines deiner Geräte schicken oder per Chat senden?";
180180
StringsDE.relative_precipitation = "relativer Niederschlag";
181181
//Link sharing
182-
StringsDE.link_join_channel = "Einem Chat-Kanal beitreten."
183-
StringsDE.link_open_url = "Einen Link öffnen."
184-
StringsDE.link_create_reminder = "Eine Erinnerung einrichten."
182+
StringsDE.link_join_channel = "Einem Chat-Kanal beitreten.";
183+
StringsDE.link_open_url = "Einen Link öffnen.";
184+
StringsDE.link_media_search = "Mediensuche";
185+
StringsDE.link_create_reminder = "Eine Erinnerung einrichten.";
185186
//Offline demo texts
186187
StringsDE.demoMode = 'Du befindest dich im Demo-Modus! In diesem Modus kannst du die Einstellungen ändern (z.B. hostname), das Tutorial anschauen und mit manchen(!) Teilen des Interfaces spielen. Um auf deinen Assistenten zuzugreifen melde dich bitte zuerst an.';
187188
StringsDE.setupMode = 'Du befindest dich im Setup-Modus! Dieser Modus kann genutzt werden um den Client lokal oder via Fernzugriff zu konfigurieren. Bitte beachte, dass die meisten Grundfunktionen nicht verfügbar sind in diesem Modus!';
@@ -391,9 +392,10 @@ function sepiaFW_build_strings(){
391392
StringsEN.broadcast_to_device_or_chat = "Broadcast to one of your devices or via chat?";
392393
StringsEN.relative_precipitation = "relative precipitation";
393394
//Link sharing
394-
StringsEN.link_join_channel = "Join a chat-channel."
395-
StringsEN.link_open_url = "Open a link."
396-
StringsEN.link_create_reminder = "Create a reminder."
395+
StringsEN.link_join_channel = "Join a chat-channel.";
396+
StringsEN.link_open_url = "Open a link.";
397+
StringsEN.link_media_search = "Media Search";
398+
StringsEN.link_create_reminder = "Create a reminder.";
397399
//Offline demo texts
398400
StringsEN.demoMode = 'You are in demo-mode! This mode only allows you to change settings (e.g. hostname), check out the tutorial and play with some(!) parts of the UI. To get access to your assistant please log-in first.';
399401
StringsEN.setupMode = 'You are in setup-mode! This mode is used to configure the client locally or via remote access. Note that most client featires will not work in this mode!';

www/scripts/sepiaFW.ui.cards.embed.js

Lines changed: 126 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,8 @@ function sepiaFW_build_ui_cards_embed(){
55
//TODO: add MediaPlayer
66
/*
77
- Controls: start, pause/stop, resume, next, previous
8-
- State functions: getState, isOnHold
9-
- Broadcast state change (start, pause, hold, resume, ?)
10-
- Make sure only one is active
118
- Implement async. callback with msgId and timeout
9+
- Get media info and use SepiaFW.audio.setPlayerTitle('', '') + setMediaSessionState (SepiaFW.audio)
1210
*/
1311
var playerWidgets = {
1412
default: "<assist_server>/widgets/mp-default.html",
@@ -24,14 +22,19 @@ function sepiaFW_build_ui_cards_embed(){
2422
function getNewMediaPlayerId(){
2523
mediaPlayerLastId++;
2624
var overflow = false;
27-
if (mediaPlayerLastId >= 20){
25+
if (mediaPlayerLastId > 5){
2826
mediaPlayerLastId = 1;
2927
overflow = true;
3028
}
3129
var newId = ("sepia-embedded-player-" + mediaPlayerLastId);
3230
if (overflow){
33-
//TODO: remove old players
34-
//activeMediaPlayers ...
31+
//remove old players
32+
var oldMp = activeMediaPlayers[newId];
33+
if (oldMp && oldMp.exists()){
34+
oldMp.release();
35+
$(oldMp.cardItem).remove();
36+
}
37+
//console.error("RELEASED and REMOVED", newId, activeMediaPlayers[newId]); //DEBUG
3538
}
3639
return newId;
3740
}
@@ -84,25 +87,32 @@ function sepiaFW_build_ui_cards_embed(){
8487
}
8588
}
8689
//Register new audio fade (stop/start) handler
87-
function registerMediaPlayerFadeInOutListener(mediaPlayer){
90+
function registerMediaPlayerFadeInOutListener(){
8891
//NOTE: will always overwrite old ones if existing (same ID for every player, we control only last one)
8992
SepiaFW.audio.registerNewFadeListener({
9093
id: "embedded-media-player",
9194
isOnHold: function(){
92-
return mediaPlayer.isOnHold();
95+
var mp = Embed.getActiveMediaPlayer();
96+
if (mp && mp.isOnHold()){
97+
return true;
98+
}else{
99+
return false;
100+
}
93101
},
94102
onFadeOutRequest: function(force){
95103
//check if player is playing
96-
if (mediaPlayer.isPlaying() && !mediaPlayer.isWaitingForPause()){
97-
mediaPlayer.fadeOut();
104+
var mp = Embed.getActiveMediaPlayer();
105+
if (mp && mp.isPlaying() && !mp.isWaitingForPause()){
106+
mp.fadeOut();
98107
return true;
99108
}else{
100109
return false;
101110
}
102111
},
103112
onFadeInRequest: function(){
104-
if (mediaPlayer.isOnHold() && mediaPlayer.isPaused()){
105-
mediaPlayer.fadeIn();
113+
var mp = Embed.getActiveMediaPlayer();
114+
if (mp && mp.isOnHold() && mp.isPaused()){
115+
mp.fadeIn();
106116
return true;
107117
}else{
108118
return false;
@@ -113,7 +123,24 @@ function sepiaFW_build_ui_cards_embed(){
113123

114124
//Get active media player
115125
Embed.getActiveMediaPlayer = function(){
116-
return lastActiveMediaPlayer;
126+
if (lastActiveMediaPlayer && lastActiveMediaPlayer.exists()){
127+
return lastActiveMediaPlayer;
128+
}else{
129+
return;
130+
}
131+
}
132+
//Stop all media players
133+
Embed.stopAllMediaPlayers = function(except){
134+
var triedToStopAtLeastOne = false;
135+
Object.values(activeMediaPlayers).forEach(function(mp){
136+
if (!except || mp != except){
137+
if (mp.isPlaying() || mp.isWaitingForPlay()){
138+
mp.pause();
139+
triedToStopAtLeastOne = true;
140+
}
141+
}
142+
});
143+
return triedToStopAtLeastOne;
117144
}
118145

119146
//MediaPlayer interface
@@ -128,13 +155,19 @@ function sepiaFW_build_ui_cards_embed(){
128155
//events: onready
129156

130157
var thisPlayer = this;
131-
console.error("TEST", "embedWebPlayer", options); //DEBUG
158+
//console.error("Embedded MediaPlayer", options); //DEBUG
132159

133160
//ID
134161
var playerId = getNewMediaPlayerId();
162+
var lastMessageId = 0;
135163
thisPlayer.getId = function(){
136164
return playerId;
137165
}
166+
function getMessageId(){
167+
lastMessageId++;
168+
if (lastMessageId > 10000) lastMessageId = 1;
169+
return (playerId + "-" + lastMessageId);
170+
}
138171

139172
//Widget URL
140173
var widgetUrl = (options.widgetUrl || playerWidgets[options.widget]).trim();
@@ -147,26 +180,61 @@ function sepiaFW_build_ui_cards_embed(){
147180
//URL parameters (so we have the data during loading)
148181
widgetUrl = SepiaFW.tools.setParameterInURL(widgetUrl, "skinStyle", SepiaFW.ui.getSkinStyle());
149182
widgetUrl = SepiaFW.tools.setParameterInURL(widgetUrl, "skinId", SepiaFW.ui.getSkin());
183+
//add more: language, isMobile, env, ...?
150184

151-
console.error("URL", widgetUrl, "isTrusted", widgetIsTrusted); //DEBUG
185+
console.error("Media Player URL", widgetUrl, "isTrusted", widgetIsTrusted); //DEBUG
152186

153187
//Create card (DOM element)
154188
var mpObj = createMediaPlayerDomElement(playerId, widgetUrl, widgetIsTrusted, function(){
155189
//on-load
156-
console.error("on-load", playerId); //DEBUG
190+
//console.error("on-load", playerId); //DEBUG
157191
});
158192
thisPlayer.iframe = mpObj.iframe;
193+
thisPlayer.cardItem = mpObj.card;
194+
thisPlayer.exists = function(){
195+
var ex = thisPlayer.cardItem && document.body.contains(thisPlayer.cardItem);
196+
if (!ex && state != 11){
197+
thisPlayer.release();
198+
}
199+
return ex;
200+
}
159201
options.parentElement.appendChild(mpObj.card);
202+
203+
//Card move and delete actions
204+
mpObj.card.sepiaCardOnBeforeMove = function(){
205+
console.error("MOVE EVENT", playerId); //DEBUG
206+
//TODO: rescue state - anything else? (note: onready is a one-time event now)
207+
}
208+
mpObj.card.sepiaCardOnBeforeRemove = function(){
209+
//TODO: currently called nowhere
210+
console.error("REMOVE EVENT", playerId); //DEBUG
211+
thisPlayer.release();
212+
}
160213

161214
//SEPIA postMessage interface
162215
function sendEvent(ev){
216+
if (state && state == 11){
217+
//player is already released
218+
SepiaFW.debug.info("Embedded MediaPlayer - Blocked 'sendEvent' because player was already released.");
219+
return;
220+
}
163221
thisPlayer.iframe.contentWindow.postMessage({
164222
type: "sepia-embedded-player-event",
223+
msgId: getMessageId(),
165224
ev: ev
166225
}, "*");
167226
}
168227
thisPlayer.eventHandler = function(ev){
169-
console.error("ev", playerId, ev); //DEBUG
228+
//player still exists? (is this even possible?)
229+
if (!thisPlayer.exists()){
230+
SepiaFW.debug.error("Embedded MediaPlayer - Player received event but was removed or released!");
231+
return;
232+
}
233+
if (state && state == 11){
234+
//player is already released
235+
SepiaFW.debug.info("Embedded MediaPlayer - Blocked 'sendEvent' because player was already released.");
236+
return;
237+
}
170238
if (ev.state != undefined){
171239
stateHandler(ev);
172240
}
@@ -187,28 +255,40 @@ function sepiaFW_build_ui_cards_embed(){
187255
thisPlayer.iframe.style.height = data.size.height;
188256
}
189257
setTimeout(function(){ $(mpObj.overlay).hide(); }, 500);
190-
//callback
191-
if (options.onready) options.onready();
258+
//callback (only once)
259+
if (options.onready){
260+
options.onready();
261+
options.onready = undefined;
262+
}
192263

193264
}else if (ev.state == 2){
194265
//on-play
195266
state = ev.state;
196267
SepiaFW.audio.broadcastAudioEvent("embedded-media-player", "start");
268+
lastActiveMediaPlayer = thisPlayer; //TODO: use?
269+
SepiaFW.debug.info("Embedded MediaPlayer - Last active player switched to: " + playerId);
270+
//Embed.stopAllMediaPlayers(thisPlayer) //this should be handled globally for ALL media
271+
197272
}else if (ev.state == 3){
198273
//on-pause
199274
state = ev.state;
200275
SepiaFW.audio.broadcastAudioEvent("embedded-media-player", "pause");
276+
201277
}else if (ev.state == 10){
202278
//on-error
279+
var lastWasError = (state == 10);
203280
state = ev.state;
204-
//TODO: check "code" and "name" - broadcast error? - reset states? - make sure all is off?
205281
//ev.code-ev.name: 1-UnknownEvent, 2-NoMediaMatch, 3-PlayerErrorNotAllowed, 4-PlayerError (any), ... tbd
206-
if (ev.name == "NoMediaMatch"){
207-
SepiaFW.client.controls.sendMediaPlayerErrorFollowUpMessage("notFound", undefined, undefined);
208-
}else if (ev.name == "PlayerErrorNotAllowed"){
209-
SepiaFW.client.controls.sendMediaPlayerErrorFollowUpMessage("notPossible", undefined, undefined);
282+
if (lastWasError){
283+
SepiaFW.ui.showInfo("Media-Player error: " + (ev.name || ev.message || "?"));
210284
}else{
211-
SepiaFW.client.controls.sendMediaPlayerErrorFollowUpMessage("error", undefined, undefined);
285+
if (ev.name == "NoMediaMatch"){
286+
SepiaFW.client.controls.sendMediaPlayerErrorFollowUpMessage("notFound", undefined, undefined);
287+
}else if (ev.name == "PlayerErrorNotAllowed"){
288+
SepiaFW.client.controls.sendMediaPlayerErrorFollowUpMessage("notPossible", undefined, undefined);
289+
}else{
290+
SepiaFW.client.controls.sendMediaPlayerErrorFollowUpMessage("error", undefined, undefined);
291+
}
212292
}
213293
SepiaFW.debug.error("Embedded MediaPlayer - Error:", ev.name, ev.message, ev.code);
214294
SepiaFW.audio.broadcastAudioEvent("embedded-media-player", "error");
@@ -223,41 +303,38 @@ function sepiaFW_build_ui_cards_embed(){
223303

224304
//state getters
225305
thisPlayer.isReady = function(){
226-
return (state >= 1 && state < 10);
306+
return (thisPlayer.exists() && state >= 1 && state < 10);
227307
}
228308
thisPlayer.isPlaying = function(){
229-
//lastActiveMediaPlayer = thisPlayer; //TODO: use?
230-
return state == 2;
309+
return (thisPlayer.exists() && state == 2);
231310
}
232311
thisPlayer.isPaused = function(){
233-
return state == 3;
312+
return (thisPlayer.exists() && state == 3);
234313
}
235314
thisPlayer.isWaitingForPlay = function(){
236-
return isWaitingForPlay;
315+
return (thisPlayer.exists() && isWaitingForPlay);
237316
}
238317
thisPlayer.isWaitingForPause = function(){
239-
return isWaitingForPause;
318+
return (thisPlayer.exists() && isWaitingForPause);
240319
}
241320
thisPlayer.isOnHold = function(){
242-
//TODO: implement
243-
return isOnHold;
321+
//TODO: implemented correct?
322+
return (thisPlayer.exists() && isOnHold);
244323
}
245324
thisPlayer.isActive = function(){
246-
return (state == 2 || isOnHold); //include 'isOnHold'? (atm we don't check this for internal player)
325+
return (thisPlayer.exists() && (state == 2 || isOnHold));
326+
//include 'isOnHold'? (atm we don't check this for internal player)
247327
}
248328

249-
//TODO: SepiaFW.audio.setPlayerTitle('', '') + setMediaSessionState (SepiaFW.audio)
250-
//TODO: move to my-view handler (avoid additional onready event due to iframe reload etc.)
251-
//TODO: prevent multiple error messages
252-
253-
//Controls - TODO: implement
329+
//Controls - TODO: implement callbacks?! more?
254330
thisPlayer.play = function(doneCallback, errorCallback){
255331
sendEvent({controls: "play"});
256332
isWaitingForPlay = true;
257333
}
258334
thisPlayer.pause = function(doneCallback, errorCallback){
259335
sendEvent({controls: "pause"});
260336
isWaitingForPause = true;
337+
isOnHold = false;
261338
}
262339
thisPlayer.fadeOut = function(doneCallback, errorCallback){
263340
sendEvent({controls: "fadeOut"});
@@ -307,14 +384,21 @@ function sepiaFW_build_ui_cards_embed(){
307384

308385
//stop, release resources and remove handle
309386
thisPlayer.release = function(doneCallback, errorCallback){
310-
//TODO: implement
311-
// - stop, remove from active players, remove from DOM?
387+
// - remove from active players, block incoming events
312388
state = 11;
389+
if (lastActiveMediaPlayer == thisPlayer) lastActiveMediaPlayer = undefined;
390+
delete activeMediaPlayers[playerId];
391+
SepiaFW.debug.info("Embedded MediaPlayer - Released player with id: " + playerId);
313392
}
314393

315-
//add audio-interface events
394+
//add audio-interface events (make sure its set)
316395
registerMediaPlayerFadeInOutListener(thisPlayer);
317396

397+
//make sure last active player is stopped
398+
if (lastActiveMediaPlayer && (lastActiveMediaPlayer.isPlaying() || lastActiveMediaPlayer.isWaitingForPlay())){
399+
lastActiveMediaPlayer.pause();
400+
}
401+
318402
//store in list
319403
activeMediaPlayers[playerId] = thisPlayer;
320404
lastActiveMediaPlayer = thisPlayer;

0 commit comments

Comments
 (0)