How to stream video to browser with Kodi - kodi

Not sure if this is the correct place to ask this but here goes nothing.
I setup openelec's Kodi on a Raspberry pi2. I uploaded a video and managed to get it to play on a connected TV via HDMI. What I can't seem to figure out is how to have Kodi serve as a media server so I can browse media using my phone's or computer's browser and play it. I've been through the settings available, installed several addons(i.e chorus etc) and I still can't see how to make this happen. Whenever I open a video on my browser after logging into the Kodi web interface, it still plays it on the TV connected to the PI.
Almost all Google results out there talk about casting from device onto TV and chromecast. I want to be able to play this media on my local browser. And no, I can't use the Kodi app because I'm using an un-supported Phone and computer OS.

In your case, it's better to use plex instead of kodi.
Kodi is not exactly a media server, it works as a media center. However, with plex, you can set up your media center and have access to your media from your web browser.
Try looking for the differences between kodi and plex.

Chorus should still have an option to play the video in the browser. It seems to not work with Chrome or Firefox anymore, but have a look here: https://github.com/xbmc/chorus2/issues/127
This functionality depends on Flash Player, this feature had been removed from most of the web-browsers.
REF: https://support.google.com/chrome/answer/6258784?visit_id=637521928282450874-904852602&rd=1

I've modified the Chorus web interface to allow streaming with a nodejs process in the background.
NodeJS script:
const express = require('express')
const fs = require('fs')
const path = require('path')
const app = express()
const url = require('url')
const gracefulFs = require('graceful-fs')
gracefulFs.gracefulify(fs)
app.get('/video', function(req, res) {
var q = url.parse(req.url, true).query;
var filepath = q.src;
fs.stat(filepath, function(err, stats){
if (err){
if (err.code === 'ENOENT'){
//404 Error if file not found
res.writeHead(404, {
"Accept-Ranges" : "bytes",
"Content-Range" : "bytes " + start + "-" + end + "/" + total,
"Content-Length" : chunksize,
"Content-Type" : "video/mp4"
});
}
res.end(err);
}
var start;
var end;
var chunksize;
var total = stats.size;
var range = req.headers.range;
if (range) {
var parts = range.replace(/bytes=/, "").split("-");
start = parseInt(parts[0], 10);
end = parts[1] ? parseInt(parts[1], 10) : total - 1;
} else {
start = 0;
end = total - 1;
}
if (start > end || start < 0 || end > total - 1){
//error 416 is "Range Not Satisfiable"
res.writeHead(416, {
"Accept-Ranges" : "bytes",
"Content-Range" : "*/" + stats.size,
"Content-Type" : "video/mp4"
});
res.end();
return;
}
if (start == 0 && end == (total -1)){
res.writeHead(200, {
'Accept-Ranges': 'bytes',
'Content-Range': `bytes ${start}-${end}/${total}`,
'Content-Length': total,
'Content-Type': 'video/mp4'
});
} else {
chunksize = (end - start) + 1;
res.writeHead(206, {
'Content-Range': `bytes ${start}-${end}/${total}`,
'Accept-Ranges': 'bytes',
'Content-Length': chunksize,
'Content-Type': 'video/mp4'
});
}
var stream = fs.createReadStream(filepath, {
start : start,
end : end
}).on("open", function() {
stream.pipe(res);
}).on("error", function(err) {
console.log(err);
res.end(err);
});
});
});
app.listen(<port>, function () {
console.log('Listening on port <port>!');
});
Modified the file "Kodi\addons\webinterface.chorus\tpl\MovieView.html" under div id="movie-watch" so:
<div id="movie-watch" class="tab-pane">
<div class="col-1">
<video id="videoPlayer" controls width="100%" height="90%" preload="metadata">
<source src="http://<mydomain>:<port>/video?src=<%=encodeURIComponent(file) %>&movieId=<%= movieid %>" type="video/mp4">
</video>
<!--
<h2>HTML5 player</h2>
<p>Codec support is very limited in the browser.
H.264 video generally works but only with 2 channel audio. Works best in Chrome, may crash browser and/or XBMC!</p>
<div class="buttons">
Launch HTML5 player
</div>
<br />
<h2>VLC player</h2>
<p>VLC Player provides an
embeddable video player, it will play most videos, but does require you to
download and install extra software.
Works well in Chrome and Firefox.</p>
<div class="buttons">
Launch VLC player
</div>-->
Modified the file "Kodi\addons\webinterface.chorus\tpl\TvshowView.html" under div id="movie-watch" so:
<div id="tv-watch" class="tab-pane">
<div class="col-1">
<video id="videoPlayer" controls width="100%" height="90%">
<source src="http://<mydomain>:<port>/video?src=<%=encodeURIComponent(file) %>&episodeId=<%= episodeid %>" type="video/mp4">
</video>
<!--
<h2>HTML5 player</h2>
<p>Codec support is very limited in the browser.
H.264 video generally works but only with 2 channel audio. Works best in Chrome, may crash browser and/or XBMC!</p>
<div class="buttons">
Launch HTML5 player
</div>
<br />
<h2>VLC player</h2>
<p>VLC Player provides an
embeddable video player, it will play most videos, but does require you to
download and install extra software.
Works well in Chrome and Firefox.</p>
<div class="buttons">
Launch VLC player
</div>-->

