mirror of
https://github.com/scottlamb/moonfire-nvr.git
synced 2025-10-28 23:35:02 -04:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff383147e4 | ||
|
|
5cc93349fc | ||
|
|
dc1909d073 | ||
|
|
bc93712314 | ||
|
|
104ffdc2dc | ||
|
|
7968700aae | ||
|
|
239256e2ba | ||
|
|
1817931c6f | ||
|
|
5147a50771 | ||
|
|
39b6f6c49c | ||
|
|
0ccc6d0769 | ||
|
|
2903b680df | ||
|
|
2985214d87 | ||
|
|
3cc9603ff3 | ||
|
|
e204fe0864 | ||
|
|
f894f889be | ||
|
|
284a3dd5a9 | ||
|
|
7f7b95c56c | ||
|
|
c75292e43b | ||
|
|
0bfa09b1f1 | ||
|
|
d780b28cc2 |
10
.github/workflows/ci.yml
vendored
10
.github/workflows/ci.yml
vendored
@ -15,7 +15,7 @@ jobs:
|
||||
name: Rust ${{ matrix.rust }}
|
||||
strategy:
|
||||
matrix:
|
||||
rust: [ "stable", "1.82", "nightly" ]
|
||||
rust: ["stable", "1.88", "nightly"]
|
||||
include:
|
||||
- rust: nightly
|
||||
extra_args: "--features nightly --benches"
|
||||
@ -29,9 +29,9 @@ jobs:
|
||||
# `git describe` output gets baked into the binary for `moonfire-nvr --version`.
|
||||
# Fetch all revs so it can see tag history.
|
||||
fetch-depth: 0
|
||||
filter: 'tree:0'
|
||||
filter: "tree:0"
|
||||
- name: Cache
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
@ -63,7 +63,7 @@ jobs:
|
||||
name: Node ${{ matrix.node }}
|
||||
strategy:
|
||||
matrix:
|
||||
node: [ "18", "20", "21" ]
|
||||
node: ["18", "20", "21"]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@ -79,7 +79,7 @@ jobs:
|
||||
- run: cd ui && pnpm run check-format
|
||||
license:
|
||||
name: Check copyright/license headers
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
24
.github/workflows/release.yml
vendored
24
.github/workflows/release.yml
vendored
@ -52,16 +52,16 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
# Note: keep these arches in sync with `Upload Docker Manifest` list.
|
||||
- arch: x86_64 # as in `uname -m` on Linux.
|
||||
rust_target: x86_64-unknown-linux-musl # as in <https://doc.rust-lang.org/rustc/platform-support.html>
|
||||
docker_platform: linux/amd64 # as in <https://docs.docker.com/build/building/multi-platform/>
|
||||
- arch: aarch64
|
||||
rust_target: aarch64-unknown-linux-musl
|
||||
docker_platform: linux/arm64
|
||||
- arch: armv7l
|
||||
rust_target: armv7-unknown-linux-musleabihf
|
||||
docker_platform: linux/arm/v7
|
||||
# Note: keep these arches in sync with `Upload Docker Manifest` list.
|
||||
- arch: x86_64 # as in `uname -m` on Linux.
|
||||
rust_target: x86_64-unknown-linux-musl # as in <https://doc.rust-lang.org/rustc/platform-support.html>
|
||||
docker_platform: linux/amd64 # as in <https://docs.docker.com/build/building/multi-platform/>
|
||||
- arch: aarch64
|
||||
rust_target: aarch64-unknown-linux-musl
|
||||
docker_platform: linux/arm64
|
||||
- arch: armv7l
|
||||
rust_target: armv7-unknown-linux-musleabihf
|
||||
docker_platform: linux/arm/v7
|
||||
fail-fast: false
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@ -90,7 +90,7 @@ jobs:
|
||||
working-directory: server
|
||||
target: ${{ matrix.rust_target }}
|
||||
command: build
|
||||
args: --release --features bundled
|
||||
args: --release --features bundled,mimalloc
|
||||
- name: Upload Docker Artifact
|
||||
run: |
|
||||
tag="${DOCKER_TAG}-${{ matrix.arch }}"
|
||||
@ -115,7 +115,7 @@ jobs:
|
||||
if-no-files-found: error
|
||||
|
||||
release:
|
||||
needs: [ base, cross ]
|
||||
needs: [base, cross]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
19
CHANGELOG.md
19
CHANGELOG.md
@ -8,6 +8,25 @@ upgrades, e.g. `v0.6.x` -> `v0.7.x`. The config file format and
|
||||
[API](ref/api.md) currently have no stability guarantees, so they may change
|
||||
even on minor releases, e.g. `v0.7.5` -> `v0.7.6`.
|
||||
|
||||
## v0.7.23 (2025-10-03)
|
||||
|
||||
* update Retina to [v0.4.14](https://github.com/scottlamb/retina/blob/main/CHANGELOG.md#v0414-2025-10-03),
|
||||
improving camera compatibility. Fixes [#344](https://github.com/scottlamb/moonfire-nvr/issues/344).
|
||||
* bump minimum Rust version to 1.88.
|
||||
|
||||
## v0.7.22 (2025-10-03)
|
||||
|
||||
(This version was tagged but never released due to an error.)
|
||||
|
||||
* switch from per-layer to global `tracing` filter to avoid missing log lines
|
||||
(tokio-rs/tracing#2519)[https://github.com/tokio-rs/tracing/issues/2519]).
|
||||
|
||||
## v0.7.21 (2025-04-04)
|
||||
|
||||
* Release with `mimalloc` allocator, which is significantly faster than the memory
|
||||
allocator built into `musl`.
|
||||
* Eliminate some memory allocations.
|
||||
|
||||
## v0.7.20 (2025-01-31)
|
||||
|
||||
* H.265 fixes.
|
||||
|
||||
@ -68,7 +68,7 @@ following command:
|
||||
$ brew install node
|
||||
```
|
||||
|
||||
Next, you need Rust 1.82+ and Cargo. The easiest way to install them is by
|
||||
Next, you need Rust 1.88+ and Cargo. The easiest way to install them is by
|
||||
following the instructions at [rustup.rs](https://www.rustup.rs/). Avoid
|
||||
your Linux distribution's Rust packages, which tend to be too old.
|
||||
(At least on Debian-based systems; Arch and Gentoo might be okay.)
|
||||
|
||||
@ -26,10 +26,10 @@ left, and pick the [latest tagged version](https://github.com/scottlamb/moonfire
|
||||
|
||||
Download the binary for your platform from the matching GitHub release.
|
||||
Install it as `/usr/local/bin/moonfire-nvr` and ensure it is executable, e.g.
|
||||
for version `v0.7.14`:
|
||||
for version `v0.7.23`:
|
||||
|
||||
```console
|
||||
$ VERSION=v0.7.14
|
||||
$ VERSION=v0.7.23
|
||||
$ ARCH=$(uname -m)
|
||||
$ curl -OL "https://github.com/scottlamb/moonfire-nvr/releases/download/$VERSION/moonfire-nvr-$VERSION-$ARCH"
|
||||
$ sudo install -m 755 "moonfire-nvr-$VERSION-$ARCH" /usr/local/bin/moonfire-nvr
|
||||
@ -65,7 +65,7 @@ services:
|
||||
moonfire-nvr:
|
||||
# The `vX.Y.Z` images will work on any architecture (x86-64, arm, or
|
||||
# aarch64); just pick the correct version.
|
||||
image: ghcr.io/scottlamb/moonfire-nvr:v0.7.11
|
||||
image: ghcr.io/scottlamb/moonfire-nvr:v0.7.23
|
||||
command: run
|
||||
|
||||
volumes:
|
||||
|
||||
393
server/Cargo.lock
generated
393
server/Cargo.lock
generated
@ -1,6 +1,6 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
@ -57,6 +57,12 @@ version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
|
||||
|
||||
[[package]]
|
||||
name = "atomic-waker"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.4.0"
|
||||
@ -104,9 +110,9 @@ checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2"
|
||||
|
||||
[[package]]
|
||||
name = "blake3"
|
||||
version = "1.5.5"
|
||||
version = "1.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8ee0c1824c4dea5b5f81736aff91bae041d2c07ee1192bec91054e10e3e601e"
|
||||
checksum = "3888aaa89e4b2a40fca9848e400f6a658a5a3978de7be858e209cafa8be9a4a0"
|
||||
dependencies = [
|
||||
"arrayref",
|
||||
"arrayvec",
|
||||
@ -126,9 +132,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bpaf"
|
||||
version = "0.9.15"
|
||||
version = "0.9.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50fd5174866dc2fa2ddc96e8fb800852d37f064f32a45c7b7c2f8fa2c64c77fa"
|
||||
checksum = "473976d7a8620bb1e06dcdd184407c2363fe4fec8e983ee03ed9197222634a31"
|
||||
dependencies = [
|
||||
"bpaf_derive",
|
||||
"owo-colors",
|
||||
@ -137,9 +143,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bpaf_derive"
|
||||
version = "0.5.13"
|
||||
version = "0.5.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf95d9c7e6aba67f8fc07761091e93254677f4db9e27197adecebc7039a58722"
|
||||
checksum = "fefb4feeec9a091705938922f26081aad77c64cd2e76cd1c4a9ece8e42e1618a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -169,9 +175,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.9.0"
|
||||
version = "1.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b"
|
||||
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
|
||||
|
||||
[[package]]
|
||||
name = "castaway"
|
||||
@ -285,14 +291,10 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cstr"
|
||||
version = "0.2.12"
|
||||
name = "cty"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68523903c8ae5aacfa32a0d9ae60cadeb764e1da14ee0d26b1f3089f13a54636"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35"
|
||||
|
||||
[[package]]
|
||||
name = "cursive"
|
||||
@ -381,9 +383,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "data-encoding"
|
||||
version = "2.7.0"
|
||||
version = "2.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e60eed09d8c01d3cee5b7d30acb059b76614c918fa0f992e0dd6eeb10daad6f"
|
||||
checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476"
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
@ -719,9 +721,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "1.2.0"
|
||||
version = "1.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea"
|
||||
checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
@ -755,12 +757,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "http-body-util"
|
||||
version = "0.1.2"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f"
|
||||
checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"futures-core",
|
||||
"http",
|
||||
"http-body",
|
||||
"pin-project-lite",
|
||||
@ -768,9 +770,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "http-serve"
|
||||
version = "0.4.0-rc.1"
|
||||
version = "0.4.0-rc.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e30e587eb43944a00ad6b239f691694564fc050c81b86e39006082037188084"
|
||||
checksum = "18a4eb23604eafa9eafc6d9c2e2032c0bfe9dd7362428eea4914d94168cc992f"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"flate2",
|
||||
@ -803,19 +805,21 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "1.5.2"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0"
|
||||
checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e"
|
||||
dependencies = [
|
||||
"atomic-waker",
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
"futures-core",
|
||||
"http",
|
||||
"http-body",
|
||||
"httparse",
|
||||
"httpdate",
|
||||
"itoa",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"smallvec",
|
||||
"tokio",
|
||||
"want",
|
||||
@ -823,16 +827,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hyper-util"
|
||||
version = "0.1.10"
|
||||
version = "0.1.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4"
|
||||
checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
"hyper",
|
||||
"ipnet",
|
||||
"libc",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"socket2",
|
||||
"tokio",
|
||||
@ -987,9 +996,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.7.1"
|
||||
version = "2.11.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652"
|
||||
checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
@ -1004,12 +1013,33 @@ dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "io-uring"
|
||||
version = "0.7.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipnet"
|
||||
version = "2.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
|
||||
|
||||
[[package]]
|
||||
name = "iri-string"
|
||||
version = "0.7.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is_ci"
|
||||
version = "1.2.0"
|
||||
@ -1018,9 +1048,9 @@ checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.12.1"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
|
||||
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
@ -1033,10 +1063,11 @@ checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
|
||||
|
||||
[[package]]
|
||||
name = "jiff"
|
||||
version = "0.1.26"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e58800dee30ca82a1c107c64a0effca7ccb4e6106645c0e3af40d5aaba9f5457"
|
||||
checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49"
|
||||
dependencies = [
|
||||
"jiff-static",
|
||||
"jiff-tzdb-platform",
|
||||
"log",
|
||||
"portable-atomic",
|
||||
@ -1046,16 +1077,27 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jiff-tzdb"
|
||||
version = "0.1.2"
|
||||
name = "jiff-static"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf2cec2f5d266af45a071ece48b1fb89f3b00b2421ac3a5fe10285a6caaa60d3"
|
||||
checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jiff-tzdb"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1283705eb0a21404d2bfd6eef2a7593d240bc42a0bdb39db0ad6fa2ec026524"
|
||||
|
||||
[[package]]
|
||||
name = "jiff-tzdb-platform"
|
||||
version = "0.1.2"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a63c62e404e7b92979d2792352d885a7f8f83fd1d0d31eea582d77b2ceca697e"
|
||||
checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8"
|
||||
dependencies = [
|
||||
"jiff-tzdb",
|
||||
]
|
||||
@ -1078,9 +1120,20 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.169"
|
||||
version = "0.2.176"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
||||
checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174"
|
||||
|
||||
[[package]]
|
||||
name = "libmimalloc-sys"
|
||||
version = "0.1.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "667f4fec20f29dfc6bc7357c582d91796c169ad7e2fce709468aefeb2c099870"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cty",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
@ -1095,9 +1148,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libsqlite3-sys"
|
||||
version = "0.31.0"
|
||||
version = "0.35.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad8935b44e7c13394a179a438e0cebba0fe08fe01b54f152e29a93b5cf993fd4"
|
||||
checksum = "133c182a6a2c87864fe97778797e46c7e999672690dc9fa3ee8e241aa4a9c13f"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"pkg-config",
|
||||
@ -1146,9 +1199,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.25"
|
||||
version = "0.4.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f"
|
||||
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
|
||||
|
||||
[[package]]
|
||||
name = "matchers"
|
||||
@ -1171,9 +1224,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
version = "2.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
@ -1225,6 +1278,7 @@ dependencies = [
|
||||
"futures",
|
||||
"jiff",
|
||||
"libc",
|
||||
"libmimalloc-sys",
|
||||
"nix",
|
||||
"nom",
|
||||
"rusqlite",
|
||||
@ -1244,7 +1298,6 @@ dependencies = [
|
||||
"base64",
|
||||
"blake3",
|
||||
"byteorder",
|
||||
"cstr",
|
||||
"diff",
|
||||
"futures",
|
||||
"h264-reader",
|
||||
@ -1267,7 +1320,6 @@ dependencies = [
|
||||
"tempfile",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"ulid",
|
||||
"url",
|
||||
"uuid",
|
||||
]
|
||||
@ -1283,6 +1335,7 @@ dependencies = [
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"cursive",
|
||||
"data-encoding",
|
||||
"flate2",
|
||||
"futures",
|
||||
"h264-reader",
|
||||
@ -1305,6 +1358,7 @@ dependencies = [
|
||||
"nom",
|
||||
"num-rational",
|
||||
"password-hash",
|
||||
"pin-project",
|
||||
"pretty-hex",
|
||||
"protobuf",
|
||||
"reffers",
|
||||
@ -1315,6 +1369,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"smallvec",
|
||||
"subtle",
|
||||
"tempfile",
|
||||
"tokio",
|
||||
"tokio-tungstenite",
|
||||
@ -1325,7 +1380,6 @@ dependencies = [
|
||||
"tracing-log",
|
||||
"tracing-subscriber",
|
||||
"tracing-test",
|
||||
"ulid",
|
||||
"url",
|
||||
"uuid",
|
||||
"walkdir",
|
||||
@ -1564,18 +1618,18 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "1.1.8"
|
||||
version = "1.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e2ec53ad785f4d35dac0adea7f7dc6f1bb277ad84a680c7afefeae05d1f5916"
|
||||
checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a"
|
||||
dependencies = [
|
||||
"pin-project-internal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-internal"
|
||||
version = "1.1.8"
|
||||
version = "1.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d56a66c0c55993aa927429d0f8a0abfd74f084e4d9c192cffed01e418d83eefb"
|
||||
checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -1647,20 +1701,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "protobuf"
|
||||
version = "3.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3a7c64d9bf75b1b8d981124c14c179074e8caa7dfe7b6a12e6222ddcd0c8f72"
|
||||
version = "3.7.2"
|
||||
source = "git+https://github.com/scottlamb/rust-protobuf?rev=593aa7f26bb5fc736c2e61d410afa34efb914ecb#593aa7f26bb5fc736c2e61d410afa34efb914ecb"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"protobuf-support",
|
||||
"thiserror 1.0.69",
|
||||
"thiserror 2.0.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "protobuf-codegen"
|
||||
version = "3.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e26b833f144769a30e04b1db0146b2aaa53fd2fd83acf10a6b5f996606c18144"
|
||||
version = "3.7.2"
|
||||
source = "git+https://github.com/scottlamb/rust-protobuf?rev=593aa7f26bb5fc736c2e61d410afa34efb914ecb#593aa7f26bb5fc736c2e61d410afa34efb914ecb"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"once_cell",
|
||||
@ -1668,14 +1720,13 @@ dependencies = [
|
||||
"protobuf-parse",
|
||||
"regex",
|
||||
"tempfile",
|
||||
"thiserror 1.0.69",
|
||||
"thiserror 2.0.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "protobuf-parse"
|
||||
version = "3.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "322330e133eab455718444b4e033ebfac7c6528972c784fcde28d2cc783c6257"
|
||||
version = "3.7.2"
|
||||
source = "git+https://github.com/scottlamb/rust-protobuf?rev=593aa7f26bb5fc736c2e61d410afa34efb914ecb#593aa7f26bb5fc736c2e61d410afa34efb914ecb"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"indexmap",
|
||||
@ -1683,17 +1734,16 @@ dependencies = [
|
||||
"protobuf",
|
||||
"protobuf-support",
|
||||
"tempfile",
|
||||
"thiserror 1.0.69",
|
||||
"thiserror 2.0.17",
|
||||
"which",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "protobuf-support"
|
||||
version = "3.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b088fd20b938a875ea00843b6faf48579462630015c3788d397ad6a786663252"
|
||||
version = "3.7.2"
|
||||
source = "git+https://github.com/scottlamb/rust-protobuf?rev=593aa7f26bb5fc736c2e61d410afa34efb914ecb#593aa7f26bb5fc736c2e61d410afa34efb914ecb"
|
||||
dependencies = [
|
||||
"thiserror 1.0.69",
|
||||
"thiserror 2.0.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1790,24 +1840,20 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.12.12"
|
||||
version = "0.12.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da"
|
||||
checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"hyper",
|
||||
"hyper-util",
|
||||
"ipnet",
|
||||
"js-sys",
|
||||
"log",
|
||||
"mime",
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"serde",
|
||||
@ -1816,19 +1862,19 @@ dependencies = [
|
||||
"sync_wrapper",
|
||||
"tokio",
|
||||
"tower",
|
||||
"tower-http",
|
||||
"tower-service",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"windows-registry",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "retina"
|
||||
version = "0.4.13"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2cb881f2fb51b0b4e814a524bfb938007bdf3df5f8430ec0b4e014d2dc877f50"
|
||||
checksum = "783b4b9caaf5c46c93b9946590b29e58b267832b2b9e71fd85740f5001586fe4"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bitstream-io",
|
||||
@ -1846,7 +1892,7 @@ dependencies = [
|
||||
"rtsp-types",
|
||||
"sdp-types",
|
||||
"smallvec",
|
||||
"thiserror 1.0.69",
|
||||
"thiserror 2.0.17",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"url",
|
||||
@ -1891,9 +1937,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rusqlite"
|
||||
version = "0.33.0"
|
||||
version = "0.37.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c6d5e5acb6f6129fe3f7ba0a7fc77bca1942cb568535e18e7bc40262baf3110"
|
||||
checksum = "165ca6e57b20e1351573e3729b958bc62f0e48025386970b6e4d29e7a7e71f3f"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"fallible-iterator",
|
||||
@ -1982,18 +2028,28 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.217"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.217"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -2002,23 +2058,24 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.137"
|
||||
version = "1.0.145"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "930cfb6e6abf99298aaad7d29abbef7a9999a9a8806a40088f55f0dcec03146b"
|
||||
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "0.6.8"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
|
||||
checksum = "5417783452c2be558477e104686f7de5dae53dba813c28435e0e70f82d9b04ee"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2091,27 +2148,24 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.9"
|
||||
version = "0.4.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.13.2"
|
||||
version = "1.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.5.8"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8"
|
||||
checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2149,9 +2203,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.96"
|
||||
version = "2.0.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80"
|
||||
checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -2215,11 +2269,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.11"
|
||||
version = "2.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc"
|
||||
checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8"
|
||||
dependencies = [
|
||||
"thiserror-impl 2.0.11",
|
||||
"thiserror-impl 2.0.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2235,9 +2289,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.11"
|
||||
version = "2.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2"
|
||||
checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -2314,19 +2368,21 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.43.0"
|
||||
version = "1.47.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e"
|
||||
checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
"io-uring",
|
||||
"libc",
|
||||
"mio",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"slab",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2367,38 +2423,43 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.19"
|
||||
version = "0.9.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e"
|
||||
checksum = "00e5e5d9bf2475ac9d4f0d9edab68cc573dc2fd644b0dba36b0c30a92dd9eaa0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"indexmap",
|
||||
"serde_core",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_edit",
|
||||
"toml_parser",
|
||||
"toml_writer",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.8"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
|
||||
checksum = "32f1085dec27c2b6632b04c80b3bb1b4300d6495d1e129693bdda7d91e72eec1"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.22.22"
|
||||
name = "toml_parser"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
|
||||
checksum = "4cf893c33be71572e0e9aa6dd15e6677937abd686b066eac3f8cd3531688a627"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_writer"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d163a63c116ce562a22cda521fcc4d79152e7aba014456fb5eb442f6d6a10109"
|
||||
|
||||
[[package]]
|
||||
name = "tower"
|
||||
version = "0.5.2"
|
||||
@ -2414,6 +2475,24 @@ dependencies = [
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-http"
|
||||
version = "0.6.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
"iri-string",
|
||||
"pin-project-lite",
|
||||
"tower",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-layer"
|
||||
version = "0.3.3"
|
||||
@ -2548,7 +2627,7 @@ dependencies = [
|
||||
"log",
|
||||
"rand",
|
||||
"sha1",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror 2.0.17",
|
||||
"utf-8",
|
||||
]
|
||||
|
||||
@ -2558,16 +2637,6 @@ version = "1.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||
|
||||
[[package]]
|
||||
name = "ulid"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f294bff79170ed1c5633812aff1e565c35d993a36e757f9bc0accf5eec4e6045"
|
||||
dependencies = [
|
||||
"rand",
|
||||
"web-time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.15"
|
||||
@ -2629,6 +2698,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3758f5e68192bb96cc8f9b7e2c2cfdabb435499a28499a42f8f984092adad4b"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"rand",
|
||||
"serde",
|
||||
]
|
||||
|
||||
@ -2756,16 +2826,6 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "web-time"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "4.4.2"
|
||||
@ -2809,36 +2869,6 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-registry"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0"
|
||||
dependencies = [
|
||||
"windows-result",
|
||||
"windows-strings",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-strings"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"
|
||||
dependencies = [
|
||||
"windows-result",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
@ -2923,12 +2953,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.6.24"
|
||||
version = "0.7.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf"
|
||||
|
||||
[[package]]
|
||||
name = "write16"
|
||||
|
||||
@ -5,7 +5,7 @@ authors = ["Scott Lamb <slamb@slamb.org>"]
|
||||
edition = "2021"
|
||||
resolver = "2"
|
||||
license-file = "../LICENSE.txt"
|
||||
rust-version = "1.82"
|
||||
rust-version = "1.88"
|
||||
publish = false
|
||||
|
||||
[features]
|
||||
@ -18,6 +18,7 @@ nightly = ["db/nightly"]
|
||||
bundled = ["rusqlite/bundled", "bundled-ui"]
|
||||
|
||||
bundled-ui = []
|
||||
mimalloc = ["base/mimalloc"]
|
||||
|
||||
[workspace]
|
||||
members = ["base", "db"]
|
||||
@ -25,26 +26,38 @@ members = ["base", "db"]
|
||||
[workspace.dependencies]
|
||||
base64 = "0.22.0"
|
||||
h264-reader = "0.8.0"
|
||||
itertools = "0.12.0"
|
||||
jiff = "0.1.8"
|
||||
itertools = "0.14.0"
|
||||
jiff = "0.2.1"
|
||||
nix = "0.27.0"
|
||||
pretty-hex = "0.4.0"
|
||||
ring = "0.17.0"
|
||||
rusqlite = "0.33.0"
|
||||
rusqlite = "0.37.0"
|
||||
tracing = { version = "0.1" }
|
||||
tracing-core = "0.1.30"
|
||||
tracing-futures = { version = "0.2.5", features = ["futures-03", "std-future"] }
|
||||
tracing-log = "0.2"
|
||||
tracing-subscriber = { version = "0.3.16" }
|
||||
uuid = { version = "1.1.2", features = ["serde", "std", "v7", "fast-rng"] }
|
||||
|
||||
# This is 3.7.2 + dependency updates.
|
||||
protobuf = { git = "https://github.com/scottlamb/rust-protobuf", rev = "593aa7f26bb5fc736c2e61d410afa34efb914ecb" }
|
||||
protobuf-codegen = { git = "https://github.com/scottlamb/rust-protobuf", rev = "593aa7f26bb5fc736c2e61d410afa34efb914ecb" }
|
||||
|
||||
[dependencies]
|
||||
base = { package = "moonfire-base", path = "base" }
|
||||
base64 = { workspace = true }
|
||||
blake3 = "1.0.0"
|
||||
bpaf = { version = "0.9.15", features = ["autocomplete", "bright-color", "derive"]}
|
||||
bpaf = { version = "0.9.15", features = [
|
||||
"autocomplete",
|
||||
"bright-color",
|
||||
"derive",
|
||||
] }
|
||||
bytes = "1"
|
||||
byteorder = "1.0"
|
||||
cursive = { version = "0.21.1", default-features = false, features = ["termion-backend"] }
|
||||
cursive = { version = "0.21.1", default-features = false, features = [
|
||||
"termion-backend",
|
||||
] }
|
||||
data-encoding = "2.7.0"
|
||||
db = { package = "moonfire-db", path = "db" }
|
||||
futures = "0.3"
|
||||
h264-reader = { workspace = true }
|
||||
@ -60,29 +73,36 @@ nix = { workspace = true, features = ["time", "user"] }
|
||||
nom = "7.0.0"
|
||||
password-hash = "0.5.0"
|
||||
pretty-hex = { workspace = true }
|
||||
protobuf = "3.0"
|
||||
protobuf = { workspace = true }
|
||||
reffers = "0.7.0"
|
||||
retina = "0.4.13"
|
||||
retina = "0.4.14"
|
||||
ring = { workspace = true }
|
||||
rusqlite = { workspace = true }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
smallvec = { version = "1.7", features = ["union"] }
|
||||
tokio = { version = "1.24", features = ["macros", "rt-multi-thread", "signal", "sync", "time"] }
|
||||
tokio = { version = "1.24", features = [
|
||||
"macros",
|
||||
"rt-multi-thread",
|
||||
"signal",
|
||||
"sync",
|
||||
"time",
|
||||
] }
|
||||
tokio-tungstenite = "0.26.1"
|
||||
toml = "0.8"
|
||||
toml = "0.9"
|
||||
tracing = { workspace = true, features = ["log"] }
|
||||
tracing-subscriber = { version = "0.3.16", features = ["env-filter", "json"] }
|
||||
tracing-core = "0.1.30"
|
||||
tracing-futures = { version = "0.2.5", features = ["futures-03", "std-future"] }
|
||||
tracing-log = { workspace = true }
|
||||
ulid = "1.0.0"
|
||||
url = "2.1.1"
|
||||
uuid = { version = "1.1.2", features = ["serde", "std", "v4"] }
|
||||
uuid = { workspace = true }
|
||||
flate2 = "1.0.26"
|
||||
hyper-util = { version = "0.1.7", features = ["server-graceful", "tokio"] }
|
||||
http-body = "1.0.1"
|
||||
http-body-util = "0.1.2"
|
||||
pin-project = "1.1.10"
|
||||
subtle = "2.6.1"
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
libsystemd = "0.7.0"
|
||||
@ -94,7 +114,9 @@ walkdir = "2.3.3"
|
||||
|
||||
[dev-dependencies]
|
||||
mp4 = { git = "https://github.com/scottlamb/mp4-rust", branch = "moonfire" }
|
||||
num-rational = { version = "0.4.0", default-features = false, features = ["std"] }
|
||||
num-rational = { version = "0.4.0", default-features = false, features = [
|
||||
"std",
|
||||
] }
|
||||
reqwest = { version = "0.12.0", default-features = false, features = ["json"] }
|
||||
tempfile = "3.2.0"
|
||||
tracing-test = "0.2.4"
|
||||
|
||||
@ -6,9 +6,10 @@ readme = "../README.md"
|
||||
edition = "2021"
|
||||
license-file = "../../LICENSE.txt"
|
||||
publish = false
|
||||
rust-version = "1.82"
|
||||
rust-version = "1.88"
|
||||
|
||||
[features]
|
||||
mimalloc = ["dep:libmimalloc-sys"]
|
||||
nightly = []
|
||||
|
||||
[lib]
|
||||
@ -16,10 +17,14 @@ path = "lib.rs"
|
||||
|
||||
[dependencies]
|
||||
ahash = "0.8"
|
||||
coded = { git = "https://github.com/scottlamb/coded", rev = "2c97994974a73243d5dd12134831814f42cdb0e8"}
|
||||
coded = { git = "https://github.com/scottlamb/coded", rev = "2c97994974a73243d5dd12134831814f42cdb0e8" }
|
||||
futures = "0.3"
|
||||
jiff = { workspace = true }
|
||||
libc = "0.2"
|
||||
libmimalloc-sys = { version = "0.1.44", features = [
|
||||
"override",
|
||||
"extended",
|
||||
], optional = true }
|
||||
nix = { workspace = true, features = ["time"] }
|
||||
nom = "7.0.0"
|
||||
rusqlite = { workspace = true }
|
||||
|
||||
@ -7,8 +7,8 @@
|
||||
//! Note these types are in a more standard nanosecond-based format, where
|
||||
//! [`crate::time`] uses Moonfire's 90 kHz time base.
|
||||
|
||||
use crate::Mutex;
|
||||
use nix::sys::time::{TimeSpec, TimeValLike as _};
|
||||
use std::sync::Mutex;
|
||||
use std::sync::{mpsc, Arc};
|
||||
use std::thread;
|
||||
pub use std::time::Duration;
|
||||
@ -220,15 +220,15 @@ impl SimulatedClocks {
|
||||
|
||||
impl Clocks for SimulatedClocks {
|
||||
fn realtime(&self) -> SystemTime {
|
||||
self.0.boot + *self.0.uptime.lock().unwrap()
|
||||
self.0.boot + *self.0.uptime.lock()
|
||||
}
|
||||
fn monotonic(&self) -> Instant {
|
||||
Instant(TimeSpec::from(*self.0.uptime.lock().unwrap()))
|
||||
Instant(TimeSpec::from(*self.0.uptime.lock()))
|
||||
}
|
||||
|
||||
/// Advances the clock by the specified amount without actually sleeping.
|
||||
fn sleep(&self, how_long: Duration) {
|
||||
let mut l = self.0.uptime.lock().unwrap();
|
||||
let mut l = self.0.uptime.lock();
|
||||
*l += how_long;
|
||||
}
|
||||
|
||||
|
||||
@ -101,7 +101,7 @@ impl ToErrKind for rusqlite::types::FromSqlError {
|
||||
impl ToErrKind for nix::Error {
|
||||
fn err_kind(&self) -> ErrorKind {
|
||||
use nix::Error;
|
||||
match self {
|
||||
match *self {
|
||||
Error::EACCES | Error::EPERM => ErrorKind::PermissionDenied,
|
||||
Error::EDQUOT => ErrorKind::ResourceExhausted,
|
||||
Error::EBUSY
|
||||
|
||||
@ -14,3 +14,77 @@ pub use crate::error::{Error, ErrorBuilder, ErrorKind, ResultExt};
|
||||
pub use ahash::RandomState;
|
||||
pub type FastHashMap<K, V> = std::collections::HashMap<K, V, ahash::RandomState>;
|
||||
pub type FastHashSet<K> = std::collections::HashSet<K, ahash::RandomState>;
|
||||
|
||||
const NOT_POISONED: &str =
|
||||
"not poisoned; this is a consequence of an earlier panic while holding this mutex; see logs.";
|
||||
|
||||
/// [`std::sync::Mutex`] wrapper which always panics on encountering poison.
|
||||
#[derive(Default)]
|
||||
pub struct Mutex<T>(std::sync::Mutex<T>);
|
||||
|
||||
impl<T> Mutex<T> {
|
||||
#[inline]
|
||||
pub const fn new(value: T) -> Self {
|
||||
Mutex(std::sync::Mutex::new(value))
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
#[inline]
|
||||
pub fn lock(&self) -> std::sync::MutexGuard<'_, T> {
|
||||
self.0.lock().expect(NOT_POISONED)
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
#[inline]
|
||||
pub fn into_inner(self) -> T {
|
||||
self.0.into_inner().expect(NOT_POISONED)
|
||||
}
|
||||
}
|
||||
|
||||
/// [`std::sync::Condvar`] wrapper which always panics on encountering poison.
|
||||
#[derive(Default)]
|
||||
pub struct Condvar(std::sync::Condvar);
|
||||
|
||||
impl Condvar {
|
||||
#[inline]
|
||||
pub const fn new() -> Self {
|
||||
Self(std::sync::Condvar::new())
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
#[inline]
|
||||
pub fn wait_timeout_while<'a, T, F>(
|
||||
&self,
|
||||
guard: std::sync::MutexGuard<'a, T>,
|
||||
dur: std::time::Duration,
|
||||
condition: F,
|
||||
) -> (std::sync::MutexGuard<'a, T>, std::sync::WaitTimeoutResult)
|
||||
where
|
||||
F: FnMut(&mut T) -> bool,
|
||||
{
|
||||
self.0
|
||||
.wait_timeout_while(guard, dur, condition)
|
||||
.expect(NOT_POISONED)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for Condvar {
|
||||
type Target = std::sync::Condvar;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ensure_malloc_used() {
|
||||
#[cfg(feature = "mimalloc")]
|
||||
{
|
||||
// This is a load-bearing debug line.
|
||||
// Building `libmimalloc-sys` with the `override` feature will override `malloc` and
|
||||
// `free` as used through the Rust global allocator, SQLite, and `libc`. But...`cargo`
|
||||
// doesn't seem to build `libmimalloc-sys` at all if it's not referenced from Rust code.
|
||||
tracing::debug!("mimalloc version {}", unsafe {
|
||||
libmimalloc_sys::mi_version()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,9 +15,10 @@ use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
use std::task::{Context, Poll, Waker};
|
||||
|
||||
use crate::Condvar;
|
||||
use crate::Mutex;
|
||||
use futures::Future;
|
||||
use slab::Slab;
|
||||
use std::sync::{Condvar, Mutex};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ShutdownError;
|
||||
@ -47,7 +48,6 @@ impl Drop for Sender {
|
||||
.0
|
||||
.wakers
|
||||
.lock()
|
||||
.unwrap()
|
||||
.take()
|
||||
.expect("only the single Sender takes the slab");
|
||||
for w in wakers.drain() {
|
||||
@ -78,14 +78,14 @@ const NO_WAKER: usize = usize::MAX;
|
||||
|
||||
impl Receiver {
|
||||
pub fn check(&self) -> Result<(), ShutdownError> {
|
||||
if self.0.wakers.lock().unwrap().is_none() {
|
||||
if self.0.wakers.lock().is_none() {
|
||||
Err(ShutdownError)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_future(&self) -> ReceiverRefFuture {
|
||||
pub fn as_future(&self) -> ReceiverRefFuture<'_> {
|
||||
ReceiverRefFuture {
|
||||
receiver: self,
|
||||
waker_i: NO_WAKER,
|
||||
@ -107,12 +107,11 @@ impl Receiver {
|
||||
}
|
||||
|
||||
pub fn wait_for(&self, timeout: std::time::Duration) -> Result<(), ShutdownError> {
|
||||
let l = self.0.wakers.lock().unwrap();
|
||||
let l = self.0.wakers.lock();
|
||||
let result = self
|
||||
.0
|
||||
.condvar
|
||||
.wait_timeout_while(l, timeout, |wakers| wakers.is_some())
|
||||
.unwrap();
|
||||
.wait_timeout_while(l, timeout, |wakers| wakers.is_some());
|
||||
if result.1.timed_out() {
|
||||
Ok(())
|
||||
} else {
|
||||
@ -122,7 +121,7 @@ impl Receiver {
|
||||
}
|
||||
|
||||
fn poll_impl(inner: &Inner, waker_i: &mut usize, cx: &mut Context<'_>) -> Poll<()> {
|
||||
let mut l = inner.wakers.lock().unwrap();
|
||||
let mut l = inner.wakers.lock();
|
||||
let wakers = match &mut *l {
|
||||
None => return Poll::Ready(()),
|
||||
Some(w) => w,
|
||||
@ -152,7 +151,7 @@ impl Drop for ReceiverRefFuture<'_> {
|
||||
if self.waker_i == NO_WAKER {
|
||||
return;
|
||||
}
|
||||
let mut l = self.receiver.0.wakers.lock().unwrap();
|
||||
let mut l = self.receiver.0.wakers.lock();
|
||||
if let Some(wakers) = &mut *l {
|
||||
wakers.remove(self.waker_i);
|
||||
}
|
||||
@ -173,7 +172,7 @@ impl Drop for ReceiverFuture {
|
||||
if self.waker_i == NO_WAKER {
|
||||
return;
|
||||
}
|
||||
let mut l = self.receiver.wakers.lock().unwrap();
|
||||
let mut l = self.receiver.wakers.lock();
|
||||
if let Some(wakers) = &mut *l {
|
||||
wakers.remove(self.waker_i);
|
||||
}
|
||||
|
||||
@ -55,7 +55,7 @@ fn fixed_len_num<'a, T: FromStr>(len: usize) -> impl FnMut(&'a str) -> IResult<'
|
||||
}
|
||||
|
||||
/// Parses `YYYY-mm-dd` into pieces.
|
||||
fn parse_datepart(input: &str) -> IResult<&str, (i16, i8, i8)> {
|
||||
fn parse_datepart(input: &str) -> IResult<'_, &str, (i16, i8, i8)> {
|
||||
tuple((
|
||||
fixed_len_num(4),
|
||||
preceded(tag("-"), fixed_len_num(2)),
|
||||
@ -64,7 +64,7 @@ fn parse_datepart(input: &str) -> IResult<&str, (i16, i8, i8)> {
|
||||
}
|
||||
|
||||
/// Parses `HH:MM[:SS[:FFFFF]]` into pieces.
|
||||
fn parse_timepart(input: &str) -> IResult<&str, (i8, i8, i8, i32)> {
|
||||
fn parse_timepart(input: &str) -> IResult<'_, &str, (i8, i8, i8, i32)> {
|
||||
let (input, (hr, _, min)) = tuple((fixed_len_num(2), tag(":"), fixed_len_num(2)))(input)?;
|
||||
let (input, stuff) = opt(tuple((
|
||||
preceded(tag(":"), fixed_len_num(2)),
|
||||
@ -75,7 +75,7 @@ fn parse_timepart(input: &str) -> IResult<&str, (i8, i8, i8, i32)> {
|
||||
}
|
||||
|
||||
/// Parses `Z` (UTC) or `{+,-,}HH:MM` into a time zone offset in seconds.
|
||||
fn parse_zone(input: &str) -> IResult<&str, i32> {
|
||||
fn parse_zone(input: &str) -> IResult<'_, &str, i32> {
|
||||
alt((
|
||||
nom::combinator::value(0, tag("Z")),
|
||||
map(
|
||||
|
||||
@ -122,33 +122,36 @@ pub fn install() {
|
||||
|
||||
match std::env::var("MOONFIRE_FORMAT") {
|
||||
Ok(s) if s == "systemd" => {
|
||||
let sub = tracing_subscriber::registry().with(
|
||||
tracing_subscriber::fmt::Layer::new()
|
||||
.with_writer(std::io::stderr)
|
||||
.with_ansi(false)
|
||||
.event_format(FormatSystemd)
|
||||
.with_filter(filter),
|
||||
);
|
||||
let sub = tracing_subscriber::registry()
|
||||
.with(
|
||||
tracing_subscriber::fmt::Layer::new()
|
||||
.with_writer(std::io::stderr)
|
||||
.with_ansi(false)
|
||||
.event_format(FormatSystemd),
|
||||
)
|
||||
.with(filter);
|
||||
tracing::subscriber::set_global_default(sub).unwrap();
|
||||
}
|
||||
Ok(s) if s == "json" => {
|
||||
let sub = tracing_subscriber::registry().with(
|
||||
tracing_subscriber::fmt::Layer::new()
|
||||
.with_writer(std::io::stderr)
|
||||
.with_thread_names(true)
|
||||
.json()
|
||||
.with_filter(filter),
|
||||
);
|
||||
let sub = tracing_subscriber::registry()
|
||||
.with(
|
||||
tracing_subscriber::fmt::Layer::new()
|
||||
.with_writer(std::io::stderr)
|
||||
.with_thread_names(true)
|
||||
.json(),
|
||||
)
|
||||
.with(filter);
|
||||
tracing::subscriber::set_global_default(sub).unwrap();
|
||||
}
|
||||
_ => {
|
||||
let sub = tracing_subscriber::registry().with(
|
||||
tracing_subscriber::fmt::Layer::new()
|
||||
.with_writer(std::io::stderr)
|
||||
.with_timer(JiffTimer)
|
||||
.with_thread_names(true)
|
||||
.with_filter(filter),
|
||||
);
|
||||
let sub = tracing_subscriber::registry()
|
||||
.with(
|
||||
tracing_subscriber::fmt::Layer::new()
|
||||
.with_writer(std::io::stderr)
|
||||
.with_timer(JiffTimer)
|
||||
.with_thread_names(true),
|
||||
)
|
||||
.with(filter);
|
||||
tracing::subscriber::set_global_default(sub).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@ authors = ["Scott Lamb <slamb@slamb.org>"]
|
||||
readme = "../README.md"
|
||||
edition = "2021"
|
||||
license-file = "../../LICENSE.txt"
|
||||
rust-version = "1.82"
|
||||
rust-version = "1.88"
|
||||
publish = false
|
||||
|
||||
[features]
|
||||
@ -19,7 +19,6 @@ base = { package = "moonfire-base", path = "../base" }
|
||||
base64 = { workspace = true }
|
||||
blake3 = "1.0.0"
|
||||
byteorder = "1.0"
|
||||
cstr = "0.2.5"
|
||||
diff = "0.1.12"
|
||||
futures = "0.3"
|
||||
h264-reader = { workspace = true }
|
||||
@ -28,9 +27,11 @@ itertools = { workspace = true }
|
||||
jiff = { workspace = true }
|
||||
libc = "0.2"
|
||||
nix = { workspace = true, features = ["dir", "feature", "fs", "mman"] }
|
||||
num-rational = { version = "0.4.0", default-features = false, features = ["std"] }
|
||||
num-rational = { version = "0.4.0", default-features = false, features = [
|
||||
"std",
|
||||
] }
|
||||
pretty-hex = { workspace = true }
|
||||
protobuf = "3.0"
|
||||
protobuf = { workspace = true }
|
||||
ring = { workspace = true }
|
||||
rusqlite = { workspace = true }
|
||||
scrypt = "0.11.0"
|
||||
@ -40,9 +41,8 @@ smallvec = "1.0"
|
||||
tempfile = "3.2.0"
|
||||
tokio = { version = "1.24", features = ["macros", "rt-multi-thread", "sync"] }
|
||||
tracing = { workspace = true }
|
||||
ulid = "1.0.0"
|
||||
url = { version = "2.1.1", features = ["serde"] }
|
||||
uuid = { version = "1.1.2", features = ["serde", "std", "v4"] }
|
||||
uuid = { workspace = true }
|
||||
|
||||
[build-dependencies]
|
||||
protobuf-codegen = "3.0"
|
||||
protobuf-codegen = { workspace = true }
|
||||
|
||||
@ -1073,8 +1073,8 @@ mod tests {
|
||||
.unwrap();
|
||||
assert_eq!(s.use_count, 1);
|
||||
|
||||
let mut tx = conn.transaction().unwrap();
|
||||
state.flush(&mut tx).unwrap();
|
||||
let tx = conn.transaction().unwrap();
|
||||
state.flush(&tx).unwrap();
|
||||
tx.commit().unwrap();
|
||||
state.post_flush();
|
||||
|
||||
@ -1224,8 +1224,8 @@ mod tests {
|
||||
c.username = "foo".to_owned();
|
||||
state.apply(&conn, c).unwrap();
|
||||
|
||||
assert!(state.users_by_name.get("slamb").is_none());
|
||||
assert!(state.users_by_name.get("foo").is_some());
|
||||
assert!(!state.users_by_name.contains_key("slamb"));
|
||||
assert!(state.users_by_name.contains_key("foo"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@ -36,6 +36,7 @@ use crate::schema;
|
||||
use crate::signal;
|
||||
use base::clock::{self, Clocks};
|
||||
use base::strutil::encode_size;
|
||||
use base::Mutex;
|
||||
use base::{bail, err, Error};
|
||||
use base::{FastHashMap, FastHashSet};
|
||||
use hashlink::LinkedHashMap;
|
||||
@ -52,7 +53,7 @@ use std::path::PathBuf;
|
||||
use std::str;
|
||||
use std::string::String;
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Mutex, MutexGuard};
|
||||
use std::sync::MutexGuard;
|
||||
use std::vec::Vec;
|
||||
use tracing::warn;
|
||||
use tracing::{error, info, trace};
|
||||
@ -562,7 +563,7 @@ impl Stream {
|
||||
pub fn days(&self) -> days::Map<days::StreamValue> {
|
||||
let mut days = self.committed_days.clone();
|
||||
for u in &self.uncommitted {
|
||||
let l = u.lock().unwrap();
|
||||
let l = u.lock();
|
||||
days.adjust(
|
||||
l.start..l.start + recording::Duration(i64::from(l.wall_duration_90k)),
|
||||
1,
|
||||
@ -650,7 +651,7 @@ pub struct CompositeId(pub i64);
|
||||
|
||||
impl CompositeId {
|
||||
pub fn new(stream_id: i32, recording_id: i32) -> Self {
|
||||
CompositeId((stream_id as i64) << 32 | recording_id as i64)
|
||||
CompositeId(((stream_id as i64) << 32) | recording_id as i64)
|
||||
}
|
||||
|
||||
pub fn stream(self) -> i32 {
|
||||
@ -896,7 +897,7 @@ impl LockedDatabase {
|
||||
);
|
||||
match stream.uncommitted.back() {
|
||||
Some(s) => {
|
||||
let l = s.lock().unwrap();
|
||||
let l = s.lock();
|
||||
r.prev_media_duration =
|
||||
l.prev_media_duration + recording::Duration(l.media_duration_90k.into());
|
||||
r.prev_runs = l.prev_runs + if l.run_offset == 0 { 1 } else { 0 };
|
||||
@ -936,7 +937,7 @@ impl LockedDatabase {
|
||||
msg("can't sync un-added recording {id}")
|
||||
);
|
||||
}
|
||||
let l = stream.uncommitted[stream.synced_recordings].lock().unwrap();
|
||||
let l = stream.uncommitted[stream.synced_recordings].lock();
|
||||
let bytes = i64::from(l.sample_file_bytes);
|
||||
stream.bytes_to_add += bytes;
|
||||
stream.fs_bytes_to_add += round_up(bytes);
|
||||
@ -1014,7 +1015,7 @@ impl LockedDatabase {
|
||||
let mut new_duration = 0;
|
||||
let mut new_runs = 0;
|
||||
for i in 0..s.synced_recordings {
|
||||
let l = s.uncommitted[i].lock().unwrap();
|
||||
let l = s.uncommitted[i].lock();
|
||||
raw::insert_recording(
|
||||
&tx,
|
||||
o,
|
||||
@ -1141,7 +1142,7 @@ impl LockedDatabase {
|
||||
let u = s.uncommitted.pop_front().unwrap();
|
||||
log.added
|
||||
.push(CompositeId::new(stream_id, s.cum_recordings));
|
||||
let l = u.lock().unwrap();
|
||||
let l = u.lock();
|
||||
s.cum_recordings += 1;
|
||||
let wall_dur = recording::Duration(l.wall_duration_90k.into());
|
||||
let media_dur = recording::Duration(l.media_duration_90k.into());
|
||||
@ -1310,7 +1311,7 @@ impl LockedDatabase {
|
||||
raw::list_recordings_by_time(&self.conn, stream_id, desired_time.clone(), f)?;
|
||||
for (i, u) in s.uncommitted.iter().enumerate() {
|
||||
let row = {
|
||||
let l = u.lock().unwrap();
|
||||
let l = u.lock();
|
||||
if l.video_samples > 0 {
|
||||
let end = l.start + recording::Duration(l.wall_duration_90k as i64);
|
||||
if l.start > desired_time.end || end < desired_time.start {
|
||||
@ -1351,7 +1352,7 @@ impl LockedDatabase {
|
||||
);
|
||||
for i in start..end {
|
||||
let row = {
|
||||
let l = s.uncommitted[i].lock().unwrap();
|
||||
let l = s.uncommitted[i].lock();
|
||||
if l.video_samples > 0 {
|
||||
l.to_list_row(
|
||||
CompositeId::new(stream_id, s.cum_recordings + i as i32),
|
||||
@ -1489,7 +1490,7 @@ impl LockedDatabase {
|
||||
),
|
||||
);
|
||||
}
|
||||
let l = s.uncommitted[i as usize].lock().unwrap();
|
||||
let l = s.uncommitted[i as usize].lock();
|
||||
return f(&RecordingPlayback {
|
||||
video_index: &l.video_index,
|
||||
});
|
||||
@ -1797,7 +1798,7 @@ impl LockedDatabase {
|
||||
|
||||
pub fn add_sample_file_dir(&mut self, path: PathBuf) -> Result<i32, Error> {
|
||||
let mut meta = schema::DirMeta::default();
|
||||
let uuid = Uuid::new_v4();
|
||||
let uuid = Uuid::now_v7();
|
||||
let uuid_bytes = &uuid.as_bytes()[..];
|
||||
let o = self
|
||||
.open
|
||||
@ -1906,7 +1907,7 @@ impl LockedDatabase {
|
||||
|
||||
/// Adds a camera.
|
||||
pub fn add_camera(&mut self, mut camera: CameraChange) -> Result<i32, Error> {
|
||||
let uuid = Uuid::new_v4();
|
||||
let uuid = Uuid::now_v7();
|
||||
let uuid_bytes = &uuid.as_bytes()[..];
|
||||
let tx = self.conn.transaction()?;
|
||||
let streams;
|
||||
@ -2227,7 +2228,7 @@ pub fn init(conn: &mut rusqlite::Connection) -> Result<(), Error> {
|
||||
tx.execute_batch(include_str!("schema.sql"))
|
||||
.map_err(|e| err!(e, msg("unable to create database schema")))?;
|
||||
{
|
||||
let uuid = ::uuid::Uuid::new_v4();
|
||||
let uuid = ::uuid::Uuid::now_v7();
|
||||
let uuid_bytes = &uuid.as_bytes()[..];
|
||||
tx.execute("insert into meta (uuid) values (?)", params![uuid_bytes])?;
|
||||
}
|
||||
@ -2319,7 +2320,7 @@ impl<C: Clocks + Clone> Drop for Database<C> {
|
||||
return; // don't flush while panicking.
|
||||
}
|
||||
if let Some(m) = self.db.take() {
|
||||
if let Err(e) = m.into_inner().unwrap().flush(&self.clocks, "drop") {
|
||||
if let Err(e) = m.into_inner().flush(&self.clocks, "drop") {
|
||||
error!(err = %e.chain(), "final database flush failed");
|
||||
}
|
||||
}
|
||||
@ -2353,7 +2354,7 @@ impl<C: Clocks + Clone> Database<C> {
|
||||
let real = recording::Time::from(clocks.realtime());
|
||||
let mut stmt = conn
|
||||
.prepare(" insert into open (uuid, start_time_90k, boot_uuid) values (?, ?, ?)")?;
|
||||
let open_uuid = SqlUuid(Uuid::new_v4());
|
||||
let open_uuid = SqlUuid(Uuid::now_v7());
|
||||
let boot_uuid = match get_boot_uuid() {
|
||||
Err(e) => {
|
||||
warn!(err = %e.chain(), "unable to get boot uuid");
|
||||
@ -2416,9 +2417,9 @@ impl<C: Clocks + Clone> Database<C> {
|
||||
|
||||
/// Locks the database; the returned reference is the only way to perform (read or write)
|
||||
/// operations.
|
||||
pub fn lock(&self) -> DatabaseGuard<C> {
|
||||
pub fn lock(&self) -> DatabaseGuard<'_, C> {
|
||||
let timer = clock::TimerGuard::new(&self.clocks, acquisition);
|
||||
let db = self.db.as_ref().unwrap().lock().unwrap();
|
||||
let db = self.db.as_ref().unwrap().lock();
|
||||
drop(timer);
|
||||
let _timer = clock::TimerGuard::<C, &'static str, fn() -> &'static str>::new(
|
||||
&self.clocks,
|
||||
@ -2435,7 +2436,7 @@ impl<C: Clocks + Clone> Database<C> {
|
||||
/// This allows verification that a newly opened database is in an acceptable state.
|
||||
#[cfg(test)]
|
||||
fn close(mut self) -> rusqlite::Connection {
|
||||
self.db.take().unwrap().into_inner().unwrap().conn
|
||||
self.db.take().unwrap().into_inner().conn
|
||||
}
|
||||
}
|
||||
|
||||
@ -2519,7 +2520,7 @@ mod tests {
|
||||
rows = 0;
|
||||
{
|
||||
let db = db.lock();
|
||||
let all_time = recording::Time(i64::min_value())..recording::Time(i64::max_value());
|
||||
let all_time = recording::Time(i64::MIN)..recording::Time(i64::MAX);
|
||||
db.list_recordings_by_time(stream_id, all_time, &mut |_row| {
|
||||
rows += 1;
|
||||
Ok(())
|
||||
@ -2546,7 +2547,7 @@ mod tests {
|
||||
let mut recording_id = None;
|
||||
{
|
||||
let db = db.lock();
|
||||
let all_time = recording::Time(i64::min_value())..recording::Time(i64::max_value());
|
||||
let all_time = recording::Time(i64::MIN)..recording::Time(i64::MAX);
|
||||
db.list_recordings_by_time(stream_id, all_time, &mut |row| {
|
||||
rows += 1;
|
||||
recording_id = Some(row.id);
|
||||
@ -2868,9 +2869,8 @@ mod tests {
|
||||
.get(&sample_file_dir_id)
|
||||
.unwrap()
|
||||
.garbage_unlinked
|
||||
.iter()
|
||||
.copied()
|
||||
.collect();
|
||||
.to_vec();
|
||||
|
||||
assert_eq!(&g, &[]);
|
||||
}
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
//! This mostly includes opening a directory and looking for recordings within it.
|
||||
//! Updates to the directory happen through [crate::writer].
|
||||
|
||||
mod reader;
|
||||
pub mod reader;
|
||||
|
||||
use crate::coding;
|
||||
use crate::db::CompositeId;
|
||||
@ -421,12 +421,12 @@ mod tests {
|
||||
meta.dir_uuid.extend_from_slice(fake_uuid);
|
||||
{
|
||||
let o = meta.last_complete_open.mut_or_insert_default();
|
||||
o.id = u32::max_value();
|
||||
o.id = u32::MAX;
|
||||
o.uuid.extend_from_slice(fake_uuid);
|
||||
}
|
||||
{
|
||||
let o = meta.in_progress_open.mut_or_insert_default();
|
||||
o.id = u32::max_value();
|
||||
o.id = u32::MAX;
|
||||
o.uuid.extend_from_slice(fake_uuid);
|
||||
}
|
||||
let data = meta
|
||||
|
||||
@ -656,13 +656,16 @@ mod bench {
|
||||
/// Benchmarks the decoder, which is performance-critical for .mp4 serving.
|
||||
#[bench]
|
||||
fn bench_decoder(b: &mut test::Bencher) {
|
||||
crate::testutil::init();
|
||||
let data = include_bytes!("testdata/video_sample_index.bin");
|
||||
b.bytes = data.len() as u64;
|
||||
b.iter(|| {
|
||||
let mut it = SampleIndexIterator::default();
|
||||
while it.next(data).unwrap() {}
|
||||
assert_eq!(30104460, it.pos);
|
||||
assert_eq!(5399985, it.start_90k);
|
||||
for _i in 0..100 {
|
||||
let mut it = SampleIndexIterator::default();
|
||||
while it.next(data).unwrap() {}
|
||||
assert_eq!(30104460, it.pos);
|
||||
assert_eq!(5399985, it.start_90k);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -81,12 +81,12 @@ impl Point {
|
||||
}
|
||||
|
||||
/// Returns an iterator over state as of immediately before this point.
|
||||
fn prev(&self) -> PointDataIterator {
|
||||
fn prev(&self) -> PointDataIterator<'_> {
|
||||
PointDataIterator::new(&self.data[0..self.changes_off])
|
||||
}
|
||||
|
||||
/// Returns an iterator over changes in this point.
|
||||
fn changes(&self) -> PointDataIterator {
|
||||
fn changes(&self) -> PointDataIterator<'_> {
|
||||
PointDataIterator::new(&self.data[self.changes_off..])
|
||||
}
|
||||
|
||||
|
||||
@ -37,6 +37,7 @@ pub const TEST_VIDEO_SAMPLE_ENTRY_DATA: &[u8] =
|
||||
/// * use a fast but insecure password hashing format.
|
||||
pub fn init() {
|
||||
INIT.call_once(|| {
|
||||
base::ensure_malloc_used();
|
||||
base::tracing_setup::install_for_tests();
|
||||
base::time::testutil::init_zone();
|
||||
crate::auth::set_test_config();
|
||||
@ -198,7 +199,7 @@ pub fn add_dummy_recordings_to_db(db: &db::Database, num: usize) {
|
||||
wall_duration_90k: 5399985,
|
||||
video_samples: 1800,
|
||||
video_sync_samples: 60,
|
||||
video_sample_entry_id: video_sample_entry_id,
|
||||
video_sample_entry_id,
|
||||
video_index: data,
|
||||
run_offset: 0,
|
||||
..Default::default()
|
||||
|
||||
@ -80,17 +80,17 @@ pub fn run(args: &super::Args, tx: &rusqlite::Transaction) -> Result<(), Error>
|
||||
create index user_session_uid on user_session (user_id);
|
||||
"#,
|
||||
)?;
|
||||
let db_uuid = ::uuid::Uuid::new_v4();
|
||||
let db_uuid = ::uuid::Uuid::now_v7();
|
||||
let db_uuid_bytes = &db_uuid.as_bytes()[..];
|
||||
tx.execute("insert into meta (uuid) values (?)", params![db_uuid_bytes])?;
|
||||
let open_uuid = ::uuid::Uuid::new_v4();
|
||||
let open_uuid = ::uuid::Uuid::now_v7();
|
||||
let open_uuid_bytes = &open_uuid.as_bytes()[..];
|
||||
tx.execute(
|
||||
"insert into open (uuid) values (?)",
|
||||
params![open_uuid_bytes],
|
||||
)?;
|
||||
let open_id = tx.last_insert_rowid() as u32;
|
||||
let dir_uuid = ::uuid::Uuid::new_v4();
|
||||
let dir_uuid = ::uuid::Uuid::now_v7();
|
||||
let dir_uuid_bytes = &dir_uuid.as_bytes()[..];
|
||||
|
||||
// Write matching metadata to the directory.
|
||||
|
||||
@ -27,7 +27,7 @@ fn default_pixel_aspect_ratio(width: u16, height: u16) -> (u16, u16) {
|
||||
(1, 1)
|
||||
}
|
||||
|
||||
fn parse(data: &[u8]) -> Result<AvcDecoderConfigurationRecord, Error> {
|
||||
fn parse(data: &[u8]) -> Result<AvcDecoderConfigurationRecord<'_>, Error> {
|
||||
if data.len() < 94 || &data[4..8] != b"avc1" || &data[90..94] != b"avcC" {
|
||||
bail!(
|
||||
DataLoss,
|
||||
|
||||
@ -10,13 +10,13 @@ use crate::recording::{self, MAX_RECORDING_WALL_DURATION};
|
||||
use base::clock::{self, Clocks};
|
||||
use base::shutdown::ShutdownError;
|
||||
use base::FastHashMap;
|
||||
use base::Mutex;
|
||||
use base::{bail, err, Error};
|
||||
use std::cmp::{self, Ordering};
|
||||
use std::convert::TryFrom;
|
||||
use std::io;
|
||||
use std::mem;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Mutex;
|
||||
use std::sync::{mpsc, Arc};
|
||||
use std::thread;
|
||||
use tracing::{debug, trace, warn};
|
||||
@ -880,7 +880,7 @@ impl<F: FileWriter> InnerWriter<F> {
|
||||
db: &db::Database<C>,
|
||||
stream_id: i32,
|
||||
) -> Result<(), Error> {
|
||||
let mut l = self.r.lock().unwrap();
|
||||
let mut l = self.r.lock();
|
||||
|
||||
// design/time.md explains these time manipulations in detail.
|
||||
let prev_media_duration_90k = l.media_duration_90k;
|
||||
@ -969,7 +969,7 @@ impl<F: FileWriter> InnerWriter<F> {
|
||||
// This always ends a live segment.
|
||||
let wall_duration;
|
||||
{
|
||||
let mut l = self.r.lock().unwrap();
|
||||
let mut l = self.r.lock();
|
||||
l.flags = flags;
|
||||
l.local_time_delta = self.local_start - l.start;
|
||||
l.sample_file_blake3 = Some(*blake3.as_bytes());
|
||||
@ -1012,11 +1012,11 @@ mod tests {
|
||||
use crate::recording;
|
||||
use crate::testutil;
|
||||
use base::clock::{Clocks, SimulatedClocks};
|
||||
use base::Mutex;
|
||||
use std::collections::VecDeque;
|
||||
use std::io;
|
||||
use std::sync::mpsc;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use tracing::{trace, warn};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -1039,10 +1039,10 @@ mod tests {
|
||||
MockDir(Arc::new(Mutex::new(VecDeque::new())))
|
||||
}
|
||||
fn expect(&self, action: MockDirAction) {
|
||||
self.0.lock().unwrap().push_back(action);
|
||||
self.0.lock().push_back(action);
|
||||
}
|
||||
fn ensure_done(&self) {
|
||||
assert_eq!(self.0.lock().unwrap().len(), 0);
|
||||
assert_eq!(self.0.lock().len(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1053,7 +1053,6 @@ mod tests {
|
||||
match self
|
||||
.0
|
||||
.lock()
|
||||
.unwrap()
|
||||
.pop_front()
|
||||
.expect("got create_file with no expectation")
|
||||
{
|
||||
@ -1068,7 +1067,6 @@ mod tests {
|
||||
match self
|
||||
.0
|
||||
.lock()
|
||||
.unwrap()
|
||||
.pop_front()
|
||||
.expect("got sync with no expectation")
|
||||
{
|
||||
@ -1080,7 +1078,6 @@ mod tests {
|
||||
match self
|
||||
.0
|
||||
.lock()
|
||||
.unwrap()
|
||||
.pop_front()
|
||||
.expect("got unlink_file with no expectation")
|
||||
{
|
||||
@ -1096,7 +1093,7 @@ mod tests {
|
||||
impl Drop for MockDir {
|
||||
fn drop(&mut self) {
|
||||
if !::std::thread::panicking() {
|
||||
assert_eq!(self.0.lock().unwrap().len(), 0);
|
||||
assert_eq!(self.0.lock().len(), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1106,6 +1103,8 @@ mod tests {
|
||||
|
||||
enum MockFileAction {
|
||||
SyncAll(Box<dyn Fn() -> Result<(), io::Error> + Send>),
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
Write(Box<dyn Fn(&[u8]) -> Result<usize, io::Error> + Send>),
|
||||
}
|
||||
|
||||
@ -1114,10 +1113,10 @@ mod tests {
|
||||
MockFile(Arc::new(Mutex::new(VecDeque::new())))
|
||||
}
|
||||
fn expect(&self, action: MockFileAction) {
|
||||
self.0.lock().unwrap().push_back(action);
|
||||
self.0.lock().push_back(action);
|
||||
}
|
||||
fn ensure_done(&self) {
|
||||
assert_eq!(self.0.lock().unwrap().len(), 0);
|
||||
assert_eq!(self.0.lock().len(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1126,7 +1125,6 @@ mod tests {
|
||||
match self
|
||||
.0
|
||||
.lock()
|
||||
.unwrap()
|
||||
.pop_front()
|
||||
.expect("got sync_all with no expectation")
|
||||
{
|
||||
@ -1138,7 +1136,6 @@ mod tests {
|
||||
match self
|
||||
.0
|
||||
.lock()
|
||||
.unwrap()
|
||||
.pop_front()
|
||||
.expect("got write with no expectation")
|
||||
{
|
||||
@ -1215,7 +1212,7 @@ mod tests {
|
||||
}
|
||||
|
||||
fn eio() -> io::Error {
|
||||
io::Error::new(io::ErrorKind::Other, "got EIO")
|
||||
io::Error::other("got EIO")
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -1263,7 +1260,7 @@ mod tests {
|
||||
&mut h.shutdown_rx,
|
||||
b"2",
|
||||
recording::Time(2),
|
||||
i32::max_value() as i64 + 1,
|
||||
i64::from(i32::MAX) + 1,
|
||||
true,
|
||||
video_sample_entry_id,
|
||||
)
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
|
||||
use base::strutil::{decode_size, encode_size};
|
||||
use base::Error;
|
||||
use base::Mutex;
|
||||
use cursive::traits::{Nameable, Resizable};
|
||||
use cursive::view::Scrollable;
|
||||
use cursive::Cursive;
|
||||
@ -11,7 +12,7 @@ use cursive::{views, With};
|
||||
use db::writer;
|
||||
use std::collections::BTreeMap;
|
||||
use std::path::Path;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::Arc;
|
||||
use tracing::{debug, trace};
|
||||
|
||||
use super::tab_complete::TabCompleteEditView;
|
||||
@ -119,7 +120,7 @@ fn confirm_deletion(model: &Mutex<Model>, siv: &mut Cursive, to_delete: i64) {
|
||||
}
|
||||
|
||||
fn actually_delete(model: &Mutex<Model>, siv: &mut Cursive) {
|
||||
let model = model.lock().unwrap();
|
||||
let model = model.lock();
|
||||
let new_limits: Vec<_> = model
|
||||
.streams
|
||||
.iter()
|
||||
@ -147,7 +148,7 @@ fn actually_delete(model: &Mutex<Model>, siv: &mut Cursive) {
|
||||
|
||||
fn press_change(model: &Arc<Mutex<Model>>, siv: &mut Cursive) {
|
||||
let to_delete = {
|
||||
let l = model.lock().unwrap();
|
||||
let l = model.lock();
|
||||
if l.errors > 0 {
|
||||
return;
|
||||
}
|
||||
@ -185,7 +186,7 @@ fn press_change(model: &Arc<Mutex<Model>>, siv: &mut Cursive) {
|
||||
siv.add_layer(dialog);
|
||||
} else {
|
||||
siv.pop_layer();
|
||||
update_limits(&model.lock().unwrap(), siv);
|
||||
update_limits(&model.lock(), siv);
|
||||
}
|
||||
}
|
||||
|
||||
@ -384,13 +385,13 @@ fn edit_dir_dialog(db: &Arc<db::Database>, siv: &mut Cursive, dir_id: i32) {
|
||||
.child(views::TextView::new("usage").fixed_width(BYTES_WIDTH))
|
||||
.child(views::TextView::new("limit").fixed_width(BYTES_WIDTH)),
|
||||
);
|
||||
let l = model.lock().unwrap();
|
||||
let l = model.lock();
|
||||
for (&id, stream) in &l.streams {
|
||||
let mut record_cb = views::Checkbox::new();
|
||||
record_cb.set_checked(stream.record);
|
||||
record_cb.set_on_change({
|
||||
let model = model.clone();
|
||||
move |_siv, record| edit_record(&mut model.lock().unwrap(), id, record)
|
||||
move |_siv, record| edit_record(&mut model.lock(), id, record)
|
||||
});
|
||||
list.add_child(
|
||||
&stream.label,
|
||||
@ -403,7 +404,7 @@ fn edit_dir_dialog(db: &Arc<db::Database>, siv: &mut Cursive, dir_id: i32) {
|
||||
.on_edit({
|
||||
let model = model.clone();
|
||||
move |siv, content, _pos| {
|
||||
edit_limit(&mut model.lock().unwrap(), siv, id, content)
|
||||
edit_limit(&mut model.lock(), siv, id, content)
|
||||
}
|
||||
})
|
||||
.on_submit({
|
||||
|
||||
@ -2,7 +2,8 @@
|
||||
// Copyright (C) 2020 The Moonfire NVR Authors; see AUTHORS and LICENSE.txt.
|
||||
// SPDX-License-Identifier: GPL-v3.0-or-later WITH GPL-3.0-linking-exception.
|
||||
|
||||
use std::sync::{Arc, Mutex};
|
||||
use base::Mutex;
|
||||
use std::sync::Arc;
|
||||
|
||||
use cursive::{
|
||||
direction::Direction,
|
||||
@ -37,25 +38,25 @@ impl TabCompleteEditView {
|
||||
}
|
||||
|
||||
pub fn get_content(&self) -> Arc<String> {
|
||||
self.edit_view.lock().unwrap().get_content()
|
||||
self.edit_view.lock().get_content()
|
||||
}
|
||||
}
|
||||
|
||||
impl View for TabCompleteEditView {
|
||||
fn draw(&self, printer: &Printer) {
|
||||
self.edit_view.lock().unwrap().draw(printer)
|
||||
self.edit_view.lock().draw(printer)
|
||||
}
|
||||
|
||||
fn layout(&mut self, size: Vec2) {
|
||||
self.edit_view.lock().unwrap().layout(size)
|
||||
self.edit_view.lock().layout(size)
|
||||
}
|
||||
|
||||
fn take_focus(&mut self, source: Direction) -> Result<EventResult, CannotFocus> {
|
||||
self.edit_view.lock().unwrap().take_focus(source)
|
||||
self.edit_view.lock().take_focus(source)
|
||||
}
|
||||
|
||||
fn on_event(&mut self, event: Event) -> EventResult {
|
||||
if !self.edit_view.lock().unwrap().is_enabled() {
|
||||
if !self.edit_view.lock().is_enabled() {
|
||||
return EventResult::Ignored;
|
||||
}
|
||||
|
||||
@ -66,12 +67,12 @@ impl View for TabCompleteEditView {
|
||||
EventResult::consumed()
|
||||
}
|
||||
} else {
|
||||
self.edit_view.lock().unwrap().on_event(event)
|
||||
self.edit_view.lock().on_event(event)
|
||||
}
|
||||
}
|
||||
|
||||
fn important_area(&self, view_size: Vec2) -> Rect {
|
||||
self.edit_view.lock().unwrap().important_area(view_size)
|
||||
self.edit_view.lock().important_area(view_size)
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,10 +81,10 @@ fn tab_complete(
|
||||
tab_completer: TabCompleteFn,
|
||||
autofill_one: bool,
|
||||
) -> EventResult {
|
||||
let completions = tab_completer(edit_view.lock().unwrap().get_content().as_str());
|
||||
let completions = tab_completer(edit_view.lock().get_content().as_str());
|
||||
EventResult::with_cb_once(move |siv| match *completions {
|
||||
[] => {}
|
||||
[ref completion] if autofill_one => edit_view.lock().unwrap().set_content(completion)(siv),
|
||||
[ref completion] if autofill_one => edit_view.lock().set_content(completion)(siv),
|
||||
[..] => {
|
||||
siv.add_layer(TabCompletePopup {
|
||||
popup: views::MenuPopup::new(Arc::new({
|
||||
@ -91,7 +92,7 @@ fn tab_complete(
|
||||
for completion in completions {
|
||||
let edit_view = edit_view.clone();
|
||||
tree.add_leaf(completion.clone(), move |siv| {
|
||||
edit_view.lock().unwrap().set_content(&completion)(siv)
|
||||
edit_view.lock().set_content(&completion)(siv)
|
||||
})
|
||||
}
|
||||
})
|
||||
@ -114,7 +115,7 @@ impl TabCompletePopup {
|
||||
let tab_completer = self.tab_completer.clone();
|
||||
EventResult::with_cb_once(move |s| {
|
||||
s.pop_layer();
|
||||
edit_view.lock().unwrap().on_event(event).process(s);
|
||||
edit_view.lock().on_event(event).process(s);
|
||||
tab_complete(edit_view, tab_completer, false).process(s);
|
||||
})
|
||||
}
|
||||
|
||||
@ -77,6 +77,7 @@ fn main() {
|
||||
}
|
||||
base::tracing_setup::install();
|
||||
base::time::init_zone(jiff::tz::TimeZone::system);
|
||||
base::ensure_malloc_used();
|
||||
|
||||
// Get the program name from the OS (e.g. if invoked as `target/debug/nvr`: `nvr`),
|
||||
// falling back to the crate name if conversion to a path/UTF-8 string fails.
|
||||
|
||||
@ -61,10 +61,10 @@ use byteorder::{BigEndian, ByteOrder, WriteBytesExt};
|
||||
use bytes::BytesMut;
|
||||
use db::dir;
|
||||
use db::recording::{self, rescale, TIME_UNITS_PER_SEC};
|
||||
use futures::stream::{self, TryStreamExt};
|
||||
use futures::Stream;
|
||||
use http::header::HeaderValue;
|
||||
use hyper::body::Buf;
|
||||
use pin_project::pin_project;
|
||||
use reffers::ARefss;
|
||||
use smallvec::SmallVec;
|
||||
use std::cmp;
|
||||
@ -403,7 +403,7 @@ impl Segment {
|
||||
.lock()
|
||||
.with_recording_playback(self.s.id, &mut |playback| self.build_index(playback))
|
||||
.map_err(|err| {
|
||||
error!(%err, recording_id = %self.s.id, "unable to build index for segment");
|
||||
error!(err = %err.chain(), recording_id = %self.s.id, "unable to build index for segment");
|
||||
})
|
||||
})
|
||||
.as_deref()
|
||||
@ -758,19 +758,37 @@ impl Slice {
|
||||
}
|
||||
}
|
||||
|
||||
#[pin_project(project = SliceStreamProj)]
|
||||
enum SliceStream {
|
||||
Once(Option<Result<Chunk, Error>>),
|
||||
File(#[pin] db::dir::reader::FileStream),
|
||||
}
|
||||
|
||||
impl futures::stream::Stream for SliceStream {
|
||||
type Item = Result<Chunk, BoxedError>;
|
||||
|
||||
fn poll_next(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
) -> std::task::Poll<Option<Self::Item>> {
|
||||
match self.project() {
|
||||
SliceStreamProj::Once(o) => {
|
||||
std::task::Poll::Ready(o.take().map(|r| r.map_err(wrap_error)))
|
||||
}
|
||||
SliceStreamProj::File(f) => f.poll_next(cx).map_ok(Chunk::from).map_err(wrap_error),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl slices::Slice for Slice {
|
||||
type Ctx = File;
|
||||
type Chunk = Chunk;
|
||||
type Stream = SliceStream;
|
||||
|
||||
fn end(&self) -> u64 {
|
||||
self.0 & 0xFF_FF_FF_FF_FF
|
||||
}
|
||||
fn get_range(
|
||||
&self,
|
||||
f: &File,
|
||||
range: Range<u64>,
|
||||
len: u64,
|
||||
) -> Box<dyn Stream<Item = Result<Self::Chunk, BoxedError>> + Send + Sync> {
|
||||
fn get_range(&self, f: &File, range: Range<u64>, len: u64) -> SliceStream {
|
||||
trace!("getting mp4 slice {:?}'s range {:?} / {}", self, range, len);
|
||||
let p = self.p();
|
||||
let res = match self.t() {
|
||||
@ -802,22 +820,20 @@ impl slices::Slice for Slice {
|
||||
SliceType::SubtitleSampleData => f.0.get_subtitle_sample_data(p, range.clone(), len),
|
||||
SliceType::Truns => self.wrap_truns(f, range.clone(), len as usize),
|
||||
};
|
||||
Box::new(stream::once(futures::future::ready(
|
||||
res.map_err(wrap_error).and_then(move |c| {
|
||||
if c.remaining() != (range.end - range.start) as usize {
|
||||
return Err(wrap_error(err!(
|
||||
Internal,
|
||||
msg(
|
||||
"{:?} range {:?} produced incorrect len {}",
|
||||
self,
|
||||
range,
|
||||
c.remaining()
|
||||
)
|
||||
)));
|
||||
}
|
||||
Ok(c)
|
||||
}),
|
||||
)))
|
||||
SliceStream::Once(Some(res.and_then(move |c| {
|
||||
if c.remaining() != (range.end - range.start) as usize {
|
||||
bail!(
|
||||
Internal,
|
||||
msg(
|
||||
"{:?} range {:?} produced incorrect len {}",
|
||||
self,
|
||||
range,
|
||||
c.remaining()
|
||||
)
|
||||
);
|
||||
}
|
||||
Ok(c)
|
||||
})))
|
||||
}
|
||||
|
||||
fn get_slices(ctx: &File) -> &Slices<Self> {
|
||||
@ -1796,32 +1812,20 @@ impl FileInner {
|
||||
.into())
|
||||
}
|
||||
|
||||
/// Gets a `Chunk` of video sample data from disk.
|
||||
/// This works by `mmap()`ing in the data. There are a couple caveats:
|
||||
///
|
||||
/// * The thread which reads the resulting slice is likely to experience major page faults.
|
||||
/// Eventually this will likely be rewritten to `mmap()` the memory in another thread, and
|
||||
/// `mlock()` and send chunks of it to be read and `munlock()`ed to avoid this problem.
|
||||
///
|
||||
/// * If the backing file is truncated, the program will crash with `SIGBUS`. This shouldn't
|
||||
/// happen because nothing should be touching Moonfire NVR's files but itself.
|
||||
fn get_video_sample_data(
|
||||
&self,
|
||||
i: usize,
|
||||
r: Range<u64>,
|
||||
) -> Box<dyn Stream<Item = Result<Chunk, BoxedError>> + Send + Sync> {
|
||||
/// Gets a stream representing a range of segment `i`'s sample data from disk.
|
||||
fn get_video_sample_data(&self, i: usize, r: Range<u64>) -> SliceStream {
|
||||
let s = &self.segments[i];
|
||||
let sr = s.s.sample_file_range();
|
||||
let f = match self.dirs_by_stream_id.get(&s.s.id.stream()) {
|
||||
None => {
|
||||
return Box::new(stream::iter(std::iter::once(Err(wrap_error(err!(
|
||||
return SliceStream::Once(Some(Err(err!(
|
||||
NotFound,
|
||||
msg("{}: stream not found", s.s.id)
|
||||
))))))
|
||||
))))
|
||||
}
|
||||
Some(d) => d.open_file(s.s.id, (r.start + sr.start)..(r.end + sr.start)),
|
||||
};
|
||||
Box::new(f.map_ok(Chunk::from).map_err(wrap_error))
|
||||
SliceStream::File(f)
|
||||
}
|
||||
|
||||
fn get_subtitle_sample_data(&self, i: usize, r: Range<u64>, len: u64) -> Result<Chunk, Error> {
|
||||
@ -2005,7 +2009,6 @@ mod tests {
|
||||
use std::fs;
|
||||
use std::ops::Range;
|
||||
use std::path::Path;
|
||||
use std::pin::Pin;
|
||||
use std::str;
|
||||
use tracing::info;
|
||||
|
||||
@ -2014,7 +2017,7 @@ mod tests {
|
||||
E::Error: ::std::fmt::Debug,
|
||||
{
|
||||
let mut p = 0;
|
||||
Pin::from(e.get_range(start..start + slice.len() as u64))
|
||||
e.get_range(start..start + slice.len() as u64)
|
||||
.try_for_each(|mut chunk| {
|
||||
let len = chunk.remaining();
|
||||
chunk.copy_to_slice(&mut slice[p..p + len]);
|
||||
@ -2040,7 +2043,7 @@ mod tests {
|
||||
hasher.update(&b"\r\n"[..]);
|
||||
}
|
||||
hasher.update(&b"\r\n"[..]);
|
||||
Pin::from(e.get_range(0..e.len()))
|
||||
e.get_range(0..e.len())
|
||||
.try_fold(hasher, |mut hasher, mut chunk| {
|
||||
while chunk.has_remaining() {
|
||||
let c = chunk.chunk();
|
||||
@ -2153,8 +2156,7 @@ mod tests {
|
||||
let interior = self.stack.last().expect("at root").interior.clone();
|
||||
let len = (interior.end - interior.start) as usize;
|
||||
trace!("get_all: start={}, len={}", interior.start, len);
|
||||
let mut out = Vec::with_capacity(len);
|
||||
unsafe { out.set_len(len) };
|
||||
let mut out = vec![0; len];
|
||||
fill_slice(&mut out[..], &self.mp4, interior.start).await;
|
||||
out
|
||||
}
|
||||
@ -2346,7 +2348,7 @@ mod tests {
|
||||
builder
|
||||
.include_timestamp_subtitle_track(include_subtitles)
|
||||
.unwrap();
|
||||
let all_time = recording::Time(i64::min_value())..recording::Time(i64::max_value());
|
||||
let all_time = recording::Time(i64::MIN)..recording::Time(i64::MAX);
|
||||
{
|
||||
let db = tdb.db.lock();
|
||||
db.list_recordings_by_time(TEST_STREAM_ID, all_time, &mut |r| {
|
||||
@ -2377,7 +2379,7 @@ mod tests {
|
||||
.open(&filename)
|
||||
.unwrap();
|
||||
use ::std::io::Write;
|
||||
Pin::from(mp4.get_range(0..mp4.len()))
|
||||
mp4.get_range(0..mp4.len())
|
||||
.try_for_each(|mut chunk| {
|
||||
while chunk.has_remaining() {
|
||||
let c = chunk.chunk();
|
||||
@ -2973,13 +2975,16 @@ mod tests {
|
||||
mod bench {
|
||||
extern crate test;
|
||||
|
||||
use std::convert::Infallible;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use super::tests::create_mp4_from_db;
|
||||
use base::clock::RealClocks;
|
||||
use db::recording;
|
||||
use db::testutil::{self, TestDb};
|
||||
use futures::future;
|
||||
use http_serve;
|
||||
use hyper;
|
||||
use hyper::service::service_fn;
|
||||
use url::Url;
|
||||
|
||||
/// An HTTP server for benchmarking.
|
||||
@ -3000,28 +3005,35 @@ mod bench {
|
||||
testutil::add_dummy_recordings_to_db(&db.db, 60);
|
||||
let mp4 = create_mp4_from_db(&db, 0, 0, false);
|
||||
let p = mp4.0.initial_sample_byte_pos;
|
||||
let make_svc = hyper::service::make_service_fn(move |_conn| {
|
||||
future::ok::<_, std::convert::Infallible>(hyper::service::service_fn({
|
||||
let addr: SocketAddr = ([127, 0, 0, 1], 0).into();
|
||||
let listener = std::net::TcpListener::bind(addr).unwrap();
|
||||
listener.set_nonblocking(true).unwrap();
|
||||
let addr = listener.local_addr().unwrap(); // resolve port 0 to a real ephemeral port number.
|
||||
let srv = async move {
|
||||
let listener = tokio::net::TcpListener::from_std(listener).unwrap();
|
||||
loop {
|
||||
let (conn, _remote_addr) = listener.accept().await.unwrap();
|
||||
conn.set_nodelay(true).unwrap();
|
||||
let io = hyper_util::rt::TokioIo::new(conn);
|
||||
let mp4 = mp4.clone();
|
||||
move |req| {
|
||||
future::ok::<hyper::Response<crate::body::Body>, hyper::Error>(
|
||||
http_serve::serve(mp4.clone(), &req),
|
||||
)
|
||||
}
|
||||
}))
|
||||
});
|
||||
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||
let srv = {
|
||||
let _guard = rt.enter();
|
||||
let addr = ([127, 0, 0, 1], 0).into();
|
||||
hyper::server::Server::bind(&addr)
|
||||
.tcp_nodelay(true)
|
||||
.serve(make_svc)
|
||||
let svc_fn = service_fn(move |req| {
|
||||
futures::future::ok::<_, Infallible>(http_serve::serve(mp4.clone(), &req))
|
||||
});
|
||||
tokio::spawn(
|
||||
hyper::server::conn::http1::Builder::new().serve_connection(io, svc_fn),
|
||||
);
|
||||
}
|
||||
};
|
||||
let addr = srv.local_addr(); // resolve port 0 to a real ephemeral port number.
|
||||
::std::thread::spawn(move || {
|
||||
rt.block_on(srv).unwrap();
|
||||
});
|
||||
std::thread::Builder::new()
|
||||
.name("bench-server".to_owned())
|
||||
.spawn(move || {
|
||||
let rt = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap();
|
||||
rt.block_on(srv)
|
||||
})
|
||||
.unwrap();
|
||||
BenchServer {
|
||||
url: Url::parse(&format!("http://{}:{}/", addr.ip(), addr.port())).unwrap(),
|
||||
generated_len: p,
|
||||
@ -3053,7 +3065,11 @@ mod bench {
|
||||
db.with_recording_playback(segment.s.id, &mut |playback| {
|
||||
let v = segment.build_index(playback).unwrap(); // warm.
|
||||
b.bytes = v.len() as u64; // define the benchmark performance in terms of output bytes.
|
||||
b.iter(|| segment.build_index(playback).unwrap());
|
||||
b.iter(|| {
|
||||
for _i in 0..100 {
|
||||
segment.build_index(playback).unwrap();
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
@ -3067,17 +3083,25 @@ mod bench {
|
||||
let p = server.generated_len;
|
||||
b.bytes = p;
|
||||
let client = reqwest::Client::new();
|
||||
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||
let rt = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap();
|
||||
let run = || {
|
||||
rt.block_on(async {
|
||||
let resp = client
|
||||
.get(server.url.clone())
|
||||
.header(reqwest::header::RANGE, format!("bytes=0-{}", p - 1))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
let b = resp.bytes().await.unwrap();
|
||||
assert_eq!(p, b.len() as u64);
|
||||
for _i in 0..100 {
|
||||
let mut resp = client
|
||||
.get(server.url.clone())
|
||||
.header(reqwest::header::RANGE, format!("bytes=0-{}", p - 1))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
let mut size = 0u64;
|
||||
while let Some(b) = resp.chunk().await.unwrap() {
|
||||
size += u64::try_from(b.len()).unwrap();
|
||||
}
|
||||
assert_eq!(p, size);
|
||||
}
|
||||
});
|
||||
};
|
||||
run(); // warm.
|
||||
@ -3090,7 +3114,9 @@ mod bench {
|
||||
let db = TestDb::new(RealClocks {});
|
||||
testutil::add_dummy_recordings_to_db(&db.db, 60);
|
||||
b.iter(|| {
|
||||
create_mp4_from_db(&db, 0, 0, false);
|
||||
for _i in 0..100 {
|
||||
create_mp4_from_db(&db, 0, 0, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,7 +17,8 @@ use tracing_futures::Instrument;
|
||||
/// Each `Slice` instance belongs to a single `Slices`.
|
||||
pub trait Slice: fmt::Debug + Sized + Sync + 'static {
|
||||
type Ctx: Send + Sync + Clone;
|
||||
type Chunk: Send + Sync;
|
||||
type Chunk: Send + Sync + 'static;
|
||||
type Stream: Stream<Item = Result<Self::Chunk, BoxedError>> + Send + Sync;
|
||||
|
||||
/// The byte position (relative to the start of the `Slices`) of the end of this slice,
|
||||
/// exclusive. Note the starting position (and thus length) are inferred from the previous
|
||||
@ -27,12 +28,10 @@ pub trait Slice: fmt::Debug + Sized + Sync + 'static {
|
||||
/// Gets the body bytes indicated by `r`, which is relative to this slice's start.
|
||||
/// The additional argument `ctx` is as supplied to the `Slices`.
|
||||
/// The additional argument `l` is the length of this slice, as determined by the `Slices`.
|
||||
fn get_range(
|
||||
&self,
|
||||
ctx: &Self::Ctx,
|
||||
r: Range<u64>,
|
||||
len: u64,
|
||||
) -> Box<dyn Stream<Item = Result<Self::Chunk, BoxedError>> + Sync + Send>;
|
||||
///
|
||||
/// Note that unlike [`http_entity::Entity::get_range`], this is called many times per request,
|
||||
/// so it's worth defining a custom stream type to avoid allocation overhead.
|
||||
fn get_range(&self, ctx: &Self::Ctx, r: Range<u64>, len: u64) -> Self::Stream;
|
||||
|
||||
fn get_slices(ctx: &Self::Ctx) -> &Slices<Self>;
|
||||
}
|
||||
@ -127,7 +126,7 @@ where
|
||||
}
|
||||
|
||||
/// Writes `range` to `out`.
|
||||
/// This interface mirrors `http_serve::Entity::write_to`, with the additional `ctx` argument.
|
||||
/// This interface mirrors `http_serve::Entity::get_range`, with the additional `ctx` argument.
|
||||
pub fn get_range(
|
||||
&self,
|
||||
ctx: &S::Ctx,
|
||||
@ -170,7 +169,7 @@ where
|
||||
let l = s_end - slice_start;
|
||||
body = s.get_range(&c, start_pos..min_end - slice_start, l);
|
||||
};
|
||||
futures::future::ready(Some((Pin::from(body), (c, i + 1, 0, min_end))))
|
||||
futures::future::ready(Some((body, (c, i + 1, 0, min_end))))
|
||||
},
|
||||
);
|
||||
Box::pin(bodies.flatten().in_current_span())
|
||||
@ -182,9 +181,8 @@ mod tests {
|
||||
use super::{Slice, Slices};
|
||||
use crate::body::BoxedError;
|
||||
use db::testutil;
|
||||
use futures::stream::{self, Stream, TryStreamExt};
|
||||
use futures::stream::{self, TryStreamExt};
|
||||
use std::ops::Range;
|
||||
use std::pin::Pin;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub struct FakeChunk {
|
||||
@ -201,6 +199,7 @@ mod tests {
|
||||
impl Slice for FakeSlice {
|
||||
type Ctx = &'static Slices<FakeSlice>;
|
||||
type Chunk = FakeChunk;
|
||||
type Stream = stream::Once<futures::future::Ready<Result<FakeChunk, BoxedError>>>;
|
||||
|
||||
fn end(&self) -> u64 {
|
||||
self.end
|
||||
@ -211,11 +210,11 @@ mod tests {
|
||||
_ctx: &&'static Slices<FakeSlice>,
|
||||
r: Range<u64>,
|
||||
_l: u64,
|
||||
) -> Box<dyn Stream<Item = Result<FakeChunk, BoxedError>> + Send + Sync> {
|
||||
Box::new(stream::once(futures::future::ok(FakeChunk {
|
||||
) -> Self::Stream {
|
||||
stream::once(futures::future::ok(FakeChunk {
|
||||
slice: self.name,
|
||||
range: r,
|
||||
})))
|
||||
}))
|
||||
}
|
||||
|
||||
fn get_slices(ctx: &&'static Slices<FakeSlice>) -> &'static Slices<Self> {
|
||||
@ -241,10 +240,7 @@ mod tests {
|
||||
|
||||
async fn get_range(r: Range<u64>) -> Vec<FakeChunk> {
|
||||
let slices = slices();
|
||||
Pin::from(slices.get_range(&slices, r))
|
||||
.try_collect()
|
||||
.await
|
||||
.unwrap()
|
||||
slices.get_range(&slices, r).try_collect().await.unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@ -8,7 +8,6 @@ use futures::StreamExt;
|
||||
use retina::client::Demuxed;
|
||||
use retina::codec::CodecItem;
|
||||
use std::pin::Pin;
|
||||
use std::result::Result;
|
||||
use tracing::Instrument;
|
||||
use url::Url;
|
||||
|
||||
|
||||
@ -6,7 +6,6 @@ use crate::stream;
|
||||
use base::clock::{Clocks, TimerGuard};
|
||||
use base::{bail, err, Error};
|
||||
use db::{dir, recording, writer, Camera, Database, Stream};
|
||||
use std::result::Result;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use tracing::{debug, info, trace, warn, Instrument};
|
||||
@ -289,12 +288,12 @@ where
|
||||
mod tests {
|
||||
use crate::stream::{self, Stream};
|
||||
use base::clock::{self, Clocks};
|
||||
use base::Mutex;
|
||||
use base::{bail, Error};
|
||||
use db::{recording, testutil, CompositeId};
|
||||
use std::cmp;
|
||||
use std::convert::TryFrom;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use tracing::trace;
|
||||
|
||||
struct ProxyingStream {
|
||||
@ -356,7 +355,7 @@ mod tests {
|
||||
);
|
||||
let duration = goal - self.slept;
|
||||
let buf_part = cmp::min(self.buffered, duration);
|
||||
self.buffered = self.buffered - buf_part;
|
||||
self.buffered -= buf_part;
|
||||
self.clocks.sleep(duration - buf_part);
|
||||
self.slept = goal;
|
||||
}
|
||||
@ -389,7 +388,7 @@ mod tests {
|
||||
_options: stream::Options,
|
||||
) -> Result<Box<dyn stream::Stream>, Error> {
|
||||
assert_eq!(&url, &self.expected_url);
|
||||
let mut l = self.streams.lock().unwrap();
|
||||
let mut l = self.streams.lock();
|
||||
match l.pop() {
|
||||
Some(stream) => {
|
||||
trace!("MockOpener returning next stream");
|
||||
@ -397,7 +396,7 @@ mod tests {
|
||||
}
|
||||
None => {
|
||||
trace!("MockOpener shutting down");
|
||||
self.shutdown_tx.lock().unwrap().take();
|
||||
self.shutdown_tx.lock().take();
|
||||
bail!(Cancelled, msg("done"))
|
||||
}
|
||||
}
|
||||
@ -441,8 +440,8 @@ mod tests {
|
||||
Box::new(stream),
|
||||
);
|
||||
stream.ts_offset = 123456; // starting pts of the input should be irrelevant
|
||||
stream.ts_offset_pkts_left = u32::max_value();
|
||||
stream.pkts_left = u32::max_value();
|
||||
stream.ts_offset_pkts_left = u32::MAX;
|
||||
stream.pkts_left = u32::MAX;
|
||||
let (shutdown_tx, shutdown_rx) = base::shutdown::channel();
|
||||
let opener = MockOpener {
|
||||
expected_url: url::Url::parse("rtsp://test-camera/main").unwrap(),
|
||||
@ -479,7 +478,7 @@ mod tests {
|
||||
.unwrap();
|
||||
}
|
||||
stream.run();
|
||||
assert!(opener.streams.lock().unwrap().is_empty());
|
||||
assert!(opener.streams.lock().is_empty());
|
||||
db.syncer_channel.flush();
|
||||
let db = db.db.lock();
|
||||
|
||||
@ -517,7 +516,6 @@ mod tests {
|
||||
assert_eq!(recording::Time(128700576719993), recordings[1].start);
|
||||
assert_eq!(db::RecordingFlags::TrailingZero as i32, recordings[1].flags);
|
||||
|
||||
drop(env);
|
||||
drop(opener);
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,7 +84,8 @@ fn serve_json<R: http_serve::AsRequest, T: serde::ser::Serialize>(
|
||||
fn csrf_matches(csrf: &str, session: auth::SessionHash) -> bool {
|
||||
let mut b64 = [0u8; 32];
|
||||
session.encode_base64(&mut b64);
|
||||
::ring::constant_time::verify_slices_are_equal(&b64[..], csrf.as_bytes()).is_ok()
|
||||
use subtle::ConstantTimeEq as _;
|
||||
b64.ct_eq(csrf.as_bytes()).into()
|
||||
}
|
||||
|
||||
/// Extracts `s` cookie from the HTTP request headers. Does not authenticate.
|
||||
@ -319,7 +320,7 @@ impl Service {
|
||||
req: Request<::hyper::body::Incoming>,
|
||||
conn_data: ConnData,
|
||||
) -> Result<Response<Body>, std::convert::Infallible> {
|
||||
let request_id = ulid::Ulid::new();
|
||||
let request_id = uuid::Uuid::now_v7();
|
||||
let authreq = auth::Request {
|
||||
when_sec: Some(self.db.clocks().realtime().as_secs()),
|
||||
addr: if self.trust_forward_hdrs {
|
||||
@ -340,7 +341,7 @@ impl Service {
|
||||
// https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/http/
|
||||
let span = tracing::info_span!(
|
||||
"request",
|
||||
%request_id,
|
||||
request_id = %data_encoding::BASE32_NOPAD.encode_display(request_id.as_bytes()),
|
||||
net.sock.peer.uid = conn_data.client_unix_uid.map(tracing::field::display),
|
||||
http.client_ip = authreq.addr.map(tracing::field::display),
|
||||
http.method = %req.method(),
|
||||
@ -755,7 +756,7 @@ mod tests {
|
||||
let s = Server::new(None);
|
||||
let cli = reqwest::Client::new();
|
||||
let resp = cli
|
||||
.get(&format!("{}/api/", &s.base_url))
|
||||
.get(format!("{}/api/", &s.base_url))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
@ -782,8 +783,11 @@ mod bench {
|
||||
extern crate test;
|
||||
|
||||
use db::testutil::{self, TestDb};
|
||||
use hyper;
|
||||
use std::sync::{Arc, OnceLock};
|
||||
use hyper::{self, service::service_fn};
|
||||
use std::{
|
||||
net::SocketAddr,
|
||||
sync::{Arc, OnceLock},
|
||||
};
|
||||
use uuid::Uuid;
|
||||
|
||||
struct Server {
|
||||
@ -807,32 +811,41 @@ mod bench {
|
||||
})
|
||||
.unwrap(),
|
||||
);
|
||||
let make_svc = hyper::service::make_service_fn(move |_conn| {
|
||||
futures::future::ok::<_, std::convert::Infallible>(hyper::service::service_fn({
|
||||
let s = Arc::clone(&service);
|
||||
move |req| {
|
||||
Arc::clone(&s).serve(
|
||||
let addr: SocketAddr = ([127, 0, 0, 1], 0).into();
|
||||
let listener = std::net::TcpListener::bind(addr).unwrap();
|
||||
listener.set_nonblocking(true).unwrap();
|
||||
let addr = listener.local_addr().unwrap(); // resolve port 0 to a real ephemeral port number.
|
||||
let srv = async move {
|
||||
let listener = tokio::net::TcpListener::from_std(listener).unwrap();
|
||||
loop {
|
||||
let (conn, _remote_addr) = listener.accept().await.unwrap();
|
||||
conn.set_nodelay(true).unwrap();
|
||||
let io = hyper_util::rt::TokioIo::new(conn);
|
||||
let service = Arc::clone(&service);
|
||||
let svc_fn = service_fn(move |req| {
|
||||
Arc::clone(&service).serve(
|
||||
req,
|
||||
super::accept::ConnData {
|
||||
client_unix_uid: None,
|
||||
client_addr: None,
|
||||
},
|
||||
)
|
||||
}
|
||||
}))
|
||||
});
|
||||
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||
let srv = {
|
||||
let _guard = rt.enter();
|
||||
let addr = ([127, 0, 0, 1], 0).into();
|
||||
hyper::server::Server::bind(&addr)
|
||||
.tcp_nodelay(true)
|
||||
.serve(make_svc)
|
||||
});
|
||||
tokio::spawn(
|
||||
hyper::server::conn::http1::Builder::new().serve_connection(io, svc_fn),
|
||||
);
|
||||
}
|
||||
};
|
||||
let addr = srv.local_addr(); // resolve port 0 to a real ephemeral port number.
|
||||
::std::thread::spawn(move || {
|
||||
rt.block_on(srv).unwrap();
|
||||
});
|
||||
std::thread::Builder::new()
|
||||
.name("bench-server".to_owned())
|
||||
.spawn(move || {
|
||||
let rt = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap();
|
||||
rt.block_on(srv)
|
||||
})
|
||||
.unwrap();
|
||||
Server {
|
||||
base_url: format!("http://{}:{}", addr.ip(), addr.port()),
|
||||
test_camera_uuid,
|
||||
@ -852,13 +865,18 @@ mod bench {
|
||||
))
|
||||
.unwrap();
|
||||
let client = reqwest::Client::new();
|
||||
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||
let rt = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap();
|
||||
let f = || {
|
||||
rt.block_on(async {
|
||||
let resp = client.get(url.clone()).send().await.unwrap();
|
||||
assert_eq!(resp.status(), reqwest::StatusCode::OK);
|
||||
let _b = resp.bytes().await.unwrap();
|
||||
});
|
||||
for _i in 0..100 {
|
||||
rt.block_on(async {
|
||||
let resp = client.get(url.clone()).send().await.unwrap();
|
||||
assert_eq!(resp.status(), reqwest::StatusCode::OK);
|
||||
let _b = resp.bytes().await.unwrap();
|
||||
});
|
||||
}
|
||||
};
|
||||
f(); // warm.
|
||||
b.iter(f);
|
||||
|
||||
@ -175,7 +175,7 @@ mod tests {
|
||||
info!("header: {}", cookie.header());
|
||||
|
||||
let resp = cli
|
||||
.get(&format!("{}/api/", &s.base_url))
|
||||
.get(format!("{}/api/", &s.base_url))
|
||||
.header(reqwest::header::COOKIE, cookie.header())
|
||||
.send()
|
||||
.await
|
||||
@ -192,7 +192,7 @@ mod tests {
|
||||
p.insert("username", "slamb");
|
||||
p.insert("password", "hunter2");
|
||||
let resp = cli
|
||||
.post(&format!("{}/api/login", &s.base_url))
|
||||
.post(format!("{}/api/login", &s.base_url))
|
||||
.json(&p)
|
||||
.send()
|
||||
.await
|
||||
@ -202,7 +202,7 @@ mod tests {
|
||||
|
||||
// A GET shouldn't work.
|
||||
let resp = cli
|
||||
.get(&format!("{}/api/logout", &s.base_url))
|
||||
.get(format!("{}/api/logout", &s.base_url))
|
||||
.header(reqwest::header::COOKIE, cookie.header())
|
||||
.send()
|
||||
.await
|
||||
@ -211,7 +211,7 @@ mod tests {
|
||||
|
||||
// Neither should a POST without a csrf token.
|
||||
let resp = cli
|
||||
.post(&format!("{}/api/logout", &s.base_url))
|
||||
.post(format!("{}/api/logout", &s.base_url))
|
||||
.header(reqwest::header::COOKIE, cookie.header())
|
||||
.send()
|
||||
.await
|
||||
@ -221,7 +221,7 @@ mod tests {
|
||||
// But it should work with the csrf token.
|
||||
// Retrieve that from the toplevel API request.
|
||||
let toplevel: serde_json::Value = cli
|
||||
.post(&format!("{}/api/", &s.base_url))
|
||||
.post(format!("{}/api/", &s.base_url))
|
||||
.header(reqwest::header::COOKIE, cookie.header())
|
||||
.send()
|
||||
.await
|
||||
@ -240,7 +240,7 @@ mod tests {
|
||||
let mut p = FastHashMap::default();
|
||||
p.insert("csrf", csrf);
|
||||
let resp = cli
|
||||
.post(&format!("{}/api/logout", &s.base_url))
|
||||
.post(format!("{}/api/logout", &s.base_url))
|
||||
.header(reqwest::header::COOKIE, cookie.header())
|
||||
.json(&p)
|
||||
.send()
|
||||
@ -255,7 +255,7 @@ mod tests {
|
||||
|
||||
// It should also be invalidated server-side.
|
||||
let resp = cli
|
||||
.get(&format!("{}/api/", &s.base_url))
|
||||
.get(format!("{}/api/", &s.base_url))
|
||||
.header(reqwest::header::COOKIE, cookie.header())
|
||||
.send()
|
||||
.await
|
||||
|
||||
@ -291,7 +291,7 @@ mod tests {
|
||||
let s = Server::new(Some(permissions));
|
||||
let cli = reqwest::Client::new();
|
||||
let resp = cli
|
||||
.get(&format!(
|
||||
.get(format!(
|
||||
"{}/api/cameras/{}/main/view.mp4",
|
||||
&s.base_url, s.db.test_camera_uuid
|
||||
))
|
||||
|
||||
@ -60,7 +60,7 @@ where
|
||||
.await;
|
||||
if let Err(err) = handler(&mut ws).await {
|
||||
// TODO: use a nice JSON message format for errors.
|
||||
tracing::error!(%err, "closing with error");
|
||||
tracing::error!(err = %err.chain(), "closing with error");
|
||||
let _ = ws
|
||||
.send(tungstenite::Message::Text(err.to_string().into()))
|
||||
.await;
|
||||
|
||||
5
ui/.gitignore
vendored
5
ui/.gitignore
vendored
@ -9,10 +9,7 @@
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production, current path
|
||||
/build
|
||||
|
||||
# production, old path
|
||||
# production
|
||||
/dist
|
||||
|
||||
# misc
|
||||
|
||||
34
ui/.prettierignore
Normal file
34
ui/.prettierignore
Normal file
@ -0,0 +1,34 @@
|
||||
|
||||
#-------------------------------------------------------------------------------------------------------------------
|
||||
# Keep this section in sync with .gitignore
|
||||
#-------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
/.idea
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/dist
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.eslintcache
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
#-------------------------------------------------------------------------------------------------------------------
|
||||
# Prettier-specific overrides
|
||||
#-------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
pnpm-lock.yaml
|
||||
@ -21,10 +21,10 @@
|
||||
"react-router-dom": "^6.22.3"
|
||||
},
|
||||
"scripts": {
|
||||
"check-format": "prettier --check --ignore-path .gitignore .",
|
||||
"check-format": "prettier --check --ignore-path .prettierignore .",
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"format": "prettier --write --ignore-path .gitignore .",
|
||||
"format": "prettier --write --ignore-path .prettierignore .",
|
||||
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||
"preview": "vite preview",
|
||||
"test": "vitest"
|
||||
|
||||
12729
ui/pnpm-lock.yaml
generated
12729
ui/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -78,7 +78,9 @@ export function combine(
|
||||
(split90k === undefined || cur.endTime90k - r.startTime90k <= split90k)
|
||||
) {
|
||||
cur.startId = r.startId;
|
||||
cur.firstUncommitted == r.firstUncommitted ?? cur.firstUncommitted;
|
||||
if (r.firstUncommitted !== undefined) {
|
||||
cur.firstUncommitted = r.firstUncommitted;
|
||||
}
|
||||
cur.startTime90k = r.startTime90k;
|
||||
cur.videoSamples += r.videoSamples;
|
||||
cur.sampleFileBytes += r.sampleFileBytes;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user