Skip to content

Commit 19eb62a

Browse files
committed
added youtube embedded support; webplay configs
1 parent 7f7dddd commit 19eb62a

File tree

5 files changed

+159
-58
lines changed

5 files changed

+159
-58
lines changed

www/scripts/sepiaFW.app.config.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,8 @@ function sepiaFW_build_config(){
147147
//Collection of universally supported apps and their names
148148
Config.musicApps = {
149149
"youtube": {name: "YouTube"},
150-
"spotify_link": {name: "Spotify (URI)"},
151-
"apple_music_link": {name: "Apple Music (URI)"}
150+
"spotify_link": {name: "Spotify"},
151+
"apple_music_link": {name: "Apple Music"}
152152
}
153153
var defaultMusicApp = "youtube";
154154

www/scripts/sepiaFW.assistant.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,8 @@ function sepiaFW_build_assistant(){
110110
//custom data
111111
var cd = {
112112
defaultMusicApp: SepiaFW.config.getDefaultMusicApp(),
113-
recentPAE: (SepiaFW.events)? SepiaFW.events.getRecentProActiveEventsReduced() : ""
113+
recentPAE: ((SepiaFW.events)? SepiaFW.events.getRecentProActiveEventsReduced() : ""),
114+
embeddedPlayers: SepiaFW.ui.cards.getSupportedWebPlayers()
114115
};
115116
State.custom_data = JSON.stringify(cd);
116117

www/scripts/sepiaFW.client.controls.js

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -120,13 +120,16 @@ function sepiaFW_build_client_controls(){
120120
//STOP
121121
if (controlData.action == "stop" || controlData.action == "pause"){
122122
//Stop internal player
123-
var isInternalPlayerStreaming = SepiaFW.audio.isMusicPlayerStreaming();
123+
var isInternalPlayerStreaming = SepiaFW.audio.isMusicPlayerStreaming() || SepiaFW.audio.isMainOnHold();
124124
if (isInternalPlayerStreaming){
125125
SepiaFW.audio.stop(SepiaFW.audio.getMusicPlayer());
126126
}
127-
//Platform specific additional STOP methods
127+
//Player and platform specific additional STOP methods
128128
var sentAdditionalEvent = false;
129-
if (SepiaFW.ui.isAndroid){
129+
if (SepiaFW.ui.cards.youTubePlayerGetState() == 1){
130+
//YouTube embedded player
131+
sentAdditionalEvent = (SepiaFW.ui.cards.youTubePlayerControls("stop") > 0);
132+
}else if (SepiaFW.ui.isAndroid){
130133
//we do this only if we have a recent Android media event - otherwhise it will activate all music apps
131134
var requireMediaAppPackage = true;
132135
sentAdditionalEvent = SepiaFW.android.broadcastMediaButtonDownUpIntent(127, requireMediaAppPackage);
@@ -144,8 +147,12 @@ function sepiaFW_build_client_controls(){
144147
SepiaFW.audio.startNextMusicStreamOfQueue(function(){}, function(err){
145148
//Failed to execute NEXT on internal player:
146149

147-
//Platform specific additional NEXT methods
148-
if (SepiaFW.ui.isAndroid){
150+
//Player or platform specific additional NEXT methods
151+
if (SepiaFW.ui.cards.youTubePlayerGetState() > 0){
152+
//YouTube embedded player
153+
SepiaFW.ui.cards.youTubePlayerControls("next");
154+
155+
}else if (SepiaFW.ui.isAndroid){
149156
//Stop internal player
150157
if (SepiaFW.audio.isMusicPlayerStreaming()){
151158
SepiaFW.audio.stop(SepiaFW.audio.getMusicPlayer());
@@ -194,7 +201,7 @@ function sepiaFW_build_client_controls(){
194201
}
195202

196203
//Embedded Player - TODO: check if service has web-player support
197-
if (controlData.uri && (SepiaFW.ui.cards.allowWebPlayer || controlData.service.indexOf("_embedded") > 0)){
204+
if (controlData.uri && (SepiaFW.ui.cards.canEmbedWebPlayer(controlData.service) || controlData.service.indexOf("_embedded") > 0)){
198205
//just skip, cards will do the rest...
199206
//YouTube
200207
//if (controlData.service.indexOf("youtube") == 0){}

www/scripts/sepiaFW.ui.build.js

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -778,12 +778,16 @@ function sepiaFW_build_ui_build(){
778778
//Short press
779779
}, function(){
780780
//Long press
781-
if (SepiaFW.ui.cards.allowWebPlayer){
782-
SepiaFW.ui.cards.allowWebPlayer = false;
783-
SepiaFW.ui.showPopup("Music Cards deactivated");
781+
if (SepiaFW.ui.cards.canEmbedSpotify){
782+
SepiaFW.ui.cards.canEmbedYouTube = false;
783+
SepiaFW.ui.cards.canEmbedSpotify = false;
784+
SepiaFW.ui.cards.canEmbedAppleMusic = false;
785+
SepiaFW.ui.showPopup("All music cards deactivated");
784786
}else{
785-
SepiaFW.ui.cards.allowWebPlayer = true;
786-
SepiaFW.ui.showPopup("Music Cards activated (for debugging)");
787+
SepiaFW.ui.cards.canEmbedYouTube = true;
788+
SepiaFW.ui.cards.canEmbedSpotify = true;
789+
SepiaFW.ui.cards.canEmbedAppleMusic = true;
790+
SepiaFW.ui.showPopup("All music cards activated (for debugging)");
787791
}
788792
}, true);
789793
//$('#sepiaFW-menu-toggle-music-cards-btn').off().on('click', function(){});
@@ -1329,16 +1333,6 @@ function sepiaFW_build_ui_build(){
13291333
*/
13301334
}
13311335

1332-
//Actions
1333-
if (isAssistMsg && SepiaFW.ui.actions){
1334-
if (!options.skipActions){
1335-
SepiaFW.ui.actions.handle(msg.data.assistAnswer, block, sender, options);
1336-
}else if (options.skipNoneButtonActions){
1337-
options.doButtonsOnly = true;
1338-
SepiaFW.ui.actions.handle(msg.data.assistAnswer, block, sender, options);
1339-
}
1340-
}
1341-
13421336
//add card-data
13431337
if (isAssistMsg && msg.data.assistAnswer.hasCard){
13441338
if (SepiaFW.ui.cards){
@@ -1391,6 +1385,16 @@ function sepiaFW_build_ui_build(){
13911385
}
13921386
}
13931387

1388+
//Actions
1389+
if (isAssistMsg && SepiaFW.ui.actions){
1390+
if (!options.skipActions){
1391+
SepiaFW.ui.actions.handle(msg.data.assistAnswer, block, sender, options);
1392+
}else if (options.skipNoneButtonActions){
1393+
options.doButtonsOnly = true;
1394+
SepiaFW.ui.actions.handle(msg.data.assistAnswer, block, sender, options);
1395+
}
1396+
}
1397+
13941398
return block;
13951399
}
13961400

www/scripts/sepiaFW.ui.cards.js

Lines changed: 123 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,32 @@ function sepiaFW_build_ui_cards(){
1818

1919
//states
2020
Cards.currentCardId = 0; //increasing id for every card element
21-
var topIndexZ = 1; //to get an active element to the top
21+
var topIndexZ = 1; //to get an active element to the top (deprecated?)
2222

2323
//some specials
24-
Cards.allowWebPlayer = false; //deactivated by default because it only works in desktop and gives no control over start/stop/volume
24+
Cards.canEmbedYouTube = true;
25+
Cards.canEmbedSpotify = false; //deactivated by default because it only works in desktop and gives no control over start/stop/volume
26+
Cards.canEmbedAppleMusic = false; //"" ""
27+
Cards.canEmbedWebPlayer = function(service){
28+
if (!service) return false;
29+
service = service.toLowerCase().replace(/\s+/,"_"); //support brands too
30+
if (service.indexOf("spotify") == 0){
31+
return Cards.canEmbedSpotify;
32+
}else if (service.indexOf("apple_music") == 0){
33+
return Cards.canEmbedAppleMusic;
34+
}else if (service.indexOf("youtube") == 0){
35+
return Cards.canEmbedYouTube;
36+
}else{
37+
return false;
38+
}
39+
}
40+
Cards.getSupportedWebPlayers = function(){
41+
var players = [];
42+
if (Cards.canEmbedYouTube) players.push("youtube");
43+
if (Cards.canEmbedSpotify) players.push("spotify");
44+
if (Cards.canEmbedAppleMusic) players.push("apple_music");
45+
return players;
46+
}
2547

2648
//get a full card result as DOM element
2749
Cards.get = function(assistAnswer, sender){
@@ -989,7 +1011,6 @@ function sepiaFW_build_ui_cards(){
9891011
//LINK
9901012

9911013
var currentLinkItemId = 0;
992-
var youTubeMessageListenerExists = false;
9931014

9941015
function buildLinkElement(cardElementInfo){
9951016
var newId = ("sepiaFW-card-id-" + Cards.currentCardId++);
@@ -1019,7 +1040,7 @@ function sepiaFW_build_ui_cards(){
10191040
}else if (data.type == "default"){
10201041
//overwrite with default link icon
10211042
leftElement = "<div class='linkCardLogo'>" + "<i class='material-icons md-mnu'>link</i>" + "</div>"; //language
1022-
}else if (data.type == "musicSearch"){
1043+
}else if (data.type == "musicSearch" || data.type == "videoSearch"){
10231044
//we could check the brand here: data.brand, e.g. Spotify, YouTube, ...
10241045
linkCardEle.className += (" " + data.brand);
10251046
}
@@ -1037,16 +1058,16 @@ function sepiaFW_build_ui_cards(){
10371058
cardBody.appendChild(linkCardEle);
10381059

10391060
//Experimenting with web players
1040-
if (Cards.allowWebPlayer || data.embedded){
1061+
if (Cards.canEmbedWebPlayer(data.brand) && linkUrl && data.type && (data.type == "musicSearch" || data.type == "videoSearch")){
10411062
//Spotify
1042-
if (data.type && data.type == "musicSearch" && data.brand == "Spotify" && linkUrl){
1063+
if (data.brand == "Spotify"){
10431064
var webPlayerDiv = document.createElement('DIV');
10441065
webPlayerDiv.className = "spotifyWebPlayer cardBodyItem fullWidthItem";
10451066
var contentUrl = linkUrl.replace("spotify:", "https://open.spotify.com/embed/").replace(":play", "").replace(/:/g, "/").trim();
10461067
webPlayerDiv.innerHTML = '<iframe src="' + contentUrl + '" width="100%" height="80" frameborder="0" allowtransparency="true" allow="encrypted-media"></iframe>';
10471068
cardBody.appendChild(webPlayerDiv);
10481069
//Apple Music
1049-
}else if (data.type && data.type == "musicSearch" && data.brand == "Apple Music" && linkUrl){
1070+
}else if (data.brand == "Apple Music"){
10501071
var webPlayerDiv = document.createElement('DIV');
10511072
webPlayerDiv.className = "appleMusicWebPlayer cardBodyItem fullWidthItem";
10521073
webPlayerDiv.innerHTML = '<iframe '
@@ -1059,7 +1080,7 @@ function sepiaFW_build_ui_cards(){
10591080
+ '</iframe>';
10601081
cardBody.appendChild(webPlayerDiv);
10611082
//YouTube - TODO: improve server-side to give brand and correct URL and return data.embedded
1062-
}else if (linkUrl.indexOf('youtube')){
1083+
}else if (data.brand == "YouTube"){
10631084
var webPlayerDiv = document.createElement('DIV');
10641085
var playerId = currentLinkItemId++;
10651086
webPlayerDiv.className = "youTubeWebPlayer cardBodyItem fullWidthItem"
@@ -1069,32 +1090,15 @@ function sepiaFW_build_ui_cards(){
10691090
f.frameBorder = 0;
10701091
f.style.width = "100%"; f.style.height = "280px"; f.style.overflow = "hidden";
10711092
f.style.border = "4px solid"; f.style.borderColor = "#212121";
1072-
f.onload = function(){
1073-
//API
1074-
if (!youTubeMessageListenerExists){
1075-
youTubeMessageListenerExists = true;
1076-
window.addEventListener('message', function(e){
1077-
if (e.origin == "https://www.youtube.com" && e.data && typeof e.data == "string" && e.data.indexOf("{") == 0){
1078-
var data = JSON.parse(e.data);
1079-
if (data && data.id){
1080-
//f.contentWindow.postMessage(JSON.stringify({event:'command', func:'stopVideo'}), "*"); //playVideo, paus.., stop.., next..,
1081-
if (data.event == 'onReady'){
1082-
$('#' + data.id)[0].contentWindow.postMessage(JSON.stringify({event:'command', func:'playVideo'}), "*");
1083-
}else if (data.event == 'infoDelivery' && data.info && data.info.playerState == -1){
1084-
//console.log(JSON.stringify(data));
1085-
//TODO: sometimes fails AND should not end up in an endless loop!
1086-
if (data.info.availableQualityLevels.length == 0){
1087-
$('#' + data.id)[0].contentWindow.postMessage(JSON.stringify({event:'command', func:'nextVideo'}), "*");
1088-
}
1089-
}
1090-
}
1091-
}
1092-
});
1093-
}
1094-
f.contentWindow.postMessage(JSON.stringify({event:'listening', id: f.id}), "*");
1095-
};
1096-
//https://www.youtube.com/results?search_query=purple+haze%2C+jimi+hendrix
1097-
f.src = "https://www.youtube.com/embed?autoplay=1&enablejsapi=1&listType=search&list=" + linkUrl.replace(/.*?search_query=/, "").trim();
1093+
if (data.autoplay){
1094+
addYouTubeControls(f);
1095+
}
1096+
if (linkUrl.indexOf("/embed") < 0){
1097+
//convert e.g.: https://www.youtube.com/results?search_query=purple+haze%2C+jimi+hendrix
1098+
f.src = "https://www.youtube.com/embed?autoplay=1&enablejsapi=1&playsinline=1&fs=1&listType=search&list=" + linkUrl.replace(/.*?search_query=/, "").trim();
1099+
}else{
1100+
f.src = linkUrl;
1101+
}
10981102
cardBody.appendChild(webPlayerDiv);
10991103
webPlayerDiv.appendChild(f);
11001104
}
@@ -1625,6 +1629,91 @@ function sepiaFW_build_ui_cards(){
16251629
}
16261630
return footer;
16271631
}
1632+
1633+
//--- YouTube Card Controls ---
1634+
1635+
var youTubeMessageListenerExists = false;
1636+
var youTubePlayersTriedToStart = {};
1637+
var youTubeLastActivePlayerId = undefined;
1638+
var youTubeLastActivePlayerState = undefined;
1639+
1640+
function addYouTubeControls(frameEle){
1641+
frameEle.onload = function(){
1642+
//API
1643+
if (!youTubeMessageListenerExists){
1644+
youTubeMessageListenerExists = true;
1645+
window.addEventListener('message', function(e){
1646+
if (e.origin == "https://www.youtube.com" && e.data && typeof e.data == "string" && e.data.indexOf("{") == 0){
1647+
var data = JSON.parse(e.data);
1648+
if (data && data.id){
1649+
var $player = $('#' + data.id);
1650+
//console.log("YouTube iframe event: " + data.event);
1651+
if ($player.length == 0){
1652+
return;
1653+
}
1654+
if (data.event == 'onReady'){
1655+
$player[0].contentWindow.postMessage(JSON.stringify({event:'command', func:'playVideo'}), "*");
1656+
}else if (data.event == 'infoDelivery' && data.info){
1657+
//console.log(JSON.stringify(data));
1658+
if (data.info.playerState != undefined) youTubeLastActivePlayerState = data.info.playerState;
1659+
if (data.info.playerState == -1){
1660+
//Skip if faulty
1661+
if (data.info.availableQualityLevels.length == 0 || data.info.videoBytesTotal == 1){
1662+
//console.log(data.info.playlist.length - 1);
1663+
//console.log(data.info.playlistIndex);
1664+
if (!youTubePlayersTriedToStart[data.id] && data.info.playlistIndex == 0){
1665+
youTubePlayersTriedToStart[data.id] = true;
1666+
$player[0].contentWindow.postMessage(JSON.stringify({event:'command', func:'nextVideo'}), "*");
1667+
}else if (data.info.playlist && data.info.playlist.length > 0){
1668+
if (data.info.playlistIndex != undefined && data.info.playlistIndex < (data.info.playlist.length - 1)){
1669+
//console.log('--- next ---');
1670+
$player[0].contentWindow.postMessage(JSON.stringify({event:'command', func:'nextVideo'}), "*");
1671+
delete youTubePlayersTriedToStart[data.id];
1672+
}
1673+
}
1674+
}
1675+
}else{
1676+
youTubeLastActivePlayerId = data.id;
1677+
}
1678+
}
1679+
}
1680+
}
1681+
});
1682+
}
1683+
frameEle.contentWindow.postMessage(JSON.stringify({event:'listening', id: frameEle.id}), "*");
1684+
};
1685+
}
1686+
Cards.youTubePlayerGetState = function(){
1687+
if (youTubeLastActivePlayerId){
1688+
var $player = $('#' + youTubeLastActivePlayerId);
1689+
if ($player.length == 0){
1690+
return 0;
1691+
}else{
1692+
return youTubeLastActivePlayerState;
1693+
}
1694+
}else{
1695+
return 0;
1696+
}
1697+
}
1698+
Cards.youTubePlayerControls = function(cmd){
1699+
if (youTubeLastActivePlayerId && youTubeLastActivePlayerState > 0){
1700+
var $player = $('#' + youTubeLastActivePlayerId);
1701+
if ($player.length == 0){
1702+
return 0;
1703+
}else{
1704+
if (cmd == "stop" || cmd == "pause"){
1705+
$player[0].contentWindow.postMessage(JSON.stringify({event:'command', func:'pauseVideo'}), "*"); //NOTE: we use pause for now because stop triggers next video
1706+
return 1;
1707+
}else if (cmd == "next"){
1708+
$player[0].contentWindow.postMessage(JSON.stringify({event:'command', func:'nextVideo'}), "*");
1709+
return 1;
1710+
}else{
1711+
return 0;
1712+
}
1713+
//frameEle.contentWindow.postMessage(JSON.stringify({event:'command', func:'stopVideo'}), "*"); //playVideo, paus.., stop.., next..,
1714+
}
1715+
}
1716+
}
16281717

16291718
return Cards;
16301719
}

0 commit comments

Comments
 (0)