Related

socketio emit not working from gpio callback

I am pulling my hair out trying to figure out what I am doing wrong.
Server shows emit is being sent, but the browser never gets it. After about a minute, the client disconnects and re-connects.
However, emit after on.connect works perfect. It's almost like the client has to send the server a request and immediately get a response to work.
For some reason, StackOverflow wants me to type more info because it looks like all code, but I'm not really sure what else I can add. I googled and tried different versions and tutorials to get this working, and I believe it should be good. It must be a typo somewhere.
Okay, that still isn't enough detail, so let's go through the entire process.
I have a Raspberry Pi 3 with an SD card, running Bullseye x64 that is up to date.
I am also using a logic level shifter to drive a few 5v transistors that turn on 4 different LEDs on 4 different buttons. The buttons are grounded when pressed, and using the Raspberry Pi's internal pull up resistor. There are also 2 relays that operate solenoid air valves, the relays are separated from the Raspberry Pi/ logic level with an optocoupler.
Essentially, I am trying to use flask-socket io to display a question on a tablet. The answer is then entered using 1 of the 4 colored buttons. If the answer is wrong then relays/transistors are activated to let air out. If the answer is correct then different relays/transistors are activated to trigger the compressor and build pressure again.
The LEDs, buttons presses, and compressors all work. I just can't seem to get socketio to receive emit messages outside of an on event.
As a workaround, I may just have Javascript poll for button status every second, until I can figure out what I'm doing wrong. At least I can get that part to work.
Sorry for the long intro. Maybe it helps, to me it seems like a lot of boring text to read through while I'm sure someone with more knowledge only needed to look at my code to see what was wrong.
Nope, still not enough text.... so let's see. I'm new to Python, and not sure what else to put in here. Actually, I'll write more boring stuff at the bottom so you don't actually have to read any of this.
Raspberry Pi3 Bullseye x64
Dependancies
bidict==0.22.0
cachelib==0.9.0
click==8.1.3
Flask==2.2.1
Flask-Login==0.6.2
Flask-Session==0.4.0
Flask-SocketIO
importlib-metadata==4.12.0
itsdangerous==2.1.2
Jinja2==3.1.2
MarkupSafe==2.1.1
python-engineio
python-socketio
six==1.11.0
Werkzeug==2.2.1
zipp==3.8.1
RPi.GPIO==0.7.1
Python code:
from flask import Flask, render_template, request
from flask_socketio import SocketIO, emit
from random import random
from threading import Lock
from datetime import datetime
import RPi.GPIO as GPIO
import time
"""
define gpio class
this holds all info for pins
"""
class rio:
def __init__(self, label, pin, state, btnPin=None):
self.label = label
self.pin = pin
self.state = state
self.btnPin = btnPin
self.checking = False
self.timeLast = time.time()
GPIO.setup(self.pin, GPIO.OUT)
if (btnPin!=None):
GPIO.setup(btnPin,GPIO.IN,pull_up_down=GPIO.PUD_UP)
GPIO.add_event_detect(self.btnPin, GPIO.FALLING, callback=self.stateChange)
GPIO.output(self.pin, self.state)
def stateChange(self,channel):
global count
duration = time.time() - self.timeLast
self.timeLast = time.time()
print(self.label + " was pressed")
count+=1
print("Sending counter event...")
socketio.emit('updateSensorData', {'value': count, "date": get_current_datetime()})
"""
Define Globals
"""
count = 0
"""
label and define what gpio
we want to use
"""
GPIO.setmode(GPIO.BCM) #enable GPIO BCM Module
yellow = rio("yellow", 22, GPIO.LOW, 14)
green = rio("green", 10, GPIO.LOW, 16)
blue = rio("blue", 9, GPIO.LOW, 20)
red = rio("red", 11, GPIO.LOW, 21)
compressor = rio("compressor", 13, GPIO.LOW)
LogicShift = rio("shifter", 26, GPIO.HIGH)
valve1 = rio("valve in", 27, GPIO.HIGH)
valve2 = rio("valve out", 17, GPIO.HIGH)
"""
create websocket
"""
app = Flask(__name__)
app.config['SECRET_KEY'] = 'donsky!'
socketio = SocketIO(app, cors_allowed_origins='*', logger=True)
"""
Get current date time
"""
def get_current_datetime():
now = datetime.now()
return now.strftime("%m/%d/%Y %H:%M:%S")
"""
Serve root index file
"""
#app.route('/')
def index():
return render_template('index.html')
"""
Decorator for connect
"""
#socketio.on('connect')
def connect():
global thread
print('Client connected')
emit('after connect', {'data':'Lets dance'})
"""
Decorator for disconnect
"""
#socketio.on('disconnect')
def disconnect():
print('Client disconnected', request.sid)
if __name__ == '__main__':
# Access app in all IP not just localhost
socketio.run(app, host="0.0.0.0")
HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Synchronized</title>
<link rel="stylesheet" href='/static/style.css' />
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css">
<script src="https://code.jquery.com/jquery-3.5.1.js"></script>
<!--script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.5.3/socket.io.js"></script-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/3.0.4/socket.io.js" integrity="sha512-aMGMvNYu8Ue4G+fHa359jcPb1u+ytAF+P2SCb+PxrjCdO3>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.1/chart.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){
// sending a connect request to the server.
var socket = io.connect('http://10.100.180.15:5000');
// An event handler for a change of value
$('#btn1').on('input', function(event) {
console.log('button changed');
socket.emit('Button value changed', {
who:$(this).attr('id'),
data: $(this).val()
});
return false;
});
socket.on('after connect', function(msg){
console.log('After connect', msg);
});
socket.on('update value', function(msg) {
console.log('Slider value updated');
$('#'+msg.who).val(msg.data);
});
socket.on('message', function(msg) {
console.log(msg);
});
socket.on('updateSensorData', function (msg) {
console.log('Received sensorData :: ' + msg.date + ' :: ' + msg.value);
$('#current-count').text(msg.value);
});
});
</script>
</head>
<body>
<h1>Data:</h1>
<div class="">
<h1>Demo</h1>
<form class="mt-5">
<div class="form-group">
<label for"formControlRange">Btn1</label>
<input type="checkbox" class"form-control-range sync" id="btn1" value="{{btn1}}">
</div>
</form>
</div>
<button class="btn-close btn btn-sm">×</button>
<div class="container">
<div class="hero">
<h1 class="title">Real-Time Conveyor Counter Using Raspberry Pi</h1>
<div class="counter">
<h1 id="current-count">0</h1>
</div>
</div>
</div>
</body>
</html>
I tried different dependencies, and different JavaScript versions of socketio, but all of them respond the same way. The browser is Chrome 107.0.5304.110. Safari responds the same way, as well.

