Your Universal Remote Control Center
RemoteCentral.com
Philips Pronto Professional Forum - View Post
Previous section Next section Up level
Up level
The following page was printed from RemoteCentral.com:

Login:
Pass:
 
 

Topic:
Any KODI users out there?
This thread has 6 replies. Displaying all posts.
Post 1 made on August 9, 2026 at 22:34
M
mpg7321
Long Time Member
Joined:
Posts:
RC XP:
June 2020
183
173⭐︎
I have been working on feedback from KODI and would love to have others test it out.
Thanks,
Mike


At the request of Mike, original code removed as replaced with code later.

Last edited by Lyndel McGee (moderator) on August 14, 2026 20:49.
OP | Post 2 made on August 10, 2026 at 22:55
M
mpg7321
Long Time Member
Joined:
Posts:
RC XP:
June 2020
183
173⭐︎
I should have a new script shortly. One, I have a working script that pulls the cover art from KODI's local library. Two, found a bug in the script above, were feedback stops working after a long idol of nothing playing. I would have to jump to another page and back to get feedback to start working again. I found a fix but now I need to test for stability.
OP | Post 3 made on August 11, 2026 at 21:41
M
mpg7321
Long Time Member
Joined:
Posts:
RC XP:
June 2020
183
173⭐︎
New code with cover art and fixed the resuming from prolong idol.

// ============================================================
// KODI TCP + COVER ART
//
// Kodi IP : 192.168.1.47
// Kodi TCP : 9090
// Kodi HTTP : 8080
//
// WIDGETS:
//
// KODI_TITLE
// KODI_SHOW
// KODI_EPISODE
// KODI_TOTAL
// KODI_REMAINING
// KODI_PROGRESS
// KODI_COVER
//
// KODI_COVER : 60 x 80
//
// Requires:
// com.philips.HttpLibrary
//
// ============================================================


var KodiTCP = {};

KodiTCP.socket = null;
KodiTCP.buffer = "";
KodiTCP.connected = false;
KodiTCP.requestID = 0;

KodiTCP.timePolling = false;
KodiTCP.timeRequestPending = false;
KodiTCP.timeRequestTime = 0;

KodiTCP.progressMaxWidth = 595;

KodiTCP.reconnectScheduled = false;

KodiTCP.watchdogRunning = false;
KodiTCP.watchdogRequestPending = false;
KodiTCP.watchdogRequestTime = 0;

KodiTCP.httpLib =
com.philips.HttpLibrary;


// ============================================================
// COVER PANEL
// ============================================================

CF.widget("KODI_COVER").stretchImage = true;


// ============================================================
// FORMAT HH:MM:SS
// ============================================================

KodiTCP.formatTime = function(seconds)
{
seconds = Math.floor(seconds);

if (seconds < 0)
seconds = 0;

var hours =
Math.floor(seconds / 3600);

var minutes =
Math.floor((seconds % 3600) / 60);

var secs =
seconds % 60;

var h = hours.toString();
var m = minutes.toString();
var s = secs.toString();

if (h.length < 2)
h = "0" + h;

if (m.length < 2)
m = "0" + m;

if (s.length < 2)
s = "0" + s;

return h + ":" + m + ":" + s;
};


// ============================================================
// CLEAR DISPLAY
//
// IMPORTANT:
// Also clears the cover artwork so the previous movie
// does not remain displayed after playback stops.
// ============================================================

KodiTCP.clearDisplay = function()
{
CF.widget("KODI_TITLE").label = "";
CF.widget("KODI_SHOW").label = "";
CF.widget("KODI_EPISODE").label = "";
CF.widget("KODI_TOTAL").label = "";
CF.widget("KODI_REMAINING").label = "";

CF.widget("KODI_PROGRESS").width = 1;

CF.widget("KODI_TITLE").visible = false;
CF.widget("KODI_SHOW").visible = false;
CF.widget("KODI_EPISODE").visible = false;

// Clear previous cover artwork.
try
{
CF.widget("KODI_COVER").setImage(null);
}
catch(e)
{
}
};


// ============================================================
// SHOW COVER
// ============================================================

