Add json capabilities to http requests
authorRob Jonson <rob@hobbyistsoftware.com>
Thu, 7 Jul 2011 12:02:42 +0000 (13:02 +0100)
committerJean-Baptiste Kempf <jb@videolan.org>
Fri, 22 Jul 2011 00:52:34 +0000 (02:52 +0200)
- refactor logic and model from status.xml and playlist.xml to prepare those to work as simple views driven by the model. This will allow the .json files to exist as models as well - so .json is also added as a mimetype
- refactor status.xml to use data from shared model in luahttp.lua
- added status.json - mimics functionality of status.xml

NEWS
share/Makefile.am
share/lua/http/requests/README.txt
share/lua/http/requests/status.json [new file with mode: 0644]
share/lua/http/requests/status.xml
share/lua/intf/http.lua
share/lua/intf/modules/httprequests.lua [new file with mode: 0644]

diff --git a/NEWS b/NEWS
index dbf29da..43c2b84 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -76,6 +76,7 @@ Interfaces:
  * dbus: Rewrite of the main loop to use a more efficient poll-based model
  * dbus: Upgrade to an mpris2 compliant interface, see http://www.mpris.org
  * webUI/http: Rewrite of the web interface, using jQuery
+ * webUI/http: some requests are now supported in JSON in addition to XML
 
 Video Output:
  * New video output based on Direct2D for Windows 7 and Vista (with Platform Update)
index af3fcf8..03f0ff2 100644 (file)
@@ -184,6 +184,7 @@ nobase_vlclib_DATA = \
 if BUILD_HTTPD
 nobase_vlclib_DATA += \
        lua/intf/http.luac \
+       lua/intf/modules/httprequests.luac \
        $(NULL)
 nobase_vlcdata_DATA += $(DIST_http_lua)
 endif
@@ -203,6 +204,7 @@ EXTRA_DIST += \
        lua/intf/luac.lua \
        lua/intf/modules/common.lua \
        lua/intf/modules/host.lua \
+       lua/intf/modules/httprequests.lua \
        lua/intf/telnet.lua \
        lua/meta/art/README.txt \
        lua/meta/art/04_musicbrainz.lua \
@@ -335,6 +337,7 @@ DIST_http_lua = \
        lua/http/requests/browse.xml \
        lua/http/requests/vlm_cmd.xml \
        lua/http/requests/status.xml \
+       lua/http/requests/status.json \
        lua/http/requests/vlm.xml \
        lua/http/index.html \
        lua/http/css/ui-lightness/jquery-ui-1.8.13.custom.css \
index b12a989..bdd0059 100644 (file)
@@ -13,9 +13,10 @@ Examples:
  space -> +
  ...
 
-status.xml:
+status.xml or status.json
 ===========
 < Get VLC status information, current item info and meta.
+< Get VLC version, and http api version
 
 > add <mrl> to playlist and start playback:
   ?command=in_play&input=<mrl>
diff --git a/share/lua/http/requests/status.json b/share/lua/http/requests/status.json
new file mode 100644 (file)
index 0000000..f98ed22
--- /dev/null
@@ -0,0 +1,40 @@
+<?vlc --[[
+vim:syntax=lua
+<!--  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >
+<  status.xml: VLC media player web interface
+<  this should mirror the content and function of status.json
+< - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >
+<  Copyright (C) 2005-2009 the VideoLAN team
+<  $Id$
+< 
+<  Authors: Rob Jonson <rob -at- hobbyistsoftware -dot- com>
+< 
+<  This program is free software; you can redistribute it and/or modify
+<  it under the terms of the GNU General Public License as published by
+<  the Free Software Foundation; either version 2 of the License, or
+<  (at your option) any later version.
+< 
+<  This program is distributed in the hope that it will be useful,
+<  but WITHOUT ANY WARRANTY; without even the implied warranty of
+<  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+<  GNU General Public License for more details.
+< 
+<  You should have received a copy of the GNU General Public License
+<  along with this program; if not, write to the Free Software
+<  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+< - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+]]?>
+<?vlc
+
+--package.loaded.httprequests = nil --uncomment to debug changes
+require "httprequests"
+
+httprequests.processcommands()
+
+local statusTable=httprequests.getstatus(true)
+
+print('{')
+httprequests.printTableAsJson(statusTable,0)
+print('}')
+
+?>
index d5b49ac..866e13d 100644 (file)
@@ -8,6 +8,7 @@ vim:syntax=lua
 <  $Id$
 < 
 <  Authors: Antoine Cellerier <dionoea -at- videolan -dot- org>