Process microphone audio samples of Web Audio API

I am trying to get the audio samples of my microphone via Web Audio using this code:
<!doctype html>
<html>
<meta charset="utf-8">
<body>
<script>
function start() {
let button = document.getElementById("start");
button.disabled = true;
let audioCtx = new (window.AudioContext || window.webkitAudioContext)();
navigator.mediaDevices.getUserMedia({
audio: {
echoCancellation: false,
noiseSuppression: false,
autoGainControl: false,
}
}).then(function (stream) {
let audioSource = audioCtx.createMediaStreamSource(stream);
let scriptNode = audioCtx.createScriptProcessor(4096, 1, 0);
scriptNode.onaudioprocess = function (audioProcessingEvent) {
console.log('foo!');
};
audioSource.connect(scriptNode);
}).catch(function (err) {
console.log('Error initializing user media stream: ' + err);
});
}
</script>
<button id="start" onclick="start()">Start</button>
</body>
</html>
For whatever reason this does not constantly output "foo!" in the console. What am I missing?
EDIT: It works in Firefox but not Chrome. Confusing...
Adding scriptNode.connect(audioCtx.destination) did the trick on Chrome. Processing only starts if the script node is actually connected to some sort of output.

Where to register the Native Messaging Host in Chrome OS

In this documentation there's the location for Windows, Mac OS and Linux.
I assumed Chrome OS would work the same as "standard" linux, but i couldnt get my app working ...
com.my_app.host.json (located in /etc/opt/chrome/native-messaging-hosts/)
{
"name": "com.my_app.host",
"description": "My Host",
"path": "/home/user/bfd93db2180e0d7645b1f4cce2d2c7ed9e0d835c/Downloads/host.sh",
"type": "stdio",
"allowed_origins": [
"chrome-extension://APP_ID/"
]
}
main.js
var port = null;
var getKeys = function(obj) {
var keys = [];
for (var key in obj) {
keys.push(key);
}
return keys;
}
function appendMessage(text) {
document.getElementById('response').innerHTML += "<p>" + text + "</p>";
}
function updateUiState() {
if (port) {
document.getElementById('connect-button').style.display = 'none';
document.getElementById('input-text').style.display = 'block';
document.getElementById('send-message-button').style.display = 'block';
} else {
document.getElementById('connect-button').style.display = 'block';
document.getElementById('input-text').style.display = 'none';
document.getElementById('send-message-button').style.display = 'none';
}
}
function sendNativeMessage() {
message = {
"text": document.getElementById('input-text').value
};
port.postMessage(message);
appendMessage("Sent message: <b>" + JSON.stringify(message) + "</b>");
}
function onNativeMessage(message) {
appendMessage("Received message: <b>" + JSON.stringify(message) + "</b>");
}
function onDisconnected() {
appendMessage("Failed to connect: " + chrome.runtime.lastError.message);
port = null;
updateUiState();
}
function connect() {
var hostName = "com.my_app.host";
appendMessage("Connecting to native messaging host <b>" + hostName + "</b>")
port = chrome.runtime.connectNative(hostName);
port.onMessage.addListener(onNativeMessage);
port.onDisconnect.addListener(onDisconnected);
updateUiState();
}
document.addEventListener('DOMContentLoaded', function() {
document.getElementById('connect-button').addEventListener(
'click', connect);
document.getElementById('send-message-button').addEventListener(
'click', sendNativeMessage);
updateUiState();
});
index.html
<html>
<head>
<script src='./main.js'></script>
</head>
<body>
<button id='connect-button'>Connect</button>
<input id='input-text' type='text' />
<button id='send-message-button'>Send</button>
<div id='response'></div>
</body>
</html>
It just says:
Connecting to native messaging host com.my_app.host
Failed to connect: Specified native messaging host not found.
Also i cant enable logging as explained in the documentation because on Chrome OS you cant just open Chrome via a command.
Would be great if someone could help me :)
Basically i just want to create a little GUI for launching Crouton commands.
Chrome OS does not support third-party native messaging hosts. As of writing, only two native messaging hosts are supported. One for testing, and one for Chrome Remote Desktop (source: native_message_host_chromeos.cc).
On Chrome OS, logs are available at /var/log/chrome/chrome and /var/log/ui/ui.LATEST. There are other ways to read/toggle the log (see
https://dev.chromium.org/chromium-os/how-tos-and-troubleshooting/building-chromium-browser#TOC-Debugging and
https://github.com/ds-hwang/wiki/wiki/Build-Chromium-for-Chromium-OS-and-Deploy-to-real-device#log).
But the absence of built-in support for native messaging hosts does not mean that you cannot achieve what you want. Start a local HTTP server in Crouton, and communicate between the Chrome extension / app and the HTTP server via the standard Web APIs (XMLHttpRequest, fetch, WebSocket, ...). On the server (running in Crouton), you can do whatever you want (e.g. starting local scripts).
Make sure that the server implements proper authentication (to prevent unauthorized access by other web sites or extensions) (and preferably bind to a local address only to make the server inaccessible over network).

How to redirect to mobile site for specific range of android versions

I would like to redirect site to mobile version only for android versions below a certain build, specifically below Ice Cream Sandwich, that is API level 14
I am able to at present only redirect for a certain build and unable to redirect for a range of builds. Please suggest how to redirect for a range of builds.
This is the code which I am using -
<script type="text/javascript"> // <![CDATA[
if ( (navigator.userAgent.indexOf('Android 4.2.2') != -1) ) {
document.location = "http://www.yoururladdress.com/yourpage.html";
} // ]]>
</script>
Found answer to this question.
Code is as follows -
<script>
<!--
var str = navigator.userAgent ;
var version = str.match(/Android\s+([\d\.]+)/)[1];
var version = version.substring(0, 3);
if(version <= 4.0)
location.replace("http://www.yoururladdress.com/yourpage.html");
}
-->
</script>