KodiTCP.showCover = function(poster)
{
if (poster == "")
return;


var imageURL =
"http://192.168.1.47:8080/image/" +
encodeURIComponent(
poster
);


KodiTCP.httpLib.getHTTPBinary(
imageURL,
function(binary)
{
if (!binary)
return;


try
{
var image =
new Image(binary);


CF.widget(
"KODI_COVER"
).setImage(
image
);
}
catch(e)
{
}
}
);
};


// ============================================================
// START TIME POLLING
// ============================================================

KodiTCP.startTimePolling = function()
{
if (!KodiTCP.connected)
return;

KodiTCP.timePolling = true;
KodiTCP.timeRequestPending = false;
KodiTCP.timeRequestTime = 0;

KodiTCP.getTime();
};


// ============================================================
// STOP TIME POLLING
// ============================================================

KodiTCP.stopTimePolling = function()
{
KodiTCP.timePolling = false;
KodiTCP.timeRequestPending = false;
KodiTCP.timeRequestTime = 0;
};


// ============================================================
// SCHEDULE NEXT TIME REQUEST
// ============================================================

KodiTCP.scheduleNextTime = function()
{
if (!KodiTCP.connected)
return;

if (!KodiTCP.timePolling)
return;

scheduleAfter(
1000,
KodiTCP.timePoll
);
};


// ============================================================
// TIME POLL CALLBACK
// ============================================================

KodiTCP.timePoll = function()
{
if (!KodiTCP.connected)
return;

if (!KodiTCP.timePolling)
return;


// --------------------------------------------------------
// If a previous request has been waiting too long,
// consider it lost.
//
// This prevents the polling system from becoming
// permanently stuck.
// --------------------------------------------------------

if (KodiTCP.timeRequestPending)
{
if (
(new Date().getTime() -
KodiTCP.timeRequestTime) > 5000
)
{
KodiTCP.timeRequestPending = false;
KodiTCP.timeRequestTime = 0;
}
else
{
KodiTCP.scheduleNextTime();
return;
}
}


KodiTCP.getTime();
};


// ============================================================
// CONNECT
// ============================================================

KodiTCP.connect = function()
{
if (KodiTCP.socket != null)
return;


KodiTCP.socket =
new TCPSocket(false);


KodiTCP.socket.onConnect = function()
{
KodiTCP.connected = true;
KodiTCP.buffer = "";
KodiTCP.timeRequestPending = false;
KodiTCP.timeRequestTime = 0;

KodiTCP.watchdogRequestPending = false;
KodiTCP.watchdogRequestTime = 0;

KodiTCP.reconnectScheduled = false;

// Always get the current Kodi state after connecting.
KodiTCP.getCurrentItem();
};


KodiTCP.socket.onData = function()
{
KodiTCP.buffer +=
KodiTCP.socket.read();

KodiTCP.processBuffer();
};


KodiTCP.socket.onClose = function()
{
KodiTCP.handleDisconnect();
};


KodiTCP.socket.onIOError = function()
{
KodiTCP.handleDisconnect();
};


KodiTCP.socket.connect(
"192.168.1.47",
9090,
5000
);
};


// ============================================================
// HANDLE DISCONNECT
//
// Previously a dead socket could leave the script stuck.
// Now we automatically reconnect.
// ============================================================

KodiTCP.handleDisconnect = function()
{
KodiTCP.stopTimePolling();

KodiTCP.connected = false;

KodiTCP.watchdogRequestPending = false;
KodiTCP.watchdogRequestTime = 0;

KodiTCP.buffer = "";


try
{
if (KodiTCP.socket != null)
KodiTCP.socket.close();
}
catch(e)
{
}


KodiTCP.socket = null;

KodiTCP.clearDisplay();

KodiTCP.scheduleReconnect();
};


// ============================================================
// SCHEDULE RECONNECT
// ============================================================

KodiTCP.scheduleReconnect = function()
{
if (KodiTCP.reconnectScheduled)
return;

KodiTCP.reconnectScheduled = true;


scheduleAfter(
5000,
function()
{
KodiTCP.reconnectScheduled = false;

if (!KodiTCP.connected &&
KodiTCP.socket == null)
{
KodiTCP.connect();
}
}
);
};


// ============================================================
// GET CURRENT ITEM
// ============================================================

