update 2026-09-24 16:56:26

This commit is contained in:
action committed 2026-09-24 16:56:26 +08:00
1 parent b31a5c3878
commit e4cee331e9
6885 files changed
+3240523

No files matched your search

+36
View File
@@ -0,0 +1,36 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-nvr
PKG_VERSION:=20230818
include $(INCLUDE_DIR)/package.mk
define Package/luci-app-nvr
SECTION:=luci
CATEGORY:=LuCI
SUBMENU:=3. Applications
TITLE:=LuCI Support for Network Video Recorder
PKGARCH:=all
DEPENDS:=+ffmpeg
endef
define Build/Prepare
$(foreach po,$(wildcard ${CURDIR}/luasrc/i18n/*.po), \
po2lmo $(po) $(PKG_BUILD_DIR)/$(patsubst %.po,%.lmo,$(notdir $(po)));)
endef
define Build/Compile
endef
define Package/luci-app-nvr/install
$(INSTALL_DIR) $(1)/usr/lib/lua/luci
cp -pR ./luasrc/* $(1)/usr/lib/lua/luci
$(INSTALL_DIR) $(1)/usr/nvr
$(INSTALL_BIN) ./files/nvr/* $(1)/usr/nvr
$(INSTALL_DIR) $(1)/etc/config
$(INSTALL_CONF) ./files/nvr.conf $(1)/etc/config/nvr
$(INSTALL_DIR) $(1)/etc/init.d
$(INSTALL_BIN) ./files/nvr.init $(1)/etc/init.d/nvr
endef
$(eval $(call BuildPackage,luci-app-nvr))
+15
View File
@@ -0,0 +1,15 @@
config nvr 'config'
option loop_write '1'
option fulldisk '1'
option storage_directory '/mnt/sda1/camera'
option disk_name 'sda1'
option disk_usage '85%'
option rec_time '600'
option storage_size '1000'
option enable_audio '1'
option enabled '0'
option nvr_sourcelist 'hikvision'
option hik_list 'none'
option total_days '30'
+36
View File
@@ -0,0 +1,36 @@
#!/bin/sh /etc/rc.common
START=99
USE_PROCD=1
PROG="/usr/nvr/nvrrecord"
init_conf() {
config_load "nvr"
config_get "enabled" "config" "enabled"
}
start_service() {
init_conf
[ "${enabled}" == "1" ] || exit 0
procd_open_instance nvr
procd_set_param command $PROG
procd_set_param respawn
procd_close_instance
}
service_triggers() {
procd_add_reload_trigger "nvr"
}
reload_service() {
stop
start
}
stop_service() {
init_conf
service_stop $PROG
}
+170
View File
@@ -0,0 +1,170 @@
#!/bin/sh
_source="$(uci get nvr.config.nvr_sourcelist)"
_directory="$(uci get nvr.config.storage_directory | sed 's/\/$//')"
_disk_name="$(uci get nvr.config.disk_name)"
_rec_time="$(uci get nvr.config.rec_time)"
_size="$(expr $(uci get nvr.config.storage_size) \* 1024)"
_total_days="$(uci get nvr.config.total_days)"
_loop_write="$(uci get nvr.config.loop_write)"
_fulldisk="$(uci get nvr.config.fulldisk)"
_disk_usage="$(echo $(uci get nvr.config.disk_usage) | cut -d '%' -f1)"
_enable_audio="$(uci get nvr.config.enable_audio)"
_all_clients=""
_disk=""
function detect_disk() {
if [ "$(df -hT | grep "${_disk_name}")" = "" ];then
_disk="X"
else
_disk=""
fi
}
function test_totaldays() {
_dir_nums="$(ls -l $_directory | grep "^d" | wc -l)"
if [ "$_total_days" -lt "$_dir_nums" ];then
_first_dir="$(ls -l $_directory | grep "^d" | head -n1 | awk '{print$9}')"
rm -rf $_directory/$_first_dir
fi
}
function count_filesize() {
_client_dir="$(ls -l $_directory/$_day_target | grep "^d" | awk '{print$9}')"
for c in $_client_dir;do
_single_filesize="$(ls -l $_directory/$_day_target/$c | head -n1 | awk '{print$5}')"
if [ "$_single_filesize" = "" ];then
_single_filesize=0
else
_single_filesize="$(expr $_single_filesize / 1024)"
fi
_nums_clientdir_file="$(ls -l $_directory/$_day_target/$c | grep "^-" | wc -l)"
_total_filesize="$(expr $_single_filesize \* $_nums_clientdir_file + $_total_filesize)"
done
}
function search_daydir_head() {
_day_target="$(ls -l $_directory | grep "^d" | head -n1 | awk '{print$9}')"
count_filesize
}
function search_daydir_tail() {
_day_target="$(ls -l $_directory | grep "^d" | tail -n1 | awk '{print$9}')"
count_filesize
}
function search_daydir_midone() {
_day_target="$(ls -l $_directory | grep "^d" | head -n2 | tail -n1 | awk '{print$9}')"
count_filesize
}
function test_dir_space() {
if [ "$(df -h | grep $_disk_name | awk '{print$5}' | head -n 1 | cut -d '%' -f1)" -ge "$_disk_usage" ];then
_dir_space="$(expr $_size + 9999)"
else
_daydir_num="$(ls -l $_directory | grep "^d" | wc -l)"
if [ "$_daydir_num" -ge 3 ];then
_day_target="$(ls -l $_directory | grep "^d" | head -n2 | tail -n1 | awk '{print$9}')"
_mid_daydir_num="$(expr $_daydir_num - 2)"
search_daydir_midone
_total_filesize="$(expr $_total_filesize \* $_mid_daydir_num)"
search_daydir_head
search_daydir_tail
elif [ "$_daydir_num" -eq 2 ];then
search_daydir_head
search_daydir_tail
else
search_daydir_head
fi
_dir_space=$_total_filesize
fi
}
function test_loopwrite() {
if [ "$_loop_write" -eq 1 ];then
_total_filesize=0
if [ "$_fulldisk" -eq 1 ];then
if [ "$(df -h | grep $_disk_name | awk '{print$5}' | head -n 1 | cut -d '%' -f1)" -ge "$_disk_usage" ];then
_dir_space="$(expr $_size + 9999)"
else
_dir_space=1
fi
else
test_dir_space
fi
while [ "$_dir_space" -ge "$_size" ];do
_first_dir="$(ls -l $_directory | grep "^d" | head -n1 | awk '{print$9}')"
_action_dir="$(ls -l $_directory/$_first_dir | grep "^d" | awk '{print$9}')"
for i in $_action_dir;do
rm "$_directory/$_first_dir/$i/$(ls -l $_directory/$_first_dir/$i | grep "^-" | head -n1 | awk '{print$9}')"
if [ "$(ls -l $_directory/$_first_dir/$i | grep "^-")" = "" ];then
rm -rf $_directory/$_first_dir/$i
fi
if [ "$(ls -l $_directory | grep "^d" | wc -l )" -gt 1 ];then
if [ "$(du -s $_directory/$_first_dir | awk '{print$1}')" -le "$(expr $_totalnum \* 4 + 4)" ];then
rm -rf $_directory/$_first_dir
break
fi
fi
done
_total_filesize=0
if [ "$_fulldisk" -eq 1 ];then
if [ "$(df -h | grep $_disk_name | awk '{print$5}' | head -n 1 | cut -d '%' -f1)" -ge "$_disk_usage" ];then
_dir_space="$(expr $_size + 9999)"
else
_dir_space=1
fi
else
test_dir_space
fi
done
else
echo "do not test looping write!"
fi
}
case $_source in
hikvision)
_hik_user="$(uci get nvr.config.hik_user)"
_hik_pass="$(uci get nvr.config.hik_pass)"
if [ "$(uci get nvr.config.hik_list)" = "one-by-one" ];then
_all_clients=$(uci get nvr.config.hikpush)
_totalnum="$(echo $_all_clients | sed 's/ /\n/g' | wc -l)"
else
_ipstart="$(uci get nvr.config.hik_batch_start)"
_ipend="$(uci get nvr.config.hik_batch_end)"
_startnum="$(echo $_ipstart | cut -d '.' -f4)"
_totalnum=$(expr $(echo $_ipend | cut -d '.' -f4) - $(echo $_ipstart | cut -d '.' -f4) + 1)
for i in $(seq 1 $_totalnum);do
_all_clients="$_all_clients $(echo $_ipstart | sed 's/[0-9]*$//')$(expr $_startnum + $i - 1)"
done
fi
;;
tplink)
_tplink_user="$(uci get nvr.config.tplink_user)"
_tplink_pass="$(uci get nvr.config.tplink_pass)"
if [ "$(uci get nvr.config.tplink_list)" = "one-by-one" ];then
_all_clients=$(uci get nvr.config.tplinkpush)
_totalnum="$(echo $_all_clients | sed 's/ /\n/g' | wc -l)"
else
_ipstart="$(uci get nvr.config.tplink_batch_start)"
_ipend="$(uci get nvr.config.tplink_batch_end)"
_startnum="$(echo $_ipstart | cut -d '.' -f4)"
_totalnum=$(expr $(echo $_ipend | cut -d '.' -f4) - $(echo $_ipstart | cut -d '.' -f4) + 1)
for i in $(seq 1 $_totalnum);do
_all_clients="$_all_clients $(echo $_ipstart | sed 's/[0-9]*$//')$(expr $_startnum + $i - 1)"
done
fi
;;
rtmp-url)
_all_clients=$(uci get nvr.config.rtmppush)
_totalnum="$(echo $_all_clients | sed 's/ /\n/g' | wc -l)"
;;
multiple-types)
_all_clients=$(uci get nvr.config.multipush)
_totalnum="$(echo $_all_clients | sed 's/ /\n/g' | wc -l)"
;;
none)
exit 0
;;
esac
+16
View File
@@ -0,0 +1,16 @@
#!/bin/sh
. /usr/nvr/nvrcommon
_app_prefix="$(uci get nvr.config.rtmp_server_app)"
for a in $_all_clients;do
if [ "$_source" = "hikvision" ];then
ffmpeg -i "rtsp://${_hik_user}:${_hik_pass}@${a}:554/h264/ch1/main/av_stream" -c copy -f flv "${_app_prefix}/${a}" &
elif [ "$_source" = "tplink" ];then
ffmpeg -i "rtsp://${_tplink_user}:${_tplink_pass}@${a}:554/stream1" -c copy -f flv "${_app_prefix}/${a}" &
elif [ "$_source" = "rtmp-url" ];then
ffmpeg -i $a -c copy -f flv "${_app_prefix}/${a}" &
elif [ "$_source" = "multiple-types" ];then
ffmpeg -i $a -c copy -f flv "${_app_prefix}/${a}" &
fi
done
+42
View File
@@ -0,0 +1,42 @@
#!/bin/sh
. /usr/nvr/nvrcommon
function main() {
test_totaldays
test_loopwrite
for a in $_all_clients;do
theday="$(date +%Y-%m-%d)"
thedir=$(echo $a | sed 's/\///g')
if [ ! -d "$_directory/$theday/$thedir" ];then
mkdir -p "$_directory/$theday/$thedir"
fi
if [ "$_source" = "hikvision" ];then
_input="rtsp://${_hik_user}:${_hik_pass}@${a}:554/h264/ch1/main/av_stream"
elif [ "$_source" = "tplink" ];then
_input="rtsp://${_tplink_user}:${_tplink_pass}@${a}:554/stream1"
elif [ "$_source" = "rtmp-url" ];then
_input="$a"
elif [ "$_source" = "multiple-types" ];then
_input="$a"
fi
if [ ! -f "${_directory}/${theday}/${thedir}/${thename}.*" ];then
kill -9 "$(ps -w | grep ffmpeg | grep "${_input}" | grep -v grep | grep -v "\-f flv")"
fi
thename="$(date +%Y-%m-%d-%H:%M:%S)"
if [ "$_enable_audio" -eq 1 ];then
ffmpeg -i "${_input}" -c copy -t ${_rec_time} "${_directory}/${theday}/${thedir}/${thename}.mkv" &
else
ffmpeg -i "${_input}" -vcodec copy -an -t ${_rec_time} "${_directory}/${theday}/${thedir}/${thename}.mp4" &
fi
done
sleep $(expr ${_rec_time} - 6)
}
while true;do
detect_disk
if [ "${_disk}" = "" ];then
main
else
sleep 10
fi
done
+10
View File
@@ -0,0 +1,10 @@
#!/bin/sh
if [ "$(uci get nvr.config.enabled)" -eq 1 ];then
/etc/init.d/nvr enable 2>&1 &
/etc/init.d/nvr start 2>&1 &
else
/usr/nvr/nvrrecord 2>&1 &
fi
+9
View File
@@ -0,0 +1,9 @@
#!/bin/sh
. /usr/nvr/nvrcommon
/etc/init.d/nvr stop
kill -9 $(busybox ps | grep nvrrecord | grep -v grep | awk '{print$1}') 2>&1 &
for i in $_all_clients;do
kill -9 $(ps -w | grep $i | grep -v grep | grep -v "\-f flv" | awk '{print$1}') 2>&1 &
done
+24
View File
@@ -0,0 +1,24 @@
module("luci.controller.nvr",package.seeall)
function index()
if not nixio.fs.access("/etc/config/nvr") then
return
end
local page = entry({"admin", "services", "nvr"}, cbi("nvr"), _("NVR"),20)
page.dependent = true
entry({"admin", "services", "nvr", "status"}, call("nvr_status")).leaf = true
end
function nvr_status()
local nvrrecord = luci.sys.exec("ps -w | grep nvrrecord | grep -v grep | awk '{print$1}' 2>/dev/null ")
local pushpid = luci.sys.exec("ps -w | grep 'f flv rtmp' | grep -v grep | awk '{print$1}' 2>/dev/null ")
local e = {
nvrrecord = nvrrecord,
pushpid = pushpid
}
luci.http.prepare_content("application/json")
luci.http.write_json(e)
end
Binary file not shown.
+209
View File
@@ -0,0 +1,209 @@
m = Map("nvr", translate("The luci-app for instead of network video recorder hardware."))
m:section(SimpleSection).template = "nvr_status"
s = m:section(TypedSection, "nvr", "", translate("Network Video Recorder"))
s.anonymous = true
s.addremove = false
s:tab("nvr", translate("Basic setting"))
enabled = s:taboption("nvr", Flag, "enabled", translate("Record daemon"))
enabled.rmempty = false
nvr_sourcelist = s:taboption("nvr", ListValue, "nvr_sourcelist", translate("camera source"))
nvr_sourcelist.placeholder = "none"
nvr_sourcelist:value("none")
nvr_sourcelist:value("hikvision",translate("hikvision"))
nvr_sourcelist:value("tplink",translate("tplink"))
nvr_sourcelist:value("rtmp-url",translate("rtmp url"))
nvr_sourcelist:value("multiple-types",translate("multiple types"))
nvr_sourcelist.default = "none"
nvr_sourcelist.rmempty = true
hik_list = s:taboption("nvr", ListValue, "hik_list", translate("camera list"))
hik_list:depends( "nvr_sourcelist", "hikvision" )
hik_list.placeholder = "none"
hik_list:value("none")
hik_list:value("one-by-one",translate("one by one"))
hik_list:value("batch-add",translate("batch add"))
hik_list.default = "none"
hik_list.rmempty = true
hik_addonebyone = s:taboption("nvr", DynamicList, "hikpush")
hik_addonebyone:depends( "hik_list", "one-by-one" )
hik_addonebyone.title = translate("camera ip address")
hik_addonebyone.datatype = "ipaddr"
hik_addonebyone.placeholder = "192.168.1.64"
hik_addonebyone.description = translate("click + to continue")
hik_batch_start=s:taboption("nvr", Value, "hik_batch_start", translate("start of the ip address"))
hik_batch_start:depends( "hik_list", "batch-add" )
hik_batch_start.datatype = "ipaddr"
hik_batch_start.placeholder = "192.168.1.64"
hik_batch_start.rmempty = true
hik_batch_end=s:taboption("nvr", Value, "hik_batch_end", translate("end of the ip address"))
hik_batch_end:depends( "hik_list", "batch-add" )
hik_batch_end.datatype = "ipaddr"
hik_batch_end.placeholder = "192.168.1.200"
hik_batch_end.rmempty = true
hik_user=s:taboption("nvr", Value, "hik_user", translate("camera username"))
hik_user:depends( "nvr_sourcelist", "hikvision" )
hik_user.datatype = "string"
hik_user.placeholder = "username"
hik_user.rmempty = true
hik_pass=s:taboption("nvr", Value, "hik_pass", translate("camera password"))
hik_pass:depends( "nvr_sourcelist", "hikvision" )
hik_pass.datatype = "string"
hik_pass.placeholder = "password"
hik_pass.rmempty = true
tplink_list = s:taboption("nvr", ListValue, "tplink_list", translate("camera list"))
tplink_list:depends( "nvr_sourcelist", "tplink" )
tplink_list.placeholder = "none"
tplink_list:value("none")
tplink_list:value("one-by-one",translate("one by one"))
tplink_list:value("batch-add",translate("batch add"))
tplink_list.default = "none"
tplink_list.rmempty = true
tplink_addonebyone = s:taboption("nvr", DynamicList, "tplinkpush")
tplink_addonebyone:depends( "tplink_list", "one-by-one" )
tplink_addonebyone.title = translate("camera ip address")
tplink_addonebyone.datatype = "ipaddr"
tplink_addonebyone.placeholder = "192.168.1.60"
tplink_addonebyone.description = translate("click + to continue")
tplink_batch_start=s:taboption("nvr", Value, "tplink_batch_start", translate("start of the ip address"))
tplink_batch_start:depends( "tplink_list", "batch-add" )
tplink_batch_start.datatype = "ipaddr"
tplink_batch_start.placeholder = "192.168.1.60"
tplink_batch_start.rmempty = true
tplink_batch_end=s:taboption("nvr", Value, "tplink_batch_end", translate("end of the ip address"))
tplink_batch_end:depends( "tplink_list", "batch-add" )
tplink_batch_end.datatype = "ipaddr"
tplink_batch_end.placeholder = "192.168.1.200"
tplink_batch_end.rmempty = true
tplink_user=s:taboption("nvr", Value, "tplink_user", translate("camera username"))
tplink_user:depends( "nvr_sourcelist", "tplink" )
tplink_user.datatype = "string"
tplink_user.placeholder = "username"
tplink_user.rmempty = true
tplink_pass=s:taboption("nvr", Value, "tplink_pass", translate("camera password"))
tplink_pass:depends( "nvr_sourcelist", "tplink" )
tplink_pass.datatype = "string"
tplink_pass.placeholder = "password"
tplink_pass.rmempty = true
rtmpurl_add = s:taboption("nvr", DynamicList, "rtmppush")
rtmpurl_add:depends( "nvr_sourcelist", "rtmp-url" )
rtmpurl_add.title = translate("rtmp url address")
rtmpurl_add.datatype = "string"
rtmpurl_add.placeholder = "rtmp://ip:1935/live/camera001"
rtmpurl_add.description = translate("adding one by one")
multi_info1 = s:taboption("nvr", DummyValue, "multi_info1", translate("information"))
multi_info1:depends( "nvr_sourcelist", "multiple-types" )
multi_info1.description = translate("hikvision: rtsp://username:password@ip:554/h264/ch1/main/av_stream")
multi_info2 = s:taboption("nvr", DummyValue, "multi_info2", translate("information"))
multi_info2:depends( "nvr_sourcelist", "multiple-types" )
multi_info2.description = translate("tplink: rtsp://username:password@ip:554/stream1")
multi_add = s:taboption("nvr", DynamicList, "multipush")
multi_add:depends( "nvr_sourcelist", "multiple-types" )
multi_add.title = translate("rtsp/rtmp/http url address")
multi_add.datatype = "string"
multi_add.placeholder = "rtmp://ip:1935/live/camera001"
multi_add.description = translate("adding one by one")
storage_directory=s:taboption("nvr", Value, "storage_directory", translate("data storage directory"))
storage_directory.rmempty = false
storage_directory.datatype = "string"
storage_directory.placeholder = "/mnt/sda1/camera"
storage_directory.default = "/mnt/sda1/camera"
disk_name=s:taboption("nvr", Value, "disk_name", translate("storage disk name"))
disk_name.rmempty = false
disk_name.datatype = "string"
disk_name.placeholder = "sda1"
disk_usage=s:taboption("nvr", Value, "disk_usage", translate("set maximum disk space usage"))
disk_usage.rmempty = false
disk_usage.datatype = "string"
disk_usage.placeholder = "85%"
rec_time=s:taboption("nvr", Value, "rec_time", translate("single file duration"))
rec_time.rmempty = false
rec_time.datatype = "uinteger"
rec_time.placeholder = "300"
rec_time.default = "300"
rec_time.description = translate("in seconds")
storage_size=s:taboption("nvr", Value, "storage_size", translate("allocate total storage space"))
storage_size.rmempty = false
storage_size.datatype = "uinteger"
storage_size.placeholder = "1000"
storage_size.default = "1000"
storage_size.description = translate("in megabytes")
total_days=s:taboption("nvr", Value, "total_days", translate("total days of videos"))
total_days.rmempty = false
total_days.datatype = "uinteger"
total_days.placeholder = "30"
total_days.default = "30"
loop_write=s:taboption("nvr", Flag, "loop_write", translate("looping writting to disk"))
loop_write.rmempty = false
fulldisk=s:taboption("nvr", Flag, "fulldisk", translate("only detect disk space"))
fulldisk.rmempty = false
enable_audio=s:taboption("nvr", Flag, "enable_audio", translate("enable audio"))
enable_audio.rmempty = false
do_push=s:taboption("nvr", Flag, "do_push", translate("whether to push stream"))
rtmp_server_app=s:taboption("nvr", Value, "rtmp_server_app", translate("rtmp server application url"))
rtmp_server_app:depends( "do_push", "1" )
rtmp_server_app.rmempty = true
rtmp_server_app.datatype = "string"
rtmp_server_app.placeholder = "rtmp://ip:1935/application_name"
s:tab("action", translate("Action"))
recordaction=s:taboption("action", Button, "recoredaction", translate("One-click Record"))
recordaction.rmempty = true
recordaction.inputstyle = "apply"
function recordaction.write(self, section)
luci.util.exec("/usr/nvr/nvrstart 2>&1 &")
end
recordstop=s:taboption("action", Button, "recordstop", translate("One-click STOP Record"))
recordstop.rmempty = true
recordstop.inputstyle = "apply"
function recordstop.write(self, section)
luci.util.exec("/usr/nvr/nvrstop 2>&1 & ")
end
pushaction=s:taboption("action", Button, "pushaction", translate("One-click Push stream"))
pushaction:depends( "do_push", "1" )
pushaction.rmempty = true
pushaction.inputstyle = "apply"
function pushaction.write(self, section)
luci.util.exec("/usr/nvr/nvrpush >/dev/null 2>&1 &")
end
pushstop = s:taboption("action", Button, "pushstop", translate("One-click STOP Push"))
pushstop:depends( "do_push", "1" )
pushstop.rmempty = true
pushstop.inputstyle = "apply"
function pushstop.write(self, section)
luci.util.exec("kill -9 $(ps -w | grep 'f flv rtmp' | grep -v grep | awk '{print$1}' 2>&1 &)")
end
return m
+26
View File
@@ -0,0 +1,26 @@
<script type="text/javascript">//<![CDATA[
XHR.poll(5, '<%=luci.dispatcher.build_url("admin", "services", "nvr", "status")%>', null,
function(x, data) {
var tb = document.getElementById('nvr_status');
if ( data && tb) {
if ( data.nvrrecord && data.pushpid ) {
tb.innerHTML = '<font color=green><%:正在录像!%><%: PID: %>' + data.nvrrecord + '</br>' +'<%:正在推流!%><%: PID : %>' + data.pushpid + '</font>';
} else if ( data.nvrrecord ) {
tb.innerHTML = '<font color=green><%:正在录像!%><%: PID: %>' + data.nvrrecord + '</font></br>' + '<font color=red><%:未推送!!%></font>';
} else if ( data.pushpid ) {
tb.innerHTML = '<font color=red><%:未录像!%></font></br>' + '<font color=green><%:正在推流!%><%: PID: %>' + data.pushpid + '</font>';
} else {
tb.innerHTML = '<font color=red><%:未运行!%></font>';
}
}
}
);
//]]>
</script>
<style>.mar-10 {margin-left: 50px; margin-right: 10px;}</style>
<fieldset class="cbi-section">
<p id="nvr_status">
<em><%:Collecting data...%></em>
</p>
</fieldset>
+126
View File
@@ -0,0 +1,126 @@
msgid ""
msgstr "Content-Type: text/plain; charset=UTF-8\n"
msgid "NVR"
msgstr "网络视频录像机"
msgid "The luci-app for instead of network video recorder hardware."
msgstr "代替网络录像机硬件的luci应用程序"
msgid "Network Video Recorder"
msgstr "网络视频录像机"
msgid "Basic setting"
msgstr "基本设置"
msgid "Record daemon"
msgstr "开启录像守护进程"
msgid "camera source"
msgstr "摄像机源"
msgid "hikvision"
msgstr "海康威视"
msgid "rtmp url"
msgstr "rtmp视频源url"
msgid "multiple types"
msgstr "多种类型视频源"
msgid "camera list"
msgstr "摄像机列表"
msgid "one by one"
msgstr "逐个添加"
msgid "batch add"
msgstr "批量添加"
msgid "camera ip address"
msgstr "摄像机ip地址"
msgid "click + to continue"
msgstr "单击+继续"
msgid "start of the ip address"
msgstr "起始的ip地址"
msgid "end of the ip address"
msgstr "结尾的ip地址"
msgid "camera username"
msgstr "摄像机用户名"
msgid "camera password"
msgstr "摄像机密码"
msgid "rtmp url address"
msgstr "rtmp视频源url地址"
msgid "hikvision: rtsp://username:password@ip:554/h264/ch1/main/av_stream"
msgstr "海康威视监控url视频源格式: rtsp://username:password@ip:554/h264/ch1/main/av_stream"
msgid "tplink: rtsp://username:password@ip:554/stream1"
msgstr "tplink监控url视频源格式: rtsp://username:password@ip:554/stream1"
msgid "rtsp/rtmp/http url address"
msgstr "rtsp/rtmp/http视频源url地址"
msgid "adding one by one"
msgstr "逐个添加"
msgid "data storage directory"
msgstr "数据存储目录"
msgid "storage disk name"
msgstr "存储数据的磁盘名"
msgid "set maximum disk space usage"
msgstr "设置最大磁盘空间占用率"
msgid "single file duration"
msgstr "每个文件的时长"
msgid "in seconds"
msgstr "以秒为单位"
msgid "allocate total storage space"
msgstr "分配总存储空间"
msgid "in megabytes"
msgstr "以兆字节为单位(M)"
msgid "total days of videos"
msgstr "循环保存视频的总天数"
msgid "looping writting to disk"
msgstr "开启循环写入磁盘"
msgid "only detect disk space"
msgstr "仅检测整个磁盘空间"
msgid "enable audio"
msgstr "开启录音"
msgid "whether to push stream"
msgstr "是否推流"
msgid "rtmp server application url"
msgstr "rtmp服务器应用程序url"
msgid "Action"
msgstr "动作"
msgid "One-click Record"
msgstr "一键录制监控视频"
msgid "One-click STOP Record"
msgstr "一键停止录制"
msgid "One-click Push stream"
msgstr "一键推流"
msgid "One-click STOP Push"
msgstr "一键停止推流"