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();