KodiTCP.getCurrentItem = function()
{
if (!KodiTCP.connected)
return;


KodiTCP.requestID++;


var request =
'{"jsonrpc":"2.0",' +
'"method":"Player.GetItem",' +
'"params":{' +
'"properties":[' +
'"title",' +
'"showtitle",' +
'"season",' +
'"episode",' +
'"art"' +
'],' +
'"playerid":1' +
'},' +
'"id":' +
KodiTCP.requestID +
'}';


try
{
KodiTCP.socket.write(
request
);
}
catch(e)
{
KodiTCP.handleDisconnect();
}
};


// ============================================================
// GET CURRENT TIME
// ============================================================

KodiTCP.getTime = function()
{
if (!KodiTCP.connected)
return;

if (KodiTCP.timeRequestPending)
return;


KodiTCP.timeRequestPending = true;

KodiTCP.timeRequestTime =
new Date().getTime();


KodiTCP.requestID++;


var request =
'{"jsonrpc":"2.0",' +
'"method":"Player.GetProperties",' +
'"params":{' +
'"playerid":1,' +
'"properties":[' +
'"time",' +
'"totaltime"' +
']' +
'},' +
'"id":' +
KodiTCP.requestID +
'}';


try
{
KodiTCP.socket.write(
request
);
}
catch(e)
{
KodiTCP.timeRequestPending = false;
KodiTCP.timeRequestTime = 0;

KodiTCP.handleDisconnect();
}
};


// ============================================================
// WATCHDOG
//
// This is independent of movie playback.
//
// It periodically asks Kodi if the TCP connection is still
// alive. This is important after the Shield has been idle
// for a long period.
//
// ============================================================

KodiTCP.startWatchdog = function()
{
if (KodiTCP.watchdogRunning)
return;

KodiTCP.watchdogRunning = true;

KodiTCP.watchdogPoll();
};


// ============================================================
// WATCHDOG POLL
// ============================================================

KodiTCP.watchdogPoll = function()
{
if (!KodiTCP.watchdogRunning)
return;


// --------------------------------------------------------
// If disconnected, make sure reconnect is attempted.
// --------------------------------------------------------

if (!KodiTCP.connected)
{
if (KodiTCP.socket == null)
KodiTCP.scheduleReconnect();

scheduleAfter(
15000,
KodiTCP.watchdogPoll
);

return;
}


// --------------------------------------------------------
// If a watchdog request has been pending too long,
// assume the connection is stale.
// --------------------------------------------------------

if (KodiTCP.watchdogRequestPending)
{
if (
(new Date().getTime() -
KodiTCP.watchdogRequestTime) > 8000
)
{
KodiTCP.watchdogRequestPending = false;

KodiTCP.handleDisconnect();

scheduleAfter(
15000,
KodiTCP.watchdogPoll
);

return;
}
}
else
{
KodiTCP.sendWatchdog();
}


scheduleAfter(
15000,
KodiTCP.watchdogPoll
);
};


// ============================================================
// SEND WATCHDOG
//
// Player.GetActivePlayers is lightweight and does not
// interfere with the normal time polling.
// ============================================================

KodiTCP.sendWatchdog = function()
{
if (!KodiTCP.connected)
return;

if (KodiTCP.watchdogRequestPending)
return;


KodiTCP.watchdogRequestPending = true;

KodiTCP.watchdogRequestTime =
new Date().getTime();


KodiTCP.requestID++;


var request =
'{"jsonrpc":"2.0",' +
'"method":"Player.GetActivePlayers",' +
'"id":' +
KodiTCP.requestID +
'}';


try
{
KodiTCP.socket.write(
request
);
}
catch(e)
{
KodiTCP.watchdogRequestPending = false;

KodiTCP.handleDisconnect();
}
};


// ============================================================
// PROCESS TCP BUFFER
// ============================================================