Errors in IE 8 from connect.facebook.net/en_US/all.js caused by credits callback

Setup:
Got a working facebook app and am correctly setup for facebook credits transactions (i.e. everything on the serverside is working fine).
In Firefox and chrome transactions complete without issue, however in IE8 the callback upon completing/closing the purchase dialog throws the following errors:
Error 1:
Line: 52 Error: Object doesn't support this property or method
Object doesn't support this property or method JScript - script block, line 52 character 37
Where the function it points to is:
ui: function( params )
{
obj = FB.JSON.parse( params );
method = obj.method;
cb = function( response ) { FBAS.getSwf().uiResponse( FB.JSON.stringify( response ), method ); }
FB.ui( obj, cb );
},
Specifically highlighting this bit:
FBAS.getSwf().uiResponse( FB.JSON.stringify( response ), method )
in the http://connect.facebook.net/en_US/all.js file
Error 2:
Line: 65 Error: Object doesn't support this action
Object doesn't support this action all.js, line 65 character 2198
[The line it points to is a stupidly long unformatted unreadable mess so I'll omit it unless requested]
Specifically highlighting this bit:
delete d._old_visibility
again in the http://connect.facebook.net/en_US/all.js file
The html I'm using (with the app identifying stuffs removed) is as follows:
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:fb="https://www.facebook.com/2008/fbml">
<head>
<meta charset="utf-8" />
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<meta http-equiv="Expires" content ="0" />
<meta http-equiv="Pragma" content ="no-cache" />
<meta http-equiv="Cache-Control" content ="no-cache" />
<title>[ APP NAME ]</title>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js"></script>
</head>
<body>
<div id="fb-root"></div>
<script type="text/javascript">
$(document).ready
(
function()
{
var appId = [ APP ID ];
var host = [ APP HOST ];
// If the user did not grant the app authorization go ahead and
// tell them that. Stop code execution.
if (0 <= window.location.href.indexOf ("error_reason"))
{
$(document.body).append ("<p>Authorization denied!</p>");
return;
}
// Loads the Facebook SDK script.
(function(d)
{
var js, id = 'facebook-jssdk'; if (d.getElementById(id)) {return;}
js = d.createElement('script'); js.id = id; js.async = true;
js.src = "//connect.facebook.net/en_US/all.js";
d.getElementsByTagName('head')[0].appendChild(js);
}(document));
// When the Facebook SDK script has finished loading init the
// SDK and then get the login status of the user. The status is
// reported in the handler.
window.fbAsyncInit = function()
{
//debugger;
FB.init(
{
appId : appId,
status : true,
cookie : true,
oauth : true,
});
FB.getLoginStatus (onCheckLoginStatus);
};
// Handles the response from getting the user's login status.
// If the user is logged in and the app is authorized go ahead
// and start running the application. If they are not logged in
// then redirect to the auth dialog.
function onCheckLoginStatus (response)
{
if (response.status != "connected")
{
top.location.href = "https://www.facebook.com/dialog/oauth?client_id=" + appId + "&redirect_uri=" + encodeURIComponent (host+"[ RELATIVE APP PATH ]") + "&scope=publish_stream,user_about_me,read_friendlists,user_photos";
}
else
{
// Start the application
loadGame();
}
}
}
);
function loadGame()
{
var flashvars = {};
var params = {};
var attributes = {};
params.allowscriptaccess = "always";
attributes.id = 'flashContent';
attributes.name = 'flashContent';
swfobject.embedSWF("[ APP SWF ]?"+Math.floor(Math.random()*10000), "flashContent", "100%", "99%", "10.0", null, flashvars, params, attributes);
}
</script>
<div id="flashContent" >
Loading...
</div>
</body>
This is just a problem for IE 8 but is stopping the app going live since a significant number of users transactions would fail (or rather would complete, be charged and not take effect due to the failed callback).
For the past few days I've been searching for others with this or a similar problem but to no avail.
I've seen some similar issues where people are warned about javascript variables being created globally and causing interfereance or variables being names using keywords reserved in IE but as far as I can tell neither is the case here. The facebook javascript code is fairly boilerplate stuff lifted from facebook dev pages and reliable sources. It may be JQuery (which I have little experience with), however, again, this is lifted from working examples and if there is a problem I can't see it.
Can anyone help?
SOLVED
I won't accept this answer because I honestly don't think the question was answerable/solvable with the info provided and feel it would be bad form. But I want to leave this here for anyone that might be looking for a solution.
Cause of the error
The problem is the result of the combination of facebook hiding the app during 'normal' facebook actions (in this case, displaying the pay prompt) and external interface calls not working in Internet explorer when the app is hidden/not visible.
Solution
Found at http://flassari.is/2012/02/external-interface-error-object-expected/#comment-1743
All of these steps may not be neccessary but in the end what I did was:
Stop facebook hiding the app by overriding the visibility using
<style>
#[ ID OF THE FLASH OBJECT]
{
visibility: visible !important;
}
</style>
Adding wmode = "opaque"; to the swfobject params
Using the optional flash_hide_callback by adding hideFlashCallback:"OnHideFlash" to the FB.init options in the actionscript to move/hide the app instead, where OnHideFlash is a javascript function:
function OnHideFlash(params)
{
if (params.state == 'opened')
{
getSwf().style.top = '-10000px';
} else
{
getSwf().style.top = '';
}
}
Where getSwf() is your prefered method of getting the flash app object.
Hopefully this will save some people the suffering of pouring through the endless 'reasons that XYXY doesn't work in IE' questions and solutions that has been my last few days.
I suggest putting your code through a JavaScript Lint tool and correcting any errors you find. IE8 is extremely picky about how JavaScript is coded, while Firefox and Chrome are ok with minor mistakes. If your code is error free (after linting), it should work properly.