db648b2c38efc5edc663c570ec12c52335753d91
[vlc.git] / share / lua / intf / modules / httprequests.lua
1 --[==========================================================================[
2  httprequests.lua: code for processing httprequests commands and output
3 --[==========================================================================[
4  Copyright (C) 2007 the VideoLAN team
5  $Id$
6
7  Authors: Antoine Cellerier <dionoea at videolan dot org>
8  Rob Jonson <rob at hobbyistsoftware.com>
9
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.
14
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.
19
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 --]==========================================================================]
24
25 module("httprequests",package.seeall)
26
27 local common = require ("common")
28 local dkjson = require ("dkjson")
29
30
31
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)
36     end
37     if type(what) == "number" then
38         return math.floor(what*math.pow(10,precision)+0.5) / math.pow(10,precision)
39     end
40     return nil
41 end
42
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
48     local list = {}
49     local pos = 1
50     if strfind("", delimiter, 1) then -- this would result in endless loops
51         error("delimiter matches empty string!")
52     end
53     local i=1
54     while 1 do
55         local first, last = strfind(text, delimiter, pos)
56         if first then -- found?
57             tinsert(list,i, strsub(text, pos, first-1))
58             pos = last+1
59         else
60             tinsert(list,i, strsub(text, pos))
61             break
62         end
63         i = i+1
64     end
65     return list
66 end
67
68 --main function to process commands sent with the request
69
70 processcommands = function ()
71
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
81         options = { options }
82     end
83
84     if command == "in_play" then
85         --[[
86         vlc.msg.err( "<options>" )
87         for a,b in ipairs(options) do
88         vlc.msg.err(b)
89         end
90         vlc.msg.err( "</options>" )
91         --]]
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
98         if id == -1 then
99             vlc.playlist.play()
100         else
101             vlc.playlist.gotoitem(id)
102         end
103     elseif command == "pl_pause" then
104         if vlc.playlist.status() == "stopped" then
105             if id == -1 then
106                 vlc.playlist.play()
107             else
108                 vlc.playlist.gotoitem(id)
109             end
110         else
111             vlc.playlist.pause()
112         end
113     elseif command == "pl_forcepause" then
114         if vlc.playlist.status() == "playing" then
115             vlc.playlist.pause()
116         end
117     elseif command == "pl_forceresume" then
118         if vlc.playlist.status() == "paused" then
119             vlc.playlist.pause()
120         end
121     elseif command == "pl_stop" then
122         vlc.playlist.stop()
123     elseif command == "pl_next" then
124         vlc.playlist.next()
125     elseif command == "pl_previous" then
126         vlc.playlist.prev()
127     elseif command == "pl_delete" then
128         vlc.playlist.delete(id)
129     elseif command == "pl_empty" then
130         vlc.playlist.clear()
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
136         vlc.playlist.loop()
137     elseif command == "pl_repeat" then
138         vlc.playlist.repeat_()
139     elseif command == "pl_sd_add" then
140         vlc.sd.add(val)
141     elseif command == "pl_sd_remove" then
142         vlc.sd.remove(val)
143     elseif command == "fullscreen" then
144         if vlc.object.vout() then
145             vlc.video.fullscreen()
146         end
147     elseif command == "snapshot" then
148         common.snapshot()
149     elseif command == "volume" then
150         common.volume(val)
151     elseif command == "seek" then
152         common.seek(val)
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)
160         if val >= 0 then
161             vlc.player.set_rate(val)
162         end
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)
169         end
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()
192         if not rd then
193             return
194         end
195         rd:select(id)
196     elseif command == "unset_renderer" then
197         rd:select(-1)
198     end
199
200     local input = nil
201     local command = nil
202     local id = nil
203     local val = nil
204
205 end
206
207 --utilities for formatting output
208
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)
214     else
215         return tostring(s)
216     end
217 end
218
219 --dkjson outputs numbered tables as arrays
220 --so we don't need the array indicators
221 function removeArrayIndicators(dict)
222     local newDict=dict
223
224     for k,v in pairs(dict) do
225         if (type(v)=="table") then
226             local arrayEntry=v._array
227             if arrayEntry then
228                 v=arrayEntry
229             end
230
231             dict[k]=removeArrayIndicators(v)
232         end
233     end
234
235     return newDict
236 end
237
238 printTableAsJson = function (dict)
239     dict=removeArrayIndicators(dict)
240
241     local output=dkjson.encode (dict, { indent = true })
242     print(output)
243 end
244
245 local printXmlKeyValue = function (k,v,indent)
246     print("\n")
247     for i=1,indent do print(" ") end
248     if (k) then
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)..'">')
254             k = "element"
255         else
256             print("<"..k..">")
257         end
258     end
259
260     if (type(v)=="table") then
261         printTableAsXml(v,indent+2)
262     else
263         print(xmlString(v))
264     end
265
266     if (k) then
267         print("</"..k..">")
268     end
269 end
270
271 printTableAsXml = function (dict,indent)
272     for k,v in pairs(dict) do
273         printXmlKeyValue(k,v,indent)
274     end
275 end
276
277 --[[
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
283 a(v,pre.."  ")
284 end
285 end
286 end
287 --]]
288
289 --main accessors
290
291 getplaylist = function ()
292     local p
293     if _GET["search"] then
294         if _GET["search"] ~= "" then
295             _G.search_key = _GET["search"]
296         else
297             _G.search_key = nil
298         end
299         local key = vlc.strings.decode_uri(_GET["search"])
300         p = vlc.playlist.search(key)
301     else
302         p = vlc.playlist.list()
303     end
304
305     --logTable(p) --Uncomment to debug
306
307     return p
308 end
309
310 parseplaylist = function (list)
311     local playlist = {}
312     local current_item_id = vlc.playlist.current()
313     for i, item in ipairs(list) do
314         local result={}
315
316         local name, path = item.name or ""
317         local path = item.path or ""
318
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"
323             end
324         end
325         result["type"]="leaf"
326         result.id=tostring(item.id)
327         result.uri=tostring(path)
328         result.name=name
329         result.duration=math.floor(item.duration)
330         playlist[i] = result
331     end
332     return playlist
333
334 end
335
336 playlisttable = function ()
337
338     local basePlaylist=getplaylist()
339
340     return parseplaylist(basePlaylist)
341 end
342
343 getbrowsetable = function ()
344
345     --paths are returned as an array of elements
346     local result = { element = { _array = {} } }
347
348     local dir
349     --uri takes precedence, but fall back to dir
350     if _GET["uri"] then
351         if _GET["uri"] == "file://~" then
352             dir = "~"
353         else
354             local uri = string.gsub(_GET["uri"], "[?#].*$", "")
355             if not string.match(uri, "/$") then
356                 uri = uri.."/"
357             end
358             dir = vlc.strings.make_path(common.realpath(uri))
359         end
360     elseif _GET["dir"] then
361         dir = _GET["dir"]
362
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
368             dir = ""
369         end
370
371         if dir ~= "" and dir ~= "~" then
372             dir = dir.."/" --luckily Windows accepts '/' as '\'
373         end
374     end
375     if not dir then
376         return result
377     end
378
379     if dir == "~" then
380         dir = vlc.config.homedir().."/"
381     end
382
383     local d = vlc.net.opendir(dir)
384     table.sort(d)
385
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, "..")
390     end
391
392     for _,f in pairs(d) do
393         if f == ".." or not string.match(f,"^%.") then
394             local path = dir..f
395             local element={}
396
397             local s = vlc.net.stat(path)
398             if (s) then
399                 for k,v in pairs(s) do
400                     element[k]=v
401                 end
402             else
403                 element["type"]="unknown"
404             end
405             element["path"]=path
406             element["name"]=f
407
408             local uri=vlc.strings.make_uri(path)
409             if uri then
410                 element["uri"]=common.realpath(uri)
411             end
412
413             table.insert(result.element._array,element)
414         end
415     end
416
417     return result;
418 end
419
420
421 getstatus = function (includecategories)
422
423
424     local item = vlc.player.item()
425     local playlist = vlc.object.playlist()
426     local vout = vlc.object.vout()
427     local aout = vlc.object.aout()
428
429     local s ={}
430
431     --update api version when new data/commands added
432     s.apiversion=4
433     s.version=vlc.misc.version()
434     s.volume=vlc.volume.get()
435
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()
442
443     if item then
444         s.length=math.floor(item:duration())
445     else
446         s.length=0
447     end
448
449     if vout then
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
453     else
454         s.fullscreen=0
455     end
456
457     if aout then
458         local filters=vlc.var.get(aout,"audio-filter")
459         local temp=strsplit(filters,":")
460         s.audiofilters={}
461         local id=0
462         for i,j in pairs(temp) do
463             s.audiofilters['filter_'..id]=j
464             id=id+1
465         end
466     end
467
468     s.videoeffects={}
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)
474
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()
479
480     s.equalizer={}
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()
486     end
487
488     if (includecategories and item) then
489         s.information={}
490         s.information.category={}
491         s.information.category.meta=item:metas()
492
493         local info = item:info()
494         for k, v in pairs(info) do
495             local streamTable={}
496             for k2, v2 in pairs(v) do
497                 local tag = string.gsub(k2," ","_")
498                 streamTable[tag]=v2
499             end
500
501             s.information.category[k]=streamTable
502         end
503
504         s.stats={}
505
506         local statsdata = item:stats()
507         for k,v in pairs(statsdata) do
508             local tag = string.gsub(k,"_","")
509             s.stats[tag]=v
510         end
511
512         s.information.chapter = vlc.player.get_chapter_index()
513         s.information.title = vlc.player.get_title_index()
514
515         s.information.chapters_count = vlc.player.get_chapters_count()
516         s.information.titles_count = vlc.player.get_titles_count()
517
518     end
519     return s
520 end
521
522 get_renderers = function()
523     local rd = get_renderer_discovery()
524     if not rd then
525         return {}
526     end
527     return rd:list()
528 end