KodiTCP.processBuffer = function()
{
while (true)
{
var depth = 0;
var start = -1;
var inString = false;
var escaped = false;
var found = false;


for (
var i = 0;
i < KodiTCP.buffer.length;
i++
)
{
var c =
KodiTCP.buffer.charAt(i);


if (escaped)
{
escaped = false;
continue;
}


if (c == "\\")
{
if (inString)
escaped = true;

continue;
}


if (c == '"')
{
inString = !inString;
continue;
}


if (inString)
continue;


if (c == "{")
{
if (depth == 0)
start = i;

depth++;
}


if (c == "}")
{
depth--;


if (
depth == 0 &&
start >= 0
)
{
var json =
KodiTCP.buffer.substring(
start,
i + 1
);


KodiTCP.buffer =
KodiTCP.buffer.substring(
i + 1
);


found = true;


KodiTCP.processJSON(
json
);


break;
}
}
}


if (!found)
break;
}
};


// ============================================================
// PROCESS JSON
// ============================================================

KodiTCP.processJSON = function(json)
{
// --------------------------------------------------------
// Any response means Kodi is alive.
// --------------------------------------------------------

if (
KodiTCP.watchdogRequestPending &&
json.indexOf('"result"') >= 0
)
{
KodiTCP.watchdogRequestPending = false;
KodiTCP.watchdogRequestTime = 0;
}


// --------------------------------------------------------
// PLAYBACK STOPPED
// --------------------------------------------------------

if (
json.indexOf(
'"method":"Player.OnStop"'
) >= 0
)
{
KodiTCP.stopTimePolling();

KodiTCP.clearDisplay();

return;
}


// --------------------------------------------------------
// NEW MEDIA STARTED
// --------------------------------------------------------

if (
json.indexOf(
'"method":"Player.OnPlay"'
) >= 0
)
{
// Make absolutely sure the old information is removed
// before loading the new movie/show.
KodiTCP.clearDisplay();

KodiTCP.getCurrentItem();

return;
}


// --------------------------------------------------------
// PLAYBACK RESUMED
// --------------------------------------------------------

if (
json.indexOf(
'"method":"Player.OnResume"'
) >= 0
)
{
KodiTCP.startTimePolling();

return;
}


// --------------------------------------------------------
// CURRENT ITEM RESPONSE
// --------------------------------------------------------

if (
json.indexOf('"title"') >= 0
)
{
KodiTCP.parseItem(json);

return;
}


// --------------------------------------------------------
// TIME RESPONSE
// --------------------------------------------------------

if (
json.indexOf('"time"') >= 0 &&
json.indexOf('"totaltime"') >= 0
)
{
KodiTCP.parseTime(json);

return;
}
};


// ============================================================
// PARSE MOVIE / TV INFORMATION
// ============================================================

KodiTCP.parseItem = function(json)
{
var title =
KodiTCP.getString(
json,
"title"
);


var show =
KodiTCP.getString(
json,
"showtitle"
);


// ========================================================
// ARTWORK
// ========================================================

var artObject =
KodiTCP.getObject(
json,
"art"
);


var poster = "";


if (artObject != "")
{
// ----------------------------------------------------
// MOVIE
// ----------------------------------------------------

if (show == "")
{
poster =
KodiTCP.getString(
artObject,
"poster"
);
}


// ----------------------------------------------------
// TV SHOW
// ----------------------------------------------------

else
{
poster =
KodiTCP.getString(
artObject,
"tvshow.poster"
);


if (poster == "")
{
poster =
KodiTCP.getString(
artObject,
"poster"
);
}
}
}


// ========================================================
// DISPLAY COVER
// ========================================================

if (poster != "")
{
KodiTCP.showCover(
poster
);
}


// ========================================================
// MOVIE
// ========================================================

if (show == "")
{
CF.widget("KODI_TITLE").visible =
true;

CF.widget("KODI_SHOW").visible =
false;

CF.widget("KODI_EPISODE").visible =
false;


CF.widget("KODI_TITLE").label =
title;

CF.widget("KODI_SHOW").label =
"";

CF.widget("KODI_EPISODE").label =
"";
}


// ========================================================
// TV SHOW
// ========================================================

else
{
CF.widget("KODI_TITLE").visible =
false;

CF.widget("KODI_SHOW").visible =
true;

CF.widget("KODI_EPISODE").visible =
true;


CF.widget("KODI_TITLE").label =
"";

CF.widget("KODI_SHOW").label =
show;

CF.widget("KODI_EPISODE").label =
title;
}


KodiTCP.startTimePolling();
};


// ============================================================
// PARSE TIME
// ============================================================