+<                      Rob Jonson <rob -at- hobbyistsoftware -dot- com>
 < 
 <  This program is free software; you can redistribute it and/or modify
 <  it under the terms of the GNU General Public License as published by
@@ -24,130 +25,25 @@ vim:syntax=lua
 <  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 < - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
 ]]?>
-<?vlc
 
-local input = _GET['input']
-local command = _GET['command']
-local id = tonumber(_GET['id'] or -1)
-local val = _GET['val']
-local options = _GET['option']
-if type(options) ~= "table" then -- Deal with the 0 or 1 option case
-  options = { options }
-end
+<?vlc
 
----[[]] vlc.msg.err("requests/status.xml got:","input: "..tostring(input),"command: "..tostring(command),"id: "..tostring(id),"val: "..tostring(val))
+--package.loaded.httprequests = nil --uncomment to debug changes
+require "httprequests"
 
-local function stripslashes(s)
-  return string.gsub(s,"\\(.)","%1")
-end
+httprequests.processcommands()
 
-if command == "in_play" then
-  --[[
-  vlc.msg.err( "<options>" )
-  for a,b in ipairs(options) do
-    vlc.msg.err(b)
-  end
-  vlc.msg.err( "</options>" )
-  --]]
-  vlc.playlist.add({{path=stripslashes(input),options=options}})
-elseif command == "in_enqueue" then
-  vlc.playlist.enqueue({{path=stripslashes(input),options=options}})
-elseif command == "pl_play" then
-  if id == -1 then
-    vlc.playlist.play()
-  else
-    vlc.playlist.goto(id)
-  end
-elseif command == "pl_pause" then
-  if vlc.playlist.status() == "stopped" then
-    if id == -1 then
-      vlc.playlist.play()
-    else
-      vlc.playlist.goto(id)
-    end
-  else
-    vlc.playlist.pause()
-  end
-elseif command == "pl_forcepause" then
-  if vlc.playlist.status() == "playing" then
-    vlc.playlist.pause()
-  end
-elseif command == "pl_forceresume" then
-  if vlc.playlist.status() == "paused" then
-    vlc.playlist.pause()
-  end
-elseif command == "pl_stop" then
-  vlc.playlist.stop()
-elseif command == "pl_next" then
-  vlc.playlist.next()
-elseif command == "pl_previous" then
-  vlc.playlist.prev()
-elseif command == "pl_delete" then
-  vlc.playlist.delete(id)
-elseif command == "pl_empty" then
-  vlc.playlist.clear()
-elseif command == "pl_sort" then
-  vlc.playlist.sort( val, id > 0 )
-elseif command == "pl_random" then
-  vlc.playlist.random()
-elseif command == "pl_loop" then
-  vlc.playlist.loop()
-elseif command == "pl_repeat" then
-  vlc.playlist.repeat_()
-elseif command == "pl_sd" then
-  if vlc.sd.is_loaded(val) then
-    vlc.sd.remove(val)
-  else
-    vlc.sd.add(val)
-  end
-elseif command == "fullscreen" then
-  vlc.video.fullscreen()
-elseif command == "snapshot" then
-  common.snapshot()
-elseif command == "volume" then
-  common.volume(val)
-elseif command == "seek" then
-  common.seek(val)
-elseif command == "key" then
-  common.hotkey("key-"..val)
-elseif command == "audiodelay" then
-  if vlc.object.input() and val then
-   vlc.var.set(vlc.object.input(),"audio-delay",val)
-  end
-elseif command == "rate" then
-  if vlc.object.input() and tonumber(val) >= 0 then
-   vlc.var.set(vlc.object.input(),"rate",val)
-  end
-elseif command == "subdelay" then
-  if vlc.object.input() then
-   vlc.var.set(vlc.object.input(),"spu-delay",val)
-  end
-end
+local statusTable=httprequests.getstatus(false)
 
