mirror of
https://github.com/scottlamb/moonfire-nvr.git
synced 2024-12-26 07:05:56 -05:00
support run splitting in json api
This commit is contained in:
parent
9041eeb907
commit
6eda26a9cc
@ -118,6 +118,8 @@ Valid request parameters:
|
||||
* `startTime90k` and and `endTime90k` limit the data returned to only
|
||||
recordings which overlap with the given half-open interval. Either or both
|
||||
may be absent; they default to the beginning and end of time, respectively.
|
||||
* `split90k` causes long runs of recordings to be split at the next
|
||||
convenient boundary after the given duration.
|
||||
* TODO(slamb): `continue` to support paging. (If data is too large, the
|
||||
server should return a `continue` key which is expected to be returned on
|
||||
following requests.)
|
||||
|
51
src/web.rs
51
src/web.rs
@ -319,8 +319,9 @@ impl Service {
|
||||
|
||||
fn camera_html(&self, db: MutexGuard<db::LockedDatabase>, query: Option<&str>,
|
||||
uuid: Uuid) -> Result<Vec<u8>, Error> {
|
||||
let (r, trim) = {
|
||||
let (r, split, trim) = {
|
||||
let mut time = recording::Time(i64::min_value()) .. recording::Time(i64::max_value());
|
||||
let mut split = recording::Duration(60 * 60 * recording::TIME_UNITS_PER_SEC);
|
||||
let mut trim = false;
|
||||
if let Some(q) = query {
|
||||
for (key, value) in form_urlencoded::parse(q.as_bytes()) {
|
||||
@ -328,12 +329,13 @@ impl Service {
|
||||
match key {
|
||||
"startTime" => time.start = recording::Time::parse(value)?,
|
||||
"endTime" => time.end = recording::Time::parse(value)?,
|
||||
"split" => split = recording::Duration(i64::from_str(value)?),
|
||||
"trim" if value == "true" => trim = true,
|
||||
_ => {},
|
||||
}
|
||||
};
|
||||
}
|
||||
(time, trim)
|
||||
(time, split, trim)
|
||||
};
|
||||
let camera = db.get_camera(uuid)
|
||||
.ok_or_else(|| Error::new("no such camera".to_owned()))?;
|
||||
@ -358,13 +360,8 @@ impl Service {
|
||||
</tr>\n",
|
||||
HtmlEscaped(&camera.short_name), HtmlEscaped(&camera.description))?;
|
||||
|
||||
// Rather than listing each 60-second recording, generate a HTML row for aggregated .mp4
|
||||
// files of up to FORCE_SPLIT_DURATION each, provided there is no gap or change in video
|
||||
// parameters between recordings.
|
||||
static FORCE_SPLIT_DURATION: recording::Duration =
|
||||
recording::Duration(60 * 60 * recording::TIME_UNITS_PER_SEC);
|
||||
let mut rows = Vec::new();
|
||||
db.list_aggregated_recordings(camera.id, r.clone(), FORCE_SPLIT_DURATION, |row| {
|
||||
db.list_aggregated_recordings(camera.id, r.clone(), split, |row| {
|
||||
rows.push(row.clone());
|
||||
Ok(())
|
||||
})?;
|
||||
@ -412,7 +409,22 @@ impl Service {
|
||||
|
||||
fn camera_recordings(&self, uuid: Uuid, query: Option<&str>, req: &Request)
|
||||
-> Result<Response<slices::Body>, Error> {
|
||||
let r = Service::get_optional_range(query)?;
|
||||
let (r, split) = {
|
||||
let mut time = recording::Time(i64::min_value()) .. recording::Time(i64::max_value());
|
||||
let mut split = recording::Duration(i64::max_value());
|
||||
if let Some(q) = query {
|
||||
for (key, value) in form_urlencoded::parse(q.as_bytes()) {
|
||||
let (key, value) = (key.borrow(), value.borrow());
|
||||
match key {
|
||||
"startTime90k" => time.start = recording::Time::parse(value)?,
|
||||
"endTime90k" => time.end = recording::Time::parse(value)?,
|
||||
"split90k" => split = recording::Duration(i64::from_str(value)?),
|
||||
_ => {},
|
||||
}
|
||||
};
|
||||
}
|
||||
(time, split)
|
||||
};
|
||||
if !is_json(req) {
|
||||
let body: slices::Body = Box::new(stream::once(
|
||||
Ok(ARefs::new(&b"only available for JSON requests"[..]))));
|
||||
@ -425,8 +437,7 @@ impl Service {
|
||||
let db = self.db.lock();
|
||||
let camera = db.get_camera(uuid)
|
||||
.ok_or_else(|| Error::new("no such camera".to_owned()))?;
|
||||
db.list_aggregated_recordings(camera.id, r, recording::Duration(i64::max_value()),
|
||||
|row| {
|
||||
db.list_aggregated_recordings(camera.id, r, split, |row| {
|
||||
let end = row.ids.end - 1; // in api, ids are inclusive.
|
||||
out.recordings.push(json::Recording {
|
||||
start_id: row.ids.start,
|
||||
@ -555,24 +566,6 @@ impl Service {
|
||||
let mp4 = builder.build(self.db.clone(), self.dir.clone())?;
|
||||
Ok(http_entity::serve(mp4, req))
|
||||
}
|
||||
|
||||
/// Parses optional `startTime90k` and `endTime90k` query parameters, defaulting to the
|
||||
/// full range of possible values.
|
||||
fn get_optional_range(query: Option<&str>) -> Result<Range<recording::Time>, Error> {
|
||||
let mut start = i64::min_value();
|
||||
let mut end = i64::max_value();
|
||||
if let Some(q) = query {
|
||||
for (key, value) in form_urlencoded::parse(q.as_bytes()) {
|
||||
let (key, value) = (key.borrow(), value.borrow());
|
||||
match key {
|
||||
"startTime90k" => start = i64::from_str(value)?,
|
||||
"endTime90k" => end = i64::from_str(value)?,
|
||||
_ => {},
|
||||
}
|
||||
};
|
||||
}
|
||||
Ok(recording::Time(start) .. recording::Time(end))
|
||||
}
|
||||
}
|
||||
|
||||
impl server::Service for Service {
|
||||
|
Loading…
Reference in New Issue
Block a user