KodiTCP.parseTime = function(json)
{
KodiTCP.timeRequestPending = false;
KodiTCP.timeRequestTime = 0;


var timeObject =
KodiTCP.getObject(
json,
"time"
);


var totalObject =
KodiTCP.getObject(
json,
"totaltime"
);


if (
timeObject == "" |
totalObject == ""
)
{
KodiTCP.scheduleNextTime();

return;
}


var currentHours =
KodiTCP.getNumber(
timeObject,
"hours"
);


var currentMinutes =
KodiTCP.getNumber(
timeObject,
"minutes"
);


var currentSeconds =
KodiTCP.getNumber(
timeObject,
"seconds"
);


var current =
(currentHours * 3600) +
(currentMinutes * 60) +
currentSeconds;


var totalHours =
KodiTCP.getNumber(
totalObject,
"hours"
);


var totalMinutes =
KodiTCP.getNumber(
totalObject,
"minutes"
);


var totalSeconds =
KodiTCP.getNumber(
totalObject,
"seconds"
);


var total =
(totalHours * 3600) +
(totalMinutes * 60) +
totalSeconds;


CF.widget("KODI_TOTAL").label =
KodiTCP.formatTime(
total
);


var remaining =
total - current;


if (remaining < 0)
remaining = 0;


CF.widget("KODI_REMAINING").label =
KodiTCP.formatTime(
remaining
);


var progressWidth = 1;


if (total > 0)
{
progressWidth =
Math.round(
(current / total) *
KodiTCP.progressMaxWidth
);
}


if (progressWidth < 1)
progressWidth = 1;


if (
progressWidth >
KodiTCP.progressMaxWidth
)
{
progressWidth =
KodiTCP.progressMaxWidth;
}


CF.widget("KODI_PROGRESS").width =
progressWidth;


KodiTCP.scheduleNextTime();
};


// ============================================================
// GET JSON OBJECT
// ============================================================

KodiTCP.getObject = function(json, key)
{
var p =
json.indexOf(
'"' + key + '"'
);


if (p < 0)
return "";


p =
json.indexOf(
"{",
p
);


if (p < 0)
return "";


var depth = 0;
var inString = false;
var escaped = false;


for (
var i = p;
i < json.length;
i++
)
{
var c =
json.charAt(i);


if (escaped)
{
escaped = false;
continue;
}


if (c == "\\")
{
if (inString)
escaped = true;

continue;
}


if (c == '"')
{
inString = !inString;
continue;
}


if (inString)
continue;


if (c == "{")
depth++;


if (c == "}")
{
depth--;


if (depth == 0)
{
return json.substring(
p,
i + 1
);
}
}
}


return "";
};


// ============================================================
// GET STRING
// ============================================================

KodiTCP.getString = function(json, key)
{
var p =
json.indexOf(
'"' + key + '"'
);


if (p < 0)
return "";


p =
json.indexOf(
":",
p
);


if (p < 0)
return "";


p++;


while (
p < json.length &&
json.charAt(p) == " "
)
{
p++;
}


if (json.charAt(p) != '"')
return "";


p++;


var end = p;


while (end < json.length)
{
if (
json.charAt(end) == '"' &&
json.charAt(end - 1) != "\\"
)
{
break;
}


end++;
}


if (end >= json.length)
return "";


return json.substring(
p,
end
);
};


// ============================================================
// GET NUMBER
// ============================================================

KodiTCP.getNumber = function(json, key)
{
var p =
json.indexOf(
'"' + key + '"'
);


if (p < 0)
return 0;


p =
json.indexOf(
":",
p
);


if (p < 0)
return 0;


p++;


while (
p < json.length &&
json.charAt(p) == " "
)
{
p++;
}


var end = p;


while (
end < json.length &&
json.charAt(end) >= "0" &&
json.charAt(end) <= "9"
)
{
end++;
}


if (end == p)
return 0;


return parseInt(
json.substring(
p,
end
),
10
);
};


// ============================================================
// INITIALIZE
// ============================================================

KodiTCP.clearDisplay();


// ============================================================
// START
// ============================================================

KodiTCP.connect();

KodiTCP.startWatchdog();

