Channelscan

Added features to TransponderList.json
   Allow to define sort order for channels
   Allow overwrite channel title,group,pids
   Define custom groups (i.e. favourites)

Removed client side sorting from browsertv.html
Reworked creating ChannelList.json from legacy database to allow
serveside sorting.
This commit is contained in:
mvoelkel 2016-01-24 12:58:42 +01:00
parent bbee4d1c4c
commit e65fb9aaab
6 changed files with 303 additions and 175 deletions

File diff suppressed because one or more lines are too long

View File

@ -51,7 +51,20 @@ local function GetGroup(ChannelList,Title)
return Group
end
local function cmp_title(a,b)
local function CompareTitle(a,b)
if a.Order then
if b.Order then
if a.Order == b.Order then
return a.Title < b.Title
else
return a.Order < b.Order
end
else
return true
end
elseif b.Order then
return false
end
return a.Title < b.Title
end
@ -108,9 +121,40 @@ if not ipAddr then
end
local tl = LoadTransponderList(infile)
local CIMappings = {}
if tl.CIMapList then
local CIMap
local ID
for _,CIMap in ipairs(tl.CIMapList) do
for _,ID in ipairs(CIMap.CIMappings) do
CIMappings[ID] = CIMap
end
end
end
local ChannelOverwrites = {}
if tl.ChannelOverwriteList then
local ChannelOverwrite
for _,ChannelOverwrite in ipairs(tl.ChannelOverwriteList) do
ChannelOverwrites[ChannelOverwrite.ID] = ChannelOverwrite
end
end
local ChannelList = {}
ChannelList.GroupList = {}
if tl.GroupList then
local Group
local Order
for _,Group in ipairs(tl.GroupList) do
local g = GetGroup(ChannelList,Group.Title)
if Group.Order then
g.Order = Group.Order
end
end
end
local Max = 999999
local Count = 0
local ChannelCount = 0
@ -118,16 +162,14 @@ local ChannelCount = 0
Report(ChannelCount,"*")
if tl.SourceList then
local Source
for _,Source in ipairs(tl.SourceList) do
if keys[Source.Key] then
Report(ChannelCount,Source.Title)
print("Scanning: "..Source.Title)
local SourceOptions = ""
if Source.UseNIT then
SourceOptions = SourceOptions.."--use_nit "
end
if Source.DVBType == "S" then
SourceOptions = '--src='..keys[Source.Key]
SourceOptions = '--src='..keys[Source.Key].." "
end
for _,Transponder in ipairs(Source.TransponderList) do
@ -152,9 +194,14 @@ if tl.SourceList then
end
end
local Options = SourceOptions
if Transponder.UseNIT then
Options = SourceOptions.."--use_nit "
end
print("--------------------------------------------------------------")
print('octoscan '..SourceOptions..Params..' '..ipAddr)
local octoscan = io.popen('octoscan '..SourceOptions..' '..Params..' '..ipAddr,"r")
print('octoscan '..Options..Params..' '..ipAddr)
local octoscan = io.popen('octoscan '..Options..Params..' '..ipAddr,"r")
if octoscan then
while true do
local line = octoscan:read("*l")
@ -181,18 +228,24 @@ if tl.SourceList then
if line == "END" then
local all_pids = "0"
if include_sitables then
if isencrypted then
all_pids = all_pids..",1"
end
--~ if isencrypted then
--~ all_pids = all_pids..",1"
--~ end
all_pids = all_pids..",16,17,18,20"
end
if #pids > 0 then
all_pids = all_pids..","..pids
local cireq = ""
local ID = string.format("%s:%d:%d:%d",Source.Key,onid,tsid,sid)
if isencrypted then
local CIMap = CIMappings[ID]
if CIMap then
local pmt = pids:match("(%d+),?")
if pmt then
cireq = "&x_ci="..CIMap.Slot.."&x_pmt="..pmt.."."..sid
isencrypted = false
end
end
end
local channel = { Title=sname,
Request = '?'..Request.."&pids="..all_pids,
Tracks=tracks,
ID = string.format("%s:%d:%d:%d",Source.Key,onid,tsid,sid) }
if pname == "" then
pname = Source.Title
end
@ -200,6 +253,29 @@ if tl.SourceList then
if isradio then
gname = "Radio - "..gname
end
local Order = none
local ChannelOverwrite = ChannelOverwrites[ID]
if ChannelOverwrite then
Order = ChannelOverwrite.Order
if ChannelOverwrite.Group then
gname = ChannelOverwrite.Group
end
if ChannelOverwrite.pids then
gname = ChannelOverwrite.pids
end
if ChannelOverwrite.Title then
sname = ChannelOverwrite.Title
end
end
if #pids > 0 then
all_pids = all_pids..","..pids
end
local channel = { Title=sname,
Request = '?'..Request..cireq.."&pids="..all_pids,
Tracks=tracks,
ID=ID, Order=Order }
if ((not isradio) or (include_radio > 0)) and ((not isencrypted) or (include_encrypted > 0)) then
local group = GetGroup(ChannelList,gname)
if group then
@ -247,13 +323,20 @@ if tl.SourceList then
for _,group in ipairs(ChannelList.GroupList) do
if sort then
table.sort(group.ChannelList,cmp_title)
table.sort(group.ChannelList,CompareTitle)
end
for _,channel in ipairs(group.ChannelList) do
channel.Order = nil
end
group.ChannelList[0] = #group.ChannelList
end
if sort then
table.sort(ChannelList.GroupList,cmp_title)
table.sort(ChannelList.GroupList,CompareTitle)
end
for _,group in ipairs(ChannelList.GroupList) do
group.Order = nil
end
ChannelList.GroupList[0] = #ChannelList.GroupList
local encode = newencoder()

