I'm developing Google Cast custom receiver app using WebTorrent (https://webtorrent.io, https://github.com/feross/webtorrent) and Google Cast sender app using JavaScript (Chrome) SDK.
The idea of my app is sending torrent id (magnet URI like magnet:?xt=urn:btih:6a9759bffd5c0af65319979fb7832189f4f3c35d or HTTP/HTTPS URL to a *.torrent file like https://webtorrent.io/torrents/sintel.torrent) from Google Cast sender to Google Cast receiver, and using WebTorrent in Google Cast receiver to display the media (video or audio) from the torrent.
Note that torrent id is not a direct URL to the media file.
Now I'm using Google Cast namespace and messageBus to send and receive the torrent id.
WebTorrent API provides 2 ways to display the media:
append it to the DOM using file.appendTo: https://webtorrent.io/docs#-file-appendto-rootelem-function-callback-err-elem-
render directly into given element (or CSS selector) using file.renderTo: https://webtorrent.io/docs#-file-renderto-elem-function-callback-err-elem-
Here is the code of my receiver:
<html>
<head>
<script src="https://www.gstatic.com/cast/sdk/libs/receiver/2.0.0/cast_receiver.js"></script>
<script src="https://cdn.jsdelivr.net/webtorrent/latest/webtorrent.min.js"></script>
</head>
<body>
<video autoplay id='media' />
<script>
window.mediaElement = document.getElementById('media');
window.mediaManager = new cast.receiver.MediaManager(window.mediaElement);
window.castReceiverManager = cast.receiver.CastReceiverManager.getInstance();
window.messageBus = window.castReceiverManager.getCastMessageBus('urn:x-cast:com.google.cast.sample.helloworld');
window.messageBus.onMessage = function(event) {
displayVideo(event.data);
// Inform all senders on the CastMessageBus of the incoming message event
// sender message listener will be invoked
window.messageBus.send(event.senderId, event.data);
};
function displayVideo(torrentId) {
var client = new WebTorrent();
client.add(torrentId, function (torrent) {
var file = torrent.files[0];
file.renderTo('video');
});
}
window.castReceiverManager.start();
</script>
</body>
</html>
Here is the code of my sender:
<!--
Copyright (C) 2014 Google Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<html>
<head>
<style type="text/css">
html, body, #wrapper {
height:100%;
width: 100%;
margin: 0;
padding: 0;
border: 0;
}
#wrapper td {
vertical-align: middle;
text-align: center;
}
input {
font-family: "Arial", Arial, sans-serif;
font-size: 40px;
font-weight: bold;
}
.border {
border: 2px solid #cccccc;
border-radius: 5px;
}
.border:focus {
outline: none;
border-color: #8ecaed;
box-shadow: 0 0 5px #8ecaed;
}
</style>
<script type="text/javascript" src="https://www.gstatic.com/cv/js/sender/v1/cast_sender.js"></script>
<script type="text/javascript">
var applicationID = 'F5304A3D';
var namespace = 'urn:x-cast:com.google.cast.sample.helloworld';
var session = null;
/**
* Call initialization for Cast
*/
if (!chrome.cast || !chrome.cast.isAvailable) {
setTimeout(initializeCastApi, 1000);
}
/**
* initialization
*/
function initializeCastApi() {
var sessionRequest = new chrome.cast.SessionRequest(applicationID);
var apiConfig = new chrome.cast.ApiConfig(sessionRequest,
sessionListener,
receiverListener);
chrome.cast.initialize(apiConfig, onInitSuccess, onError);
};
/**
* initialization success callback
*/
function onInitSuccess() {
appendMessage("onInitSuccess");
}
/**
* initialization error callback
*/
function onError(message) {
appendMessage("onError: "+JSON.stringify(message));
}
/**
* generic success callback
*/
function onSuccess(message) {
appendMessage("onSuccess: "+message);
}
/**
* callback on success for stopping app
*/
function onStopAppSuccess() {
appendMessage('onStopAppSuccess');
}
/**
* session listener during initialization
*/
function sessionListener(e) {
appendMessage('New session ID:' + e.sessionId);
session = e;
session.addUpdateListener(sessionUpdateListener);
session.addMessageListener(namespace, receiverMessage);
}
/**
* listener for session updates
*/
function sessionUpdateListener(isAlive) {
var message = isAlive ? 'Session Updated' : 'Session Removed';
message += ': ' + session.sessionId;
appendMessage(message);
if (!isAlive) {
session = null;
}
};
/**
* utility function to log messages from the receiver
* #param {string} namespace The namespace of the message
* #param {string} message A message string
*/
function receiverMessage(namespace, message) {
appendMessage("receiverMessage: "+namespace+", "+message);
};
/**
* receiver listener during initialization
*/
function receiverListener(e) {
if( e === 'available' ) {
appendMessage("receiver found");
}
else {
appendMessage("receiver list empty");
}
}
/**
* stop app/session
*/
function stopApp() {
session.stop(onStopAppSuccess, onError);
}
/**
* send a message to the receiver using the custom namespace
* receiver CastMessageBus message handler will be invoked
* #param {string} message A message string
*/
function sendMessage(message) {
if (session!=null) {
session.sendMessage(namespace, message, onSuccess.bind(this, "Message sent: " + message), onError);
}
else {
chrome.cast.requestSession(function(e) {
session = e;
session.sendMessage(namespace, message, onSuccess.bind(this, "Message sent: " + message), onError);
}, onError);
}
}
/**
* append message to debug message window
* #param {string} message A message string
*/
function appendMessage(message) {
console.log(message);
var dw = document.getElementById("debugmessage");
dw.innerHTML += '\n' + JSON.stringify(message);
};
/**
* utility function to handle text typed in by user in the input field
*/
function update() {
sendMessage(document.getElementById("input").value);
}
/**
* handler for the transcribed text from the speech input
* #param {string} words A transcibed speech string
*/
function transcribe(words) {
sendMessage(words);
}
</script>
</head>
<body>
<table id="wrapper">
<tr>
<td>
<form method="get" action="JavaScript:update();">
<input id="input" class="border" type="text" size="30" onwebkitspeechchange="transcribe(this.value)" x-webkit-speech/>
</form>
</td>
</tr>
</table>
<!-- Debbugging output -->
<div style="margin:10px; visibility:hidden;">
<textarea rows="20" cols="70" id="debugmessage">
</textarea>
</div>
<script type="text/javascript">
document.getElementById("input").focus();
</script>
</body>
</html>
The problem: The receiver handles torrent id from sender and video plays as expected. But official Google Cast app or official Google Cast extension for Chrome doesn't show standard media controls for playing video to pause, stop, seek, etc.
This is what I have (this is a screenshot of standard built-in modal dialog for Google Cast in the latest version of Google Chrome):
This is what I want to achieve (this is a screenshot of standard built-in modal dialog for Google Cast in the latest version of Google Chrome):
Adding
window.mediaElement = document.getElementById('media');
window.mediaManager = new cast.receiver.MediaManager(window.mediaElement);
for
<video autoplay id='media' />
element don't help.
Should I add something to sender and/or receiver to add standard media controls for <video autoplay id='media' /> on all senders?
Maybe there is another way to send and receive torrent id without using Google Cast namespace and messageBus?
UPD
Looks like I've found the root of my problem...
How to enable default media controls for existing playing video in receiver?
For example, the receiver app already have playing video:
<video autoplay id='media'
src='https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4'
/>
How to enable default media controls - working buttons "Play/Pause", working progress bar (on all senders like official Google Cast extension for Chrome) for this playing video?
Looks like adding the following code not help:
window.mediaElement = document.getElementById('media');
window.mediaManager = new cast.receiver.MediaManager(window.mediaElement);
window.castReceiverManager = cast.receiver.CastReceiverManager.getInstance();
window.castReceiverManager.start();
Here is the full source code of receiver:
<html>
<head>
<script src="https://www.gstatic.com/cast/sdk/libs/receiver/2.0.0/cast_receiver.js"></script>
</head>
<body>
<video autoplay id='media'
src='https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4'
/>
<script>
window.mediaElement = document.getElementById('media');
window.mediaManager = new cast.receiver.MediaManager(window.mediaElement);
window.castReceiverManager = cast.receiver.CastReceiverManager.getInstance();
window.castReceiverManager.start();
</script>
</body>
</html>
UPD2:
Looks like it is possible to use any text string (the torrent id in my case) instead of media URL in chrome.cast.media.MediaInfo and use the media namespace instead of using custom namespace and custom message bus (i.e. without using https://developers.google.com/cast/docs/reference/receiver/cast.receiver.CastReceiverManager#getCastMessageBus and https://developers.google.com/cast/docs/reference/receiver/cast.receiver.CastMessageBus and https://developers.google.com/cast/docs/reference/chrome/chrome.cast.Session#sendMessage):
function cast() {
url = 'magnet:?xt=urn:btih:6a9759bffd5c0af65319979fb7832189f4f3c35d';
chrome.cast.requestSession(function(session) {
var mediaInfo = new chrome.cast.media.MediaInfo(url);
//mediaInfo.contentType = 'video/mp4';
//mediaInfo.contentType = 'audio/mpeg';
//mediaInfo.contentType = 'image/jpeg';
var request = new chrome.cast.media.LoadRequest(mediaInfo);
request.autoplay = true;
session.loadMedia(request, function() {}, onError);
}, onError);
}
But how to handle it on the receiver in this case?
There is actually an existing Google Cast UX Guidelines which states that the sender application must provide a top-level Cast button. There are three ways to support a Cast button which were fully discussed in Android Sender App Development
Using the MediaRouter ActionBar provider:
android.support.v7.app.MediaRouteActionProvider
Using the MediaRouter Cast button:
android.support.v7.app.MediaRouteButton
Developing a custom UI with the MediaRouter API’s and
MediaRouter.Callback
To show standard media controls once the media is playing, the sender application can control the media playback using the RemoteMediaPlayer instance. Steps and examples can be found in the documentation.
For a comprehensive listing of all classes, methods and events in the Google Cast Android SDK, see the Google Cast Android API Reference.
I realize it's been 3 years, but what jumps out at me is that you are missing the "controls" attribute on your video tag! Without the attribute, a player will render the video but provide no ui for controlling playback....
Syntax is the same as it is for autoplay: the controls attribute is standalone and takes no value. It is all or nothing - a standard set of controls with default styling, or none at all... if you were building for the browser, you might choose to omit the attribute and make your own controls to match the look and feel of the page. But based on the screenshots you shared, there's no need (and it might not work in a chromecast receiver environment)
The correct html is below, and that should be all you need :)
https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4'
/>
Please let us know if and how you ended up fixing your problem (and send me a DM... I recently started integrating webtorrent into a streaming video platform I'm creating and so far, so good, but the documentation is pretty bad and I have a few questions. Thanks!)
Related
On some environments, it sounds like the exercise 'Implement authentication in a custom tab' for MS-600 certification, using MSAL/ADAL and react doesn't work properly. (here). The authentication popup never closes.
In those environments, unplugging or disabling a monitor on the computer solves the issue. You will find below an environment description on which it fails always. Unplugging the monitor DELL U2713HM (in this case) and rebooting Microsoft TEAMS solves the issue.
Is someone experiencing the same behavior? Are there some monitors that aren't compatible with Microsoft teams and the javascript SDK?
Operating System
Windows 10 Professionnel 64-bit
CPU
Intel Core i7 # 2.90GHz 70 °C
Haswell 22nm Technology
RAM
16,0 Go Dual-Channel DDR3 # 798MHz (11-11-11-28)
Motherboard
Dell Inc. 0V5GVY (SOCKET 0)
Graphics
DELL U2713HM (2560x1440#59Hz)
Generic PnP Monitor (1680x1050#60Hz)
DELL E228WFP (1680x1050#60Hz)
2047MB NVIDIA Quadro K2100M (Dell) 46 °C
Storage
465GB Crucial CT500MX500SSD1 (SATA (SSD))
931GB Western Digital WDC WD10JPVX-75JC3T0 (SATA ) 36 °C
Optical Drives
No optical disk drives detected
Audio
Périphérique audio USB
EDIT
First of all, I strongly insist on the fact that the problem occurs only within microsoft teams desktop client, not within microsoft teams webapp.
In order to debug within microsoft teams, I had to launch the client on 'developper preview'.
This is the code I'm running inside auth-start.html
<!DOCTYPE html>
<html>
<body>
<script src="https://statics.teams.cdn.office.net/sdk/v1.9.0/js/MicrosoftTeams.min.js" crossorigin="anonymous"></script>
<script src="https://secure.aadcdn.microsoftonline-p.com/lib/1.0.17/js/adal.min.js" crossorigin="anonymous"></script>
<script type="text/javascript">
console.log("initializing");
microsoftTeams.initialize();
console.log("initialized");
try
{
microsoftTeams.getContext(function (msTeamsContext) {
try{
console.log("auth-start");
// ADAL.js configuration
let config = {
clientId: "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
redirectUri: window.location.origin + "/auth-end.html",
cacheLocation: "localStorage",
endpoints: {
"https://graph.microsoft.com": "https://graph.microsoft.com"
}
};
// add extra query parameters Azure AD login request
// include scope for OpenID connect and log-in hint by using the current Microsoft Teams logged-in user
config.extraQueryParameters = "scope=open+profile";
if (msTeamsContext.upn) {
config.extraQueryParameters += "&login-hint=" + encodeURIComponent(msTeamsContext.userProfileName);
}
// check if consent required for new permission
if (getUrlParameter('prompt') !== "") {
config.extraQueryParameters += "&prompt=" + getUrlParameter('prompt');
}
// override URL to Azure AD auth endpoint to include extra query parameters
config.displayCall = function (urlNavigate) {
if (urlNavigate) {
if (config.extraQueryParameters) {
urlNavigate += "&" + config.extraQueryParameters;
}
window.location.replace(urlNavigate);
}
}
console.log("auth-context");
// login
let authContext = new AuthenticationContext(config);
console.log("clear-cache");
authContext.clearCache();
console.log("login");
authContext.login();
}
catch(error2) {
console.log("error2");
console.log(error2);
}
});
}
catch(error) {
console.log("error");
console.log(error);
}
function getUrlParameter(name) {
name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
var regex = new RegExp('[\\?&]' + name + '=([^&#]*)');
var results = regex.exec(location.search);
return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
};
</script>
</body>
</html>
This is the weird result I get when opening developper preview. It sounds that the getContext callback is never reached.
I am building a Hybrid Android app using Phonegap, HTML5, JavaScript and IDE(eclipse).
This app is the example app for the push notification. The push notification made based on the Java server.
But when the app is launched, the push notification is normally operated and when the app is terminated or it is launched in the back ground, the push notification is not opened.
Even when it does how, and the app is not executing, can the push notification be gotten?
[index.html - In hybrid app project]
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="format-detection" content="telephone=no" />
<!-- WARNING: for iOS 7, remove the width=device-width and height=device-height attributes. See https://issues.apache.org/jira/browse/CB-4323 -->
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi" />
<link rel="stylesheet" type="text/css" href="css/index.css" />
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.css" />
<script src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
<script src="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.js"></script>
<script type="text/javascript" charset="utf-8" src="cordova.js"></script>
<script type="text/javascript">
/**
* tokenHandler
*
* #param result
*/
function tokenHandler(result){
console.log('deviceToken:' + result);
}
/**
* errorHandler
*
* #param err
*/
function errorHandler(err){
console.log('error:' + err);
}
/**
* successHandler
*
* #param result
*/
function successHandler(result){
console.log('result:'+result);
}
/**
* onNotificationAPN
*
* #param event
*/
function onNotificationAPN (event){
if (event.alert){
navigator.notification.alert(event.alert);
}
if (event.sound){
var snd = new Media(event.sound);
snd.play();
}
if (event.badge){
window.plugins.pushNotification.setApplicationIconBadgeNumber(successHandler, errorHandler, event.badge);
}
}
/**
* onNotificationGCM
*
* #param e
*/
function onNotificationGCM (e){
switch (e.event) {
case 'registered':
{
console.log(e.regid);
$('#token').val(e.regid);
break;
}
case 'message':
{
if (e.foreground){
var soundfile = e.soundname || e.payload.sound;
var my_media = new Media("/android_asset/www/" + soundfile);
my_media.play();
}
else {
if (e.coldstart) {
console.log("Case : coldstart");
}
else {
console.log("background");
}
}
console.log(e.payload.title);
navigator.notification.alert(e.payload.title);
}
break;
case 'error':
console.log('error:' + e.msg);
break;
case 'default':
console.log('The event which it cannot know');
break;
}
}
document.addEventListener("deviceready", function(){
console.log(device.platform);
if(device.platform.toUpperCase() == 'ANDROID'){
window.plugins.pushNotification.register(successHandler,errorHandler, {
"senderID" : "My sender ID", //
"ecb" : "onNotificationGCM" //
});
}
else
{
window.plugins.pushNotification.register(tokenHandler, errorHandler, {
"badge":"true",
"sound":"true",
"alert":"true",
"ecb": "onNotificationAPN"
});
}
});
</script>
<script type="text/javascript">
function insert(){
var formData = $("#memForm").serialize();
alert(formData);
$.ajax({
type:"POST",
data:formData,
url:"http://192.168.0.26:9102/insertMember.do",
success:function(data){
alert("Success");
location.reload();
},
error:function(request,status,error){
alert("code:"+request.status+"\n"+"message:"+request.responseText+"\n"+"error:"+error);
}
});
}
function pushTest(){
$.ajax({
type:"POST",
url:"http://192.168.0.26:9102/pushTest.do",
success:function(data){
alert("push Test!");
},
error:function(request,status,error){
alert("code:"+request.status+"\n"+"message:"+request.responseText+"\n"+"error:"+error);
}
});
}
</script>
<title>Hello World</title>
</head>
<body>
<div data-role="page">
<div data-role="header" data-position="fixed">
<h1>hairShare</h1>
</div>
<form id="memForm">
<div role="main" class="ui-content" data-inset="false">
<input type="hidden" name="token" id="token" value="">
<input type="text" name="id">
<input type="password" name="pw">
<input type="text" name="email">
<input type="button" onclick="insert()" value="Submit">
<input type="button" onclick="pushTest()" value="Push">
</div>
</form>
</div>
</body>
</html>
[GcmTestServer() - In Spring MVC Web Project]
#Controller
public class GcmTestServer {
#RequestMapping(value = "/pushTest.do", method = {RequestMethod.GET, RequestMethod.POST})
#ResponseBody
public void pushTest() throws Exception {
try {
// TODO Auto-generated method stub
Sender sender = new Sender("AIzaSyBiJUHJ47I_eAZehDC0lx-1k5FSpcQPqyM"); // 서버 API Key 입력
String regId = "APA91bFDd3srzd8UaaTFvCv4xheJ-WSXsU5eoQY8-veT1TSLQvDyXTtjSfiaw94nZjlj8nIonL6-PNw4lzKX0rb-bju8jnSbsMeQ93LGuvBGUuguOfwhCJ4"; // 단말기 RegID 입력
String sendTlt = "send Title";
String sendMsg = "send Message";
Message message = new Message.Builder()
.addData("title", sendTlt)
.addData("msg", sendMsg)
.build();
List<String> list = new ArrayList<String>();
list.add(regId);
MulticastResult multiResult;
multiResult = sender.send(message, list, 5);
if (multiResult != null) {
List<Result> resultList = multiResult.getResults();
for (Result result : resultList) {
System.out.println(result.getMessageId());
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
<---- Updated Contents ---->
Flag could be added according to #Mr.Rebot's answer and the information for was searched.
Intent intent = new Intent("What does insert in here?");
intent.addFlags(FLAG_INCLUDE_STOPPED_PACKAGES);
sendBroadcast(intent);
Above can code be added to my MainActivity ?
[MainActivity.java]
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
package com.su.project;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import org.apache.cordova.*;
public class MainActivity extends CordovaActivity
{
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// Set by <content src="index.html" /> in config.xml
loadUrl(launchUrl);
}
}
(For Android) It may be because of some changes starting from Android 3.1 where Launch controls behavior which when a stopped application will stop receiving any notification as "Apps that are in the stopped state do not receive broadcast Intents." Check this related SO question and GCM push notification.
Note that the system adds FLAG_EXCLUDE_STOPPED_PACKAGES to all broadcast intents. It does this to prevent broadcasts from background services from inadvertently or unnecessarily launching components of stoppped applications. A background service or application can override this behavior by adding the FLAG_INCLUDE_STOPPED_PACKAGES flag to broadcast intents that should be allowed to activate stopped applications.
Applications are in a stopped state when they are first installed but are not yet launched and when they are manually stopped by the user (in Manage Applications).
Hope this information helps!
I want to realize zipping .exe files from server, while somebody downloads files using checkboxes, but not the point. Trying to understand how jszip works.
But I have some issues : downloader example doesn't work correctly.
I take an example from https://stuk.github.io/jszip/documentation/examples/downloader.html , delete some excess features like styles, links etc.
Almost all runs correct except block: JSZipUtils.getBinaryContent(url, function (err, data) {..}
- this section doesn't execute, it gives me an error : " Error: InvalidStateError: Failed to read the 'responseText' property from 'XMLHttpRequest': The value is only accessible if the object's 'responseType' is" or 'text'(was'arraybuffer'). "
Also I have another question: if I place script "demo.js" in header section script doesn't run by pressing button. Scripts should be downloaded because they are small size and header loading faster then body. I read that best way to include scripts is to place they right in the end of body, before closing tag. But if I place all my scripts in the end of body nothing happens when button is pressed. Why it works that way? I red many articles but don't find an answer. I'm new in JS so don't beat me hard).
I'll be very appreciate for any help and suggestions.
this is my 1.html file:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>something</title>
<script type="text/javascript" src="dist/jszip.js"></script>
<script type="text/javascript" src="dist/jszip-utils.js"></script>
<script type="text/javascript" src="dist/jquery-1.8.3.min.js"></script>
<script type="text/javascript" src="dist/FileSaver.js"></script>
</head>
<body>
<form action="#" id="download_form">
<ul>
<li>
<label>
<input type="checkbox" data-url="dist/jszip.js" checked />
dist/jszip.js
</label>
</li>
<li>
<label>
<input type="checkbox" data-url="dist/demo.js" checked />
dist/demo.js
</label>
</li>
<li>
<label>
<input type="checkbox" data-url="dist/FileSaver.js" />
dist/FileSaver.js
</label>
</li>
</ul>
<button type="submit" class="btn btn-primary">pack them !</button>
</form>
<script type="text/javascript" src="dist/demo.js"></script>
</body>
</html>
and here is demo.js:
jQuery(function ($) {
"use strict";
/**
* Reset the message.
*/
function resetMessage () {
$("#result")
.removeClass()
.text("");
}
/**
* show a successful message.
* #param {String} text the text to show.
*/
function showMessage(text) {
resetMessage();
$("#result")
.addClass("alert alert-success")
.text(text);
}
/**
* show an error message.
* #param {String} text the text to show.
*/
function showError(text) {
resetMessage();
$("#result")
.addClass("alert alert-danger")
.text(text);
}
/**
* Fetch the content, add it to the JSZip object
* and use a jQuery deferred to hold the result.
* #param {String} url the url of the content to fetch.
* #param {String} filename the filename to use in the JSZip object.
* #param {JSZip} zip the JSZip instance.
* #return {jQuery.Deferred} the deferred containing the data.
*/
function deferredAddZip(url, filename, zip) {
var deferred = $.Deferred();
JSZipUtils.getBinaryContent(url, function (err, data) {
if(err) {
deferred.reject(err);
} else {
zip.file(filename, data, {binary:true});
deferred.resolve(data);
}
});
return deferred;
}
if(!JSZip.support.blob) {
showError("This demo works only with a recent browser !");
return;
}
var $form = $("#download_form").on("submit", function () {
alert("button press"); /* it works fine */
resetMessage();
var zip = new JSZip();
var deferreds = [];
// find every checked item
$(this).find(":checked").each(function () {
var $this = $(this);
var url = $this.data("url");
var filename = url.replace(/.*\//g, "");
deferreds.push(deferredAddZip(url, filename, zip));
});
// when everything has been downloaded, we can trigger the dl
// section below doesn't work at all - alerts used for simplest debugging
$.when.apply($, deferreds).done(function () {
//alert("downloading must start");
var blob = zip.generate({type:"blob"});
// see FileSaver.js
saveAs(blob, "example.zip");
showMessage("done !");
}).fail(function (err) {
showError(err);
});
return false;
});
});
my file folder which includes files looks like:
-root folder:
1.html
subfolder "dist":
demo.js
FileSaver.js
jquery-1.8.3.min.js
jszip.js
jszip-utils.js
Resolved my problem.
The problem is that html file was opened in browser on my local machine using file:/// protocol. For correct work it should use http protocol.
So I download apache server, run on my pc and start my html file on apache server which worked correctly.
I am using tokbox trial for video chatting on my website. But the problem i am facing is that ::: User 1 can see and hear User 2 clearly. User 2 can see User 1 clearly, but user 2 couldnt here user 1. And code i am using
<html>
<head>
<title>Monkvyasa | Test</title>
<script src='http://static.opentok.com/webrtc/v2.2/js/opentok.min.js'></script>
<script type="text/javascript">
// Initialize API key, session, and token...
// Think of a session as a room, and a token as the key to get in to the room
// Sessions and tokens are generated on your server and passed down to the client
var apiKey = "xxxxxxx";
var API_KEY=apiKey;
var sessionId = "2_MX40NTAyMDgxMn5-xxxxxxxxxxxxxxxxxxxxHBXZEZoWHN-fg";
var token = "T1==cGFydG5lcl9pZD00NTAyMDgxMiZzaWc9ZDNiYjYyZGE2NTBkYmUzMTUyNGNjNDZjYzAzY2NjZWRhZGY3NTEyZjpyb2xlPW1vZGVyYXRvciZzZXNzaW9uX2lkPTJfTVg0xxxxxxxxxxxxxxxxxxxxxxxxBNM1JsYlRCUFdXWkhSSEJYWkVab1dITi1mZyZjcmVhdGVfdGltZT0xNDEzMjAwMjIxJm5vbmNlPTAuMTk1MzEwNTU0MzY1MjEwNSZleHBpcmVfdGltZT0xNDEzMjg0MzY5";
// Initialize session, set up event listeners, and connect
var session;
var connectionCount = 0;
function connect() {
session = TB.initSession(sessionId);
session.addEventListener("sessionConnected", sessionConnectHandler);
session.addEventListener('streamCreated', function(event){
e=event;
console.log(e);
for (var i = 0; i < event.streams.length; i++) {
streams = event.streams;
// Make sure we don't subscribe to ourself
alert("new user connected :)");
if (streams[i].connection.connectionId == session.connection.connectionId) {
return;
}
// Create the div to put the subscriber element in to
var div = document.createElement('div');
div.setAttribute('id', 'stream' + streams[i].streamId);
document.body.appendChild(div);
session.subscribe(streams[i], div.id);
}
});
session.connect(API_KEY, token);
}
function sessionConnectHandler(event) {
var div = document.createElement('div');
div.setAttribute('id', 'publisher');
var publisherContainer = document.getElementById('publisherContainer');
// This example assumes that a publisherContainer div exists
publisherContainer.appendChild(div);
var publisherProperties = {width: 500, height:450};
publisher = TB.initPublisher(API_KEY, 'publisher', publisherProperties);
session.publish(publisher);
}
function disconnect() {
session.disconnect();
}
connect();
</script>
</head>
<body>
<h1>Monkvysa videofeed test!</h1>
<input style="display:block" type="button" id="disconnectBtn" value="Disconnect" onClick="disconnect()">
<table>
<tr>
<td> <div id="publisherContainer"></div></td> <td><div id="myPublisherDiv"></div></td>
</tr>
</table>
</body>
</html>
Thanks in advance
The code looks mostly correct, except you're using an older form of the 'streamCreated' event handler. In the latest version of the API, you no longer need to iterate through the event.streams array, you actually get one invocation of the event handler per stream.
In order to further dig into the problem, would you be able to add a link to a gist containing all the console logs? To make sure the logs are being outputted, you can call OT.setLogLevel(OT.DEBUG); at the beginning of the script.
Lastly, the newer API is greatly simplified and you could save yourself the effort of DOM element creation and iteration. What you have implemented is basically identical to our Hello World sample applications, which you can find in any of our server SDKs, for example here: https://github.com/opentok/opentok-node/blob/61fb4db35334cd30248362e9b10c0bbf5476c802/sample/HelloWorld/public/js/helloworld.js
I am running into an issue where all google app endpoints are running smoothly. No issue at getting, updating or deleting entries while using the API explorer.
I am running into issue when trying to mesh all this together in an html file.
Updates and deletes work fine... Only get/list methods are returning no record.
Any idea what the issue is? What is best to troubleshoot this kind of issue?
Thanks.
test.java
import com.googlecode.objectify.annotation.*;
#Entity
#Index
public class Test {
#Id Long number;
Long number2;
public Test(){
}
public Test(Long number, Long number2){
this.number = number;
this.number2 = number2;
}
public Test(Long number2){
this.number2 = number2;
}
public Long getNumber2() {
return number2;
}
public void setNumber2(Long number2) {
this.number2 = number2;
}
public Long getNumber() {
return number;
}
public void setNumber(Long number) {
this.number = number;
}
}
test.html
<html>
<head>
<title></title>
</head>
<body>
<div>
<form>
<div>
<input type="text" id="insert" value="2"><br><br>
<button type="submit" id="getTestButton" onclick="getTest()" disabled>Get</button>
</div>
</form>
<p id="result"></p>
<hr>
</div>
<!--
Load the Google APIs Client Library for JavaScript
More info here : https://developers.google.com/api-client- library/javascript/reference/referencedocs
-->
<script type="text/javascript">
function init() {
gapi.client.load('testEndpoint', 'v1', enableButton, 'http://localhost:8080/_ah/api');
document.getElementById('getTestButton').onclick = function() {
getTest();
}
}
function enableButton() {
console.log("enabling button");
getTestButton.disabled = false;
}
function getTest() {
console.log("entering getTest function");
var features = {};
features.number2 = document.getElementById("insert").value;
console.log(features);
var req = gapi.client.testEndpoint.getTest(features);
req.execute(function(data) {
console.log(data);
document.getElementById("result").innerHTML = data.number + " " + data.number2;
});
}
</script>
<script src="https://apis.google.com/js/client.js?onload=init"></script>
</body>
</html>
TestEndpoint.java
import com.google.api.server.spi.config.Api;
import com.google.api.server.spi.config.ApiMethod;
import com.google.api.server.spi.config.ApiNamespace;
import java.util.logging.Logger;
import javax.inject.Named;
import com.googlecode.objectify.cmd.Query;
import static com.Backend.OfyService.ofy;
/** An endpoint class we are exposing */
#Api(name = "testEndpoint", version = "v1", namespace = #ApiNamespace(ownerDomain = "Backend.com", ownerName = "Backend.com", packagePath=""))
public class TestEndpoint {
// Make sure to add this endpoint to your web.xml file if this is a web application.
private static final Logger LOG = Logger.getLogger(TestEndpoint.class.getName());
/**
* This method gets the <code>Test</code> object associated with the specified <code>id</code>.
* #param id The id of the object to be returned.
* #return The <code>Test</code> associated with <code>id</code>.
*/
#ApiMethod(name = "getTest")
public Test getTest(#Named("number2") Long number2) {
// Implement this function
LOG.info("***GetTest***NUMBER=" + number2);
Test t = new Test(1L, 2L);
//t = ofy().load().type(Test.class).filter("number2", number2).first().now();
LOG.info("t.getNumber()==> " + t.getNumber().toString());
LOG.info("Calling getTest method");
return t;
}
}
Seems legit to me. To isolate the cause of the error, I would suggest to do the following :
Test your getTest endpoint method through the API explorer, located at http://yourAppAddress/_ah/api/explorer . If it works you know the issue is in your client code.
If it did not work, then the issue is either with the code inside getTest or with the way you use endpoints (or with endpoints itself). To check this, create a pure Java servlet that will call the getTest() method directly, and log the result. If it works you know the issue is either on the way you use endpoints or on endpoints itself.
Sorry I don't have an immediate answer to your question.
EDIT : Since you tell me that the API Explorer test works, I checked your javascript code and there's something weird : you try to get resp.items.length in the callback. But your endpoint method only returns a Test entity, so there are no items attribute.
Can you try this code ?
gapi.client.testEndpoint.getTest(requestData).execute(function(resp) {
console.log(resp);
});
EDIT 2 : So you've found the cause of the issue by yourself : you cannot call the endpoints method before the API client is loaded. Since your method is called by an onClick event, probably on a button, you must do the following :
By default, disable the button
In the gapi.client.load 's callback, enable the button
That way you will be sure to only click on the button when the Google service is initialized.
EDIT 3 : It is actually a problem in your html. You have a submit button, which makes Chrome reload the page. Every time you click the button the page reloads and you never see the result of your endpoint query.
This HTML file works for me on the dev server :
<html>
<head>
<title></title>
</head>
<body>
<div>
<div>
<input type="text" id="insert" value="2"><br><br>
<button id="getTestButton" onclick="getTest()" disabled>Get</button>
</div>
<p id="result"></p>
<hr>
</div>
<!--
Load the Google APIs Client Library for JavaScript
More info here : https://developers.google.com/api-client- library/javascript/reference/referencedocs
-->
<script type="text/javascript">
function init() {
gapi.client.load('testEndpoint', 'v1', enableButton, '/_ah/api');
document.getElementById('getTestButton').onclick = function () {
getTest();
}
}
function enableButton() {
console.log("enabling button");
getTestButton.disabled = false;
}
function getTest() {
console.log("entering getTest function");
var features = {};
features.number2 = document.getElementById("insert").value;
console.log(features);
var req = gapi.client.testEndpoint.getTest(features);
req.execute(function (data) {
console.log(data);
document.getElementById("result").innerHTML = data.number + " " + data.number2;
});
}
</script>
<script src="https://apis.google.com/js/client.js?onload=init"></script>
</body>
</html>