Post 4 made on August 13, 2026 at 12:23
L
MOD
Lyndel McGee
RC Moderator
Joined:
Posts:
RC XP:
August 2001
13,176
596⭐︎
Thanks for sharing all of this.

There's lots of code here and includes the use of the Philips Http Library. The code posted above will be untestable by others without having that exact library.

A few recommendations if I may.

1) Did you add the Http library in PEP2 at the activity level?
2) What is the version of the Http library? Is it one you received from me earlier or did you download it from some other configuration?
3) Where does all this script reside? I'm assuming you put it into a Page.
4) I see references to many widgets. Are all of these panels on the same page?
5) As a reminder, script is good for example but if you want others to be able to run, you need a working reference. Do you have a small XCF file that you'd be willing to share?


Do you have a reference to the JSONRPC docs for Kodi?
I found these.

[Link: kodi.wiki]
[Link: kodi.wiki]

It mentions many version of Kodi and which Version of the API are you using?
The link above also mentions pretty-printing of the JSONRPC.

I see you configured port 9090 (default). I think out of the box, JSONRPC endpoint is turned off. What did you have to do to enable this? Did you also turn on pretty-printing?

All your JSON code for getNumber, getString, getObject functions works only if your 'key' is unique. Does Kodi return a list of tracks or other things which might be represented as an array of objects with the same keys (field names)? Methods like Playlist.GetItems or AudioLibrary.GetSongs likely meet this condition. Once you start using these methods, I think you will determine that
When you get to this point, I think a JSON Library will better serve you here.

I ask all these things because I've never played with Kodi. Maybe I should go setup a small system with it.

Would it be possible for you to provide a small writeup of the installation process of Kodi, the platform, etc?

If you want to connect offline, that's fine too. Let me know.

Thanks,
Lyndel

Lyndel McGee
Philips Pronto Addict/Beta Tester
OP | Post 5 made on August 13, 2026 at 23:14
M
mpg7321
Long Time Member
Joined:
Posts:
RC XP:
June 2020
183
173⭐︎
Lyndel, check your email. Let me know if it doesn't work.

Can you delete my very first post in this thread, I know theses are long post and the first one is useless. If not maybe delete this tread and I can create a new one.

You asked a lot of questions, hopefully my file will answer them, if you need help setting up KIDI let me know.

So far it has worked perfectly.

Thanks
Mike

Post 6 made on August 14, 2026 at 12:26
L
MOD
Lyndel McGee
RC Moderator
Joined:
Posts:
RC XP:
August 2001
13,176
596⭐︎
Thanks. Will do.
Lyndel McGee
Philips Pronto Addict/Beta Tester
Post 7 made on August 14, 2026 at 20:54
L
MOD
Lyndel McGee
RC Moderator
Joined:
Posts:
RC XP:
August 2001
13,176
596⭐︎
OK. I had a look at the code. For much of the stuff you are doing. Cursor, etc... you can use the Kodi WebServer endpoint 8080

http ://kodi_ip:8080/jsonrpc and create a POST request with the content body.

You are also using the Version 1.4 of the Philips HTTP Library. I have a version 2.1 which support HTTP chunking which was necessary for use with the Logitech/Lyrion Media Server and COMETD protocol.

I will work to clean up what I can as I have Kodi installed on my local machine and can debug everything without having to put stuff onto a pi.

Consider using Port 8080 and HTTP Post. See the note about port 8090 and sending \n or 0x1A (Escape) between requests for framing. I'll also double-check if Kodi is sending this framing for responses/notifications. I doubt it does. I've also configured my Kodi to pretty-print the JSONRPC responses using userdata/advancedsettings.xml which by all means breaks the \n rule mentioned in the link below.

[Link: google.com]


Lyndel

Last edited by Lyndel McGee on August 14, 2026 21:18.
Lyndel McGee
Philips Pronto Addict/Beta Tester

Jump to


Protected Feature Before you can reply to a message...
You must first register for a Remote Central user account - it's fast and free! Or, if you already have an account, please login now.

Please read the following: Unsolicited commercial advertisements are absolutely not permitted on this forum. Other private buy & sell messages should be posted to our Marketplace. For information on how to advertise your service or product click here. Remote Central reserves the right to remove or modify any post that is deemed inappropriate.