View File

@ -127,12 +127,12 @@ function AddRow(table,name,request,tracks)
table.appendChild(row);
}
function TitleCompare(a,b)
{
if( a.Title.toUpperCase() < b.Title.toUpperCase() ) return -1;
if( a.Title.toUpperCase() > b.Title.toUpperCase() ) return 1;
return 0;
}
// function TitleCompare(a,b)
// {
// if( a.Title.toUpperCase() < b.Title.toUpperCase() ) return -1;
// if( a.Title.toUpperCase() > b.Title.toUpperCase() ) return 1;
// return 0;
// }
var xmlhttp = new XMLHttpRequest();
var url = "/channellist.lua?select=json";
@ -200,10 +200,10 @@ function myFunction(response) {
Index = Index + 1;
}
}
for(var i = 0; i < SourceList.length; i++ )
{
SourceList[i].ChannelList.sort(TitleCompare);
}
// for(var i = 0; i < SourceList.length; i++ )
// {
// SourceList[i].ChannelList.sort(TitleCompare);
// }
document.Source.Select.selectedIndex = LastSource;
SetSource(LastSource);
}

View File

@ -70,44 +70,52 @@ function CreateM3U(host)
return table.concat(m3u)
end
function JSONSource(host,SourceList,Title,System)
local json = {}
local src = ""
local sep1 = "\n"
local sep2 = "\n"
local function CompareTitle(a,b)
return a.Title < b.Title
end
function Legacy2JSON()
local newencoder = require("lunajson.encoder")
local SourceList = LoadSourceList()
local ChannelList = {}
ChannelList.GroupList = {}
local Group
local Channel
local f,c
table.insert(json,' "'..Title..'": [')
sep1 = "\n"
for _,f in pairs(SourceList) do
if not System or f.system == System or f.system == System.."2" then
table.insert(json,sep1)
sep1 = ",\n"
table.insert(json,' {\n')
table.insert(json,' "Title": "'..f.title..'",\n')
table.insert(json,' "ChannelList": [')
Group = { Title=f.title, ChannelList = { } }
table.insert(ChannelList.GroupList,Group)
if System == "dvbs" then
src = 'src='..f.src..'&'
if f.system == "dvbs" or f.system == "dvbs2" then
src = 'src='..f.src..'&'
end
for _,c in ipairs(f.ChannelList) do
local tracks = {}
local track
for track in c.tracks:gmatch("%d+") do
table.insert(tracks,tonumber(track))
end
tracks[0] = #tracks
sep2 = "\n"
for _,c in ipairs(f.ChannelList) do
table.insert(json,sep2)
sep2 = ",\n"
table.insert(json,' {\n')
table.insert(json,' "Title": "'..string.gsub(c.title,'"','\\"')..'",\n')
table.insert(json,' "Request": "?'..src..c.request..'",\n')
table.insert(json,' "Tracks": ['..c.tracks..']\n')
table.insert(json,' }')
end
Channel = { Title=c.title,
Request = '?'..src..c.request,
Tracks=tracks }
table.insert(json,'\n ]\n')
table.insert(json,' }')
table.insert(Group.ChannelList,Channel)
end
end
table.insert(json,'\n ]')
return table.concat(json)
for _,Group in ipairs(ChannelList.GroupList) do
table.sort(Group.ChannelList,CompareTitle)
Group.ChannelList[0] = #Group.ChannelList
end
table.sort(ChannelList.GroupList,CompareTitle)
ChannelList.GroupList[0] = #ChannelList.GroupList
local encode = newencoder()
local data = encode(ChannelList)
return data
end
function CreateJSON(host)
@ -117,17 +125,7 @@ function CreateJSON(host)
data = file:read("*a")
file:close()
else
local SourceList = LoadSourceList()
local json = {}
table.insert(json,"{\n")
table.insert(json,JSONSource(host,SourceList,"GroupList",nil) .. "\n")
--~ table.insert(json,JSONSource(host,SourceList,"SourceListSat","dvbs") .. ",\n")
--~ table.insert(json,JSONSource(host,SourceList,"SourceListCable","dvbc") .. ",\n")
--~ table.insert(json,JSONSource(host,SourceList,"SourceListTer","dvbt") .. "\n")
table.insert(json,"}\n")
data = table.concat(json)
data = Legacy2JSON()
end
return data
end
@ -160,6 +158,7 @@ if method == "GET" then
contenttype = "text/m3u; charset=utf-8"
data = CreateM3U(host)
elseif string.match(query,"select=json") then
filename = "ChannelList.json"
contenttype = "application/json; charset=utf-8"
data = CreateJSON(host)
end

