source: ipk/source.sh4/network_xupnpd/_path_/etc/xupnpd/ui/xupnpd_ui.lua @ 34402

Last change on this file since 34402 was 34402, checked in by Stephan, 9 years ago

new xupnpd version 1.033

File size: 20.3 KB
Line 
1-- Copyright (C) 2011-2012 Anton Burdinuk
2-- clark15b@gmail.com
3-- https://tsdemuxer.googlecode.com/svn/trunk/xupnpd
4
5ui_args=nil
6ui_data=nil
7
8function ui_main()
9    http.sendtfile(cfg.ui_path..'ui_main.html',http_vars)
10end
11
12function ui_error()
13    http.send('<h2>Error occurred</h3>')
14    http.send('<br/><a class="btn btn-info" href="/ui">Back</a>')
15end
16
17function ui_downloads()
18    http.send('<h3>Downloads</h3>')
19    http.send('<br/><table class="table">')
20    if playlist_data.elements[1] then
21        for i,j in ipairs(playlist_data.elements[1].elements) do
22            http.send(string.format('<tr><td><a href="/ui/%s.m3u">%s</a></td></tr>',j.name,j.name))
23        end
24    end
25    http.send('</table>')
26    http.send('<br/><a class="btn btn-info" href="/ui">Back</a>')
27end
28
29function ui_download(name)
30    name=util.urldecode(string.match(name,'(.+)%.m3u$'))
31
32    local pls=nil
33
34    for i,j in ipairs(playlist_data.elements[1].elements) do
35        if j.name==name then pls=j break end
36    end
37
38    if not pls then
39        http.send(
40            string.format(
41                'HTTP/1.1 404 Not found\r\nPragma: no-cache\r\nCache-control: no-cache\r\nDate: %s\r\nServer: %s\r\n'..
42                'Connection: close\r\n\r\n',os.date('!%a, %d %b %Y %H:%M:%S GMT'),ssdp_server)
43        )
44        return
45    end
46
47    http.send(
48        string.format(
49            'HTTP/1.1 200 Ok\r\nPragma: no-cache\r\nCache-control: no-cache\r\nDate: %s\r\nServer: %s\r\nAccept-Ranges: none\r\n'..
50            'Connection: close\r\nContent-Type: audio/x-mpegurl\r\n\r\n',os.date('!%a, %d %b %Y %H:%M:%S GMT'),ssdp_server)
51    )
52
53    http.send('#EXTM3U\n')
54    for i,j in ipairs(pls.elements) do
55        http.send('#EXTINF:0,'..j.name..'\n'..playlist_get_url(j)..'\n')
56    end
57end
58
59function ui_playlists()
60    http.send('<h3>Playlists</h3>')
61    http.send('<br/><table class="table">')
62
63    function f(path,args)
64        local d=util.dir(path)
65        if d then
66            table.sort(d)
67            for i,j in ipairs(d) do
68                if string.find(j,'.+%.m3u$') then
69                    local fname=util.urlencode(j)
70                    http.send(string.format('<tr><td><a href=\'/ui/show?fname=%s&%s\'>%s</a> [<a href=\'/ui/remove?fname=%s&%s\'>x</a>]</td></tr>\n',fname,args,j,fname,args))
71                end
72            end
73        end
74    end
75
76    f(cfg.playlists_path,'')
77
78    if cfg.feeds_path~=cfg.playlists_path then f(cfg.feeds_path,'feed=1') end
79
80    http.send('</table>')
81
82    http.send('<br/><h3>Upload *.m3u file</h3>')
83    http.send('<form method=post action="/ui/upload" enctype="multipart/form-data">')
84    http.send('<input type=file name=m3ufile><br /><br />')
85    http.send('<input class="btn btn-primary" type=submit value=Send>')
86    http.send('</form><hr/>')
87    http.send('<br/><a class="btn btn-primary" href="/ui/reload">Reload</a> <a class="btn btn-primary" href="/ui/reload_feeds?return_url=/ui/playlists">Reload feeds</a> <a class="btn btn-info" href="/ui">Back</a>')
88end
89
90function ui_feeds()
91    http.send('<h3>Feeds</h3>')
92    http.send('<br/><table class="table">')
93
94    for i,j in ipairs(feeds) do
95        http.send(string.format('<tr><td>%s [<a href="/ui/remove_feed?id=%s">x</a>]</td></tr>\n',j[3],i))
96    end
97
98    http.send('</table>')
99
100    http.send('<h3>Add feed</h3>')
101
102    http.send('<form method=get action="/ui/add_feed">')
103
104    http.send('<div class="controls controls-row"><div class="span2">Plugin</div><select class="span4" name="plugin">')
105
106    for plugin_name,plugin in pairs(plugins) do
107        if plugin.name and plugin.disabled~=true then
108            http.send(string.format('<option value="%s">%s</option>',plugin_name,plugin.name))
109        end
110    end
111
112    http.send('</select></div>')
113
114    http.send('<div class="controls controls-row"><div class="span2">Feed data</div><input class="span4" type="text" name="feed"><div class="span1"><a href="/ui/fhelp" target="_blank">?</a></div></div>')
115
116    http.send('<div class="controls controls-row"><div class="span2">Name</div><input class="span4" type="text" name="name"></div>')
117
118    http.send('<br/><input class="btn btn-primary" type=submit value=Add> <a class="btn btn-info" href="/ui/fhelp" target="_blank">Help</a>')
119    http.send('</form><hr/>')
120
121    http.send('<br/><a class="btn btn-primary" href="/ui/save_feeds">Save</a> <a class="btn btn-primary" href="/ui/reload_feeds?return_url=/ui/feeds">Reload feeds</a> <a class="btn btn-info" href="/ui">Back</a>')
122end
123
124function ui_fhelp()
125
126    http.send('<br/>')
127
128    for plugin_name,plugin in pairs(plugins) do
129        if plugin.name and plugin.desc and plugin.disabled~=true then
130            http.send(string.format('<b>%s</b>: ',plugin.name))
131            http.send(plugin.desc)
132            http.send('<br/><br/>\n\n')
133        end
134    end
135end
136
137function ui_mhelp()
138
139    http.send('<br/><h3>MIME-Types</h3>\n<table class="table">\n<tr><th>File Type</th><th>MIME-Type</th><th>UPnP Proto</th><th>DLNA.ORG Profile</th></tr>\n')
140
141    for i,j in pairs(mime) do
142        if j then
143            local ext=string.match(j[5],'^(DLNA.ORG_PN=[%w_]+);')
144
145            http.send(string.format('<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>\n',i,j[3],j[4],ext or j[5]))
146        end
147    end
148
149    http.send('</table>\n\n<a class="btn btn-info" href="/ui/ehelp">DLNA Extras</a> <a class="btn btn-info" href="/ui/config">Back</a>')
150end
151
152function ui_ehelp()
153    http.send('<br/><h3>DLNA Extras</h3>\n<table class="table">\n<tr><th>Name</th><th>DLNA.ORG Extras</th></tr>\n')
154
155    for i,j in pairs(dlna_org_extras) do
156        http.send(string.format('<tr><td>%s</td><td>%s</td></tr>\n',i,j))
157    end
158
159    http.send('</table>\n\n<a class="btn btn-info" href="/ui/mhelp">Back</a>')
160end
161
162function ui_plugins()
163
164    if ui_args.n and ui_args.s then
165        local p=plugins[ui_args.n]
166
167        if p then
168            if ui_args.s=='on' then p.disabled=false elseif ui_args.s=='off' then p.disabled=true end
169            core.sendevent('plugin',ui_args.n,ui_args.s)
170        end
171    end
172
173    http.send('<br/><h3>Plugins</h3>\n<table class="table">\n<tr><th>Name</th><th>Status</th></tr>\n')
174
175    for i,j in pairs(plugins) do
176        if j.name then
177            local status
178           
179            if j.disabled==true then
180                status=string.format('<a href="/ui/plugins?n=%s&s=on">on</a> | <b>off</b>',util.urlencode(i))
181            else
182                status=string.format('<b>on</b> | <a href="/ui/plugins?n=%s&s=off">off</a>',util.urlencode(i))
183            end
184
185            http.send(string.format('<tr><td>%s</td><td>%s</td></tr>\n',j.name,status))
186        end
187    end
188
189    http.send('</table>\n\n<a class="btn btn-primary" href="/ui/plapply">Save</a> <a class="btn btn-info" href="/ui/config">Back</a>')
190end
191
192function ui_plapply()
193    local f=io.open(cfg.config_path..'postinit/plugins.lua','w')
194    if f then
195        for i,j in pairs(plugins) do
196            if j.name then
197                f:write(string.format('plugins["%s"].disabled=%s\n',i,tostring(j.disabled or false)))
198            end
199        end
200        f:close()
201    end
202
203    http.send('<h3>OK</h3>')
204    http.send('<br/><a class="btn btn-info" href="/ui/plugins">Back</a>')
205end
206
207
208function ui_profiles()
209
210    if ui_args.n and ui_args.s then
211        local p=profiles[ui_args.n]
212
213        if p then
214            if ui_args.s=='on' then p.disabled=false elseif ui_args.s=='off' then p.disabled=true end
215            core.sendevent('profile',ui_args.n,ui_args.s)
216        end
217    end
218
219    http.send('<br/><h3>Profiles</h3>\n<table class="table">\n<tr><th>Name</th><th>Status</th></tr>\n')
220
221    for i,j in pairs(profiles) do
222        local status
223           
224        if j.disabled==true then
225            status=string.format('<a href="/ui/profiles?n=%s&s=on">on</a> | <b>off</b>',util.urlencode(i))
226        else
227            status=string.format('<b>on</b> | <a href="/ui/profiles?n=%s&s=off">off</a>',util.urlencode(i))
228        end
229
230        http.send(string.format('<tr><td>%s</td><td>%s</td></tr>\n',j.desc,status))
231    end
232
233    http.send('</table>\n\n<a class="btn btn-primary" href="/ui/prapply">Save</a> <a class="btn btn-info" href="/ui/config">Back</a>')
234end
235
236function ui_prapply()
237    local f=io.open(cfg.config_path..'postinit/profiles.lua','w')
238    if f then
239        for i,j in pairs(profiles) do
240            f:write(string.format('profiles["%s"].disabled=%s\n',i,tostring(j.disabled or false)))
241        end
242        f:close()
243    end
244
245    http.send('<h3>OK</h3>')
246    http.send('<br/><a class="btn btn-info" href="/ui/profiles">Back</a>')
247end
248
249function ui_show()
250    if ui_args.fname then
251        local real_name=util.urldecode(ui_args.fname)
252        if string.find(real_name,'^[^/\\]+%.m3u$') then
253
254            local path=cfg.playlists_path
255            if ui_args.feed=='1' then path=cfg.feeds_path end
256
257            local pls=m3u.parse(path..real_name)
258
259            if pls then
260                http.send('<h3>'..pls.name..'</h3>')
261                http.send('<br/><table class="table">')
262                for i,j in ipairs(pls.elements) do
263                    http.send(string.format('<tr><td><a href="%s">%s</a></td></tr>',j.url,j.name))
264                end
265                http.send('</table>')
266            end
267        end
268    end
269
270    http.send('<br/><a class="btn btn-info" href="/ui/playlists">Back</a>')
271end
272
273function ui_remove()
274    if ui_args.fname then
275        local real_name=util.urldecode(ui_args.fname)
276        if string.find(real_name,'^[^-/\\]+%.m3u$') then
277
278            local path=cfg.playlists_path
279            if ui_args.feed=='1' then path=cfg.feeds_path end
280
281            if os.remove(path..real_name) then
282                core.sendevent('reload')
283                http.send('<h3>OK</h3>')
284            else
285                http.send('<h3>Fail</h3>')
286            end
287        end
288    end
289
290    http.send('<br/><a class="btn btn-info" href="/ui/playlists">Back</a>')
291end
292
293function ui_remove_feed()
294    if ui_args.id and feeds[tonumber(ui_args.id)] then
295        core.sendevent('remove_feed',ui_args.id)
296        http.send('<h3>OK</h3>')
297    else
298        http.send('<h3>Fail</h3>')
299    end
300
301    http.send('<br/><a class="btn btn-info" href="/ui/feeds">Back</a>')
302end
303
304function ui_add_feed()
305    if ui_args.plugin and ui_args.feed then
306        if not ui_args.name or string.len(ui_args.name)==0 then ui_args.name=ui_args.plugin..' '..string.gsub(ui_args.feed,'/',' ') end
307        core.sendevent('add_feed',ui_args.plugin,ui_args.feed,ui_args.name)
308        http.send('<h3>OK</h3>')
309    else
310        http.send('<h3>Fail</h3>')
311    end
312
313    http.send('<br/><a class="btn btn-info" href="/ui/feeds">Back</a>')
314end
315
316function save_feeds()
317
318    local f=io.open(cfg.config_path..'feeds.lua','w')
319    if f then
320        f:write('feeds=\n{\n')
321
322        for i,j in ipairs(feeds) do
323            f:write(string.format('   { "%s", "%s", "%s" },\n',j[1],j[2],j[3]))
324        end
325
326        f:write('}\n')
327
328        f:close()
329        return true
330    end
331
332    return false
333end
334
335
336function ui_save_feeds()
337    if save_feeds() then http.send('<h3>OK</h3>') else http.send('<h3>Fail</h3>') end
338
339    http.send('<br/><a class="btn btn-info" href="/ui/feeds">Back</a>')
340end
341
342function ui_reload()
343    core.sendevent('reload')
344    http.send('<h3>OK</h3>')
345    http.send('<br/><a class="btn btn-info" href="/ui/playlists">Back</a>')
346end
347
348function ui_reload_feeds()
349    update_feeds_async()
350    http.send('<h3>OK</h3>')
351    http.send('<br/><a class="btn btn-info" href="'.. (ui_args.return_url or '/ui') ..'">Back</a>')
352end
353
354function ui_config()
355    for plugin_name,plugin in pairs(plugins) do
356        if plugin.ui_vars then
357            for i,var in ipairs(plugin.ui_vars) do
358                http_vars[ var[1] ]=var[2]
359            end
360        end
361    end
362
363
364    http.sendtfile(cfg.ui_path..'ui_config.html',http_vars)
365
366    http.send('<script>\n')
367    http.send('function set_select_value(id,value)\n')
368    http.send('    { var obj=document.getElementById(id); for(i=0;i<obj.length;i++) { if(obj.options[i].value==value) { obj.options[i].selected=true; break; } } }\n')
369    http.send('function set_input_value(id,value)\n')
370    http.send('    { var obj=document.getElementById(id); if(obj) { obj.value=value } }\n')
371                       
372    for plugin_name,plugin in pairs(plugins) do
373        if plugin.ui_config_vars then
374            for i,var in ipairs(plugin.ui_config_vars) do
375                http.send(string.format('set_%s_value("%s","%s");\n',var[1],var[2],tostring(cfg[ var[2] ])))
376            end
377        end
378    end
379
380    http.send('</script>\n')
381
382end
383
384function ui_apply()
385
386    local args=util.parse_postdata(ui_data)
387
388    local f=io.open(cfg.config_path..'common.lua','w')
389    if f then
390
391        for plugin_name,plugin in pairs(plugins) do
392            if plugin.ui_config_vars then
393                for i,var in ipairs(plugin.ui_config_vars) do
394                    local v=args[ var[2] ]
395                    local t=var[3]
396
397                    if not v then if t=="int" then v=0 elseif t=="bool" then v=false else v="" end end
398
399                    if t=="int" or t=="bool" then
400                        f:write(string.format('cfg["%s"]=%s\n',var[2],tostring(v)))
401                    else
402                        f:write(string.format('cfg["%s"]="%s"\n',var[2],v))
403                    end
404                end
405            end
406        end
407
408        f:close()
409        core.sendevent('config')
410    end
411
412    http.send('<h3>OK</h3>')
413    http.send('<br/><a class="btn btn-info" href="/ui/config">Back</a>')
414end
415
416function ui_status()
417    http.send('<h3>Status</h3>')
418    http.send('<br/><table class="table">')
419
420    for i,j in pairs(childs) do
421        if j.status then
422            http.send(string.format('<tr><td>%s [<a href="/ui/kill?pid=%s">x</a>]</td></tr>',j.status,i))
423        end
424    end
425
426    http.send('</table>')
427
428    http.send('<br/><a class="btn btn-primary" href="/ui/status">Refresh</a> <a class="btn btn-info" href="/ui">Back</a>')
429end
430
431function ui_kill()
432    if ui_args.pid and childs[tonumber(ui_args.pid)] then
433        util.kill(ui_args.pid)
434        http.send('<h3>OK</h3>')
435    else
436        http.send('<h3>Fail</h3>')
437    end
438    http.send('<br/><a class="btn btn-info" href="/ui/status">Back</a>')
439end
440
441function ui_upload()
442    local tt=util.multipart_split(ui_data)
443    ui_data=nil
444
445    if tt and tt[1] then
446        local n,m=string.find(tt[1],'\r?\n\r?\n')
447
448        if n then
449            local fname=string.match(string.sub(tt[1],1,n-1),'filename=\"(.+)\"')
450
451            if fname and string.find(fname,'.+%.m3u$') then
452                local tfname=cfg.tmp_path..fname
453
454                local fd=io.open(tfname,'w+')
455                if fd then
456                    fd:write(string.sub(tt[1],m+1))
457                    fd:close()
458                end
459
460                local pls=m3u.parse(tfname)
461
462                if pls then
463                    if os.execute(string.format('mv "%s" "%s"',tfname,cfg.playlists_path..fname))~=0 then
464                        os.remove(tfname)
465                        http.send('<h3>Fail</h3>')
466                    else
467                        core.sendevent('reload')
468                        http.send('<h3>OK</h3>')
469                    end
470                else
471                    os.remove(tfname)
472                    http.send('<h3>Fail</h3>')
473                end
474            else
475                http.send('<h3>Fail</h3>')
476            end
477        end
478    end
479
480    http.send('<br/><a class="btn btn-info" href="/ui/playlists">Back</a>')
481end
482
483function ui_api_call(args)
484    http_send_headers(200,'txt')
485
486    if args.action=='feeds' then
487        for i,j in ipairs(feeds) do http.send(string.format('%s;%s\r\n',i,j[3])) end
488    elseif args.action=='reload' then
489        core.sendevent('reload')
490        http.send('OK\r\n')
491    elseif args.action=='add_feed' then
492        core.sendevent('add_feed',args.plugin or '',args.feed or '',args.name or '')
493        http.send('OK\r\n')
494    elseif args.action=='remove_feed' then
495        core.sendevent('remove_feed',args.id)
496        http.send('OK\r\n')
497    elseif args.action=='save_feeds' then
498        save_feeds()
499        update_feeds_async()
500        http.send('OK\r\n')
501    elseif args.action=='update_feeds' then
502        update_feeds_async()
503        http.send('OK\r\n')
504    elseif args.action=='status' then
505        for i,j in pairs(childs) do
506            if j.status then
507                http.send(string.format('%s;%s\r\n',i,j.status))
508            end
509        end
510    elseif args.action=='kill' then
511        if args.pid and childs[tonumber(args.pid)] then
512            util.kill(args.pid)
513            http.send('OK\r\n')
514        else
515            http.send('ERR\r\n')
516        end
517    elseif args.action=='playlists' then
518        for i,j in ipairs(playlist_data.elements[1].elements) do
519            http.send(string.format('%s;%s\r\n',i,j.name))
520        end
521    elseif args.action=='playlist' then
522        local pls=playlist_data.elements[1].elements[tonumber(args.id)]
523        if pls then
524            for i,j in ipairs(pls.elements) do
525                http.send(string.format('%s;%s;%s;%s\r\n',i,j.logo or '',j.name,playlist_get_url(j)))
526            end
527        end
528    else
529        http.send('ERR\r\n')
530    end
531end
532
533function ui_restart()
534    if core.restart(cfg.pid_file,"./xupnpd") then http.send('<h3>Attempt to restart...</h3>') else http.send('<h3>Unable to restart.</h3>') end
535
536    http.send('<br/><form method=get action="/ui"><input class="btn btn-primary" type=submit value=OK></form>')
537
538    http.send('<script>setTimeout("document.forms[0].submit()",3000)</script>')
539end
540
541ui_actions=
542{
543    ['main']            = { 'xupnpd', ui_main },
544    ['error']           = { 'xupnpd - error', ui_error },
545    ['downloads']       = { 'xupnpd - downloads', ui_downloads },
546    ['playlists']       = { 'xupnpd - playlists', ui_playlists },
547    ['feeds']           = { 'xupnpd - feeds', ui_feeds },
548    ['show']            = { 'xupnpd - show', ui_show },
549    ['remove']          = { 'xupnpd - remove', ui_remove },
550    ['remove_feed']     = { 'xupnpd - remove feed', ui_remove_feed },
551    ['reload']          = { 'xupnpd - reload', ui_reload },
552    ['reload_feeds']    = { 'xupnpd - reload feeds', ui_reload_feeds },
553    ['save_feeds']      = { 'xupnpd - save feeds', ui_save_feeds },
554    ['add_feed']        = { 'xupnpd - add feed', ui_add_feed },
555    ['config']          = { 'xupnpd - config', ui_config },
556    ['status']          = { 'xupnpd - status', ui_status },
557    ['kill']            = { 'xupnpd - kill', ui_kill },
558    ['upload']          = { 'xupnpd - upload', ui_upload },
559    ['apply']           = { 'xupnpd - apply', ui_apply },
560    ['plugins']         = { 'xupnpd - plugins', ui_plugins },
561    ['plapply']         = { 'xupnpd - plugins apply', ui_plapply },
562    ['profiles']        = { 'xupnpd - profiles', ui_profiles },
563    ['prapply']         = { 'xupnpd - profiles apply', ui_prapply },
564    ['fhelp']           = { 'xupnpd - feeds help', ui_fhelp },
565    ['mhelp']           = { 'xupnpd - mimes help', ui_mhelp },
566    ['ehelp']           = { 'xupnpd - extras help', ui_ehelp },
567    ['restart']         = { 'xupnpd - restart', ui_restart }
568}
569
570function ui_handler(args,data,ip,url)
571    for plugin_name,plugin in pairs(plugins) do
572        if plugin.ui_actons then
573            for act_name,act in pairs(plugin.ui_actons) do
574                ui_actions[act_name]=act
575            end
576        end
577    end
578
579    local action=string.match(url,'^/ui/(.+)$')
580
581    if action=='style' then
582        http_send_headers(200,'css')
583        http.sendfile(cfg.ui_path..'bootstrap.min.css')
584        return
585    elseif action=='api' then
586        ui_api_call(args)
587        return
588    end
589
590    if action and string.find(action,'.+%.m3u$') then
591        ui_download(action)
592        return
593    end
594
595    if not action then action='main' end
596
597    http_send_headers(200,'html')
598
599    local act=ui_actions[action]
600
601    if not act then act=ui_actions['error'] end
602
603    http_vars.title=act[1]
604    http_vars.content=act[2]
605
606    ui_args=args
607    ui_data=data
608
609    http.sendtfile(cfg.ui_path..'ui_template.html',http_vars)
610end
611
612
613plugins["ui"]={}
614plugins.ui.ui_config_vars=
615{
616    { "input",  "ssdp_interface" },
617    { "input",  "ssdp_notify_interval", "int" },
618    { "input",  "ssdp_max_age", "int" },
619    { "input",  "http_port", "int" },
620    { "input",  "mcast_interface" },
621    { "select", "proxy", "int" },
622    { "input",  "user_agent" },
623    { "input",  "http_timeout", "int" },
624    { "select", "dlna_notify", "bool"},
625    { "input",  "dlna_subscribe_ttl", "int"},
626    { "select", "group", "bool" },
627    { "select", "sort_files", "bool" },
628    { "input",  "name" },
629    { "input",  "uuid" },
630    { "input",  "default_mime_type" },
631    { "input",  "feeds_update_interval", "int" },
632    { "input",  "playlists_update_interval", "int" },
633    { "input",  "drive" }
634}
Note: See TracBrowser for help on using the repository browser.