1 --[==========================================================================[
2 httprequests.lua: code for processing httprequests commands and output
3 --[==========================================================================[
4 Copyright (C) 2007 the VideoLAN team
7 Authors: Antoine Cellerier <dionoea at videolan dot org>
8 Rob Jonson <rob at hobbyistsoftware.com>
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 --]==========================================================================]
25 module("httprequests",package.seeall)
27 local common = require ("common")
28 local dkjson = require ("dkjson")
32 --Round the number to the specified precision
33 function round(what, precision)
34 if type(what) == "string" then
35 what = common.us_tonumber(what)
37 if type(what) == "number" then
38 return math.floor(what*math.pow(10,precision)+0.5) / math.pow(10,precision)
43 --split text where it matches the delimiter
44 function strsplit(text, delimiter)
45 local strfind = string.find
46 local strsub = string.sub
47 local tinsert = table.insert
50 if strfind("", delimiter, 1) then -- this would result in endless loops
51 error("delimiter matches empty string!")
55 local first, last = strfind(text, delimiter, pos)
56 if first then -- found?
57 tinsert(list,i, strsub(text, pos, first-1))
60 tinsert(list,i, strsub(text, pos))
68 --main function to process commands sent with the request
70 processcommands = function ()
72 local input = _GET['input']
73 local command = _GET['command']
74 local id = tonumber(_GET['id'] or -1)
75 local val = _GET['val']
76 local options = _GET['option']
77 local band = tonumber(_GET['band'])
78 local name = _GET['name']
79 local duration = tonumber(_GET['duration'])
80 if type(options) ~= "table" then -- Deal with the 0 or 1 option case
84 if command == "in_play" then
86 vlc.msg.err( "<options>" )
87 for a,b in ipairs(options) do
90 vlc.msg.err( "</options>" )
92 vlc.playlist.add({{path=vlc.strings.make_uri(input),options=options,name=name,duration=duration}})
93 elseif command == "addsubtitle" then
94 vlc.player.add_subtitle(val)
95 elseif command == "in_enqueue" then
96 vlc.playlist.enqueue({{path=vlc.strings.make_uri(input),options=options,name=name,duration=duration}})
97 elseif command == "pl_play" then
101 vlc.playlist.gotoitem(id)
103 elseif command == "pl_pause" then
104 if vlc.playlist.status() == "stopped" then
108 vlc.playlist.gotoitem(id)
113 elseif command == "pl_forcepause" then
114 if vlc.playlist.status() == "playing" then
117 elseif command == "pl_forceresume" then
118 if vlc.playlist.status() == "paused" then
121 elseif command == "pl_stop" then
123 elseif command == "pl_next" then
125 elseif command == "pl_previous" then
127 elseif command == "pl_delete" then
128 vlc.playlist.delete(id)
129 elseif command == "pl_empty" then
131 elseif command == "pl_sort" then
132 vlc.playlist.sort( val, id > 0 )
133 elseif command == "pl_random" then
134 vlc.playlist.random()
135 elseif command == "pl_loop" then
137 elseif command == "pl_repeat" then
138 vlc.playlist.repeat_()
139 elseif command == "pl_sd_add" then
141 elseif command == "pl_sd_remove" then
143 elseif command == "fullscreen" then
144 if vlc.object.vout() then
145 vlc.video.fullscreen()
147 elseif command == "snapshot" then
149 elseif command == "volume" then
151 elseif command == "seek" then
153 elseif command == "key" then
154 common.hotkey("key-"..val)
155 elseif command == "audiodelay" then
156 val = common.us_tonumber(val)
157 vlc.player.set_audio_delay(val)
158 elseif command == "rate" then
159 val = common.us_tonumber(val)
161 vlc.player.set_rate(val)
163 elseif command == "subdelay" then
164 val = common.us_tonumber(val)
165 vlc.player.set_subtitle_delay(val)
166 elseif command == "aspectratio" then
167 if vlc.object.vout() then
168 vlc.var.set(vlc.object.vout(),"aspect-ratio",val)
170 elseif command == "preamp" then
171 val = common.us_tonumber(val)
172 vlc.equalizer.preampset(val)
173 elseif command == "equalizer" then
174 val = common.us_tonumber(val)
175 vlc.equalizer.equalizerset(band,val)
176 elseif command == "enableeq" then
177 if val == '0' then vlc.equalizer.enable(false) else vlc.equalizer.enable(true) end
178 elseif command == "setpreset" then
179 vlc.equalizer.setpreset(val)
180 elseif command == "title" then
181 vlc.player.title_goto(val)
182 elseif command == "chapter" then
183 vlc.player.chapter_goto(val)
184 elseif command == "audio_track" then
185 vlc.player.toggle_audio_track(val)
186 elseif command == "video_track" then
187 vlc.player.toggle_video_track(val)
188 elseif command == "subtitle_track" then
189 vlc.player.toggle_spu_track(val)
190 elseif command == "set_renderer" then
191 local rd = get_renderer_discovery()
196 elseif command == "unset_renderer" then
207 --utilities for formatting output
209 function xmlString(s)
210 if (type(s)=="string") then
211 return vlc.strings.convert_xml_special_chars(s)
212 elseif (type(s)=="number") then
213 return common.us_tostring(s)
219 --dkjson outputs numbered tables as arrays
220 --so we don't need the array indicators
221 function removeArrayIndicators(dict)
224 for k,v in pairs(dict) do
225 if (type(v)=="table") then
226 local arrayEntry=v._array
231 dict[k]=removeArrayIndicators(v)
238 printTableAsJson = function (dict)
239 dict=removeArrayIndicators(dict)
241 local output=dkjson.encode (dict, { indent = true })
245 local printXmlKeyValue = function (k,v,indent)
247 for i=1,indent do print(" ") end
249 --XML element naming rules: this is a bit more conservative
250 --than it strictly needs to be
251 if not string.match(k, "^[a-zA-Z_][a-zA-Z0-9_%-%.]*$")
252 or string.match(k, "^[xX][mM][lL]") then
253 print('<element name="'..xmlString(k)..'">')
260 if (type(v)=="table") then
261 printTableAsXml(v,indent+2)
271 printTableAsXml = function (dict,indent)
272 for k,v in pairs(dict) do
273 printXmlKeyValue(k,v,indent)
278 function logTable(t,pre)
279 local pre = pre or ""
280 for k,v in pairs(t) do
281 vlc.msg.err(pre..tostring(k).." : "..tostring(v))
282 if type(v) == "table" then
291 getplaylist = function ()
293 if _GET["search"] then
294 if _GET["search"] ~= "" then
295 _G.search_key = _GET["search"]
299 local key = vlc.strings.decode_uri(_GET["search"])
300 p = vlc.playlist.search(key)
302 p = vlc.playlist.list()
305 --logTable(p) --Uncomment to debug
310 parseplaylist = function (list)
312 local current_item_id = vlc.playlist.current()
313 for i, item in ipairs(list) do
316 local name, path = item.name or ""
317 local path = item.path or ""
319 -- Is the item the one currently played
320 if(current_item_id ~= nil) then
321 if(current_item_id == item.id) then
322 result.current = "current"
325 result["type"]="leaf"
326 result.id=tostring(item.id)
327 result.uri=tostring(path)
329 result.duration=math.floor(item.duration)
336 playlisttable = function ()
338 local basePlaylist=getplaylist()
340 return parseplaylist(basePlaylist)
343 getbrowsetable = function ()
345 --paths are returned as an array of elements
346 local result = { element = { _array = {} } }
349 --uri takes precedence, but fall back to dir
351 if _GET["uri"] == "file://~" then
354 local uri = string.gsub(_GET["uri"], "[?#].*$", "")
355 if not string.match(uri, "/$") then
358 dir = vlc.strings.make_path(common.realpath(uri))
360 elseif _GET["dir"] then
363 -- "" dir means listing all drive letters e.g. "A:\", "C:\"...
364 --however the opendir() API won't resolve "X:\.." to that behavior,
365 --so we offer this resolution as "backwards compatibility"
366 if string.match(dir, '^[a-zA-Z]:[\\/]*%.%.$') or
367 string.match(dir, '^[a-zA-Z]:[\\/]*%.%.[\\/]') then
371 if dir ~= "" and dir ~= "~" then
372 dir = dir.."/" --luckily Windows accepts '/' as '\'
380 dir = vlc.config.homedir().."/"
383 local d = vlc.net.opendir(dir)
386 --FIXME: this is the wrong place to do this, but this still offers
387 --some useful mitigation: see #25021
388 if #d == 0 and dir ~= "" then
389 table.insert(d, "..")
392 for _,f in pairs(d) do
393 if f == ".." or not string.match(f,"^%.") then
397 local s = vlc.net.stat(path)
399 for k,v in pairs(s) do
403 element["type"]="unknown"
408 local uri=vlc.strings.make_uri(path)
410 element["uri"]=common.realpath(uri)
413 table.insert(result.element._array,element)
421 getstatus = function (includecategories)
424 local item = vlc.player.item()
425 local playlist = vlc.object.playlist()
426 local vout = vlc.object.vout()
427 local aout = vlc.object.aout()
431 --update api version when new data/commands added
433 s.version=vlc.misc.version()
434 s.volume=vlc.volume.get()
436 s.time = vlc.player.get_time() / 1000000
437 s.position = vlc.player.get_position()
438 s.currentplid = vlc.playlist.current()
439 s.audiodelay = vlc.player.get_audio_delay()
440 s.rate = vlc.player.get_rate()
441 s.subtitledelay = vlc.player.get_subtitle_delay()
444 s.length=math.floor(item:duration())
450 s.fullscreen=vlc.var.get(vout,"fullscreen")
451 s.aspectratio=vlc.var.get(vout,"aspect-ratio");
452 if s.aspectratio=="" then s.aspectratio = "default" end
458 local filters=vlc.var.get(aout,"audio-filter")
459 local temp=strsplit(filters,":")
462 for i,j in pairs(temp) do
463 s.audiofilters['filter_'..id]=j
469 s.videoeffects.hue=round(vlc.config.get("hue"),2)
470 s.videoeffects.brightness=round(vlc.config.get("brightness"),2)
471 s.videoeffects.contrast=round(vlc.config.get("contrast"),2)
472 s.videoeffects.saturation=round(vlc.config.get("saturation"),2)
473 s.videoeffects.gamma=round(vlc.config.get("gamma"),2)
475 s.state=vlc.playlist.status()
476 s.random = vlc.playlist.get_random()
477 s.loop = vlc.playlist.get_loop()
478 s["repeat"] = vlc.playlist.get_repeat()
481 s.equalizer.preamp=round(vlc.equalizer.preampget(),2)
482 s.equalizer.bands=vlc.equalizer.equalizerget()
483 if s.equalizer.bands ~= null then
484 for k,i in pairs(s.equalizer.bands) do s.equalizer.bands[k]=round(i,2) end
485 s.equalizer.presets=vlc.equalizer.presets()
488 if (includecategories and item) then
490 s.information.category={}
491 s.information.category.meta=item:metas()
493 local info = item:info()
494 for k, v in pairs(info) do
496 for k2, v2 in pairs(v) do
497 local tag = string.gsub(k2," ","_")
501 s.information.category[k]=streamTable
506 local statsdata = item:stats()
507 for k,v in pairs(statsdata) do
508 local tag = string.gsub(k,"_","")
512 s.information.chapter = vlc.player.get_chapter_index()
513 s.information.title = vlc.player.get_title_index()
515 s.information.chapters_count = vlc.player.get_chapters_count()
516 s.information.titles_count = vlc.player.get_titles_count()
522 get_renderers = function()
523 local rd = get_renderer_discovery()