-local input = nil
-local command = nil
-local id = nil
-local val = nil
+print('<root>\n')
+httprequests.printTableAsXml(statusTable,0)
 
-local input = vlc.object.input()
 local item = vlc.input.item()
-local playlist = vlc.object.playlist()
-local vout = input and vlc.object.find(input,'vout','child')
+
+--data in the information section is presented in a non-standard way to keep compatibility.
+
 ?>
-<root>
-  <volume><?vlc print(vlc.volume.get()) ?></volume>
-  <audiodelay><?vlc if input then print(vlc.var.get(input,"audio-delay")) else print (0) end ?></audiodelay>
-  <rate><?vlc if input then print(vlc.var.get(input,"rate")) else print (1) end ?></rate>
-  <subtitledelay><?vlc if input then print(vlc.var.get(input,"spu-delay")) else print (0) end ?></subtitledelay>
-  <length><?vlc if input then print(math.floor(vlc.var.get(input,"length"))) else print(0) end?></length>
-  <time><?vlc if input then print(math.floor(vlc.var.get(input,"time"))) else print(0) end?></time>
-  <state><?vlc print(vlc.playlist.status()) ?></state>
-  <position><?vlc if input then print(vlc.var.get(input,"position")) else print(0) end?></position>
-  <fullscreen><?vlc if vout then vlc.var.get(vout,"fullscreen") else print(0) end?></fullscreen>
-  <random><?vlc print(vlc.var.get(playlist,"random")) ?></random>
-  <loop><?vlc print(vlc.var.get(playlist,"loop")) ?></loop>
-  <repeat><?vlc print(vlc.var.get(playlist,"repeat")) ?></repeat>
-  <information>
+<information>
     <category name="meta">
     <?vlc
       if item then
