From 1b8e6e01b7225ea7a43487a79bc881d8b58cc639 Mon Sep 17 00:00:00 2001 From: Ayushman Tripathi Date: Fri, 11 Aug 2023 23:58:47 +0530 Subject: [PATCH] luci-app-mjpg-streamer: migrate to js Signed-off-by: Ayushman Tripathi [fix commit message] Signed-off-by: Jo-Philipp Wich --- applications/luci-app-mjpg-streamer/Makefile | 2 +- .../view/mjpg-streamer/mjpg-streamer.js | 264 ++++++++++++++++++ .../luasrc/model/cbi/mjpg-streamer.lua | 224 --------------- .../luci/menu.d/luci-app-mjpg-streamer.json | 13 +- .../rpcd/acl.d/luci-app-mjpg-streamer.json | 10 +- 5 files changed, 280 insertions(+), 233 deletions(-) create mode 100644 applications/luci-app-mjpg-streamer/htdocs/luci-static/resources/view/mjpg-streamer/mjpg-streamer.js delete mode 100644 applications/luci-app-mjpg-streamer/luasrc/model/cbi/mjpg-streamer.lua diff --git a/applications/luci-app-mjpg-streamer/Makefile b/applications/luci-app-mjpg-streamer/Makefile index 876e99dd41..b4f1f14127 100644 --- a/applications/luci-app-mjpg-streamer/Makefile +++ b/applications/luci-app-mjpg-streamer/Makefile @@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk LUCI_TITLE:=MJPG-Streamer service configuration module -LUCI_DEPENDS:=+luci-compat +mjpg-streamer +LUCI_DEPENDS:= +mjpg-streamer include ../../luci.mk diff --git a/applications/luci-app-mjpg-streamer/htdocs/luci-static/resources/view/mjpg-streamer/mjpg-streamer.js b/applications/luci-app-mjpg-streamer/htdocs/luci-static/resources/view/mjpg-streamer/mjpg-streamer.js new file mode 100644 index 0000000000..6fb6f3c27b --- /dev/null +++ b/applications/luci-app-mjpg-streamer/htdocs/luci-static/resources/view/mjpg-streamer/mjpg-streamer.js @@ -0,0 +1,264 @@ +'use strict'; +'require view'; +'require form'; +'require uci'; +'require ui'; +'require poll'; + +/* Copyright 2014 Roger D < rogerdammit@gmail.com> +Licensed to the public under the Apache License 2.0. */ + +return view.extend({ + load: function () { + var self = this; + poll.add(function () { + self.render(); + }, 5); + + document + .querySelector('head') + .appendChild( + E('style', { type: 'text/css' }, [ + '.img-preview {display: inline-block !important;height: auto;width: 640px;padding: 4px;line-height: 1.428571429;background-color: #fff;border: 1px solid #ddd;border-radius: 4px;-webkit-transition: all .2s ease-in-out;transition: all .2s ease-in-out;margin-bottom: 5px;display: none;}', + ]), + ); + + return Promise.all([uci.load('mjpg-streamer')]); + }, + render: function () { + var m, s, o; + + m = new form.Map('mjpg-streamer', 'MJPG-streamer', _('mjpg streamer is a streaming application for Linux-UVC compatible webcams')); + + //General settings + + var section_gen = m.section(form.TypedSection, 'mjpg-streamer', _('General')); + section_gen.addremove = false; + section_gen.anonymous = true; + + var enabled = section_gen.option(form.Flag, 'enabled', _('Enabled'), _('Enable MJPG-streamer')); + + var input = section_gen.option(form.ListValue, 'input', _('Input plugin')); + input.depends('enabled', '1'); + input.value('uvc', 'UVC'); + // input: value("file", "File") + input.optional = false; + + var output = section_gen.option(form.ListValue, 'output', _('Output plugin')); + output.depends('enabled', '1'); + output.value('http', 'HTTP'); + output.value('file', 'File'); + output.optional = false; + + //Plugin settings + + s = m.section(form.TypedSection, 'mjpg-streamer', _('Plugin settings')); + s.addremove = false; + s.anonymous = true; + + s.tab('output_http', _('HTTP output')); + s.tab('output_file', _('File output')); + s.tab('input_uvc', _('UVC input')); + // s: tab("input_file", _("File input")) + + // Input UVC settings + + var this_tab = 'input_uvc'; + + var device = s.taboption(this_tab, form.Value, 'device', _('Device')); + device.default = '/dev/video0'; + //device.datatype = "device" + device.value('/dev/video0', '/dev/video0'); + device.value('/dev/video1', '/dev/video1'); + device.value('/dev/video2', '/dev/video2'); + device.optional = false; + + var resolution = s.taboption(this_tab, form.Value, 'resolution', _('Resolution')); + resolution.default = '640x480'; + resolution.value('320x240', '320x240'); + resolution.value('640x480', '640x480'); + resolution.value('800x600', '800x600'); + resolution.value('864x480', '864x480'); + resolution.value('960x544', '960x544'); + resolution.value('960x720', '960x720'); + resolution.value('1280x720', '1280x720'); + resolution.value('1280x960', '1280x960'); + resolution.value('1920x1080', '1920x1080'); + resolution.optional = true; + + var fps = s.taboption(this_tab, form.Value, 'fps', _('Frames per second')); + fps.datatype = 'and(uinteger, min(1))'; + fps.placeholder = '5'; + fps.optional = true; + + var yuv = s.taboption(this_tab, form.Flag, 'yuv', _('Enable YUYV format'), _('Automatic disabling of MJPEG mode')); + + var quality = s.taboption( + this_tab, + form.Value, + 'quality', + _('JPEG compression quality'), + _('Set the quality in percent. This setting activates YUYV format, disables MJPEG'), + ); + quality.datatype = 'range(0, 100)'; + + var minimum_size = s.taboption( + this_tab, + form.Value, + 'minimum_size', + _('Drop frames smaller than this limit'), + _('Set the minimum size if the webcam produces small-sized garbage frames. May happen under low light conditions'), + ); + minimum_size.datatype = 'uinteger'; + + var no_dynctrl = s.taboption(this_tab, form.Flag, 'no_dynctrl', _("Don't initialize dynctrls"), _('Do not initialize dynctrls of Linux-UVC driver')); + + var led = s.taboption(this_tab, form.ListValue, 'led', _('Led control')); + led.value('on', _('On')); + led.value('off', _('Off')); + led.value('blink', _('Blink')); + led.value('auto', _('Auto')); + led.optional = true; + + // Output HTTP settings + + this_tab = 'output_http'; + + var port = s.taboption(this_tab, form.Value, 'port', _('Port'), _('TCP port for this HTTP server')); + port.datatype = 'port'; + port.placeholder = '8080'; + + var enable_auth = s.taboption(this_tab, form.Flag, 'enable_auth', _('Authentication required'), _('Ask for username and password on connect')); + enable_auth.default = false; + + var username = s.taboption(this_tab, form.Value, 'username', _('Username')); + username.depends('enable_auth', '1'); + username.optional = false; + + var password = s.taboption(this_tab, form.Value, 'password', _('Password')); + password.depends('enable_auth', '1'); + password.password = true; + password.optional = false; + password.default = false; + + var www = s.taboption(this_tab, form.Value, 'www', _('WWW folder'), _('Folder that contains webpages')); + www.datatype = 'directory'; + www.default = '/www/webcam/'; + www.optional = false; + + + function init_stream() { + console.log('init_stream'); + start_stream(); + } + + function _start_stream() { + console.log('_start_stream'); + + var port = uci.get('mjpg-streamer', 'core', 'port'); + + if (uci.get('mjpg-streamer', 'core', 'enable_auth') == '1') { + var user = uci.get('mjpg-streamer', 'core', 'username'); + var pass = uci.get('mjpg-streamer', 'core', 'password'); + var login = user + ':' + pass + '@'; + } else { + var login = ''; + } + + var img = document.getElementById('video_preview') || video_preview; + img.src = 'http://' + login + location.hostname + ':' + port + '/?action=snapshot' + '&t=' + new Date().getTime(); + } + + function start_stream() { + console.log('start_stream'); + + setTimeout(function () { + _start_stream(); + }, 500); + } + + function on_error() { + console.log('on_error'); + + var img = video_preview; + img.style.display = 'none'; + + var stream_stat = document.getElementById('stream_status') || stream_status; + stream_stat.style.display = 'block'; + + start_stream(); + } + + function on_load() { + console.log('on_load'); + + var img = video_preview; + img.style.display = 'block'; + + var stream_stat = stream_status; + stream_stat.style.display = 'none'; + } + + //HTTP preview + var video_preview = E('img', { + 'id': 'video_preview', + 'class': 'img-preview', + 'error': on_error, + 'load': on_load, + }); + + var stream_status = E( + 'p', + { + 'id': 'stream_status', + 'style': 'text-align: center; color: orange; font-weight: bold;', + }, + _('Stream unavailable'), + ); + + + init_stream(); + + var preview = s.taboption(this_tab, form.DummyValue, '_dummy'); + preview.render = L.bind(function (view, section_id) { + return E([], [ + video_preview, + stream_status + ]); + }, preview, this); + preview.depends('output', 'http'); + + //Output file settings + + this_tab = 'output_file'; + + var folder = s.taboption(this_tab, form.Value, 'folder', _('Folder'), _('Set folder to save pictures')); + folder.placeholder = '/tmp/images'; + folder.datatype = 'directory'; + + //mjpeg=s.taboption(this_tab, Value, "mjpeg", _("Mjpeg output"), _("Check to save the stream to an mjpeg file")) + + var delay = s.taboption(this_tab, form.Value, 'delay', _('Interval between saving pictures'), _('Set the interval in millisecond')); + delay.placeholder = '5000'; + delay.datatype = 'uinteger'; + + var ringbuffer = s.taboption(this_tab, form.Value, 'ringbuffer', _('Ring buffer size'), _('Max. number of pictures to hold')); + ringbuffer.placeholder = '10'; + ringbuffer.datatype = 'uinteger'; + + var exceed = s.taboption(this_tab, form.Value, 'exceed', _('Exceed'), _('Allow ringbuffer to exceed limit by this amount')); + exceed.datatype = 'uinteger'; + + var command = s.taboption( + this_tab, + form.Value, + 'command', + _('Command to run'), + _('Execute command after saving picture. Mjpg-streamer parses the filename as first parameter to your script.'), + ); + + var link = s.taboption(this_tab, form.Value, 'link', _('Link newest picture to fixed file name'), _('Link the last picture in ringbuffer to fixed named file provided.')); + + return m.render(); + }, +}); diff --git a/applications/luci-app-mjpg-streamer/luasrc/model/cbi/mjpg-streamer.lua b/applications/luci-app-mjpg-streamer/luasrc/model/cbi/mjpg-streamer.lua deleted file mode 100644 index 86ea302c2f..0000000000 --- a/applications/luci-app-mjpg-streamer/luasrc/model/cbi/mjpg-streamer.lua +++ /dev/null @@ -1,224 +0,0 @@ --- Copyright 2014 Roger D --- Licensed to the public under the Apache License 2.0. - -m = Map("mjpg-streamer", "MJPG-streamer", translate("mjpg streamer is a streaming application for Linux-UVC compatible webcams")) - ---- General settings --- - -section_gen = m:section(TypedSection, "mjpg-streamer", translate("General")) - section_gen.addremove=false - section_gen.anonymous=true - -enabled = section_gen:option(Flag, "enabled", translate("Enabled"), translate("Enable MJPG-streamer")) - -input = section_gen:option(ListValue, "input", translate("Input plugin")) - input:depends("enabled", "1") - input:value("uvc", "UVC") - ---input:value("file", "File") - input.optional = false - -output = section_gen:option(ListValue, "output", translate("Output plugin")) - output:depends("enabled", "1") - output:value("http", "HTTP") - output:value("file", "File") - output.optional = false - - ---- Plugin settings --- - -s = m:section(TypedSection, "mjpg-streamer", translate("Plugin settings")) - s.addremove=false - s.anonymous=true - - s:tab("output_http", translate("HTTP output")) - s:tab("output_file", translate("File output")) - s:tab("input_uvc", translate("UVC input")) - ---s:tab("input_file", translate("File input")) - - ---- Input UVC settings --- - -this_tab = "input_uvc" - -device = s:taboption(this_tab, Value, "device", translate("Device")) - device.default="/dev/video0" - --device.datatype = "device" - device:value("/dev/video0", "/dev/video0") - device:value("/dev/video1", "/dev/video1") - device:value("/dev/video2", "/dev/video2") - device.optional = false - -resolution = s:taboption(this_tab, Value, "resolution", translate("Resolution")) - resolution.default = "640x480" - resolution:value("320x240", "320x240") - resolution:value("640x480", "640x480") - resolution:value("800x600", "800x600") - resolution:value("864x480", "864x480") - resolution:value("960x544", "960x544") - resolution:value("960x720", "960x720") - resolution:value("1280x720", "1280x720") - resolution:value("1280x960", "1280x960") - resolution:value("1920x1080", "1920x1080") - resolution.optional = true - -fps = s:taboption(this_tab, Value, "fps", translate("Frames per second")) - fps.datatype = "and(uinteger, min(1))" - fps.placeholder = "5" - fps.optional = true - -yuv = s:taboption(this_tab, Flag, "yuv", translate("Enable YUYV format"), translate("Automatic disabling of MJPEG mode")) - -quality = s:taboption(this_tab, Value, "quality", translate("JPEG compression quality"), translate("Set the quality in percent. This setting activates YUYV format, disables MJPEG")) - quality.datatype = "range(0, 100)" - -minimum_size = s:taboption(this_tab, Value, "minimum_size", translate("Drop frames smaller than this limit"),translate("Set the minimum size if the webcam produces small-sized garbage frames. May happen under low light conditions")) - minimum_size.datatype = "uinteger" - -no_dynctrl = s:taboption(this_tab, Flag, "no_dynctrl", translate("Don't initialize dynctrls"), translate("Do not initialize dynctrls of Linux-UVC driver")) - -led = s:taboption(this_tab, ListValue, "led", translate("Led control")) - led:value("on", translate("On")) - led:value("off", translate("Off")) - led:value("blink", translate("Blink")) - led:value("auto", translate("Auto")) - led.optional = true - - ---- Output HTTP settings --- - -this_tab = "output_http" - -port=s:taboption(this_tab, Value, "port", translate("Port"), translate("TCP port for this HTTP server")) - port.datatype = "port" - port.placeholder = "8080" - -enable_auth = s:taboption(this_tab, Flag, "enable_auth", translate("Authentication required"), translate("Ask for username and password on connect")) - enable_auth.default = false - -username = s:taboption(this_tab, Value, "username", translate("Username")) - username:depends("enable_auth", "1") - username.optional = false - -password = s:taboption(this_tab, Value, "password", translate("Password")) - password:depends("enable_auth", "1") - password.password = true - password.optional = false - password.default = false - -www = s:taboption(this_tab, Value, "www", translate("WWW folder"), translate("Folder that contains webpages")) - www.datatype = "directory" - www.default = "/www/webcam/" - www.optional = false - - ---- HTTP preview --- - -html = [[ - - -
- -

Stream unavailable

-
- - -]] - -preview = s:taboption(this_tab, DummyValue, "_dummy", html) - preview:depends("output", "http") - ---- Output file settings --- - -this_tab = "output_file" - -folder=s:taboption(this_tab, Value, "folder", translate("Folder"), translate("Set folder to save pictures")) - folder.placeholder="/tmp/images" - folder.datatype = "directory" - ---mjpeg=s:taboption(this_tab, Value, "mjpeg", translate("Mjpeg output"), translate("Check to save the stream to an mjpeg file")) - -delay=s:taboption(this_tab, Value, "delay", translate("Interval between saving pictures"), translate("Set the interval in millisecond")) - delay.placeholder="5000" - delay.datatype = "uinteger" - -ringbuffer=s:taboption(this_tab, Value, "ringbuffer", translate("Ring buffer size"), translate("Max. number of pictures to hold")) - ringbuffer.placeholder="10" - ringbuffer.datatype = "uinteger" - -exceed=s:taboption(this_tab, Value, "exceed", translate("Exceed"), translate("Allow ringbuffer to exceed limit by this amount")) - exceed.datatype = "uinteger" - -command=s:taboption(this_tab, Value, "command", translate("Command to run"), translate("Execute command after saving picture. Mjpg-streamer parses the filename as first parameter to your script.")) - -link=s:taboption(this_tab, Value, "link", translate("Link newest picture to fixed file name"), translate("Link the last picture in ringbuffer to fixed named file provided.")) - -return m diff --git a/applications/luci-app-mjpg-streamer/root/usr/share/luci/menu.d/luci-app-mjpg-streamer.json b/applications/luci-app-mjpg-streamer/root/usr/share/luci/menu.d/luci-app-mjpg-streamer.json index 6f79358e49..9a5a8bd9eb 100644 --- a/applications/luci-app-mjpg-streamer/root/usr/share/luci/menu.d/luci-app-mjpg-streamer.json +++ b/applications/luci-app-mjpg-streamer/root/usr/share/luci/menu.d/luci-app-mjpg-streamer.json @@ -2,13 +2,16 @@ "admin/services/mjpg-streamer": { "title": "MJPG-streamer", "action": { - "type": "cbi", - "path": "mjpg-streamer", - "post": { "cbi.submit": true } + "type": "view", + "path": "mjpg-streamer/mjpg-streamer" }, "depends": { - "acl": [ "luci-app-mjpg-streamer" ], - "uci": { "mjpg-streamer": true } + "acl": [ + "luci-app-mjpg-streamer" + ], + "uci": { + "mjpg-streamer": true + } } } } diff --git a/applications/luci-app-mjpg-streamer/root/usr/share/rpcd/acl.d/luci-app-mjpg-streamer.json b/applications/luci-app-mjpg-streamer/root/usr/share/rpcd/acl.d/luci-app-mjpg-streamer.json index ab68a6e1c2..4a2f1df55a 100644 --- a/applications/luci-app-mjpg-streamer/root/usr/share/rpcd/acl.d/luci-app-mjpg-streamer.json +++ b/applications/luci-app-mjpg-streamer/root/usr/share/rpcd/acl.d/luci-app-mjpg-streamer.json @@ -2,10 +2,14 @@ "luci-app-mjpg-streamer": { "description": "Grant UCI access for luci-app-mjpg-streamer", "read": { - "uci": [ "mjpg-streamer" ] + "uci": [ + "mjpg-streamer" + ] }, "write": { - "uci": [ "mjpg-streamer" ] + "uci": [ + "mjpg-streamer" + ] } } -} +} \ No newline at end of file -- 2.30.2