View File

@ -130,38 +130,36 @@ ScanReq.onreadystatechange=function()
function GetStatus()
{
ScanReq.open("GET", "/channelscan.lua?select=status", true);
ScanReq.open("GET", "/channelscan.lua?select=status&t=" + Math.random(), true);
ScanReq.send();
}
function ScanStatus(response)
{
var s = JSON.parse(response);
var done = false;
var done = true;
if( s.status )
{
if( s.status == "active" )
{
document.getElementById("scancount").firstChild.nodeValue = s.count;
document.getElementById("scantext").firstChild.nodeValue = s.msg;
done = false;
}
else if( s.status == "busy" )
{
document.getElementById("scancount").firstChild.nodeValue = "\u00A0";
document.getElementById("scantext").firstChild.nodeValue = "BUSY";
done = true;
}
else if( s.status == "done" )
{
document.getElementById("scancount").firstChild.nodeValue = s.count;
document.getElementById("scantext").firstChild.nodeValue = "Channels found";
done = true;
}
else if( s.status == "deleted" )
{
document.getElementById("scancount").firstChild.nodeValue = "\u00A0";
document.getElementById("scantext").firstChild.nodeValue = "Channel list deleted";
done = true;
}
else if( s.status == "restored" )
{
@ -170,7 +168,10 @@ function ScanStatus(response)
document.getElementById("scantext").firstChild.nodeValue = "Nothing restored";
else
document.getElementById("scantext").firstChild.nodeValue = "Previous channel list restored";
done = true;
}
else if( s.status == "retry" )
{
done = false;
}
}
@ -216,8 +217,9 @@ function InitiateScan()
if( param != "" )
{
ScanReq.open("GET", "/channelscan.lua?select=scan" + param + "&sitables=1&sort=1&restartdms=1", true);
ScanReq.send();
ScanReq.open("POST", "/channelscan.lua", true);
ScanReq.setRequestHeader("Content-type","application/x-www-form-urlencoded");
ScanReq.send("select=scan" + param + "&sitables=1&sort=1&restartdms=1");
document.getElementById("scancount").firstChild.nodeValue = "\u00A0";
document.getElementById("scantext").firstChild.nodeValue = "Scanning...";
}
@ -237,15 +239,16 @@ function PollStatus()
function DeleteScan()
{
DisableButtons(true);
ScanReq.open("GET", "/channelscan.lua?select=delete", true);
ScanReq.send();
ScanReq.setRequestHeader("Content-type","application/x-www-form-urlencoded");
ScanReq.send("select=delete");
}
function RestoreScan()
{
DisableButtons(true);
ScanReq.open("GET", "/channelscan.lua?select=restore", true);
ScanReq.send();
ScanReq.open("POST", "/channelscan.lua", true);
ScanReq.setRequestHeader("Content-type","application/x-www-form-urlencoded");
ScanReq.send("select=restore");
}
</script>

View File

@ -55,121 +55,164 @@ local function LoadTransponderList()
return tl
end
if method == "GET" then
local filename = nil
local contenttype = "application/json; charset=utf-8"
local data = nil
local function GetCMD(s)
local q,v
local params = ""
local cmd = ""
for q,v in query:gmatch("(%a+)=([%w%.]+)") do
for q,v in s:gmatch("(%a+)=([%w%.]+)") do
if q == "select" then
cmd = v
else
elseif q ~= "t" then
params = params.." "..q.."="..v
end
end
return cmd,params
end
if cmd == "keys" then
local tl = LoadTransponderList()
if tl then
local kl = { KeyList = { } }
local s
for _,s in ipairs(tl.SourceList) do
table.insert(kl.KeyList, { Key=s.Key,Title=s.Title,DVBType=s.DVBType })
end
kl.KeyList[0] = #kl.KeyList
local encode = newencoder()
data = encode(kl)
local function Keys()
local data = nil
local tl = LoadTransponderList()
if tl then
local kl = { KeyList = { } }
local s
for _,s in ipairs(tl.SourceList) do
table.insert(kl.KeyList, { Key=s.Key,Title=s.Title,DVBType=s.DVBType })
end
elseif cmd == "scan" then
local rc = os.execute("mkdir /tmp/doscan.lock")
if rc ~= 0 then
data = '{"status":"busy"}'
else
data = '{"status":"retry"}'
local f = io.open("/tmp/doscan.msg","w+")
if f then
f:write("Scanning")
f:close()
end
os.execute("/var/channels/doscan.lua "..params.." >/tmp/doscan.log 2>&1 &")
end
elseif cmd == "status" then
local js = { }
local f = io.open("/tmp/doscan.lock/doscan.msg")
kl.KeyList[0] = #kl.KeyList
local encode = newencoder()
data = encode(kl)
end
return data
end
local function Scan(params)
local data = nil
local rc = os.execute("mkdir /tmp/doscan.lock")
if rc ~= 0 then
data = '{"status":"busy"}'
else
data = '{"status":"retry"}'
local f = io.open("/tmp/doscan.msg","w+")
if f then
f:write("Scanning")
f:close()
end
os.execute("/var/channels/doscan.lua "..params.." >/tmp/doscan.log 2>&1 &")
end
return data
end
local function Status()
local data = nil
local js = { }
local f = io.open("/tmp/doscan.lock/doscan.msg")
if f then
js.status = "active"
local m = f:read("*l")
local count,msg = m:match("(%d+):(.*)")
js.count = count
js.msg = msg
f:close()
else
f = io.open("/tmp/doscan.msg")
if f then
js.status = "active"
local m = f:read("*l")
local count,msg = m:match("(%d+):(.*)")
js.count = count
js.msg = msg
if count and msg then
js.count = count
js.msg = msg
js.status = "done"
else
if m == "Scanning" then
js.status = "retry"
else
js.status = nil
end
end
f:close()
else
f = io.open("/tmp/doscan.msg")
if f then
local m = f:read("*l")
local count,msg = m:match("(%d+):(.*)")
if count and msg then
js.count = count
js.msg = msg
js.status = "done"
else
if m == "Scanning" then
js.status = "retry"
else
js.status = nil
end
end
f:close()
else
js.status = ""
end
end
local encode = newencoder()
data = encode(js)
elseif cmd == "delete" then
local rc = os.execute("mkdir /tmp/doscan.lock")
if rc ~= 0 then
data = '{"status":"busy"}'
else
data = '{"status":"deleted"}'
os.execute("rm /config/ChannelList.json");
os.execute("rm /tmp/doscan.msg");
os.execute("rm -rf /tmp/doscan.lock");
end
elseif cmd == "restore" then
local rc = os.execute("mkdir /tmp/doscan.lock")
if rc ~= 0 then
data = '{"status":"busy"}'
else
local rc = os.execute("mv /config/ChannelList.bak /config/ChannelList.json");
if rc == 0 then
data = '{"status":"restored", "count":1}'
else
data = '{"status":"restored", "count":0}'
end
os.execute("rm /tmp/doscan.msg");
os.execute("rm -rf /tmp/doscan.lock");
js.status = ""
end
end
local encode = newencoder()
data = encode(js)
return data
end
if data then
http_print(proto.." 200" )
http_print("Pragma: no-cache")
http_print("Cache-Control: no-cache")
http_print("Content-Type: "..contenttype)
if filename then
http_print('Content-Disposition: filename="'..filename..'"')
end
http_print(string.format("Content-Length: %d",#data))
http_print()
http_print(data)
local function Delete()
local data = nil
local rc = os.execute("mkdir /tmp/doscan.lock")
if rc ~= 0 then
data = '{"status":"busy"}'
else
SendError("404","not found")
data = '{"status":"deleted"}'
os.execute("rm /config/ChannelList.json");
os.execute("rm /tmp/doscan.msg");
os.execute("rm -rf /tmp/doscan.lock");
end
return data
end
local function Restore()
local data = nil
local rc = os.execute("mkdir /tmp/doscan.lock")
if rc ~= 0 then
data = '{"status":"busy"}'
else
local rc = os.execute("mv /config/ChannelList.bak /config/ChannelList.json");
if rc == 0 then
data = '{"status":"restored", "count":1}'
else
data = '{"status":"restored", "count":0}'
end
os.execute("rm /tmp/doscan.msg");
os.execute("rm -rf /tmp/doscan.lock");
end
return data
end
local filename = nil
local contenttype = "application/json; charset=utf-8"
local data = nil
local cmd = ""
local params
if method == "GET" then
cmd,params = GetCMD(query)
elseif method == "POST" and clength and ctype then
if ctype:match("application/x%-www%-form%-urlencoded") then
local query = io.read(tonumber(clength))
query = string.gsub(query,"\r","")
cmd,params = GetCMD(query)
end
else
SendError("500","What")
return
end
if cmd == "keys" then
data = Keys()
elseif cmd == "scan" then
data = Scan(params)
elseif cmd == "status" then
data = Status()
elseif cmd == "delete" then
data = Delete()
elseif cmd == "restore" then
data = Restore()
end
if data then
http_print(proto.." 200" )
http_print("Pragma: no-cache")
http_print("Cache-Control: no-cache")
http_print("Content-Type: "..contenttype)
if filename then
http_print('Content-Disposition: filename="'..filename..'"')
end
http_print(string.format("Content-Length: %d",#data))
http_print()
http_print(data)
else
SendError("404","not found")
end