index 5481941..fcebdd1 100644 (file)
@@ -41,6 +41,7 @@ close_tag = "?>"
 -- TODO: use internal VLC mime lookup function for mimes not included here
 local mimes = {
     txt = "text/plain",
+    json = "text/plain",
     html = "text/html",
     xml = "text/xml",
     js = "text/javascript",
diff --git a/share/lua/intf/modules/httprequests.lua b/share/lua/intf/modules/httprequests.lua
new file mode 100644 (file)
index 0000000..b00584c
--- /dev/null
@@ -0,0 +1,373 @@
+--[==========================================================================[
+ httprequests.lua: code for processing httprequests commands and output
+--[==========================================================================[
+ Copyright (C) 2007 the VideoLAN team
+ $Id$
+
+ Authors: Antoine Cellerier <dionoea at videolan dot org>
+ Rob Jonson <rob at hobbyistsoftware.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+--]==========================================================================]
+
+module("httprequests",package.seeall)
+
+
+--input utilities
+
+local function stripslashes(s)
+  return string.gsub(s,"\\(.)","%1")
+end
+
+--main function to process commands sent with the request
+
+processcommands = function ()
+       
+       local input = _GET['input']
+       local command = _GET['command']
+       local id = tonumber(_GET['id'] or -1)
+       local val = _GET['val']
+       local options = _GET['option']
+       if type(options) ~= "table" then -- Deal with the 0 or 1 option case
+         options = { options }
+       end
+       
+       if command == "in_play" then
+         --[[
+         vlc.msg.err( "<options>" )
+         for a,b in ipairs(options) do
+               vlc.msg.err(b)
+         end
+         vlc.msg.err( "</options>" )
+         --]]
+         vlc.playlist.add({{path=stripslashes(input),options=options}})
+       elseif command == "in_enqueue" then
+         vlc.playlist.enqueue({{path=stripslashes(input),options=options}})
+       elseif command == "pl_play" then
+         if id == -1 then
+               vlc.playlist.play()
+         else
+               vlc.playlist.goto(id)
+         end
+       elseif command == "pl_pause" then
+         if vlc.playlist.status() == "stopped" then
+               if id == -1 then
+                 vlc.playlist.play()
+               else
+                 vlc.playlist.goto(id)
+               end
+         else
+               vlc.playlist.pause()
+         end
+       elseif command == "pl_forcepause" then
+         if vlc.playlist.status() == "playing" then
+               vlc.playlist.pause()
+         end
+       elseif command == "pl_forceresume" then
+         if vlc.playlist.status() == "paused" then
+               vlc.playlist.pause()
+         end
+       elseif command == "pl_stop" then
+         vlc.playlist.stop()
+       elseif command == "pl_next" then
+         vlc.playlist.next()
+       elseif command == "pl_previous" then
+         vlc.playlist.prev()
+       elseif command == "pl_delete" then
+         vlc.playlist.delete(id)
+       elseif command == "pl_empty" then
+         vlc.playlist.clear()
+       elseif command == "pl_sort" then
+         vlc.playlist.sort( val, id > 0 )
+       elseif command == "pl_random" then
+         vlc.playlist.random()
+       elseif command == "pl_loop" then
+         vlc.playlist.loop()
+       elseif command == "pl_repeat" then
+         vlc.playlist.repeat_()
+       elseif command == "pl_sd" then
+         if vlc.sd.is_loaded(val) then
+               vlc.sd.remove(val)
+         else
+               vlc.sd.add(val)
+         end
+       elseif command == "fullscreen" then
+         vlc.video.fullscreen()
+       elseif command == "snapshot" then
+         common.snapshot()
+       elseif command == "volume" then
+         common.volume(val)
+       elseif command == "seek" then
+         common.seek(val)
+       elseif command == "key" then
+         common.hotkey("key-"..val)
+       elseif command == "audiodelay" then
+         if vlc.object.input() and val then
+          vlc.var.set(vlc.object.input(),"audio-delay",val)
+         end
+       elseif command == "rate" then
+         if vlc.object.input() and tonumber(val) >= 0 then
+          vlc.var.set(vlc.object.input(),"rate",val)
+         end
+       elseif command == "subdelay" then
+         if vlc.object.input() then
+          vlc.var.set(vlc.object.input(),"spu-delay",val)
+         end
+       end
+       
+       local input = nil
+       local command = nil
+       local id = nil
+       local val = nil
+
+end
+
+--utilities for formatting output
+
+local function xmlString(s)
+  if (type(s)=="string") then
+       return vlc.strings.convert_xml_special_chars(s)
+  else
+       return tostring(s)
+  end
+end
+
+local printJsonKeyValue = function (k,v,indent)
+       print("\n")
+       for i=1,indent do print(" ") end
+       if (k) then
+               print("\""..k.."\":")
+       end
+       
+       if (type(v)=="number") then
+               print(xmlString(v))
+       elseif (type(v)=="table") then 
+                if (v._array==NULL) then                       
+               print("{\n")
+               printTableAsJson(v,indent+2)
+               print("\n}")  
+          else 
+               print("[")
+               printArrayAsJson(v._array,indent+2)
+               print("\n]") 
+          end
+       else
+       print("\""..xmlString(v).."\"")
+    end
+end
+
+
+printArrayAsJson = function(array,indent)
+       first=true
+       for i,v in ipairs(array) do
+               if not first then print(",") end
+               printJsonKeyValue(NULL,v,indent)        
+               first=false
+       end
+end
+
+printTableAsJson = function (dict,indent)
+       first=true
+       for k,v in pairs(dict) do
+               if not first then print(",") end
+               printJsonKeyValue(k,v,indent)
+               first=false
+    end
+end
+
+local printXmlKeyValue = function (k,v,indent)
+       print("\n")
+       for i=1,indent do print(" ") end
+       if (k) then
+               print("<"..k..">")
+       end
+       
+       if (type(v)=="table") then
+               printTableAsXml(v,indent+2)
+       else
+       print(xmlString(v))
+    end
+    
+    if (k) then
+               print("</"..k..">")
+       end
+end
+
+printTableAsXml = function (dict,indent)
+       for k,v in pairs(dict) do
+        printXmlKeyValue(k,v,indent)
+    end
+end
+
+--[[
+function logTable(t,pre)
+  local pre = pre or ""
+  for k,v in pairs(t) do
+    vlc.msg.err(pre..tostring(k).." : "..tostring(v))
+    if type(v) == "table" then
+      a(v,pre.."  ")
+    end
+  end
+end
+--]]
+
+--main accessors
+
+getplaylist = function ()
+       local p
+       
+       if _GET["search"] then
+         if _GET["search"] ~= "" then
+               _G.search_key = _GET["search"]
+         else
+               _G.search_key = nil
+         end
+         local key = vlc.strings.decode_uri(_GET["search"])
+         p = vlc.playlist.search(key)
+       else
+         p = vlc.playlist.get()
+       end
+       
+       --logTable(p) --Uncomment to debug
+       
+       return p
+end
+
+parseplaylist = function (item)
+       if item.flags.disabled then return end
+       
+       if (item.children) then
+               local result={}
+               local name = vlc.strings.convert_xml_special_chars(item.name or "")
+               
+               result["type"]="node"
+               result.id=tostring(item.id)
+               result.name=tostring(name)
+               result.ro=item.flags.ro and "ro" or "rw"
+               
+               --store children in an array
+               --we use _array as a proxy for arrays
+               result.children={}
+               result.children._array={}
+               
+               for _, child in ipairs(item.children) do
+                       local nextChild=parseplaylist(child)
+            table.insert(result.children._array,nextChild)
+        end 
+               
+               return result
+       else
+               local result={}
+               local name, path = vlc.strings.convert_xml_special_chars(item.name or "", item.path or "")
+               local current_item = vlc.input.item()
+               
+               -- Is the item the one currently played
+               if(current_item ~= nil) then
+            if(vlc.input.item().uri(current_item) == path) then
+                result.current = "current"
+            end
+        end
+               
+               result["type"]="leaf"
+               result.id=tostring(item.id)
+               result.uri=tostring(path)
+               result.name=name
+               result.ro=item.flags.ro and "ro" or "rw"
+               result.duration=math.floor(item.duration)
+               
+               return result
+       end
+
+end
+
+playlisttable = function ()
+
+       local basePlaylist=getplaylist()
+       
+       return parseplaylist(basePlaylist)
+end
+
+
+getstatus = function (includecategories)
+
+
+local input = vlc.object.input()
+local item = vlc.input.item()
+local playlist = vlc.object.playlist()
+local vout = input and vlc.object.find(input,'vout','child')
+
+       local s ={}
+       
+       --update api version when new data/commands added
+       s.apiversion=1
+       s.version=vlc.misc.version()
+       s.volume=vlc.volume.get()
+       
+       if input then 
+               s.length=math.floor(vlc.var.get(input,"length"))
+               s.time=math.floor(vlc.var.get(input,"time"))
+               s.position=vlc.var.get(input,"position")
+               s.audiodelay=vlc.var.get(input,"audio-delay")
+               s.rate=vlc.var.get(input,"rate")
+               s.subtitledelay=vlc.var.get(input,"spu-delay")
+       else 
+               s.length=0
+               s.time=0
+               s.position=0
+               s.audiodelay=0
+               s.rate=1
+               s.subtitledelay=0
+       end
+       
+       if vout then
+               s.fullscreen=vlc.var.get(vout,"fullscreen")
+       else
+               s.fullscreen=0
+       end
+       
+       s.state=vlc.playlist.status()
+       s.random=vlc.var.get(playlist,"random")
+       s.loop=vlc.var.get(playlist,"loop")
+       s["repeat"]=vlc.var.get(playlist,"repeat")
+       
+       if (includecategories and item) then
+               s.information={}
+               s.information.category={}
+               s.information.category.meta=item:metas()
+               
+               local info = item:info()
+               for k, v in pairs(info) do
+                       local streamTable={}
+                       for k2, v2 in pairs(v) do
+                               local tag = string.gsub(k2," ","_")
+                               streamTable[xmlString(tag)]=xmlString(v2)
+                       end
+                       
+                       s.information.category[xmlString(k)]=streamTable
+               end
+               
+               s.stats={}
+               
+               local statsdata = item:stats()
+       for k,v in pairs(statsdata) do
+               local tag = string.gsub(k,"_","")
+        s.stats[tag]=xmlString(v)
+      end
+               
+               
+       end
+
+       return s
+end