Compare commits
12 commits
notashelf/
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
520489ab48 |
|||
|
273d0244aa |
|||
|
d61b5d32d1 |
|||
|
00bab69598 |
|||
|
70b0113d8a |
|||
|
d18317b49b |
|||
|
e7e9ea6036 |
|||
|
6900984e46 |
|||
|
9e5eb41d39 |
|||
|
aa9c55277c |
|||
|
f55edcdedd |
|||
| 035825a402 |
403 changed files with 57223 additions and 53193 deletions
32
.editorconfig
Normal file
32
.editorconfig
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.rs]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[justfile]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[*.toml]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
[*.nix]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[*.scss]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
BIN
Cargo.lock
generated
BIN
Cargo.lock
generated
Binary file not shown.
52
Cargo.toml
52
Cargo.toml
|
|
@ -1,5 +1,5 @@
|
||||||
[workspace]
|
[workspace]
|
||||||
members = ["crates/*", "xtask"]
|
members = ["crates/*", "packages/*", "xtask"]
|
||||||
exclude = ["crates/pinakes-core/tests/fixtures/test-plugin"]
|
exclude = ["crates/pinakes-core/tests/fixtures/test-plugin"]
|
||||||
resolver = "3"
|
resolver = "3"
|
||||||
|
|
||||||
|
|
@ -11,35 +11,44 @@ readme = true
|
||||||
rust-version = "1.95.0" # follows nightly Rust
|
rust-version = "1.95.0" # follows nightly Rust
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
# Crate components for Pinakes.
|
# Crate components for Pinakes. Those are the internal dependencies that are built
|
||||||
|
# while building any package.
|
||||||
pinakes-core = { path = "./crates/pinakes-core" }
|
pinakes-core = { path = "./crates/pinakes-core" }
|
||||||
pinakes-server = { path = "./crates/pinakes-server" }
|
|
||||||
pinakes-plugin-api = { path = "./crates/pinakes-plugin-api" }
|
pinakes-plugin-api = { path = "./crates/pinakes-plugin-api" }
|
||||||
pinakes-ui = { path = "./crates/pinakes-ui" }
|
|
||||||
pinakes-tui = { path = "./crates/pinakes-tui" }
|
|
||||||
|
|
||||||
tokio = { version = "1.49.0", features = ["full"] }
|
# Pinakes itself is a REST API server. UI and TUI are official visual components
|
||||||
|
# that connect to the server. Using the API documentation, the user can write
|
||||||
|
# their own clients, but we separate "crates" and "packages" to establish the
|
||||||
|
# distinction properly.
|
||||||
|
pinakes-server = { path = "./packages/pinakes-server" }
|
||||||
|
pinakes-ui = { path = "./packages/pinakes-ui" }
|
||||||
|
pinakes-tui = { path = "./packages/pinakes-tui" }
|
||||||
|
|
||||||
|
# Other dependencies. Declaring them in the virtual manifests lets use reuse the crates
|
||||||
|
# without having to track individual crate version across different types of crates. This
|
||||||
|
# also includes *dev* dependencies.
|
||||||
|
tokio = { version = "1.50.0", features = ["full"] }
|
||||||
tokio-util = { version = "0.7.18", features = ["rt"] }
|
tokio-util = { version = "0.7.18", features = ["rt"] }
|
||||||
serde = { version = "1.0.228", features = ["derive"] }
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
serde_json = "1.0.149"
|
serde_json = "1.0.149"
|
||||||
toml = "1.0.3"
|
toml = "1.0.7"
|
||||||
clap = { version = "4.5.60", features = ["derive", "env"] }
|
clap = { version = "4.6.0", features = ["derive", "env"] }
|
||||||
chrono = { version = "0.4.44", features = ["serde"] }
|
chrono = { version = "0.4.44", features = ["serde"] }
|
||||||
uuid = { version = "1.21.0", features = ["v7", "serde"] }
|
uuid = { version = "1.22.0", features = ["v7", "serde"] }
|
||||||
thiserror = "2.0.18"
|
thiserror = "2.0.18"
|
||||||
anyhow = "1.0.102"
|
anyhow = "1.0.102"
|
||||||
tracing = "0.1.44"
|
tracing = "0.1.44"
|
||||||
tracing-subscriber = { version = "0.3.22", features = ["env-filter", "json"] }
|
tracing-subscriber = { version = "0.3.23", features = ["env-filter", "json"] }
|
||||||
blake3 = "1.8.3"
|
blake3 = "1.8.3"
|
||||||
rustc-hash = "2.1.1"
|
rustc-hash = "2.1.1"
|
||||||
ed25519-dalek = { version = "2.1.1", features = ["std"] }
|
ed25519-dalek = { version = "2.2.0", features = ["std"] }
|
||||||
lofty = "0.23.2"
|
lofty = "0.23.3"
|
||||||
lopdf = "0.39.0"
|
lopdf = "0.40.0"
|
||||||
epub = "2.1.5"
|
epub = "2.1.5"
|
||||||
matroska = "0.30.0"
|
matroska = "0.30.0"
|
||||||
gray_matter = "0.3.2"
|
gray_matter = "0.3.2"
|
||||||
kamadak-exif = "0.6.1"
|
kamadak-exif = "0.6.1"
|
||||||
rusqlite = { version = "=0.37.0", features = ["bundled", "column_decltype"] }
|
rusqlite = { version = "0.37.0", features = ["bundled", "column_decltype"] }
|
||||||
tokio-postgres = { version = "0.7.16", features = [
|
tokio-postgres = { version = "0.7.16", features = [
|
||||||
"with-uuid-1",
|
"with-uuid-1",
|
||||||
"with-chrono-0_4",
|
"with-chrono-0_4",
|
||||||
|
|
@ -52,7 +61,7 @@ native-tls = "0.2.18"
|
||||||
refinery = { version = "0.9.0", features = ["rusqlite", "tokio-postgres"] }
|
refinery = { version = "0.9.0", features = ["rusqlite", "tokio-postgres"] }
|
||||||
walkdir = "2.5.0"
|
walkdir = "2.5.0"
|
||||||
notify = { version = "8.2.0", features = ["macos_fsevent"] }
|
notify = { version = "8.2.0", features = ["macos_fsevent"] }
|
||||||
winnow = "0.7.14"
|
winnow = "1.0.0"
|
||||||
axum = { version = "0.8.8", features = ["macros", "multipart"] }
|
axum = { version = "0.8.8", features = ["macros", "multipart"] }
|
||||||
axum-server = { version = "0.8.0" }
|
axum-server = { version = "0.8.0" }
|
||||||
tower = "0.5.3"
|
tower = "0.5.3"
|
||||||
|
|
@ -67,7 +76,7 @@ dioxus = { version = "0.7.3", features = ["desktop", "router"] }
|
||||||
dioxus-core = { version = "0.7.3" }
|
dioxus-core = { version = "0.7.3" }
|
||||||
async-trait = "0.1.89"
|
async-trait = "0.1.89"
|
||||||
futures = "0.3.32"
|
futures = "0.3.32"
|
||||||
image = { version = "0.25.9", default-features = false, features = [
|
image = { version = "0.25.10", default-features = false, features = [
|
||||||
"jpeg",
|
"jpeg",
|
||||||
"png",
|
"png",
|
||||||
"webp",
|
"webp",
|
||||||
|
|
@ -75,7 +84,7 @@ image = { version = "0.25.9", default-features = false, features = [
|
||||||
"tiff",
|
"tiff",
|
||||||
"bmp",
|
"bmp",
|
||||||
] }
|
] }
|
||||||
pulldown-cmark = "0.13.1"
|
pulldown-cmark = "0.13.3"
|
||||||
ammonia = "4.1.2"
|
ammonia = "4.1.2"
|
||||||
argon2 = { version = "0.5.3", features = ["std"] }
|
argon2 = { version = "0.5.3", features = ["std"] }
|
||||||
mime_guess = "2.0.5"
|
mime_guess = "2.0.5"
|
||||||
|
|
@ -84,17 +93,18 @@ dioxus-free-icons = { version = "0.10.0", features = ["font-awesome-solid"] }
|
||||||
rfd = "0.17.2"
|
rfd = "0.17.2"
|
||||||
gloo-timers = { version = "0.3.0", features = ["futures"] }
|
gloo-timers = { version = "0.3.0", features = ["futures"] }
|
||||||
rand = "0.10.0"
|
rand = "0.10.0"
|
||||||
moka = { version = "0.12.14", features = ["future"] }
|
moka = { version = "0.12.15", features = ["future"] }
|
||||||
urlencoding = "2.1.3"
|
urlencoding = "2.1.3"
|
||||||
image_hasher = "3.1.1"
|
image_hasher = "3.1.1"
|
||||||
percent-encoding = "2.3.2"
|
percent-encoding = "2.3.2"
|
||||||
http = "1.4.0"
|
http = "1.4.0"
|
||||||
wasmtime = { version = "42.0.1", features = ["component-model"] }
|
wasmtime = { version = "43.0.0", features = ["component-model"] }
|
||||||
wit-bindgen = "0.53.1"
|
wit-bindgen = "0.54.0"
|
||||||
tempfile = "3.26.0"
|
tempfile = "3.27.0"
|
||||||
utoipa = { version = "5.4.0", features = ["axum_extras", "uuid", "chrono"] }
|
utoipa = { version = "5.4.0", features = ["axum_extras", "uuid", "chrono"] }
|
||||||
utoipa-axum = { version = "0.2.0" }
|
utoipa-axum = { version = "0.2.0" }
|
||||||
utoipa-swagger-ui = { version = "9.0.2", features = ["axum"] }
|
utoipa-swagger-ui = { version = "9.0.2", features = ["axum"] }
|
||||||
|
http-body-util = "0.1.3"
|
||||||
|
|
||||||
# See:
|
# See:
|
||||||
# <https://doc.rust-lang.org/rustc/lints/listing/allowed-by-default.html>
|
# <https://doc.rust-lang.org/rustc/lints/listing/allowed-by-default.html>
|
||||||
|
|
|
||||||
32
HACKING.md
32
HACKING.md
|
|
@ -1,32 +0,0 @@
|
||||||
# Hacking Pinakes
|
|
||||||
|
|
||||||
Pinakes is a lot of things. One of the things it aims to be is _complete_. To be
|
|
||||||
complete in features, to be complete in documentation and to be complete in
|
|
||||||
hackability. This document covers, very comprehensively, how you may:
|
|
||||||
|
|
||||||
- Build Pinakes
|
|
||||||
- Develop Pinakes
|
|
||||||
- Contribute to Pinakes
|
|
||||||
|
|
||||||
for developers as well as:
|
|
||||||
|
|
||||||
- Distribute Pinakes
|
|
||||||
|
|
||||||
for Pinakes maintainers and packagers.
|
|
||||||
|
|
||||||
## Building Pinakes
|
|
||||||
|
|
||||||
Pinakes is built with Rust (nightly edition) and various crates. The most
|
|
||||||
_notable_ crate among those is Dioxus, which provides its own toolkit. The UI
|
|
||||||
for Pinakes is usually _not_ built with the Dioxus CLI but instead with
|
|
||||||
`cargo build`. This also applies to distributable build results.
|
|
||||||
|
|
||||||
[Direnv]: https://direnv.net
|
|
||||||
|
|
||||||
To build Pinakes, simply pick the components you want and build them with
|
|
||||||
`cargo build --release --package <component>`. A Nix shell is provided for
|
|
||||||
reproducible developer environments and you may obtain all build dependencies by
|
|
||||||
simply running `nix develop` or `direnv allow` if you use [Direnv]. Nix is a
|
|
||||||
cross-platform build tool and works on most Linux distributions as well as
|
|
||||||
Darwin. While distro-specific package managers _might_ work, Nix is the only
|
|
||||||
supported one.
|
|
||||||
BIN
LICENSE
Normal file
BIN
LICENSE
Normal file
Binary file not shown.
|
|
@ -4,6 +4,9 @@ edition.workspace = true
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|
||||||
|
[features]
|
||||||
|
ffmpeg-tests = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
|
|
@ -43,18 +46,13 @@ moka = { workspace = true }
|
||||||
urlencoding = { workspace = true }
|
urlencoding = { workspace = true }
|
||||||
image_hasher = { workspace = true }
|
image_hasher = { workspace = true }
|
||||||
rustc-hash = { workspace = true }
|
rustc-hash = { workspace = true }
|
||||||
|
pinakes-plugin-api = { workspace = true }
|
||||||
# Plugin system
|
wasmtime = { workspace = true }
|
||||||
pinakes-plugin-api.workspace = true
|
ed25519-dalek = { workspace = true }
|
||||||
wasmtime.workspace = true
|
|
||||||
ed25519-dalek.workspace = true
|
|
||||||
|
|
||||||
[features]
|
|
||||||
ffmpeg-tests = []
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tempfile = { workspace = true }
|
tempfile = { workspace = true }
|
||||||
rand = { workspace = true }
|
rand = { workspace = true }
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
|
||||||
|
|
@ -4,32 +4,25 @@ version.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
wasm = ["wit-bindgen"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Core dependencies
|
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
async-trait = { workspace = true }
|
async-trait = { workspace = true }
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
|
|
||||||
# For plugin manifest parsing
|
|
||||||
toml = { workspace = true }
|
toml = { workspace = true }
|
||||||
|
|
||||||
# For media types and identifiers
|
|
||||||
uuid = { workspace = true }
|
uuid = { workspace = true }
|
||||||
chrono = { workspace = true }
|
chrono = { workspace = true }
|
||||||
mime_guess = { workspace = true }
|
mime_guess = { workspace = true }
|
||||||
rustc-hash = { workspace = true }
|
rustc-hash = { workspace = true }
|
||||||
|
|
||||||
# WASM bridge types
|
|
||||||
wit-bindgen = { workspace = true, optional = true }
|
wit-bindgen = { workspace = true, optional = true }
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = []
|
|
||||||
wasm = ["wit-bindgen"]
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tokio = { workspace = true, features = ["rt", "rt-multi-thread", "macros"] }
|
tokio = { workspace = true, features = ["rt", "rt-multi-thread", "macros"] }
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
|
||||||
1
crates/pinakes-ui/assets/css/main.css
vendored
1
crates/pinakes-ui/assets/css/main.css
vendored
File diff suppressed because one or more lines are too long
276
docs/HACKING.md
Normal file
276
docs/HACKING.md
Normal file
|
|
@ -0,0 +1,276 @@
|
||||||
|
# Hacking Pinakes
|
||||||
|
|
||||||
|
Pinakes is a lot of things. It also _plans_ to be a lot of things. One of those
|
||||||
|
things, as you might have noticed, is _complete_. To be complete in features, to
|
||||||
|
be complete in documentation and to be complete in hackability. This document
|
||||||
|
covers, very comprehensively, how you may:
|
||||||
|
|
||||||
|
- Develop Pinakes
|
||||||
|
- Build Pinakes
|
||||||
|
- Contribute to Pinakes
|
||||||
|
|
||||||
|
for developers as well as:
|
||||||
|
|
||||||
|
- Distribute Pinakes
|
||||||
|
|
||||||
|
for Pinakes maintainers and packagers.
|
||||||
|
|
||||||
|
## Development Environment
|
||||||
|
|
||||||
|
[Nix]: https://nixos.org
|
||||||
|
[Direnv]: https://direnv.net
|
||||||
|
|
||||||
|
Pinakes uses [Nix] with flakes for pure, reproducible developer environments.
|
||||||
|
All of the build dependencies that you need, from Rust to GTK and webkit, are
|
||||||
|
provided in the devshell. In addition to _not_ requiring system libraries, we'd
|
||||||
|
like to _actively discourage you_ from relying on system libraries.
|
||||||
|
|
||||||
|
[Direnv] may be preferred by some users as an alternative to `nix develop`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Enter the dev shell
|
||||||
|
$ nix develop
|
||||||
|
|
||||||
|
# Or with direnv (recommended)
|
||||||
|
$ direnv allow
|
||||||
|
```
|
||||||
|
|
||||||
|
Nix is the only supported way to get all dependencies. Distro package managers
|
||||||
|
_may_ work for the core crates but are not supported for the UI.
|
||||||
|
|
||||||
|
## Building Pinakes
|
||||||
|
|
||||||
|
Pinakes is a Cargo workspace with multiple crates targeting multiple aspects of
|
||||||
|
the projects. The user-facing crates are provided in `packages/*`.
|
||||||
|
`pinakes-server` and `pinakes-tui` can be built normally with `cargo` while the
|
||||||
|
UI crate, i.e., `pinakes-ui` **must** be built with the Dioxus CLI (`dx`) so
|
||||||
|
that the SCSS stylesheets are compiled correctly. Everything else should work
|
||||||
|
perfectly fine with the standard `cargo build`.
|
||||||
|
|
||||||
|
[Just]: https://just.systems
|
||||||
|
|
||||||
|
To make the things a _little_ bit more convenient, this project uses [Just] as
|
||||||
|
its command runner and provides a few recipes for common tasks.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build all crates (server + TUI + UI)
|
||||||
|
$ just build-all
|
||||||
|
|
||||||
|
# Build individual components
|
||||||
|
$ just build-server # pinakes-server (HTTP API)
|
||||||
|
$ just build-tui # pinakes-tui (terminal UI)
|
||||||
|
$ just build-ui # pinakes-ui (Dioxus desktop/web UI, requires dx)
|
||||||
|
```
|
||||||
|
|
||||||
|
The dependencies required for the recipes, including Just itself, is provided by
|
||||||
|
the Nix devshell. You may also run the cargo equivalents of those commands:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Core crates
|
||||||
|
$ cargo build -p pinakes-server
|
||||||
|
$ cargo build -p pinakes-tui
|
||||||
|
|
||||||
|
# The UI crate must be built with dx, not cargo.
|
||||||
|
$ dx build -p pinakes-ui
|
||||||
|
```
|
||||||
|
|
||||||
|
Release builds follow the same pattern, but with `--release`. Nothing else
|
||||||
|
should be required for the most part.
|
||||||
|
|
||||||
|
## Running Pinakes
|
||||||
|
|
||||||
|
For the time being, UI clients require the server to be running before they can
|
||||||
|
function. Offline sync is planned, but likely come later alongside cross-device
|
||||||
|
sync. All UIs connect to the server over HTTP.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Copy and edit the example config (first time only)
|
||||||
|
$ cp pinakes.example.toml pinakes.toml
|
||||||
|
|
||||||
|
# 2. Start the server (defaults to 127.0.0.1:3000)
|
||||||
|
$ cargo run -p pinakes-server -- pinakes.toml
|
||||||
|
|
||||||
|
# 3a. Run the TUI client
|
||||||
|
$ cargo run -p pinakes-tui
|
||||||
|
|
||||||
|
# 3b. Run the desktop/web UI
|
||||||
|
$ cargo run -p pinakes-ui
|
||||||
|
|
||||||
|
# Connect TUI to a non-default server address
|
||||||
|
$ cargo run -p pinakes-tui -- --server http://localhost:3000
|
||||||
|
```
|
||||||
|
|
||||||
|
The GUI reads `PINAKES_SERVER_URL` if set; otherwise it assumes
|
||||||
|
`http://127.0.0.1:3000`. You may change the host and port if you require.
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
[cargo-nextest]: https://nexte.st
|
||||||
|
|
||||||
|
Pinakes boasts, or well, attempts to boast a very comprehensive testing suite to
|
||||||
|
ensure no regressions are introduced while in development. For fast parallel
|
||||||
|
test execution, we use [cargo-nextest] and while you are recommended to use it
|
||||||
|
over `cargo test` both should be supported. For CI and package tests, only
|
||||||
|
`cargo-nextest` is supported.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run all workspace tests (preferred)
|
||||||
|
$ just test
|
||||||
|
|
||||||
|
# Equivalent without Just
|
||||||
|
$ cargo nextest run --workspace
|
||||||
|
|
||||||
|
# Tests for a single crate
|
||||||
|
$ cargo nextest run -p pinakes-core
|
||||||
|
$ cargo nextest run -p pinakes-server
|
||||||
|
|
||||||
|
# Run a specific test by name and show output
|
||||||
|
$ cargo test -p pinakes-core -- test_name --nocapture
|
||||||
|
```
|
||||||
|
|
||||||
|
## Linting and Formatting
|
||||||
|
|
||||||
|
### Formatting
|
||||||
|
|
||||||
|
There is a treewide formatter provided within the `flake.nix` that invokes the
|
||||||
|
recommended formatter tooling across the various filetypes, including database
|
||||||
|
migrations and Markdown sources. While a `just fmt` recipe is provided, it will
|
||||||
|
not cover most of the files. Still, you may format the Rust sources as such:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Format all code
|
||||||
|
$ just fmt # or: cargo fmt
|
||||||
|
|
||||||
|
# Check formatting without modifying files
|
||||||
|
$ cargo fmt --check
|
||||||
|
```
|
||||||
|
|
||||||
|
### Linting
|
||||||
|
|
||||||
|
For now lints only cover Rust sources, and are provided by the Clippy tool. You
|
||||||
|
may, as usual, invoke it with `just lint`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run Clippy
|
||||||
|
$ just lint # or: cargo clippy --workspace
|
||||||
|
|
||||||
|
# Treat Clippy warnings as errors (used in CI)
|
||||||
|
$ cargo clippy --workspace -- -D warnings # `-D warnings` is recommended
|
||||||
|
```
|
||||||
|
|
||||||
|
All Clippy warnings, besides some of the really annoying ones or false
|
||||||
|
positives, must be resolved at the source. **Do not** use `#[allow(...)]` or
|
||||||
|
`#[expect(...)]` to silence warnings unless there is a documented reason.
|
||||||
|
|
||||||
|
It may be okay, at times, to suppress lints but you should focus on _resolving_
|
||||||
|
them rather than suppressing them. _If_ suppressing, prefer `#[expect]` and
|
||||||
|
provide the `reason` parameter.
|
||||||
|
|
||||||
|
## Generating API Documentation
|
||||||
|
|
||||||
|
The REST API documentation is generated from OpenAPI annotations in
|
||||||
|
`pinakes-server` using `cargo xtask`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Generate the API documentation at docs/api/
|
||||||
|
$ just docs # or: cargo xtask docs
|
||||||
|
```
|
||||||
|
|
||||||
|
This writes:
|
||||||
|
|
||||||
|
- `docs/api.md` - Index linking all generated files
|
||||||
|
- `docs/api/<tag>.md` - One Markdown file per API tag
|
||||||
|
- `docs/api/openapi.json` - Full OpenAPI 3.0 specification
|
||||||
|
|
||||||
|
Please do not edit files under `docs/api/` by hand; they are regenerated on each
|
||||||
|
run.
|
||||||
|
|
||||||
|
## Code Style
|
||||||
|
|
||||||
|
There are many conventions that are in play within the Pinakes codebase. You'll
|
||||||
|
get used and adjust to most of them naturally, but here are some of the general
|
||||||
|
conventions written down for convenience.
|
||||||
|
|
||||||
|
### General
|
||||||
|
|
||||||
|
1. **Make illegal states unrepresentable**; ıse ADTs and newtype wrappers to
|
||||||
|
encode constraints in the type system. Parse and validate at system
|
||||||
|
boundaries (user input, external APIs); trust internal types everywhere else.
|
||||||
|
2. This one is kind of obvious, but please use the `tracing` crate with
|
||||||
|
structured fields. Do not use `println!` or `eprintln!` in library or server
|
||||||
|
code.
|
||||||
|
3. All storage operations must be implemented for both the SQLite and PostgreSQL
|
||||||
|
backends. Neither backend is optional.
|
||||||
|
|
||||||
|
Naming is kind of obvious, but I'd like to remind you that we use `snake_case`
|
||||||
|
for functions and variables, `PascalCase` for types and traits,
|
||||||
|
`SCREAMING_SNAKE_CASE` for constants. There is not much else to it.
|
||||||
|
|
||||||
|
### Error Handling
|
||||||
|
|
||||||
|
- **`unwrap()` and `expect()` are banned** in non-test code. Use `?`,
|
||||||
|
`ok_or_else`, `match`, or `if let` instead.
|
||||||
|
- The only accepted exception is when a failure is truly impossible (e.g., a
|
||||||
|
hardcoded regex that is known-valid at compile time). In that case, add
|
||||||
|
`#[expect(clippy::expect_used, reason = "...")]` with a clear explanation.
|
||||||
|
- Wrap storage errors with the `db_ctx` helper so error messages include the
|
||||||
|
operation and relevant ID:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
stmt.execute(params).map_err(db_ctx("insert_media", &media_id))?;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Disallowed Types and APIs
|
||||||
|
|
||||||
|
Pinakes uses hashers from `rustc_hash`, i.e., `FxHashMap` and `FxHashSet` over
|
||||||
|
the `std` equivalents due to their faster nature without random state. This is
|
||||||
|
enforced by clippy.
|
||||||
|
|
||||||
|
As Pinakes uses Rust 1.90+, the now-deprecated `once_cell` crate is also banned.
|
||||||
|
Use the stdlib equivalents:
|
||||||
|
|
||||||
|
- `once_cell::unsync::OnceCell` -> `std::cell::OnceCell`
|
||||||
|
- `once_cell::sync::OnceCell` -> `std::sync::OnceLock`
|
||||||
|
- `once_cell::unsync::Lazy` -> `std::cell::LazyCell`
|
||||||
|
- `once_cell::sync::Lazy` -> `std::sync::LazyLock`
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
1. Fork the repository and create a feature branch.
|
||||||
|
2. Enter the dev shell: `nix develop` (or `direnv allow`).
|
||||||
|
3. Make your changes; keep commits focused and descriptive. We use **scoped
|
||||||
|
commits**.
|
||||||
|
4. Run `just fmt` and `just lint` before opening a pull request.
|
||||||
|
5. Run `just test` to verify nothing is broken.
|
||||||
|
6. Open a pull request against `main`. Describe what you changed and why.
|
||||||
|
|
||||||
|
For significant changes, consider opening an issue first to discuss the
|
||||||
|
approach.
|
||||||
|
|
||||||
|
## Packaging and Distribution
|
||||||
|
|
||||||
|
Pinakes is released under EUPL v1.2. For distributable release builds, please
|
||||||
|
respect the license. You may create release builds for the user-facing crates
|
||||||
|
using `cargo build --release` for server and TUI components. Use `dx` instead
|
||||||
|
for the GUI:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Server
|
||||||
|
$ cargo build --release -p pinakes-server
|
||||||
|
|
||||||
|
# TUI
|
||||||
|
$ cargo build --release -p pinakes-tui
|
||||||
|
|
||||||
|
# GUI
|
||||||
|
$ dx build --release -p pinakes-ui
|
||||||
|
```
|
||||||
|
|
||||||
|
The resulting binaries are self-contained except for system libraries required
|
||||||
|
by the UI (GTK3, libsoup, webkit2gtk). Database migrations run automatically on
|
||||||
|
first server startup via the `refinery` crate; no manual migration step is
|
||||||
|
needed.
|
||||||
|
|
||||||
|
SQLite (the default backend) requires no external database server. PostgreSQL
|
||||||
|
deployments need the `pg_trgm` extension enabled on the target database. It is
|
||||||
|
generally advisable to document those requirements for the users, even though
|
||||||
|
they do not require additional packaging steps.
|
||||||
104
docs/README.md
104
docs/README.md
|
|
@ -11,19 +11,41 @@ PostgreSQL (production deployments) as available database backends.
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
|
This project uses [Just](https://just.systems/) as its command runner to help
|
||||||
|
run cargo with a consistent interface. You are recommended to get it with Nix,
|
||||||
|
using the default devshell which provides Just.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Build all compilable crates
|
# Build everything (core crates + UI)
|
||||||
|
$ just build
|
||||||
|
|
||||||
|
# Build only core crates (cargo)
|
||||||
|
$ just build-core
|
||||||
|
|
||||||
|
# Build only the UI (uses dx - see note below)
|
||||||
|
$ just build-ui
|
||||||
|
```
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> The Dioxus UI (`pinakes-ui`) must be built with `dx` (Dioxus CLI) to compile
|
||||||
|
> SCSS stylesheets. Using `cargo build -p pinakes-ui` will not work correctly as
|
||||||
|
> it skips the SCSS compilation step. This was previously "remedied" with an
|
||||||
|
> intermediate build wrapper, which turned out to be fragile. It is highly
|
||||||
|
> recommended that you prefer `just` to build.
|
||||||
|
|
||||||
|
Manual build commands if not using Just:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Core crates (standard cargo)
|
||||||
$ cargo build -p pinakes-core -p pinakes-server -p pinakes-tui
|
$ cargo build -p pinakes-core -p pinakes-server -p pinakes-tui
|
||||||
|
|
||||||
# The Dioxus UI requires GTK3 and libsoup system libraries:
|
# UI (requires Dioxus CLI for SCSS compilation)
|
||||||
|
$ dx build -p pinakes-ui
|
||||||
|
|
||||||
|
# System dependencies for the UI:
|
||||||
# On Debian/Ubuntu: apt install libgtk-3-dev libsoup-3.0-dev libwebkit2gtk-4.1-dev
|
# On Debian/Ubuntu: apt install libgtk-3-dev libsoup-3.0-dev libwebkit2gtk-4.1-dev
|
||||||
# On Fedora: dnf install gtk3-devel libsoup3-devel webkit2gtk4.1-devel
|
# On Fedora: dnf install gtk3-devel libsoup3-devel webkit2gtk4.1-devel
|
||||||
# On Nix: Use the dev shell, everything is provided :)
|
# On Nix: Use the dev shell, everything is provided :)
|
||||||
$ cargo build -p pinakes-ui
|
|
||||||
|
|
||||||
# Alternatively, while app deps are in PATH, you may simply build the entire
|
|
||||||
# workspace.
|
|
||||||
$ cargo build --workspace
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
@ -53,17 +75,20 @@ Key settings:
|
||||||
|
|
||||||
## Running
|
## Running
|
||||||
|
|
||||||
|
All commands are available via Just. Run `just --list` to see all available
|
||||||
|
recipes.
|
||||||
|
|
||||||
### Server
|
### Server
|
||||||
|
|
||||||
To use Pinakes, you will need the server to be running. The GUI on its own will
|
To use Pinakes, you will need the server to be running. The GUI on its own will
|
||||||
work, but it will not be functional without the server.
|
work, but it will not be functional without the server.
|
||||||
|
|
||||||
```sh
|
```bash
|
||||||
# Start the server first
|
# Using Just
|
||||||
$ cargo run -p pinakes-server -- pinakes.toml
|
$ just run-server
|
||||||
|
|
||||||
# or:
|
# Or manually:
|
||||||
$ cargo run -p pinakes-server -- --config pinakes.toml
|
$ cargo run -p pinakes-server -- pinakes.toml
|
||||||
```
|
```
|
||||||
|
|
||||||
The server starts on the configured host:port (default `127.0.0.1:3000`). In a
|
The server starts on the configured host:port (default `127.0.0.1:3000`). In a
|
||||||
|
|
@ -77,11 +102,11 @@ terminal. While the server is running you may connect to it using the `--server`
|
||||||
flag.
|
flag.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Using defaults
|
# Using Just
|
||||||
$ cargo run -p pinakes-tui
|
$ just run-tui
|
||||||
|
|
||||||
# or with a custom server URL:
|
# Or manually:
|
||||||
$ cargo run -p pinakes-tui -- --server http://localhost:3000
|
$ cargo run -p pinakes-tui
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Keybindings
|
#### Keybindings
|
||||||
|
|
@ -120,9 +145,15 @@ Pinakes features a fully fledged Desktop and Web UI powered by Dioxus. Those two
|
||||||
components are meant as a GUI frontend for the Pinakes server, and are
|
components are meant as a GUI frontend for the Pinakes server, and are
|
||||||
interchangeable in terms of usage.
|
interchangeable in terms of usage.
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> The UI must be run with `dx` (Dioxus CLI), not `cargo run`.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Build the UI
|
# Using Just
|
||||||
$ cargo run -p pinakes-ui
|
$ just run-ui
|
||||||
|
|
||||||
|
# Or manually with dx:
|
||||||
|
$ dx serve -p pinakes-ui
|
||||||
```
|
```
|
||||||
|
|
||||||
> [!TIP]
|
> [!TIP]
|
||||||
|
|
@ -170,8 +201,11 @@ and design.
|
||||||
## Storage Backends
|
## Storage Backends
|
||||||
|
|
||||||
Two storage backends are supported. For convenience, SQLite is the default
|
Two storage backends are supported. For convenience, SQLite is the default
|
||||||
backend out of the box but for production deployments you may choose to prefer
|
backend out of the box but for production deployments, with improved search and
|
||||||
PostgreSQL.
|
scaling capabilities you may choose to prefer PostgreSQL. Both backends are
|
||||||
|
considered first-class citizens, and will be developed as such going further. If
|
||||||
|
your needs for Pinakes are modest, or if you are simply testing it out, SQLite
|
||||||
|
is the recommended database.
|
||||||
|
|
||||||
### **SQLite** (default)
|
### **SQLite** (default)
|
||||||
|
|
||||||
|
|
@ -180,6 +214,34 @@ guarantees FTS5 availability.
|
||||||
|
|
||||||
### **PostgreSQL**
|
### **PostgreSQL**
|
||||||
|
|
||||||
|
[pg_trgm]: https://www.postgresql.org/docs/current/pgtrgm.html
|
||||||
|
|
||||||
Native async with connection pooling (deadpool-postgres). Uses tsvector with
|
Native async with connection pooling (deadpool-postgres). Uses tsvector with
|
||||||
weighted columns for full-text search and pg_trgm for fuzzy matching. Requires
|
weighted columns for full-text search and [pg_trgm] for fuzzy matching. Requires
|
||||||
the `pg_trgm` extension.
|
the `pg_trgm` extension.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
[HACKING document]: ./HACKING.md
|
||||||
|
|
||||||
|
Pinakes, despite all the work going into it, is still in an early beta. Some of
|
||||||
|
the features still lack the polish they deserve and there _may_ be breaking
|
||||||
|
changes to the UI, databases, and the APIs. You may find a comprehensive
|
||||||
|
introduction to developing Pinakes in the [HACKING document].
|
||||||
|
|
||||||
|
It is generally advisable that you familiarize yourself with the codebase before
|
||||||
|
proposing or contributing changes. Pinakes consists of _many_ moving parts, and
|
||||||
|
it is better for the contributor experience to approach issues slowly and
|
||||||
|
discuss them beforehand.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
<!-- markdownlint-disable MD059 -->
|
||||||
|
|
||||||
|
[here]: https://interoperable-europe.ec.europa.eu/sites/default/files/custom-page/attachment/eupl_v1.2_en.pdf
|
||||||
|
|
||||||
|
This project is made available under European Union Public Licence (EUPL)
|
||||||
|
version 1.2. See [LICENSE](LICENSE) for more details on the exact conditions. An
|
||||||
|
online copy is provided [here].
|
||||||
|
|
||||||
|
<!-- markdownlint-enable MD059 -->
|
||||||
|
|
|
||||||
47
docs/api.md
Normal file
47
docs/api.md
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
# API Documentation
|
||||||
|
|
||||||
|
This is the index of all generated REST API documentation for Pinakes.
|
||||||
|
|
||||||
|
Documentation is generated from OpenAPI annotations via `cargo xtask docs` (or
|
||||||
|
`just docs`). Do not edit generated files by hand.
|
||||||
|
|
||||||
|
## Reference
|
||||||
|
|
||||||
|
- [openapi.json](api/openapi.json) - Full OpenAPI 3.0 specification
|
||||||
|
|
||||||
|
## Endpoints by Tag
|
||||||
|
|
||||||
|
- [Analytics](api/analytics.md) - Usage analytics and viewing history
|
||||||
|
- [Audit](api/audit.md) - Audit log entries
|
||||||
|
- [Auth](api/auth.md) - Authentication and session management
|
||||||
|
- [Backup](api/backup.md) - Database backup
|
||||||
|
- [Books](api/books.md) - Book metadata, series, authors, and reading progress
|
||||||
|
- [Collections](api/collections.md) - Media collections
|
||||||
|
- [Config](api/config.md) - Server configuration
|
||||||
|
- [Database](api/database.md) - Database administration
|
||||||
|
- [Duplicates](api/duplicates.md) - Duplicate media detection
|
||||||
|
- [Enrichment](api/enrichment.md) - External metadata enrichment
|
||||||
|
- [Export](api/export.md) - Media library export
|
||||||
|
- [Health](api/health.md) - Server health checks
|
||||||
|
- [Integrity](api/integrity.md) - Library integrity checks and repairs
|
||||||
|
- [Jobs](api/jobs.md) - Background job management
|
||||||
|
- [Media](api/media.md) - Media item management
|
||||||
|
- [Notes](api/notes.md) - Markdown notes link graph
|
||||||
|
- [Photos](api/photos.md) - Photo timeline and map view
|
||||||
|
- [Playlists](api/playlists.md) - Media playlists
|
||||||
|
- [Plugins](api/plugins.md) - Plugin management
|
||||||
|
- [Saved_searches](api/saved_searches.md) - Saved search queries
|
||||||
|
- [Scan](api/scan.md) - Directory scanning
|
||||||
|
- [Scheduled_tasks](api/scheduled_tasks.md) - Scheduled background tasks
|
||||||
|
- [Search](api/search.md) - Full-text media search
|
||||||
|
- [Shares](api/shares.md) - Media sharing and notifications
|
||||||
|
- [Social](api/social.md) - Ratings, comments, favorites, and share links
|
||||||
|
- [Statistics](api/statistics.md) - Library statistics
|
||||||
|
- [Streaming](api/streaming.md) - HLS and DASH adaptive streaming
|
||||||
|
- [Subtitles](api/subtitles.md) - Media subtitle management
|
||||||
|
- [Sync](api/sync.md) - Multi-device library synchronization
|
||||||
|
- [Tags](api/tags.md) - Media tag management
|
||||||
|
- [Transcode](api/transcode.md) - Video transcoding sessions
|
||||||
|
- [Upload](api/upload.md) - File upload and managed storage
|
||||||
|
- [Users](api/users.md) - User and library access management
|
||||||
|
- [Webhooks](api/webhooks.md) - Webhook configuration
|
||||||
19
docs/api/analytics.md
vendored
19
docs/api/analytics.md
vendored
|
|
@ -17,7 +17,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Event recorded |
|
| 200 | Event recorded |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 500 | Internal server error |
|
| 500 | Internal server error |
|
||||||
|
|
@ -31,14 +31,14 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| -------- | ----- | -------- | ------------------------- |
|
||||||
| `limit` | query | No | Maximum number of results |
|
| `limit` | query | No | Maximum number of results |
|
||||||
| `offset` | query | No | Pagination offset |
|
| `offset` | query | No | Pagination offset |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Most viewed media |
|
| 200 | Most viewed media |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 500 | Internal server error |
|
| 500 | Internal server error |
|
||||||
|
|
@ -52,14 +52,14 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| -------- | ----- | -------- | ------------------------- |
|
||||||
| `limit` | query | No | Maximum number of results |
|
| `limit` | query | No | Maximum number of results |
|
||||||
| `offset` | query | No | Pagination offset |
|
| `offset` | query | No | Pagination offset |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Recently viewed media |
|
| 200 | Recently viewed media |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 500 | Internal server error |
|
| 500 | Internal server error |
|
||||||
|
|
@ -73,13 +73,13 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ------------- |
|
||||||
| `id` | path | Yes | Media item ID |
|
| `id` | path | Yes | Media item ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Watch progress |
|
| 200 | Watch progress |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 404 | Not found |
|
| 404 | Not found |
|
||||||
|
|
@ -94,7 +94,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ------------- |
|
||||||
| `id` | path | Yes | Media item ID |
|
| `id` | path | Yes | Media item ID |
|
||||||
|
|
||||||
#### Request Body
|
#### Request Body
|
||||||
|
|
@ -106,7 +106,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Progress updated |
|
| 200 | Progress updated |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -114,4 +114,3 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
| 500 | Internal server error |
|
| 500 | Internal server error |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
5
docs/api/audit.md
vendored
5
docs/api/audit.md
vendored
|
|
@ -11,17 +11,16 @@ Audit log entries
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| -------- | ----- | -------- | ----------------- |
|
||||||
| `offset` | query | No | Pagination offset |
|
| `offset` | query | No | Pagination offset |
|
||||||
| `limit` | query | No | Page size |
|
| `limit` | query | No | Page size |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Audit log entries |
|
| 200 | Audit log entries |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 500 | Internal server error |
|
| 500 | Internal server error |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
16
docs/api/auth.md
vendored
16
docs/api/auth.md
vendored
|
|
@ -17,7 +17,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Login successful |
|
| 200 | Login successful |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Invalid credentials |
|
| 401 | Invalid credentials |
|
||||||
|
|
@ -32,7 +32,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Logged out |
|
| 200 | Logged out |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 500 | Internal server error |
|
| 500 | Internal server error |
|
||||||
|
|
@ -46,7 +46,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Current user info |
|
| 200 | Current user info |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 500 | Internal server error |
|
| 500 | Internal server error |
|
||||||
|
|
@ -55,15 +55,14 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
|
|
||||||
### POST /api/v1/auth/refresh
|
### POST /api/v1/auth/refresh
|
||||||
|
|
||||||
Refresh the current session, extending its expiry by the configured
|
Refresh the current session, extending its expiry by the configured duration.
|
||||||
duration.
|
|
||||||
|
|
||||||
**Authentication:** Required (Bearer JWT)
|
**Authentication:** Required (Bearer JWT)
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Session refreshed |
|
| 200 | Session refreshed |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 500 | Internal server error |
|
| 500 | Internal server error |
|
||||||
|
|
@ -79,7 +78,7 @@ Revoke all sessions for the current user
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | All sessions revoked |
|
| 200 | All sessions revoked |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 500 | Internal server error |
|
| 500 | Internal server error |
|
||||||
|
|
@ -93,11 +92,10 @@ Revoke all sessions for the current user
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Active sessions |
|
| 200 | Active sessions |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
| 500 | Internal server error |
|
| 500 | Internal server error |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
11
docs/api/backup.md
vendored
11
docs/api/backup.md
vendored
|
|
@ -6,22 +6,21 @@ Database backup
|
||||||
|
|
||||||
### POST /api/v1/admin/backup
|
### POST /api/v1/admin/backup
|
||||||
|
|
||||||
Create a database backup and return it as a downloadable file.
|
Create a database backup and return it as a downloadable file. POST
|
||||||
POST /api/v1/admin/backup
|
/api/v1/admin/backup
|
||||||
|
|
||||||
For `SQLite`: creates a backup via VACUUM INTO and returns the file.
|
For `SQLite`: creates a backup via VACUUM INTO and returns the file. For
|
||||||
For `PostgreSQL`: returns unsupported error (use `pg_dump` instead).
|
`PostgreSQL`: returns unsupported error (use `pg_dump` instead).
|
||||||
|
|
||||||
**Authentication:** Required (Bearer JWT)
|
**Authentication:** Required (Bearer JWT)
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Backup file download |
|
| 200 | Backup file download |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
| 500 | Internal server error |
|
| 500 | Internal server error |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
37
docs/api/books.md
vendored
37
docs/api/books.md
vendored
|
|
@ -13,7 +13,7 @@ List all books with optional search filters
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ----------- | ----- | -------- | ------------------- |
|
||||||
| `isbn` | query | No | Filter by ISBN |
|
| `isbn` | query | No | Filter by ISBN |
|
||||||
| `author` | query | No | Filter by author |
|
| `author` | query | No | Filter by author |
|
||||||
| `series` | query | No | Filter by series |
|
| `series` | query | No | Filter by series |
|
||||||
|
|
@ -25,7 +25,7 @@ List all books with optional search filters
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | List of books |
|
| 200 | List of books |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 500 | Internal server error |
|
| 500 | Internal server error |
|
||||||
|
|
@ -41,14 +41,14 @@ List all authors with book counts
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| -------- | ----- | -------- | ----------------- |
|
||||||
| `offset` | query | No | Pagination offset |
|
| `offset` | query | No | Pagination offset |
|
||||||
| `limit` | query | No | Pagination limit |
|
| `limit` | query | No | Pagination limit |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ------------------------ |
|
||||||
| 200 | Authors with book counts |
|
| 200 | Authors with book counts |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
||||||
|
|
@ -63,7 +63,7 @@ Get books by a specific author
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| -------- | ----- | -------- | ----------------- |
|
||||||
| `name` | path | Yes | Author name |
|
| `name` | path | Yes | Author name |
|
||||||
| `offset` | query | No | Pagination offset |
|
| `offset` | query | No | Pagination offset |
|
||||||
| `limit` | query | No | Pagination limit |
|
| `limit` | query | No | Pagination limit |
|
||||||
|
|
@ -71,7 +71,7 @@ Get books by a specific author
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------- |
|
||||||
| 200 | Books by author |
|
| 200 | Books by author |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
||||||
|
|
@ -86,13 +86,13 @@ Get user's reading list
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| -------- | ----- | -------- | ------------------------------------------------------------------------------ |
|
||||||
| `status` | query | No | Filter by reading status |
|
| `status` | query | No | Filter by reading status. Valid values: to_read, reading, completed, abandoned |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ------------ |
|
||||||
| 200 | Reading list |
|
| 200 | Reading list |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
||||||
|
|
@ -107,7 +107,7 @@ List all series with book counts
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | -------------------------- |
|
||||||
| 200 | List of series with counts |
|
| 200 | List of series with counts |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
||||||
|
|
@ -122,13 +122,13 @@ Get books in a specific series
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ------ | ---- | -------- | ----------- |
|
||||||
| `name` | path | Yes | Series name |
|
| `name` | path | Yes | Series name |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------- |
|
||||||
| 200 | Books in series |
|
| 200 | Books in series |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
||||||
|
|
@ -143,13 +143,13 @@ Get book metadata by media ID
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ------------- |
|
||||||
| `id` | path | Yes | Media item ID |
|
| `id` | path | Yes | Media item ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ------------- |
|
||||||
| 200 | Book metadata |
|
| 200 | Book metadata |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 404 | Not found |
|
| 404 | Not found |
|
||||||
|
|
@ -165,13 +165,13 @@ Get reading progress for a book
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ------------- |
|
||||||
| `id` | path | Yes | Media item ID |
|
| `id` | path | Yes | Media item ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ---------------- |
|
||||||
| 200 | Reading progress |
|
| 200 | Reading progress |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 404 | Not found |
|
| 404 | Not found |
|
||||||
|
|
@ -187,7 +187,7 @@ Update reading progress for a book
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ------------- |
|
||||||
| `id` | path | Yes | Media item ID |
|
| `id` | path | Yes | Media item ID |
|
||||||
|
|
||||||
#### Request Body
|
#### Request Body
|
||||||
|
|
@ -199,10 +199,9 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ---------------- |
|
||||||
| 204 | Progress updated |
|
| 204 | Progress updated |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
25
docs/api/collections.md
vendored
25
docs/api/collections.md
vendored
|
|
@ -11,7 +11,7 @@ Media collections
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | List of collections |
|
| 200 | List of collections |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 500 | Internal server error |
|
| 500 | Internal server error |
|
||||||
|
|
@ -31,7 +31,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Collection created |
|
| 200 | Collection created |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -47,13 +47,13 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ------------- |
|
||||||
| `id` | path | Yes | Collection ID |
|
| `id` | path | Yes | Collection ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Collection |
|
| 200 | Collection |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 404 | Not found |
|
| 404 | Not found |
|
||||||
|
|
@ -68,13 +68,13 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ------------- |
|
||||||
| `id` | path | Yes | Collection ID |
|
| `id` | path | Yes | Collection ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Collection deleted |
|
| 200 | Collection deleted |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -90,13 +90,13 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ------------- |
|
||||||
| `id` | path | Yes | Collection ID |
|
| `id` | path | Yes | Collection ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Collection members |
|
| 200 | Collection members |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 404 | Not found |
|
| 404 | Not found |
|
||||||
|
|
@ -111,7 +111,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ------------- |
|
||||||
| `id` | path | Yes | Collection ID |
|
| `id` | path | Yes | Collection ID |
|
||||||
|
|
||||||
#### Request Body
|
#### Request Body
|
||||||
|
|
@ -123,7 +123,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Member added |
|
| 200 | Member added |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -139,14 +139,14 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---------- | ---- | -------- | ------------- |
|
||||||
| `id` | path | Yes | Collection ID |
|
| `id` | path | Yes | Collection ID |
|
||||||
| `media_id` | path | Yes | Media item ID |
|
| `media_id` | path | Yes | Media item ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Member removed |
|
| 200 | Member removed |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -154,4 +154,3 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
| 500 | Internal server error |
|
| 500 | Internal server error |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
13
docs/api/config.md
vendored
13
docs/api/config.md
vendored
|
|
@ -11,7 +11,7 @@ Server configuration
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ---------------------------- |
|
||||||
| 200 | Current server configuration |
|
| 200 | Current server configuration |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -32,7 +32,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Updated configuration |
|
| 200 | Updated configuration |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -54,7 +54,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Updated configuration |
|
| 200 | Updated configuration |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -75,7 +75,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Updated configuration |
|
| 200 | Updated configuration |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -90,7 +90,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | UI configuration |
|
| 200 | UI configuration |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 500 | Internal server error |
|
| 500 | Internal server error |
|
||||||
|
|
@ -110,11 +110,10 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ------------------------ |
|
||||||
| 200 | Updated UI configuration |
|
| 200 | Updated UI configuration |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
| 500 | Internal server error |
|
| 500 | Internal server error |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
7
docs/api/database.md
vendored
7
docs/api/database.md
vendored
|
|
@ -11,7 +11,7 @@ Database administration
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Database cleared |
|
| 200 | Database cleared |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -26,7 +26,7 @@ Database administration
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Database statistics |
|
| 200 | Database statistics |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -41,11 +41,10 @@ Database administration
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Database vacuumed |
|
| 200 | Database vacuumed |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
| 500 | Internal server error |
|
| 500 | Internal server error |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
3
docs/api/duplicates.md
vendored
3
docs/api/duplicates.md
vendored
|
|
@ -11,10 +11,9 @@ Duplicate media detection
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Duplicate groups |
|
| 200 | Duplicate groups |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 500 | Internal server error |
|
| 500 | Internal server error |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
11
docs/api/enrichment.md
vendored
11
docs/api/enrichment.md
vendored
|
|
@ -17,7 +17,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ------------------------ |
|
||||||
| 200 | Enrichment job submitted |
|
| 200 | Enrichment job submitted |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -33,13 +33,13 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ------------- |
|
||||||
| `id` | path | Yes | Media item ID |
|
| `id` | path | Yes | Media item ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ------------------------ |
|
||||||
| 200 | Enrichment job submitted |
|
| 200 | Enrichment job submitted |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -55,17 +55,16 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ------------- |
|
||||||
| `id` | path | Yes | Media item ID |
|
| `id` | path | Yes | Media item ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | External metadata |
|
| 200 | External metadata |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 404 | Not found |
|
| 404 | Not found |
|
||||||
| 500 | Internal server error |
|
| 500 | Internal server error |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
5
docs/api/export.md
vendored
5
docs/api/export.md
vendored
|
|
@ -11,7 +11,7 @@ Media library export
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Export job submitted |
|
| 200 | Export job submitted |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -32,11 +32,10 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Export job submitted |
|
| 200 | Export job submitted |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
| 500 | Internal server error |
|
| 500 | Internal server error |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
17
docs/api/health.md
vendored
17
docs/api/health.md
vendored
|
|
@ -13,7 +13,7 @@ Comprehensive health check - includes database, filesystem, and cache status
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ------------- |
|
||||||
| 200 | Health status |
|
| 200 | Health status |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
@ -25,39 +25,38 @@ Comprehensive health check - includes database, filesystem, and cache status
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ---------------------- |
|
||||||
| 200 | Detailed health status |
|
| 200 | Detailed health status |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### GET /api/v1/health/live
|
### GET /api/v1/health/live
|
||||||
|
|
||||||
Liveness probe - just checks if the server is running
|
Liveness probe - just checks if the server is running Returns 200 OK if the
|
||||||
Returns 200 OK if the server process is alive
|
server process is alive
|
||||||
|
|
||||||
**Authentication:** Required (Bearer JWT)
|
**Authentication:** Required (Bearer JWT)
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------- |
|
||||||
| 200 | Server is alive |
|
| 200 | Server is alive |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### GET /api/v1/health/ready
|
### GET /api/v1/health/ready
|
||||||
|
|
||||||
Readiness probe - checks if the server can serve requests
|
Readiness probe - checks if the server can serve requests Returns 200 OK if
|
||||||
Returns 200 OK if database is accessible
|
database is accessible
|
||||||
|
|
||||||
**Authentication:** Required (Bearer JWT)
|
**Authentication:** Required (Bearer JWT)
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ---------------- |
|
||||||
| 200 | Server is ready |
|
| 200 | Server is ready |
|
||||||
| 503 | Server not ready |
|
| 503 | Server not ready |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
11
docs/api/integrity.md
vendored
11
docs/api/integrity.md
vendored
|
|
@ -11,7 +11,7 @@ Library integrity checks and repairs
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ------------------------------ |
|
||||||
| 200 | Orphan detection job submitted |
|
| 200 | Orphan detection job submitted |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -32,7 +32,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Orphans resolved |
|
| 200 | Orphans resolved |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -47,7 +47,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ------------------------------- |
|
||||||
| 200 | Thumbnail cleanup job submitted |
|
| 200 | Thumbnail cleanup job submitted |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -68,7 +68,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ---------------------------------- |
|
||||||
| 200 | Thumbnail generation job submitted |
|
| 200 | Thumbnail generation job submitted |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -89,11 +89,10 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ------------------------------------ |
|
||||||
| 200 | Integrity verification job submitted |
|
| 200 | Integrity verification job submitted |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
| 500 | Internal server error |
|
| 500 | Internal server error |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
11
docs/api/jobs.md
vendored
11
docs/api/jobs.md
vendored
|
|
@ -11,7 +11,7 @@ Background job management
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ------------ |
|
||||||
| 200 | List of jobs |
|
| 200 | List of jobs |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -25,13 +25,13 @@ Background job management
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ----------- |
|
||||||
| `id` | path | Yes | Job ID |
|
| `id` | path | Yes | Job ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ------------ |
|
||||||
| 200 | Job details |
|
| 200 | Job details |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -46,17 +46,16 @@ Background job management
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ----------- |
|
||||||
| `id` | path | Yes | Job ID |
|
| `id` | path | Yes | Job ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ------------- |
|
||||||
| 200 | Job cancelled |
|
| 200 | Job cancelled |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
| 404 | Not found |
|
| 404 | Not found |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
89
docs/api/media.md
vendored
89
docs/api/media.md
vendored
|
|
@ -11,7 +11,7 @@ Media item management
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| -------- | ----- | -------- | ----------------- |
|
||||||
| `offset` | query | No | Pagination offset |
|
| `offset` | query | No | Pagination offset |
|
||||||
| `limit` | query | No | Page size |
|
| `limit` | query | No | Page size |
|
||||||
| `sort` | query | No | Sort field |
|
| `sort` | query | No | Sort field |
|
||||||
|
|
@ -19,7 +19,7 @@ Media item management
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | List of media items |
|
| 200 | List of media items |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 500 | Internal server error |
|
| 500 | Internal server error |
|
||||||
|
|
@ -33,7 +33,7 @@ Media item management
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | All media deleted |
|
| 200 | All media deleted |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -54,7 +54,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ----------------------- |
|
||||||
| 200 | Batch collection result |
|
| 200 | Batch collection result |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -76,7 +76,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Batch delete result |
|
| 200 | Batch delete result |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -98,7 +98,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Batch move result |
|
| 200 | Batch move result |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -120,7 +120,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Batch tag result |
|
| 200 | Batch tag result |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -142,7 +142,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Batch update result |
|
| 200 | Batch update result |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -158,7 +158,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Media count |
|
| 200 | Media count |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 500 | Internal server error |
|
| 500 | Internal server error |
|
||||||
|
|
@ -178,7 +178,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Media imported |
|
| 200 | Media imported |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -200,7 +200,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Batch import results |
|
| 200 | Batch import results |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -222,7 +222,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ------------------------ |
|
||||||
| 200 | Directory import results |
|
| 200 | Directory import results |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -244,7 +244,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Media imported |
|
| 200 | Media imported |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -266,7 +266,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Directory preview |
|
| 200 | Directory preview |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -282,14 +282,14 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| -------- | ----- | -------- | ----------------- |
|
||||||
| `offset` | query | No | Pagination offset |
|
| `offset` | query | No | Pagination offset |
|
||||||
| `limit` | query | No | Page size |
|
| `limit` | query | No | Page size |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Trashed media items |
|
| 200 | Trashed media items |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 500 | Internal server error |
|
| 500 | Internal server error |
|
||||||
|
|
@ -303,7 +303,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Trash emptied |
|
| 200 | Trash emptied |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -318,7 +318,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Trash info |
|
| 200 | Trash info |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 500 | Internal server error |
|
| 500 | Internal server error |
|
||||||
|
|
@ -332,13 +332,13 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ------------- |
|
||||||
| `id` | path | Yes | Media item ID |
|
| `id` | path | Yes | Media item ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Media item |
|
| 200 | Media item |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 404 | Not found |
|
| 404 | Not found |
|
||||||
|
|
@ -353,7 +353,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ------------- |
|
||||||
| `id` | path | Yes | Media item ID |
|
| `id` | path | Yes | Media item ID |
|
||||||
|
|
||||||
#### Request Body
|
#### Request Body
|
||||||
|
|
@ -365,7 +365,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Updated media item |
|
| 200 | Updated media item |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -382,13 +382,13 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ------------- |
|
||||||
| `id` | path | Yes | Media item ID |
|
| `id` | path | Yes | Media item ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Media deleted |
|
| 200 | Media deleted |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -404,7 +404,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ------------- |
|
||||||
| `id` | path | Yes | Media item ID |
|
| `id` | path | Yes | Media item ID |
|
||||||
|
|
||||||
#### Request Body
|
#### Request Body
|
||||||
|
|
@ -416,7 +416,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Custom field set |
|
| 200 | Custom field set |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -433,14 +433,14 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ------ | ---- | -------- | ----------------- |
|
||||||
| `id` | path | Yes | Media item ID |
|
| `id` | path | Yes | Media item ID |
|
||||||
| `name` | path | Yes | Custom field name |
|
| `name` | path | Yes | Custom field name |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Custom field deleted |
|
| 200 | Custom field deleted |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -456,7 +456,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ------------- |
|
||||||
| `id` | path | Yes | Media item ID |
|
| `id` | path | Yes | Media item ID |
|
||||||
|
|
||||||
#### Request Body
|
#### Request Body
|
||||||
|
|
@ -468,7 +468,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Moved media item |
|
| 200 | Moved media item |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -485,13 +485,13 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ------------- |
|
||||||
| `id` | path | Yes | Media item ID |
|
| `id` | path | Yes | Media item ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Media opened |
|
| 200 | Media opened |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 404 | Not found |
|
| 404 | Not found |
|
||||||
|
|
@ -506,14 +506,14 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ----------- | ----- | -------- | ------------------------------------ |
|
||||||
| `id` | path | Yes | Media item ID |
|
| `id` | path | Yes | Media item ID |
|
||||||
| `permanent` | query | No | Set to 'true' for permanent deletion |
|
| `permanent` | query | No | Set to 'true' for permanent deletion |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Media deleted |
|
| 200 | Media deleted |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -529,7 +529,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ------------- |
|
||||||
| `id` | path | Yes | Media item ID |
|
| `id` | path | Yes | Media item ID |
|
||||||
|
|
||||||
#### Request Body
|
#### Request Body
|
||||||
|
|
@ -541,7 +541,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Renamed media item |
|
| 200 | Renamed media item |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -558,13 +558,13 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ------------- |
|
||||||
| `id` | path | Yes | Media item ID |
|
| `id` | path | Yes | Media item ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Media restored |
|
| 200 | Media restored |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -580,13 +580,13 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ------------- |
|
||||||
| `id` | path | Yes | Media item ID |
|
| `id` | path | Yes | Media item ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Media stream |
|
| 200 | Media stream |
|
||||||
| 206 | Partial content |
|
| 206 | Partial content |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -602,13 +602,13 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ------------- |
|
||||||
| `id` | path | Yes | Media item ID |
|
| `id` | path | Yes | Media item ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Thumbnail image |
|
| 200 | Thumbnail image |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 404 | Not found |
|
| 404 | Not found |
|
||||||
|
|
@ -623,13 +623,13 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ------------- |
|
||||||
| `id` | path | Yes | Media item ID |
|
| `id` | path | Yes | Media item ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Media moved to trash |
|
| 200 | Media moved to trash |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -637,4 +637,3 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
| 500 | Internal server error |
|
| 500 | Internal server error |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
21
docs/api/notes.md
vendored
21
docs/api/notes.md
vendored
|
|
@ -15,13 +15,13 @@ GET /api/v1/media/{id}/backlinks
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ------------- |
|
||||||
| `id` | path | Yes | Media item ID |
|
| `id` | path | Yes | Media item ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Backlinks |
|
| 200 | Backlinks |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 404 | Not found |
|
| 404 | Not found |
|
||||||
|
|
@ -40,13 +40,13 @@ GET /api/v1/media/{id}/outgoing-links
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ------------- |
|
||||||
| `id` | path | Yes | Media item ID |
|
| `id` | path | Yes | Media item ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Outgoing links |
|
| 200 | Outgoing links |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 404 | Not found |
|
| 404 | Not found |
|
||||||
|
|
@ -65,13 +65,13 @@ POST /api/v1/media/{id}/reindex-links
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ------------- |
|
||||||
| `id` | path | Yes | Media item ID |
|
| `id` | path | Yes | Media item ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Links reindexed |
|
| 200 | Links reindexed |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 404 | Not found |
|
| 404 | Not found |
|
||||||
|
|
@ -90,14 +90,14 @@ GET /api/v1/notes/graph?center={uuid}&depth={n}
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| -------- | ----- | -------- | ---------------------------------- |
|
||||||
| `center` | query | No | Center node ID |
|
| `center` | query | No | Center node ID |
|
||||||
| `depth` | query | No | Traversal depth (max 5, default 2) |
|
| `depth` | query | No | Traversal depth (max 5, default 2) |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Graph data |
|
| 200 | Graph data |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 500 | Internal server error |
|
| 500 | Internal server error |
|
||||||
|
|
@ -115,7 +115,7 @@ POST /api/v1/notes/resolve-links
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Links resolved |
|
| 200 | Links resolved |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 500 | Internal server error |
|
| 500 | Internal server error |
|
||||||
|
|
@ -133,10 +133,9 @@ GET /api/v1/notes/unresolved-count
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Unresolved link count |
|
| 200 | Unresolved link count |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 500 | Internal server error |
|
| 500 | Internal server error |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
63
docs/api/openapi.json
vendored
63
docs/api/openapi.json
vendored
|
|
@ -1275,7 +1275,7 @@
|
||||||
{
|
{
|
||||||
"name": "status",
|
"name": "status",
|
||||||
"in": "query",
|
"in": "query",
|
||||||
"description": "Filter by reading status",
|
"description": "Filter by reading status. Valid values: to_read, reading, completed, abandoned",
|
||||||
"required": false,
|
"required": false,
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
|
@ -4704,14 +4704,11 @@
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"description": "Subtitles",
|
"description": "Subtitles and available embedded tracks",
|
||||||
"content": {
|
"content": {
|
||||||
"application/json": {
|
"application/json": {
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "array",
|
"$ref": "#/components/schemas/SubtitleListResponse"
|
||||||
"items": {
|
|
||||||
"$ref": "#/components/schemas/SubtitleResponse"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -8529,6 +8526,7 @@
|
||||||
"integer",
|
"integer",
|
||||||
"null"
|
"null"
|
||||||
],
|
],
|
||||||
|
"format": "int32",
|
||||||
"minimum": 0
|
"minimum": 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -11787,6 +11785,28 @@
|
||||||
],
|
],
|
||||||
"description": "Response for accessing shared content.\nSingle-media shares return the media object directly (backwards compatible).\nCollection/Tag/SavedSearch shares return a list of items."
|
"description": "Response for accessing shared content.\nSingle-media shares return the media object directly (backwards compatible).\nCollection/Tag/SavedSearch shares return a list of items."
|
||||||
},
|
},
|
||||||
|
"SubtitleListResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Response for listing subtitles on a media item.",
|
||||||
|
"required": [
|
||||||
|
"subtitles",
|
||||||
|
"available_tracks"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"available_tracks": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/components/schemas/SubtitleTrackInfoResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"subtitles": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/components/schemas/SubtitleResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"SubtitleResponse": {
|
"SubtitleResponse": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
|
|
@ -11829,10 +11849,41 @@
|
||||||
"integer",
|
"integer",
|
||||||
"null"
|
"null"
|
||||||
],
|
],
|
||||||
|
"format": "int32",
|
||||||
"minimum": 0
|
"minimum": 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"SubtitleTrackInfoResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Information about an embedded subtitle track available for extraction.",
|
||||||
|
"required": [
|
||||||
|
"index",
|
||||||
|
"format"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"format": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"index": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int32",
|
||||||
|
"minimum": 0
|
||||||
|
},
|
||||||
|
"language": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"title": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"SyncChangeResponse": {
|
"SyncChangeResponse": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
|
|
|
||||||
9
docs/api/photos.md
vendored
9
docs/api/photos.md
vendored
|
|
@ -13,7 +13,7 @@ Get photos in a bounding box for map view
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ------ | ----- | -------- | ------------------------ |
|
||||||
| `lat1` | query | Yes | Bounding box latitude 1 |
|
| `lat1` | query | Yes | Bounding box latitude 1 |
|
||||||
| `lon1` | query | Yes | Bounding box longitude 1 |
|
| `lon1` | query | Yes | Bounding box longitude 1 |
|
||||||
| `lat2` | query | Yes | Bounding box latitude 2 |
|
| `lat2` | query | Yes | Bounding box latitude 2 |
|
||||||
|
|
@ -22,7 +22,7 @@ Get photos in a bounding box for map view
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Map markers |
|
| 200 | Map markers |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -39,7 +39,7 @@ Get timeline of photos grouped by date
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---------- | ----- | -------- | -------------------------- |
|
||||||
| `group_by` | query | No | Grouping: day, month, year |
|
| `group_by` | query | No | Grouping: day, month, year |
|
||||||
| `year` | query | No | Filter by year |
|
| `year` | query | No | Filter by year |
|
||||||
| `month` | query | No | Filter by month |
|
| `month` | query | No | Filter by month |
|
||||||
|
|
@ -48,10 +48,9 @@ Get timeline of photos grouped by date
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Photo timeline groups |
|
| 200 | Photo timeline groups |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 500 | Internal server error |
|
| 500 | Internal server error |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
37
docs/api/playlists.md
vendored
37
docs/api/playlists.md
vendored
|
|
@ -11,7 +11,7 @@ Media playlists
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | List of playlists |
|
| 200 | List of playlists |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 500 | Internal server error |
|
| 500 | Internal server error |
|
||||||
|
|
@ -31,7 +31,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Playlist created |
|
| 200 | Playlist created |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -46,13 +46,13 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ----------- |
|
||||||
| `id` | path | Yes | Playlist ID |
|
| `id` | path | Yes | Playlist ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ---------------- |
|
||||||
| 200 | Playlist details |
|
| 200 | Playlist details |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -67,7 +67,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ----------- |
|
||||||
| `id` | path | Yes | Playlist ID |
|
| `id` | path | Yes | Playlist ID |
|
||||||
|
|
||||||
#### Request Body
|
#### Request Body
|
||||||
|
|
@ -79,7 +79,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ---------------- |
|
||||||
| 200 | Playlist updated |
|
| 200 | Playlist updated |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -95,13 +95,13 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ----------- |
|
||||||
| `id` | path | Yes | Playlist ID |
|
| `id` | path | Yes | Playlist ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ---------------- |
|
||||||
| 200 | Playlist deleted |
|
| 200 | Playlist deleted |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -116,13 +116,13 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ----------- |
|
||||||
| `id` | path | Yes | Playlist ID |
|
| `id` | path | Yes | Playlist ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | -------------- |
|
||||||
| 200 | Playlist items |
|
| 200 | Playlist items |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -137,7 +137,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ----------- |
|
||||||
| `id` | path | Yes | Playlist ID |
|
| `id` | path | Yes | Playlist ID |
|
||||||
|
|
||||||
#### Request Body
|
#### Request Body
|
||||||
|
|
@ -149,7 +149,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ------------ |
|
||||||
| 200 | Item added |
|
| 200 | Item added |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -164,7 +164,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ----------- |
|
||||||
| `id` | path | Yes | Playlist ID |
|
| `id` | path | Yes | Playlist ID |
|
||||||
|
|
||||||
#### Request Body
|
#### Request Body
|
||||||
|
|
@ -176,7 +176,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | -------------- |
|
||||||
| 200 | Item reordered |
|
| 200 | Item reordered |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -191,14 +191,14 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---------- | ---- | -------- | ------------- |
|
||||||
| `id` | path | Yes | Playlist ID |
|
| `id` | path | Yes | Playlist ID |
|
||||||
| `media_id` | path | Yes | Media item ID |
|
| `media_id` | path | Yes | Media item ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ------------ |
|
||||||
| 200 | Item removed |
|
| 200 | Item removed |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -213,17 +213,16 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ----------- |
|
||||||
| `id` | path | Yes | Playlist ID |
|
| `id` | path | Yes | Playlist ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ----------------------- |
|
||||||
| 200 | Shuffled playlist items |
|
| 200 | Shuffled playlist items |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
| 404 | Not found |
|
| 404 | Not found |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
29
docs/api/plugins.md
vendored
29
docs/api/plugins.md
vendored
|
|
@ -13,7 +13,7 @@ List all installed plugins
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | List of plugins |
|
| 200 | List of plugins |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 500 | Internal server error |
|
| 500 | Internal server error |
|
||||||
|
|
@ -35,7 +35,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ---------------- |
|
||||||
| 200 | Plugin installed |
|
| 200 | Plugin installed |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -59,7 +59,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | -------------- |
|
||||||
| 200 | Event received |
|
| 200 | Event received |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
||||||
|
|
@ -74,7 +74,7 @@ List all UI pages provided by loaded plugins
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------- |
|
||||||
| 200 | Plugin UI pages |
|
| 200 | Plugin UI pages |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
||||||
|
|
@ -89,7 +89,7 @@ List merged CSS custom property overrides from all enabled plugins
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | -------------------------- |
|
||||||
| 200 | Plugin UI theme extensions |
|
| 200 | Plugin UI theme extensions |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
||||||
|
|
@ -104,7 +104,7 @@ List all UI widgets provided by loaded plugins
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ----------------- |
|
||||||
| 200 | Plugin UI widgets |
|
| 200 | Plugin UI widgets |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
||||||
|
|
@ -119,13 +119,13 @@ Get a specific plugin by ID
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ----------- |
|
||||||
| `id` | path | Yes | Plugin ID |
|
| `id` | path | Yes | Plugin ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | -------------- |
|
||||||
| 200 | Plugin details |
|
| 200 | Plugin details |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 404 | Not found |
|
| 404 | Not found |
|
||||||
|
|
@ -141,13 +141,13 @@ Uninstall a plugin
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ----------- |
|
||||||
| `id` | path | Yes | Plugin ID |
|
| `id` | path | Yes | Plugin ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ------------------ |
|
||||||
| 200 | Plugin uninstalled |
|
| 200 | Plugin uninstalled |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -164,13 +164,13 @@ Reload a plugin (for development)
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ----------- |
|
||||||
| `id` | path | Yes | Plugin ID |
|
| `id` | path | Yes | Plugin ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------- |
|
||||||
| 200 | Plugin reloaded |
|
| 200 | Plugin reloaded |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -187,7 +187,7 @@ Enable or disable a plugin
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ----------- |
|
||||||
| `id` | path | Yes | Plugin ID |
|
| `id` | path | Yes | Plugin ID |
|
||||||
|
|
||||||
#### Request Body
|
#### Request Body
|
||||||
|
|
@ -199,11 +199,10 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | -------------- |
|
||||||
| 200 | Plugin toggled |
|
| 200 | Plugin toggled |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
| 404 | Not found |
|
| 404 | Not found |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
9
docs/api/saved_searches.md
vendored
9
docs/api/saved_searches.md
vendored
|
|
@ -11,7 +11,7 @@ Saved search queries
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ---------------------- |
|
||||||
| 200 | List of saved searches |
|
| 200 | List of saved searches |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 500 | Internal server error |
|
| 500 | Internal server error |
|
||||||
|
|
@ -31,7 +31,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Search saved |
|
| 200 | Search saved |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -46,17 +46,16 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | --------------- |
|
||||||
| `id` | path | Yes | Saved search ID |
|
| `id` | path | Yes | Saved search ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Saved search deleted |
|
| 200 | Saved search deleted |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 404 | Not found |
|
| 404 | Not found |
|
||||||
| 500 | Internal server error |
|
| 500 | Internal server error |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
5
docs/api/scan.md
vendored
5
docs/api/scan.md
vendored
|
|
@ -19,7 +19,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Scan job submitted |
|
| 200 | Scan job submitted |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -34,9 +34,8 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ------------ |
|
||||||
| 200 | Scan status |
|
| 200 | Scan status |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
11
docs/api/scheduled_tasks.md
vendored
11
docs/api/scheduled_tasks.md
vendored
|
|
@ -11,7 +11,7 @@ Scheduled background tasks
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ----------------------- |
|
||||||
| 200 | List of scheduled tasks |
|
| 200 | List of scheduled tasks |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -25,13 +25,13 @@ Scheduled background tasks
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ----------- |
|
||||||
| `id` | path | Yes | Task ID |
|
| `id` | path | Yes | Task ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | -------------- |
|
||||||
| 200 | Task triggered |
|
| 200 | Task triggered |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -46,17 +46,16 @@ Scheduled background tasks
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ----------- |
|
||||||
| `id` | path | Yes | Task ID |
|
| `id` | path | Yes | Task ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ------------ |
|
||||||
| 200 | Task toggled |
|
| 200 | Task toggled |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
| 404 | Not found |
|
| 404 | Not found |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
7
docs/api/search.md
vendored
7
docs/api/search.md
vendored
|
|
@ -11,7 +11,7 @@ Full-text media search
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| -------- | ----- | -------- | ----------------- |
|
||||||
| `q` | query | Yes | Search query |
|
| `q` | query | Yes | Search query |
|
||||||
| `sort` | query | No | Sort order |
|
| `sort` | query | No | Sort order |
|
||||||
| `offset` | query | No | Pagination offset |
|
| `offset` | query | No | Pagination offset |
|
||||||
|
|
@ -20,7 +20,7 @@ Full-text media search
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Search results |
|
| 200 | Search results |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -41,11 +41,10 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Search results |
|
| 200 | Search results |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 500 | Internal server error |
|
| 500 | Internal server error |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
77
docs/api/shares.md
vendored
77
docs/api/shares.md
vendored
|
|
@ -6,15 +6,14 @@ Media sharing and notifications
|
||||||
|
|
||||||
### GET /api/v1/notifications/shares
|
### GET /api/v1/notifications/shares
|
||||||
|
|
||||||
Get unread share notifications
|
Get unread share notifications GET /api/notifications/shares
|
||||||
GET /api/notifications/shares
|
|
||||||
|
|
||||||
**Authentication:** Required (Bearer JWT)
|
**Authentication:** Required (Bearer JWT)
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | -------------------- |
|
||||||
| 200 | Unread notifications |
|
| 200 | Unread notifications |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
||||||
|
|
@ -22,15 +21,14 @@ GET /api/notifications/shares
|
||||||
|
|
||||||
### POST /api/v1/notifications/shares/read-all
|
### POST /api/v1/notifications/shares/read-all
|
||||||
|
|
||||||
Mark all notifications as read
|
Mark all notifications as read POST /api/notifications/shares/read-all
|
||||||
POST /api/notifications/shares/read-all
|
|
||||||
|
|
||||||
**Authentication:** Required (Bearer JWT)
|
**Authentication:** Required (Bearer JWT)
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | -------------------------------- |
|
||||||
| 200 | All notifications marked as read |
|
| 200 | All notifications marked as read |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
||||||
|
|
@ -38,21 +36,20 @@ POST /api/notifications/shares/read-all
|
||||||
|
|
||||||
### POST /api/v1/notifications/shares/{id}/read
|
### POST /api/v1/notifications/shares/{id}/read
|
||||||
|
|
||||||
Mark a notification as read
|
Mark a notification as read POST /api/notifications/shares/{id}/read
|
||||||
POST /api/notifications/shares/{id}/read
|
|
||||||
|
|
||||||
**Authentication:** Required (Bearer JWT)
|
**Authentication:** Required (Bearer JWT)
|
||||||
|
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | --------------- |
|
||||||
| `id` | path | Yes | Notification ID |
|
| `id` | path | Yes | Notification ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------------- |
|
||||||
| 200 | Notification marked as read |
|
| 200 | Notification marked as read |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
||||||
|
|
@ -60,22 +57,21 @@ POST /api/notifications/shares/{id}/read
|
||||||
|
|
||||||
### GET /api/v1/shared/{token}
|
### GET /api/v1/shared/{token}
|
||||||
|
|
||||||
Access a public shared resource
|
Access a public shared resource GET /api/shared/{token}
|
||||||
GET /api/shared/{token}
|
|
||||||
|
|
||||||
**Authentication:** Required (Bearer JWT)
|
**Authentication:** Required (Bearer JWT)
|
||||||
|
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---------- | ----- | -------- | -------------------------- |
|
||||||
| `token` | path | Yes | Share token |
|
| `token` | path | Yes | Share token |
|
||||||
| `password` | query | No | Share password if required |
|
| `password` | query | No | Share password if required |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | -------------- |
|
||||||
| 200 | Shared content |
|
| 200 | Shared content |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 404 | Not found |
|
| 404 | Not found |
|
||||||
|
|
@ -84,8 +80,7 @@ GET /api/shared/{token}
|
||||||
|
|
||||||
### POST /api/v1/shares
|
### POST /api/v1/shares
|
||||||
|
|
||||||
Create a new share
|
Create a new share POST /api/shares
|
||||||
POST /api/shares
|
|
||||||
|
|
||||||
**Authentication:** Required (Bearer JWT)
|
**Authentication:** Required (Bearer JWT)
|
||||||
|
|
||||||
|
|
@ -98,7 +93,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Share created |
|
| 200 | Share created |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -108,8 +103,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
|
|
||||||
### POST /api/v1/shares/batch/delete
|
### POST /api/v1/shares/batch/delete
|
||||||
|
|
||||||
Batch delete shares
|
Batch delete shares POST /api/shares/batch/delete
|
||||||
POST /api/shares/batch/delete
|
|
||||||
|
|
||||||
**Authentication:** Required (Bearer JWT)
|
**Authentication:** Required (Bearer JWT)
|
||||||
|
|
||||||
|
|
@ -122,7 +116,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | -------------- |
|
||||||
| 200 | Shares deleted |
|
| 200 | Shares deleted |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -132,22 +126,21 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
|
|
||||||
### GET /api/v1/shares/incoming
|
### GET /api/v1/shares/incoming
|
||||||
|
|
||||||
List incoming shares (shares shared with me)
|
List incoming shares (shares shared with me) GET /api/shares/incoming
|
||||||
GET /api/shares/incoming
|
|
||||||
|
|
||||||
**Authentication:** Required (Bearer JWT)
|
**Authentication:** Required (Bearer JWT)
|
||||||
|
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| -------- | ----- | -------- | ----------------- |
|
||||||
| `offset` | query | No | Pagination offset |
|
| `offset` | query | No | Pagination offset |
|
||||||
| `limit` | query | No | Pagination limit |
|
| `limit` | query | No | Pagination limit |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------- |
|
||||||
| 200 | Incoming shares |
|
| 200 | Incoming shares |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
||||||
|
|
@ -155,22 +148,21 @@ GET /api/shares/incoming
|
||||||
|
|
||||||
### GET /api/v1/shares/outgoing
|
### GET /api/v1/shares/outgoing
|
||||||
|
|
||||||
List outgoing shares (shares I created)
|
List outgoing shares (shares I created) GET /api/shares/outgoing
|
||||||
GET /api/shares/outgoing
|
|
||||||
|
|
||||||
**Authentication:** Required (Bearer JWT)
|
**Authentication:** Required (Bearer JWT)
|
||||||
|
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| -------- | ----- | -------- | ----------------- |
|
||||||
| `offset` | query | No | Pagination offset |
|
| `offset` | query | No | Pagination offset |
|
||||||
| `limit` | query | No | Pagination limit |
|
| `limit` | query | No | Pagination limit |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------- |
|
||||||
| 200 | Outgoing shares |
|
| 200 | Outgoing shares |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
||||||
|
|
@ -178,21 +170,20 @@ GET /api/shares/outgoing
|
||||||
|
|
||||||
### GET /api/v1/shares/{id}
|
### GET /api/v1/shares/{id}
|
||||||
|
|
||||||
Get share details
|
Get share details GET /api/shares/{id}
|
||||||
GET /api/shares/{id}
|
|
||||||
|
|
||||||
**Authentication:** Required (Bearer JWT)
|
**Authentication:** Required (Bearer JWT)
|
||||||
|
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ----------- |
|
||||||
| `id` | path | Yes | Share ID |
|
| `id` | path | Yes | Share ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ------------- |
|
||||||
| 200 | Share details |
|
| 200 | Share details |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -202,15 +193,14 @@ GET /api/shares/{id}
|
||||||
|
|
||||||
### PATCH /api/v1/shares/{id}
|
### PATCH /api/v1/shares/{id}
|
||||||
|
|
||||||
Update a share
|
Update a share PATCH /api/shares/{id}
|
||||||
PATCH /api/shares/{id}
|
|
||||||
|
|
||||||
**Authentication:** Required (Bearer JWT)
|
**Authentication:** Required (Bearer JWT)
|
||||||
|
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ----------- |
|
||||||
| `id` | path | Yes | Share ID |
|
| `id` | path | Yes | Share ID |
|
||||||
|
|
||||||
#### Request Body
|
#### Request Body
|
||||||
|
|
@ -222,7 +212,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ------------- |
|
||||||
| 200 | Share updated |
|
| 200 | Share updated |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -232,21 +222,20 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
|
|
||||||
### DELETE /api/v1/shares/{id}
|
### DELETE /api/v1/shares/{id}
|
||||||
|
|
||||||
Delete (revoke) a share
|
Delete (revoke) a share DELETE /api/shares/{id}
|
||||||
DELETE /api/shares/{id}
|
|
||||||
|
|
||||||
**Authentication:** Required (Bearer JWT)
|
**Authentication:** Required (Bearer JWT)
|
||||||
|
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ----------- |
|
||||||
| `id` | path | Yes | Share ID |
|
| `id` | path | Yes | Share ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ------------- |
|
||||||
| 204 | Share deleted |
|
| 204 | Share deleted |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -256,15 +245,14 @@ DELETE /api/shares/{id}
|
||||||
|
|
||||||
### GET /api/v1/shares/{id}/activity
|
### GET /api/v1/shares/{id}/activity
|
||||||
|
|
||||||
Get share activity log
|
Get share activity log GET /api/shares/{id}/activity
|
||||||
GET /api/shares/{id}/activity
|
|
||||||
|
|
||||||
**Authentication:** Required (Bearer JWT)
|
**Authentication:** Required (Bearer JWT)
|
||||||
|
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| -------- | ----- | -------- | ----------------- |
|
||||||
| `id` | path | Yes | Share ID |
|
| `id` | path | Yes | Share ID |
|
||||||
| `offset` | query | No | Pagination offset |
|
| `offset` | query | No | Pagination offset |
|
||||||
| `limit` | query | No | Pagination limit |
|
| `limit` | query | No | Pagination limit |
|
||||||
|
|
@ -272,11 +260,10 @@ GET /api/shares/{id}/activity
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | -------------- |
|
||||||
| 200 | Share activity |
|
| 200 | Share activity |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
| 404 | Not found |
|
| 404 | Not found |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
31
docs/api/social.md
vendored
31
docs/api/social.md
vendored
|
|
@ -11,7 +11,7 @@ Ratings, comments, favorites, and share links
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | User favorites |
|
| 200 | User favorites |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 500 | Internal server error |
|
| 500 | Internal server error |
|
||||||
|
|
@ -31,7 +31,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Added to favorites |
|
| 200 | Added to favorites |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 500 | Internal server error |
|
| 500 | Internal server error |
|
||||||
|
|
@ -45,13 +45,13 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---------- | ---- | -------- | ------------- |
|
||||||
| `media_id` | path | Yes | Media item ID |
|
| `media_id` | path | Yes | Media item ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ---------------------- |
|
||||||
| 200 | Removed from favorites |
|
| 200 | Removed from favorites |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 500 | Internal server error |
|
| 500 | Internal server error |
|
||||||
|
|
@ -71,7 +71,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Share link created |
|
| 200 | Share link created |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -86,13 +86,13 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ------------- |
|
||||||
| `id` | path | Yes | Media item ID |
|
| `id` | path | Yes | Media item ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Media comments |
|
| 200 | Media comments |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 500 | Internal server error |
|
| 500 | Internal server error |
|
||||||
|
|
@ -106,7 +106,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ------------- |
|
||||||
| `id` | path | Yes | Media item ID |
|
| `id` | path | Yes | Media item ID |
|
||||||
|
|
||||||
#### Request Body
|
#### Request Body
|
||||||
|
|
@ -118,7 +118,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Comment added |
|
| 200 | Comment added |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -133,7 +133,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ------------- |
|
||||||
| `id` | path | Yes | Media item ID |
|
| `id` | path | Yes | Media item ID |
|
||||||
|
|
||||||
#### Request Body
|
#### Request Body
|
||||||
|
|
@ -145,7 +145,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Rating saved |
|
| 200 | Rating saved |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -160,13 +160,13 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ------------- |
|
||||||
| `id` | path | Yes | Media item ID |
|
| `id` | path | Yes | Media item ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Media ratings |
|
| 200 | Media ratings |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 500 | Internal server error |
|
| 500 | Internal server error |
|
||||||
|
|
@ -180,17 +180,16 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---------- | ----- | -------- | -------------- |
|
||||||
| `token` | path | Yes | Share token |
|
| `token` | path | Yes | Share token |
|
||||||
| `password` | query | No | Share password |
|
| `password` | query | No | Share password |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ------------ |
|
||||||
| 200 | Shared media |
|
| 200 | Shared media |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 404 | Not found |
|
| 404 | Not found |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
3
docs/api/statistics.md
vendored
3
docs/api/statistics.md
vendored
|
|
@ -11,10 +11,9 @@ Library statistics
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Library statistics |
|
| 200 | Library statistics |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 500 | Internal server error |
|
| 500 | Internal server error |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
21
docs/api/streaming.md
vendored
21
docs/api/streaming.md
vendored
|
|
@ -11,13 +11,13 @@ HLS and DASH adaptive streaming
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ------------- |
|
||||||
| `id` | path | Yes | Media item ID |
|
| `id` | path | Yes | Media item ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ------------- |
|
||||||
| 200 | DASH manifest |
|
| 200 | DASH manifest |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -32,7 +32,7 @@ HLS and DASH adaptive streaming
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| --------- | ---- | -------- | ---------------------- |
|
||||||
| `id` | path | Yes | Media item ID |
|
| `id` | path | Yes | Media item ID |
|
||||||
| `profile` | path | Yes | Transcode profile name |
|
| `profile` | path | Yes | Transcode profile name |
|
||||||
| `segment` | path | Yes | Segment filename |
|
| `segment` | path | Yes | Segment filename |
|
||||||
|
|
@ -40,7 +40,7 @@ HLS and DASH adaptive streaming
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ------------------------- |
|
||||||
| 200 | DASH segment data |
|
| 200 | DASH segment data |
|
||||||
| 202 | Segment not yet available |
|
| 202 | Segment not yet available |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
|
|
@ -55,13 +55,13 @@ HLS and DASH adaptive streaming
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ------------- |
|
||||||
| `id` | path | Yes | Media item ID |
|
| `id` | path | Yes | Media item ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ------------------- |
|
||||||
| 200 | HLS master playlist |
|
| 200 | HLS master playlist |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 404 | Not found |
|
| 404 | Not found |
|
||||||
|
|
@ -75,14 +75,14 @@ HLS and DASH adaptive streaming
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| --------- | ---- | -------- | ---------------------- |
|
||||||
| `id` | path | Yes | Media item ID |
|
| `id` | path | Yes | Media item ID |
|
||||||
| `profile` | path | Yes | Transcode profile name |
|
| `profile` | path | Yes | Transcode profile name |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | -------------------- |
|
||||||
| 200 | HLS variant playlist |
|
| 200 | HLS variant playlist |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -97,7 +97,7 @@ HLS and DASH adaptive streaming
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| --------- | ---- | -------- | ---------------------- |
|
||||||
| `id` | path | Yes | Media item ID |
|
| `id` | path | Yes | Media item ID |
|
||||||
| `profile` | path | Yes | Transcode profile name |
|
| `profile` | path | Yes | Transcode profile name |
|
||||||
| `segment` | path | Yes | Segment filename |
|
| `segment` | path | Yes | Segment filename |
|
||||||
|
|
@ -105,11 +105,10 @@ HLS and DASH adaptive streaming
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ------------------------- |
|
||||||
| 200 | HLS segment data |
|
| 200 | HLS segment data |
|
||||||
| 202 | Segment not yet available |
|
| 202 | Segment not yet available |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
23
docs/api/subtitles.md
vendored
23
docs/api/subtitles.md
vendored
|
|
@ -11,14 +11,14 @@ Media subtitle management
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ------------- |
|
||||||
| `id` | path | Yes | Media item ID |
|
| `id` | path | Yes | Media item ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------------------------- |
|
||||||
| 200 | Subtitles |
|
| 200 | Subtitles and available embedded tracks |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 404 | Not found |
|
| 404 | Not found |
|
||||||
|
|
||||||
|
|
@ -31,7 +31,7 @@ Media subtitle management
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ------------- |
|
||||||
| `id` | path | Yes | Media item ID |
|
| `id` | path | Yes | Media item ID |
|
||||||
|
|
||||||
#### Request Body
|
#### Request Body
|
||||||
|
|
@ -43,7 +43,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Subtitle added |
|
| 200 | Subtitle added |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -58,14 +58,14 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ------------- | ---- | -------- | ------------- |
|
||||||
| `media_id` | path | Yes | Media item ID |
|
| `media_id` | path | Yes | Media item ID |
|
||||||
| `subtitle_id` | path | Yes | Subtitle ID |
|
| `subtitle_id` | path | Yes | Subtitle ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ---------------- |
|
||||||
| 200 | Subtitle content |
|
| 200 | Subtitle content |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 404 | Not found |
|
| 404 | Not found |
|
||||||
|
|
@ -79,13 +79,13 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ----------- |
|
||||||
| `id` | path | Yes | Subtitle ID |
|
| `id` | path | Yes | Subtitle ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ---------------- |
|
||||||
| 200 | Subtitle deleted |
|
| 200 | Subtitle deleted |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 404 | Not found |
|
| 404 | Not found |
|
||||||
|
|
@ -99,7 +99,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ----------- |
|
||||||
| `id` | path | Yes | Subtitle ID |
|
| `id` | path | Yes | Subtitle ID |
|
||||||
|
|
||||||
#### Request Body
|
#### Request Body
|
||||||
|
|
@ -111,10 +111,9 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | -------------- |
|
||||||
| 200 | Offset updated |
|
| 200 | Offset updated |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 404 | Not found |
|
| 404 | Not found |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
111
docs/api/sync.md
vendored
111
docs/api/sync.md
vendored
|
|
@ -6,8 +6,7 @@ Multi-device library synchronization
|
||||||
|
|
||||||
### POST /api/v1/sync/ack
|
### POST /api/v1/sync/ack
|
||||||
|
|
||||||
Acknowledge processed changes
|
Acknowledge processed changes POST /api/sync/ack
|
||||||
POST /api/sync/ack
|
|
||||||
|
|
||||||
**Authentication:** Required (Bearer JWT)
|
**Authentication:** Required (Bearer JWT)
|
||||||
|
|
||||||
|
|
@ -20,7 +19,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | -------------------- |
|
||||||
| 200 | Changes acknowledged |
|
| 200 | Changes acknowledged |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -29,22 +28,21 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
|
|
||||||
### GET /api/v1/sync/changes
|
### GET /api/v1/sync/changes
|
||||||
|
|
||||||
Get changes since cursor
|
Get changes since cursor GET /api/sync/changes
|
||||||
GET /api/sync/changes
|
|
||||||
|
|
||||||
**Authentication:** Required (Bearer JWT)
|
**Authentication:** Required (Bearer JWT)
|
||||||
|
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| -------- | ----- | -------- | ---------------------- |
|
||||||
| `cursor` | query | No | Sync cursor |
|
| `cursor` | query | No | Sync cursor |
|
||||||
| `limit` | query | No | Max changes (max 1000) |
|
| `limit` | query | No | Max changes (max 1000) |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | -------------------- |
|
||||||
| 200 | Changes since cursor |
|
| 200 | Changes since cursor |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -53,15 +51,14 @@ GET /api/sync/changes
|
||||||
|
|
||||||
### GET /api/v1/sync/conflicts
|
### GET /api/v1/sync/conflicts
|
||||||
|
|
||||||
List unresolved conflicts
|
List unresolved conflicts GET /api/sync/conflicts
|
||||||
GET /api/sync/conflicts
|
|
||||||
|
|
||||||
**Authentication:** Required (Bearer JWT)
|
**Authentication:** Required (Bearer JWT)
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | -------------------- |
|
||||||
| 200 | Unresolved conflicts |
|
| 200 | Unresolved conflicts |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
||||||
|
|
@ -69,15 +66,14 @@ GET /api/sync/conflicts
|
||||||
|
|
||||||
### POST /api/v1/sync/conflicts/{id}/resolve
|
### POST /api/v1/sync/conflicts/{id}/resolve
|
||||||
|
|
||||||
Resolve a sync conflict
|
Resolve a sync conflict POST /api/sync/conflicts/{id}/resolve
|
||||||
POST /api/sync/conflicts/{id}/resolve
|
|
||||||
|
|
||||||
**Authentication:** Required (Bearer JWT)
|
**Authentication:** Required (Bearer JWT)
|
||||||
|
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ----------- |
|
||||||
| `id` | path | Yes | Conflict ID |
|
| `id` | path | Yes | Conflict ID |
|
||||||
|
|
||||||
#### Request Body
|
#### Request Body
|
||||||
|
|
@ -89,7 +85,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ----------------- |
|
||||||
| 200 | Conflict resolved |
|
| 200 | Conflict resolved |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -98,15 +94,14 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
|
|
||||||
### GET /api/v1/sync/devices
|
### GET /api/v1/sync/devices
|
||||||
|
|
||||||
List user's sync devices
|
List user's sync devices GET /api/sync/devices
|
||||||
GET /api/sync/devices
|
|
||||||
|
|
||||||
**Authentication:** Required (Bearer JWT)
|
**Authentication:** Required (Bearer JWT)
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------- |
|
||||||
| 200 | List of devices |
|
| 200 | List of devices |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
||||||
|
|
@ -114,8 +109,7 @@ GET /api/sync/devices
|
||||||
|
|
||||||
### POST /api/v1/sync/devices
|
### POST /api/v1/sync/devices
|
||||||
|
|
||||||
Register a new sync device
|
Register a new sync device POST /api/sync/devices
|
||||||
POST /api/sync/devices
|
|
||||||
|
|
||||||
**Authentication:** Required (Bearer JWT)
|
**Authentication:** Required (Bearer JWT)
|
||||||
|
|
||||||
|
|
@ -128,7 +122,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Device registered |
|
| 200 | Device registered |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -138,21 +132,20 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
|
|
||||||
### GET /api/v1/sync/devices/{id}
|
### GET /api/v1/sync/devices/{id}
|
||||||
|
|
||||||
Get device details
|
Get device details GET /api/sync/devices/{id}
|
||||||
GET /api/sync/devices/{id}
|
|
||||||
|
|
||||||
**Authentication:** Required (Bearer JWT)
|
**Authentication:** Required (Bearer JWT)
|
||||||
|
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ----------- |
|
||||||
| `id` | path | Yes | Device ID |
|
| `id` | path | Yes | Device ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | -------------- |
|
||||||
| 200 | Device details |
|
| 200 | Device details |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -162,15 +155,14 @@ GET /api/sync/devices/{id}
|
||||||
|
|
||||||
### PUT /api/v1/sync/devices/{id}
|
### PUT /api/v1/sync/devices/{id}
|
||||||
|
|
||||||
Update a device
|
Update a device PUT /api/sync/devices/{id}
|
||||||
PUT /api/sync/devices/{id}
|
|
||||||
|
|
||||||
**Authentication:** Required (Bearer JWT)
|
**Authentication:** Required (Bearer JWT)
|
||||||
|
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ----------- |
|
||||||
| `id` | path | Yes | Device ID |
|
| `id` | path | Yes | Device ID |
|
||||||
|
|
||||||
#### Request Body
|
#### Request Body
|
||||||
|
|
@ -182,7 +174,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | -------------- |
|
||||||
| 200 | Device updated |
|
| 200 | Device updated |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -192,21 +184,20 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
|
|
||||||
### DELETE /api/v1/sync/devices/{id}
|
### DELETE /api/v1/sync/devices/{id}
|
||||||
|
|
||||||
Delete a device
|
Delete a device DELETE /api/sync/devices/{id}
|
||||||
DELETE /api/sync/devices/{id}
|
|
||||||
|
|
||||||
**Authentication:** Required (Bearer JWT)
|
**Authentication:** Required (Bearer JWT)
|
||||||
|
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ----------- |
|
||||||
| `id` | path | Yes | Device ID |
|
| `id` | path | Yes | Device ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | -------------- |
|
||||||
| 204 | Device deleted |
|
| 204 | Device deleted |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -216,21 +207,20 @@ DELETE /api/sync/devices/{id}
|
||||||
|
|
||||||
### POST /api/v1/sync/devices/{id}/token
|
### POST /api/v1/sync/devices/{id}/token
|
||||||
|
|
||||||
Regenerate device token
|
Regenerate device token POST /api/sync/devices/{id}/token
|
||||||
POST /api/sync/devices/{id}/token
|
|
||||||
|
|
||||||
**Authentication:** Required (Bearer JWT)
|
**Authentication:** Required (Bearer JWT)
|
||||||
|
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ----------- |
|
||||||
| `id` | path | Yes | Device ID |
|
| `id` | path | Yes | Device ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ----------------- |
|
||||||
| 200 | Token regenerated |
|
| 200 | Token regenerated |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -240,21 +230,20 @@ POST /api/sync/devices/{id}/token
|
||||||
|
|
||||||
### GET /api/v1/sync/download/{path}
|
### GET /api/v1/sync/download/{path}
|
||||||
|
|
||||||
Download a file for sync (supports Range header)
|
Download a file for sync (supports Range header) GET /api/sync/download/{*path}
|
||||||
GET /api/sync/download/{*path}
|
|
||||||
|
|
||||||
**Authentication:** Required (Bearer JWT)
|
**Authentication:** Required (Bearer JWT)
|
||||||
|
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ------ | ---- | -------- | ----------- |
|
||||||
| `path` | path | Yes | File path |
|
| `path` | path | Yes | File path |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------- |
|
||||||
| 200 | File content |
|
| 200 | File content |
|
||||||
| 206 | Partial content |
|
| 206 | Partial content |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -264,8 +253,7 @@ GET /api/sync/download/{*path}
|
||||||
|
|
||||||
### POST /api/v1/sync/report
|
### POST /api/v1/sync/report
|
||||||
|
|
||||||
Report local changes from client
|
Report local changes from client POST /api/sync/report
|
||||||
POST /api/sync/report
|
|
||||||
|
|
||||||
**Authentication:** Required (Bearer JWT)
|
**Authentication:** Required (Bearer JWT)
|
||||||
|
|
||||||
|
|
@ -278,7 +266,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ----------------- |
|
||||||
| 200 | Changes processed |
|
| 200 | Changes processed |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -287,8 +275,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
|
|
||||||
### POST /api/v1/sync/upload
|
### POST /api/v1/sync/upload
|
||||||
|
|
||||||
Create an upload session for chunked upload
|
Create an upload session for chunked upload POST /api/sync/upload
|
||||||
POST /api/sync/upload
|
|
||||||
|
|
||||||
**Authentication:** Required (Bearer JWT)
|
**Authentication:** Required (Bearer JWT)
|
||||||
|
|
||||||
|
|
@ -301,7 +288,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ---------------------- |
|
||||||
| 200 | Upload session created |
|
| 200 | Upload session created |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -310,21 +297,20 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
|
|
||||||
### GET /api/v1/sync/upload/{id}
|
### GET /api/v1/sync/upload/{id}
|
||||||
|
|
||||||
Get upload session status
|
Get upload session status GET /api/sync/upload/{id}
|
||||||
GET /api/sync/upload/{id}
|
|
||||||
|
|
||||||
**Authentication:** Required (Bearer JWT)
|
**Authentication:** Required (Bearer JWT)
|
||||||
|
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ----------------- |
|
||||||
| `id` | path | Yes | Upload session ID |
|
| `id` | path | Yes | Upload session ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Upload session status |
|
| 200 | Upload session status |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 404 | Not found |
|
| 404 | Not found |
|
||||||
|
|
@ -333,21 +319,20 @@ GET /api/sync/upload/{id}
|
||||||
|
|
||||||
### DELETE /api/v1/sync/upload/{id}
|
### DELETE /api/v1/sync/upload/{id}
|
||||||
|
|
||||||
Cancel an upload session
|
Cancel an upload session DELETE /api/sync/upload/{id}
|
||||||
DELETE /api/sync/upload/{id}
|
|
||||||
|
|
||||||
**Authentication:** Required (Bearer JWT)
|
**Authentication:** Required (Bearer JWT)
|
||||||
|
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ----------------- |
|
||||||
| `id` | path | Yes | Upload session ID |
|
| `id` | path | Yes | Upload session ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ---------------- |
|
||||||
| 204 | Upload cancelled |
|
| 204 | Upload cancelled |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 404 | Not found |
|
| 404 | Not found |
|
||||||
|
|
@ -356,29 +341,27 @@ DELETE /api/sync/upload/{id}
|
||||||
|
|
||||||
### PUT /api/v1/sync/upload/{id}/chunks/{index}
|
### PUT /api/v1/sync/upload/{id}/chunks/{index}
|
||||||
|
|
||||||
Upload a chunk
|
Upload a chunk PUT /api/sync/upload/{id}/chunks/{index}
|
||||||
PUT /api/sync/upload/{id}/chunks/{index}
|
|
||||||
|
|
||||||
**Authentication:** Required (Bearer JWT)
|
**Authentication:** Required (Bearer JWT)
|
||||||
|
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ------- | ---- | -------- | ----------------- |
|
||||||
| `id` | path | Yes | Upload session ID |
|
| `id` | path | Yes | Upload session ID |
|
||||||
| `index` | path | Yes | Chunk index |
|
| `index` | path | Yes | Chunk index |
|
||||||
|
|
||||||
#### Request Body
|
#### Request Body
|
||||||
|
|
||||||
Chunk binary data
|
Chunk binary data `Content-Type: application/octet-stream`
|
||||||
`Content-Type: application/octet-stream`
|
|
||||||
|
|
||||||
See `docs/api/openapi.json` for the full schema.
|
See `docs/api/openapi.json` for the full schema.
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | -------------- |
|
||||||
| 200 | Chunk received |
|
| 200 | Chunk received |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -388,25 +371,23 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
|
|
||||||
### POST /api/v1/sync/upload/{id}/complete
|
### POST /api/v1/sync/upload/{id}/complete
|
||||||
|
|
||||||
Complete an upload session
|
Complete an upload session POST /api/sync/upload/{id}/complete
|
||||||
POST /api/sync/upload/{id}/complete
|
|
||||||
|
|
||||||
**Authentication:** Required (Bearer JWT)
|
**Authentication:** Required (Bearer JWT)
|
||||||
|
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ----------------- |
|
||||||
| `id` | path | Yes | Upload session ID |
|
| `id` | path | Yes | Upload session ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ---------------- |
|
||||||
| 200 | Upload completed |
|
| 200 | Upload completed |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 404 | Not found |
|
| 404 | Not found |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
25
docs/api/tags.md
vendored
25
docs/api/tags.md
vendored
|
|
@ -11,13 +11,13 @@ Media tag management
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---------- | ---- | -------- | ------------- |
|
||||||
| `media_id` | path | Yes | Media item ID |
|
| `media_id` | path | Yes | Media item ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Media tags |
|
| 200 | Media tags |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 404 | Not found |
|
| 404 | Not found |
|
||||||
|
|
@ -32,7 +32,7 @@ Media tag management
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---------- | ---- | -------- | ------------- |
|
||||||
| `media_id` | path | Yes | Media item ID |
|
| `media_id` | path | Yes | Media item ID |
|
||||||
|
|
||||||
#### Request Body
|
#### Request Body
|
||||||
|
|
@ -44,7 +44,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Tag applied |
|
| 200 | Tag applied |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -60,14 +60,14 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---------- | ---- | -------- | ------------- |
|
||||||
| `media_id` | path | Yes | Media item ID |
|
| `media_id` | path | Yes | Media item ID |
|
||||||
| `tag_id` | path | Yes | Tag ID |
|
| `tag_id` | path | Yes | Tag ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Tag removed |
|
| 200 | Tag removed |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -83,7 +83,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | List of tags |
|
| 200 | List of tags |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 500 | Internal server error |
|
| 500 | Internal server error |
|
||||||
|
|
@ -103,7 +103,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Tag created |
|
| 200 | Tag created |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -119,13 +119,13 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ----------- |
|
||||||
| `id` | path | Yes | Tag ID |
|
| `id` | path | Yes | Tag ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Tag |
|
| 200 | Tag |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 404 | Not found |
|
| 404 | Not found |
|
||||||
|
|
@ -140,13 +140,13 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ----------- |
|
||||||
| `id` | path | Yes | Tag ID |
|
| `id` | path | Yes | Tag ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | Tag deleted |
|
| 200 | Tag deleted |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -154,4 +154,3 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
| 500 | Internal server error |
|
| 500 | Internal server error |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
15
docs/api/transcode.md
vendored
15
docs/api/transcode.md
vendored
|
|
@ -11,7 +11,7 @@ Video transcoding sessions
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ------------- |
|
||||||
| `id` | path | Yes | Media item ID |
|
| `id` | path | Yes | Media item ID |
|
||||||
|
|
||||||
#### Request Body
|
#### Request Body
|
||||||
|
|
@ -23,7 +23,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ----------------------- |
|
||||||
| 200 | Transcode job submitted |
|
| 200 | Transcode job submitted |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -38,7 +38,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | -------------------------- |
|
||||||
| 200 | List of transcode sessions |
|
| 200 | List of transcode sessions |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
||||||
|
|
@ -51,13 +51,13 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | -------------------- |
|
||||||
| `id` | path | Yes | Transcode session ID |
|
| `id` | path | Yes | Transcode session ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ------------------------- |
|
||||||
| 200 | Transcode session details |
|
| 200 | Transcode session details |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 404 | Not found |
|
| 404 | Not found |
|
||||||
|
|
@ -71,16 +71,15 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | -------------------- |
|
||||||
| `id` | path | Yes | Transcode session ID |
|
| `id` | path | Yes | Transcode session ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------------- |
|
||||||
| 200 | Transcode session cancelled |
|
| 200 | Transcode session cancelled |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 404 | Not found |
|
| 404 | Not found |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
25
docs/api/upload.md
vendored
25
docs/api/upload.md
vendored
|
|
@ -6,15 +6,14 @@ File upload and managed storage
|
||||||
|
|
||||||
### GET /api/v1/managed/stats
|
### GET /api/v1/managed/stats
|
||||||
|
|
||||||
Get managed storage statistics
|
Get managed storage statistics GET /api/managed/stats
|
||||||
GET /api/managed/stats
|
|
||||||
|
|
||||||
**Authentication:** Required (Bearer JWT)
|
**Authentication:** Required (Bearer JWT)
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | -------------------------- |
|
||||||
| 200 | Managed storage statistics |
|
| 200 | Managed storage statistics |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 500 | Internal server error |
|
| 500 | Internal server error |
|
||||||
|
|
@ -23,21 +22,20 @@ GET /api/managed/stats
|
||||||
|
|
||||||
### GET /api/v1/media/{id}/download
|
### GET /api/v1/media/{id}/download
|
||||||
|
|
||||||
Download a managed file
|
Download a managed file GET /api/media/{id}/download
|
||||||
GET /api/media/{id}/download
|
|
||||||
|
|
||||||
**Authentication:** Required (Bearer JWT)
|
**Authentication:** Required (Bearer JWT)
|
||||||
|
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ------------- |
|
||||||
| `id` | path | Yes | Media item ID |
|
| `id` | path | Yes | Media item ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ------------ |
|
||||||
| 200 | File content |
|
| 200 | File content |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -47,21 +45,20 @@ GET /api/media/{id}/download
|
||||||
|
|
||||||
### POST /api/v1/media/{id}/move-to-managed
|
### POST /api/v1/media/{id}/move-to-managed
|
||||||
|
|
||||||
Migrate an external file to managed storage
|
Migrate an external file to managed storage POST /api/media/{id}/move-to-managed
|
||||||
POST /api/media/{id}/move-to-managed
|
|
||||||
|
|
||||||
**Authentication:** Required (Bearer JWT)
|
**Authentication:** Required (Bearer JWT)
|
||||||
|
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ------------- |
|
||||||
| `id` | path | Yes | Media item ID |
|
| `id` | path | Yes | Media item ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 204 | File migrated |
|
| 204 | File migrated |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -71,19 +68,17 @@ POST /api/media/{id}/move-to-managed
|
||||||
|
|
||||||
### POST /api/v1/upload
|
### POST /api/v1/upload
|
||||||
|
|
||||||
Upload a file to managed storage
|
Upload a file to managed storage POST /api/upload
|
||||||
POST /api/upload
|
|
||||||
|
|
||||||
**Authentication:** Required (Bearer JWT)
|
**Authentication:** Required (Bearer JWT)
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | File uploaded |
|
| 200 | File uploaded |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 500 | Internal server error |
|
| 500 | Internal server error |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
29
docs/api/users.md
vendored
29
docs/api/users.md
vendored
|
|
@ -13,7 +13,7 @@ List all users (admin only)
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ------------- |
|
||||||
| 200 | List of users |
|
| 200 | List of users |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -36,7 +36,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------- |
|
||||||
| 200 | User created |
|
| 200 | User created |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -54,13 +54,13 @@ Get a specific user by ID
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ----------- |
|
||||||
| `id` | path | Yes | User ID |
|
| `id` | path | Yes | User ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ------------ |
|
||||||
| 200 | User details |
|
| 200 | User details |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -77,7 +77,7 @@ Update a user
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ----------- |
|
||||||
| `id` | path | Yes | User ID |
|
| `id` | path | Yes | User ID |
|
||||||
|
|
||||||
#### Request Body
|
#### Request Body
|
||||||
|
|
@ -90,7 +90,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ------------ |
|
||||||
| 200 | User updated |
|
| 200 | User updated |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -108,13 +108,13 @@ Delete a user (admin only)
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ----------- |
|
||||||
| `id` | path | Yes | User ID |
|
| `id` | path | Yes | User ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ------------ |
|
||||||
| 200 | User deleted |
|
| 200 | User deleted |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -131,13 +131,13 @@ Get user's accessible libraries
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ----------- |
|
||||||
| `id` | path | Yes | User ID |
|
| `id` | path | Yes | User ID |
|
||||||
|
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | -------------- |
|
||||||
| 200 | User libraries |
|
| 200 | User libraries |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -153,7 +153,7 @@ Grant library access to a user (admin only)
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ----------- |
|
||||||
| `id` | path | Yes | User ID |
|
| `id` | path | Yes | User ID |
|
||||||
|
|
||||||
#### Request Body
|
#### Request Body
|
||||||
|
|
@ -165,7 +165,7 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | -------------- |
|
||||||
| 200 | Access granted |
|
| 200 | Access granted |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
|
|
@ -185,7 +185,7 @@ slashes that conflict with URL routing.
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | In | Required | Description |
|
| Name | In | Required | Description |
|
||||||
|------|----|----------|-------------|
|
| ---- | ---- | -------- | ----------- |
|
||||||
| `id` | path | Yes | User ID |
|
| `id` | path | Yes | User ID |
|
||||||
|
|
||||||
#### Request Body
|
#### Request Body
|
||||||
|
|
@ -197,11 +197,10 @@ See `docs/api/openapi.json` for the full schema.
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | -------------- |
|
||||||
| 200 | Access revoked |
|
| 200 | Access revoked |
|
||||||
| 400 | Bad request |
|
| 400 | Bad request |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
5
docs/api/webhooks.md
vendored
5
docs/api/webhooks.md
vendored
|
|
@ -11,7 +11,7 @@ Webhook configuration
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | --------------------------- |
|
||||||
| 200 | List of configured webhooks |
|
| 200 | List of configured webhooks |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
@ -25,10 +25,9 @@ Webhook configuration
|
||||||
#### Responses
|
#### Responses
|
||||||
|
|
||||||
| Status | Description |
|
| Status | Description |
|
||||||
|--------|-------------|
|
| ------ | ----------------- |
|
||||||
| 200 | Test webhook sent |
|
| 200 | Test webhook sent |
|
||||||
| 401 | Unauthorized |
|
| 401 | Unauthorized |
|
||||||
| 403 | Forbidden |
|
| 403 | Forbidden |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,518 +0,0 @@
|
||||||
# Pinakes Plugin Examples
|
|
||||||
|
|
||||||
This directory contains example plugins demonstrating the Pinakes plugin system.
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
Pinakes supports extensibility through a WASM-based plugin system. Plugins can
|
|
||||||
extend Pinakes functionality by:
|
|
||||||
|
|
||||||
- **Media Type Providers**: Add support for new file formats
|
|
||||||
- **Metadata Extractors**: Extract metadata from files
|
|
||||||
- **Thumbnail Generators**: Generate thumbnails for media types
|
|
||||||
- **Search Backends**: Implement custom search algorithms
|
|
||||||
- **Event Handlers**: React to system events
|
|
||||||
- **Theme Providers**: Provide custom UI themes
|
|
||||||
|
|
||||||
## Example Plugins
|
|
||||||
|
|
||||||
### 1. Markdown Metadata Extractor
|
|
||||||
|
|
||||||
**Directory**: `markdown-metadata/`
|
|
||||||
|
|
||||||
Enhances markdown file support with advanced frontmatter parsing.
|
|
||||||
|
|
||||||
**Demonstrates**:
|
|
||||||
|
|
||||||
- Metadata extraction from files
|
|
||||||
- Plugin configuration via `plugin.toml`
|
|
||||||
- Minimal capability requirements
|
|
||||||
|
|
||||||
**Plugin Kind**: `metadata_extractor`
|
|
||||||
|
|
||||||
### 2. HEIF/HEIC Support
|
|
||||||
|
|
||||||
**Directory**: `heif-support/`
|
|
||||||
|
|
||||||
Adds support for HEIF and HEIC image formats.
|
|
||||||
|
|
||||||
**Demonstrates**:
|
|
||||||
|
|
||||||
- Media type registration
|
|
||||||
- Metadata extraction from binary formats
|
|
||||||
- Thumbnail generation
|
|
||||||
- Resource limits (memory, CPU time)
|
|
||||||
|
|
||||||
**Plugin Kinds**: `media_type`, `metadata_extractor`, `thumbnail_generator`
|
|
||||||
|
|
||||||
## Plugin Architecture
|
|
||||||
|
|
||||||
### Plugin Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
my-plugin/
|
|
||||||
├── plugin.toml # Plugin manifest
|
|
||||||
├── Cargo.toml # Rust project configuration
|
|
||||||
├── src/
|
|
||||||
│ └── lib.rs # Plugin implementation
|
|
||||||
└── README.md # Plugin documentation
|
|
||||||
```
|
|
||||||
|
|
||||||
### Plugin Manifest (plugin.toml)
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[plugin]
|
|
||||||
name = "my-plugin"
|
|
||||||
version = "1.0.0"
|
|
||||||
api_version = "1.0"
|
|
||||||
author = "Your Name"
|
|
||||||
description = "Description of your plugin"
|
|
||||||
kind = ["metadata_extractor"]
|
|
||||||
|
|
||||||
[plugin.binary]
|
|
||||||
wasm = "my_plugin.wasm"
|
|
||||||
|
|
||||||
[capabilities]
|
|
||||||
network = false
|
|
||||||
|
|
||||||
[capabilities.filesystem]
|
|
||||||
read = ["/path/to/read"]
|
|
||||||
write = ["/path/to/write"]
|
|
||||||
|
|
||||||
[config]
|
|
||||||
# Plugin-specific configuration
|
|
||||||
option1 = "value1"
|
|
||||||
option2 = 42
|
|
||||||
```
|
|
||||||
|
|
||||||
### Manifest Fields
|
|
||||||
|
|
||||||
#### [plugin] Section
|
|
||||||
|
|
||||||
- `name`: Plugin identifier (must be unique)
|
|
||||||
- `version`: Semantic version (e.g., "1.0.0")
|
|
||||||
- `api_version`: Pinakes Plugin API version (currently "1.0")
|
|
||||||
- `author`: Plugin author (optional)
|
|
||||||
- `description`: Short description (optional)
|
|
||||||
- `homepage`: Plugin homepage URL (optional)
|
|
||||||
- `license`: License identifier (optional)
|
|
||||||
- `kind`: Array of plugin kinds
|
|
||||||
- `dependencies`: Array of plugin names this plugin depends on (optional)
|
|
||||||
|
|
||||||
#### [plugin.binary] Section
|
|
||||||
|
|
||||||
- `wasm`: Path to WASM binary (relative to manifest)
|
|
||||||
- `entrypoint`: Custom entrypoint function name (optional, default: "_start")
|
|
||||||
|
|
||||||
#### [capabilities] Section
|
|
||||||
|
|
||||||
Capabilities define what the plugin can access:
|
|
||||||
|
|
||||||
**Filesystem**:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[capabilities.filesystem]
|
|
||||||
read = ["/tmp/cache", "/var/data"]
|
|
||||||
write = ["/tmp/output"]
|
|
||||||
```
|
|
||||||
|
|
||||||
**Network**:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[capabilities]
|
|
||||||
network = true # or false
|
|
||||||
```
|
|
||||||
|
|
||||||
**Environment**:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[capabilities]
|
|
||||||
environment = ["PATH", "HOME"] # or omit for no access
|
|
||||||
```
|
|
||||||
|
|
||||||
**Resource Limits**:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[capabilities]
|
|
||||||
max_memory_mb = 128
|
|
||||||
max_cpu_time_secs = 10
|
|
||||||
```
|
|
||||||
|
|
||||||
### Plugin Kinds
|
|
||||||
|
|
||||||
#### media_type
|
|
||||||
|
|
||||||
Register new media types with file extensions and MIME types.
|
|
||||||
|
|
||||||
**Trait**: `MediaTypeProvider`
|
|
||||||
|
|
||||||
**Methods**:
|
|
||||||
|
|
||||||
- `supported_media_types()`: Returns list of media type definitions
|
|
||||||
- `can_handle(path, mime_type)`: Check if plugin can handle a file
|
|
||||||
|
|
||||||
#### metadata_extractor
|
|
||||||
|
|
||||||
Extract metadata from files.
|
|
||||||
|
|
||||||
**Trait**: `MetadataExtractor`
|
|
||||||
|
|
||||||
**Methods**:
|
|
||||||
|
|
||||||
- `extract_metadata(path)`: Extract metadata from file
|
|
||||||
- `supported_types()`: Returns list of supported media type IDs
|
|
||||||
|
|
||||||
#### thumbnail_generator
|
|
||||||
|
|
||||||
Generate thumbnails for media files.
|
|
||||||
|
|
||||||
**Trait**: `ThumbnailGenerator`
|
|
||||||
|
|
||||||
**Methods**:
|
|
||||||
|
|
||||||
- `generate_thumbnail(path, output_path, options)`: Generate thumbnail
|
|
||||||
- `supported_types()`: Returns list of supported media type IDs
|
|
||||||
|
|
||||||
#### search_backend
|
|
||||||
|
|
||||||
Implement custom search algorithms.
|
|
||||||
|
|
||||||
**Trait**: `SearchBackend`
|
|
||||||
|
|
||||||
**Methods**:
|
|
||||||
|
|
||||||
- `index_item(item)`: Index a media item
|
|
||||||
- `remove_item(item_id)`: Remove item from index
|
|
||||||
- `search(query)`: Perform search
|
|
||||||
- `get_stats()`: Get index statistics
|
|
||||||
|
|
||||||
#### event_handler
|
|
||||||
|
|
||||||
React to system events.
|
|
||||||
|
|
||||||
**Trait**: `EventHandler`
|
|
||||||
|
|
||||||
**Methods**:
|
|
||||||
|
|
||||||
- `handle_event(event)`: Handle an event
|
|
||||||
- `interested_events()`: Returns list of event types to receive
|
|
||||||
|
|
||||||
#### theme_provider
|
|
||||||
|
|
||||||
Provide UI themes.
|
|
||||||
|
|
||||||
**Trait**: `ThemeProvider`
|
|
||||||
|
|
||||||
**Methods**:
|
|
||||||
|
|
||||||
- `get_themes()`: List available themes
|
|
||||||
- `load_theme(theme_id)`: Load theme data
|
|
||||||
|
|
||||||
## Creating a Plugin
|
|
||||||
|
|
||||||
### Step 1: Set Up Project
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Create new Rust library project
|
|
||||||
cargo new --lib my-plugin
|
|
||||||
cd my-plugin
|
|
||||||
|
|
||||||
# Add dependencies
|
|
||||||
cat >> Cargo.toml <<EOF
|
|
||||||
[dependencies]
|
|
||||||
pinakes-plugin-api = { path = "../../crates/pinakes-plugin-api" }
|
|
||||||
async-trait = "0.1"
|
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
crate-type = ["cdylib"]
|
|
||||||
|
|
||||||
[profile.release]
|
|
||||||
opt-level = "z"
|
|
||||||
lto = true
|
|
||||||
EOF
|
|
||||||
```
|
|
||||||
|
|
||||||
### Step 2: Implement Plugin
|
|
||||||
|
|
||||||
```rust
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use pinakes_plugin_api::*;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
pub struct MyPlugin {
|
|
||||||
context: Option<PluginContext>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl Plugin for MyPlugin {
|
|
||||||
fn metadata(&self) -> &PluginMetadata {
|
|
||||||
// Return plugin metadata
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn initialize(&mut self, context: PluginContext) -> PluginResult<()> {
|
|
||||||
self.context = Some(context);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn shutdown(&mut self) -> PluginResult<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn health_check(&self) -> PluginResult<HealthStatus> {
|
|
||||||
Ok(HealthStatus {
|
|
||||||
healthy: true,
|
|
||||||
message: None,
|
|
||||||
metrics: Default::default(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl MetadataExtractor for MyPlugin {
|
|
||||||
async fn extract_metadata(&self, path: &PathBuf) -> PluginResult<ExtractedMetadata> {
|
|
||||||
// Extract metadata from file
|
|
||||||
}
|
|
||||||
|
|
||||||
fn supported_types(&self) -> Vec<String> {
|
|
||||||
vec!["my_type".to_string()]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Step 3: Build to WASM
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Install WASM target
|
|
||||||
rustup target add wasm32-wasi
|
|
||||||
|
|
||||||
# Build
|
|
||||||
cargo build --target wasm32-wasi --release
|
|
||||||
|
|
||||||
# Optimize (optional, wasm-tools provides wasm-strip functionality)
|
|
||||||
cargo install wasm-tools
|
|
||||||
wasm-tools strip target/wasm32-wasi/release/my_plugin.wasm -o target/wasm32-wasi/release/my_plugin.wasm
|
|
||||||
|
|
||||||
# Copy to plugin directory
|
|
||||||
cp target/wasm32-wasi/release/my_plugin.wasm .
|
|
||||||
```
|
|
||||||
|
|
||||||
### Step 4: Create Manifest
|
|
||||||
|
|
||||||
Create `plugin.toml` with appropriate configuration (see examples above).
|
|
||||||
|
|
||||||
### Step 5: Install Plugin
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Copy to plugins directory
|
|
||||||
cp -r my-plugin ~/.config/pinakes/plugins/
|
|
||||||
|
|
||||||
# Or use API
|
|
||||||
curl -X POST http://localhost:3000/api/v1/plugins/install \
|
|
||||||
-d '{"source": "/path/to/my-plugin"}'
|
|
||||||
```
|
|
||||||
|
|
||||||
## Testing Plugins
|
|
||||||
|
|
||||||
### Unit Tests
|
|
||||||
|
|
||||||
```rust
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_metadata_extraction() {
|
|
||||||
let mut plugin = MyPlugin::default();
|
|
||||||
let context = PluginContext {
|
|
||||||
data_dir: PathBuf::from("/tmp/data"),
|
|
||||||
cache_dir: PathBuf::from("/tmp/cache"),
|
|
||||||
config: Default::default(),
|
|
||||||
capabilities: Default::default(),
|
|
||||||
};
|
|
||||||
|
|
||||||
plugin.initialize(context).await.unwrap();
|
|
||||||
|
|
||||||
let metadata = plugin
|
|
||||||
.extract_metadata(&PathBuf::from("test.txt"))
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert!(metadata.title.is_some());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Integration Tests
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Load plugin in test Pinakes instance
|
|
||||||
pinakes --config test-config.toml plugin load /path/to/plugin
|
|
||||||
|
|
||||||
# Verify plugin is loaded
|
|
||||||
pinakes plugin list
|
|
||||||
|
|
||||||
# Test plugin functionality
|
|
||||||
pinakes scan /path/to/test/files
|
|
||||||
```
|
|
||||||
|
|
||||||
## Security Considerations
|
|
||||||
|
|
||||||
### Capability-Based Security
|
|
||||||
|
|
||||||
Plugins operate in a sandbox with explicit capabilities. Only request the
|
|
||||||
minimum capabilities needed:
|
|
||||||
|
|
||||||
**Good**:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[capabilities.filesystem]
|
|
||||||
read = ["/tmp/cache"]
|
|
||||||
```
|
|
||||||
|
|
||||||
**Bad**:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[capabilities.filesystem]
|
|
||||||
read = ["/", "/home", "/etc"] # Too broad!
|
|
||||||
```
|
|
||||||
|
|
||||||
### Resource Limits
|
|
||||||
|
|
||||||
Always set appropriate resource limits:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[capabilities]
|
|
||||||
max_memory_mb = 128 # Reasonable for image processing
|
|
||||||
max_cpu_time_secs = 10 # Prevent runaway operations
|
|
||||||
```
|
|
||||||
|
|
||||||
### Input Validation
|
|
||||||
|
|
||||||
Always validate input in your plugin:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
async fn extract_metadata(&self, path: &PathBuf) -> PluginResult<ExtractedMetadata> {
|
|
||||||
// Check file exists
|
|
||||||
if !path.exists() {
|
|
||||||
return Err(PluginError::InvalidInput("File not found".to_string()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check file size
|
|
||||||
let metadata = std::fs::metadata(path)
|
|
||||||
.map_err(|e| PluginError::IoError(e.to_string()))?;
|
|
||||||
if metadata.len() > 10_000_000 { // 10MB limit
|
|
||||||
return Err(PluginError::InvalidInput("File too large".to_string()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process file...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Best Practices
|
|
||||||
|
|
||||||
### Error Handling
|
|
||||||
|
|
||||||
- Use descriptive error messages
|
|
||||||
- Return appropriate `PluginError` variants
|
|
||||||
- Don't panic - return errors instead
|
|
||||||
|
|
||||||
### Performance
|
|
||||||
|
|
||||||
- Avoid blocking operations in async functions
|
|
||||||
- Use streaming for large files
|
|
||||||
- Implement timeouts for external operations
|
|
||||||
- Cache results when appropriate
|
|
||||||
|
|
||||||
### Configuration
|
|
||||||
|
|
||||||
- Provide sensible defaults
|
|
||||||
- Document all configuration options
|
|
||||||
- Validate configuration during initialization
|
|
||||||
|
|
||||||
### Documentation
|
|
||||||
|
|
||||||
- Write clear README with examples
|
|
||||||
- Document all configuration options
|
|
||||||
- Include troubleshooting section
|
|
||||||
- Provide integration examples
|
|
||||||
|
|
||||||
## API Reference
|
|
||||||
|
|
||||||
See the
|
|
||||||
[pinakes-plugin-api documentation](../../crates/pinakes-plugin-api/README.md)
|
|
||||||
for detailed API reference.
|
|
||||||
|
|
||||||
## Plugin Distribution
|
|
||||||
|
|
||||||
### Plugin Registry (Future)
|
|
||||||
|
|
||||||
A centralized plugin registry is planned for future releases:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Install from registry
|
|
||||||
pinakes plugin install markdown-metadata
|
|
||||||
|
|
||||||
# Search plugins
|
|
||||||
pinakes plugin search heif
|
|
||||||
|
|
||||||
# Update all plugins
|
|
||||||
pinakes plugin update --all
|
|
||||||
```
|
|
||||||
|
|
||||||
### Manual Distribution
|
|
||||||
|
|
||||||
Currently, plugins are distributed as directories containing:
|
|
||||||
|
|
||||||
- `plugin.toml` manifest
|
|
||||||
- WASM binary
|
|
||||||
- README and documentation
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### Plugin Won't Load
|
|
||||||
|
|
||||||
**Check manifest syntax**:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Validate TOML syntax
|
|
||||||
taplo check plugin.toml
|
|
||||||
```
|
|
||||||
|
|
||||||
**Check API version**: Ensure `api_version = "1.0"` in manifest.
|
|
||||||
|
|
||||||
**Check binary path**: Verify WASM binary exists at path specified in
|
|
||||||
`plugin.binary.wasm`.
|
|
||||||
|
|
||||||
### Plugin Crashes
|
|
||||||
|
|
||||||
**Check resource limits**: Increase `max_memory_mb` or `max_cpu_time_secs` if
|
|
||||||
operations are timing out.
|
|
||||||
|
|
||||||
**Check capabilities**: Ensure plugin has necessary filesystem/network
|
|
||||||
capabilities.
|
|
||||||
|
|
||||||
**Check logs**:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# View plugin logs
|
|
||||||
tail -f ~/.local/share/pinakes/logs/plugin.log
|
|
||||||
```
|
|
||||||
|
|
||||||
### Permission Denied Errors
|
|
||||||
|
|
||||||
**Filesystem capabilities**: Add required paths to
|
|
||||||
`capabilities.filesystem.read` or `.write`.
|
|
||||||
|
|
||||||
**Network capabilities**: Set `capabilities.network = true` if plugin needs
|
|
||||||
network access.
|
|
||||||
|
|
||||||
## Support
|
|
||||||
|
|
||||||
- **Issues**: https://github.com/notashelf/pinakes/issues
|
|
||||||
- **Discussions**: https://github.com/notashelf/pinakes/discussions
|
|
||||||
- **Documentation**: https://pinakes.readthedocs.io
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
All example plugins are licensed under MIT.
|
|
||||||
|
|
@ -1,257 +0,0 @@
|
||||||
# HEIF/HEIC Support Plugin
|
|
||||||
|
|
||||||
This example plugin adds support for HEIF (High Efficiency Image Format) and HEIC (HEIF Container) to Pinakes.
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
HEIF is a modern image format that provides better compression than JPEG while maintaining higher quality. This plugin enables Pinakes to:
|
|
||||||
- Recognize HEIF/HEIC files as a media type
|
|
||||||
- Extract metadata from HEIF images
|
|
||||||
- Generate thumbnails from HEIF images
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- **Media Type Registration**: Registers `.heif`, `.heic`, `.hif` extensions as image media types
|
|
||||||
- **EXIF Extraction**: Extracts EXIF metadata including camera info, GPS coordinates, timestamps
|
|
||||||
- **Thumbnail Generation**: Generates thumbnails in JPEG, PNG, or WebP format
|
|
||||||
- **Resource Limits**: Configurable memory and CPU limits for safe processing
|
|
||||||
- **Large Image Support**: Handles images up to 8192x8192 pixels
|
|
||||||
|
|
||||||
## Supported Formats
|
|
||||||
|
|
||||||
- **HEIF**: High Efficiency Image Format (`.heif`, `.hif`)
|
|
||||||
- **HEIC**: HEIF Container format used by Apple devices (`.heic`)
|
|
||||||
- **HEIF Sequences**: Multi-image HEIF files
|
|
||||||
- **HEIF with Alpha**: HEIF images with transparency
|
|
||||||
|
|
||||||
## Implementation
|
|
||||||
|
|
||||||
The plugin implements three traits:
|
|
||||||
|
|
||||||
### MediaTypeProvider
|
|
||||||
|
|
||||||
```rust
|
|
||||||
#[async_trait]
|
|
||||||
impl MediaTypeProvider for HeifPlugin {
|
|
||||||
fn supported_media_types(&self) -> Vec<MediaTypeDefinition> {
|
|
||||||
vec![MediaTypeDefinition {
|
|
||||||
id: "heif".to_string(),
|
|
||||||
name: "HEIF Image".to_string(),
|
|
||||||
category: "image".to_string(),
|
|
||||||
extensions: vec!["heif".to_string(), "heic".to_string(), "hif".to_string()],
|
|
||||||
mime_types: vec!["image/heif".to_string(), "image/heic".to_string()],
|
|
||||||
icon: Some("image".to_string()),
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn can_handle(&self, path: &PathBuf, mime_type: Option<&str>) -> PluginResult<bool> {
|
|
||||||
// Check file extension and/or MIME type
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### MetadataExtractor
|
|
||||||
|
|
||||||
```rust
|
|
||||||
#[async_trait]
|
|
||||||
impl MetadataExtractor for HeifPlugin {
|
|
||||||
async fn extract_metadata(&self, path: &PathBuf) -> PluginResult<ExtractedMetadata> {
|
|
||||||
// 1. Parse HEIF file structure
|
|
||||||
// 2. Extract EXIF metadata
|
|
||||||
// 3. Get image dimensions
|
|
||||||
// 4. Return ExtractedMetadata
|
|
||||||
}
|
|
||||||
|
|
||||||
fn supported_types(&self) -> Vec<String> {
|
|
||||||
vec!["heif".to_string()]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### ThumbnailGenerator
|
|
||||||
|
|
||||||
```rust
|
|
||||||
#[async_trait]
|
|
||||||
impl ThumbnailGenerator for HeifPlugin {
|
|
||||||
async fn generate_thumbnail(
|
|
||||||
&self,
|
|
||||||
path: &PathBuf,
|
|
||||||
output_path: &PathBuf,
|
|
||||||
options: ThumbnailOptions,
|
|
||||||
) -> PluginResult<ThumbnailInfo> {
|
|
||||||
// 1. Decode HEIF image
|
|
||||||
// 2. Resize to thumbnail dimensions
|
|
||||||
// 3. Encode to output format
|
|
||||||
// 4. Save to output_path
|
|
||||||
// 5. Return ThumbnailInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
fn supported_types(&self) -> Vec<String> {
|
|
||||||
vec!["heif".to_string()]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Dependencies
|
|
||||||
|
|
||||||
The plugin uses the following Rust crates (compiled to WASM):
|
|
||||||
- `libheif-rs`: HEIF decoding and encoding
|
|
||||||
- `image`: Image processing and thumbnail generation
|
|
||||||
- `kamadak-exif`: EXIF metadata parsing
|
|
||||||
|
|
||||||
## Building
|
|
||||||
|
|
||||||
### Prerequisites
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Install WASM target
|
|
||||||
rustup target add wasm32-wasi
|
|
||||||
|
|
||||||
# Install wasm-tools for optimization (provides strip functionality)
|
|
||||||
cargo install wasm-tools
|
|
||||||
```
|
|
||||||
|
|
||||||
### Build Process
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Build the plugin
|
|
||||||
cargo build --target wasm32-wasi --release
|
|
||||||
|
|
||||||
# Strip debug symbols to reduce size
|
|
||||||
wasm-tools strip target/wasm32-wasi/release/heif_support.wasm -o target/wasm32-wasi/release/heif_support.wasm
|
|
||||||
|
|
||||||
# Copy to plugin directory
|
|
||||||
cp target/wasm32-wasi/release/heif_support.wasm .
|
|
||||||
```
|
|
||||||
|
|
||||||
### Size Optimization
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Use wasm-opt for further optimization
|
|
||||||
wasm-opt -Oz heif_support.wasm -o heif_support_optimized.wasm
|
|
||||||
```
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
### Manual Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Copy plugin directory to Pinakes plugins directory
|
|
||||||
cp -r examples/plugins/heif-support ~/.config/pinakes/plugins/
|
|
||||||
```
|
|
||||||
|
|
||||||
### Via API
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -X POST http://localhost:3000/api/v1/plugins/install \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{"source": "/path/to/heif-support"}'
|
|
||||||
```
|
|
||||||
|
|
||||||
### Via Plugin Manager
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pinakes plugin install /path/to/heif-support
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
The plugin can be configured through the `config` section in `plugin.toml`:
|
|
||||||
|
|
||||||
### EXIF Extraction
|
|
||||||
- `extract_exif`: Enable EXIF metadata extraction (default: true)
|
|
||||||
|
|
||||||
### Thumbnail Generation
|
|
||||||
- `generate_thumbnails`: Enable thumbnail generation (default: true)
|
|
||||||
- `thumbnail_quality`: JPEG quality for thumbnails, 1-100 (default: 85)
|
|
||||||
- `thumbnail_format`: Output format - "jpeg", "png", or "webp" (default: "jpeg")
|
|
||||||
|
|
||||||
### Resource Limits
|
|
||||||
- `max_memory_mb`: Maximum memory the plugin can use in megabytes (default: 256, set in `[capabilities]`)
|
|
||||||
- `max_width`: Maximum image width to process (default: 8192)
|
|
||||||
- `max_height`: Maximum image height to process (default: 8192)
|
|
||||||
|
|
||||||
## Security
|
|
||||||
|
|
||||||
### Capabilities
|
|
||||||
|
|
||||||
- **Filesystem Read**: Only files being processed (via Pinakes)
|
|
||||||
- **Filesystem Write**: Thumbnail directory only
|
|
||||||
- **Network**: Disabled
|
|
||||||
- **Environment**: No access
|
|
||||||
|
|
||||||
### Resource Limits
|
|
||||||
|
|
||||||
- **Memory**: 256 MB maximum
|
|
||||||
- **CPU Time**: 30 seconds maximum per operation
|
|
||||||
|
|
||||||
### Sandboxing
|
|
||||||
|
|
||||||
The plugin runs in a WASM sandbox with:
|
|
||||||
- No access to host filesystem beyond granted paths
|
|
||||||
- No network access
|
|
||||||
- No arbitrary code execution
|
|
||||||
- Memory and CPU time limits enforced by runtime
|
|
||||||
|
|
||||||
## Performance
|
|
||||||
|
|
||||||
### Typical Performance
|
|
||||||
|
|
||||||
- **Metadata Extraction**: ~50-100ms for typical HEIF files
|
|
||||||
- **Thumbnail Generation**: ~200-500ms depending on source image size
|
|
||||||
- **Memory Usage**: 50-150 MB typical, 256 MB maximum
|
|
||||||
|
|
||||||
### Optimization Tips
|
|
||||||
|
|
||||||
1. Keep source images below 8192x8192 for best performance
|
|
||||||
2. Use JPEG thumbnail format for smaller file sizes
|
|
||||||
3. Adjust thumbnail quality vs file size tradeoff with `thumbnail_quality`
|
|
||||||
|
|
||||||
## Error Handling
|
|
||||||
|
|
||||||
The plugin handles:
|
|
||||||
- **Corrupted Files**: Returns descriptive error
|
|
||||||
- **Unsupported Variants**: Gracefully skips unsupported HEIF features
|
|
||||||
- **Memory Limits**: Fails safely if image too large
|
|
||||||
- **Timeout**: Returns error if processing exceeds CPU time limit
|
|
||||||
|
|
||||||
## Testing
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Run unit tests
|
|
||||||
cargo test
|
|
||||||
|
|
||||||
# Test with sample HEIF files
|
|
||||||
cargo test --test integration -- --nocapture
|
|
||||||
```
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### Plugin Fails to Load
|
|
||||||
|
|
||||||
- Check that `heif_support.wasm` exists in plugin directory
|
|
||||||
- Verify `plugin.toml` is valid TOML
|
|
||||||
- Check Pinakes logs for detailed error messages
|
|
||||||
|
|
||||||
### Thumbnails Not Generated
|
|
||||||
|
|
||||||
- Verify `generate_thumbnails = true` in config
|
|
||||||
- Check filesystem write permissions for thumbnail directory
|
|
||||||
- Ensure source image is below size limits
|
|
||||||
|
|
||||||
### Out of Memory Errors
|
|
||||||
|
|
||||||
- Reduce `max_width` and `max_height` in config
|
|
||||||
- Increase `max_memory_mb` if source images are large
|
|
||||||
- Check that source files aren't corrupted
|
|
||||||
|
|
||||||
## Future Enhancements
|
|
||||||
|
|
||||||
- Support for HEIF image sequences (burst photos)
|
|
||||||
- HDR metadata extraction
|
|
||||||
- Live Photo support
|
|
||||||
- AVIF format support (similar to HEIF)
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
MIT
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
[plugin]
|
|
||||||
name = "heif-support"
|
|
||||||
version = "1.0.0"
|
|
||||||
api_version = "1.0"
|
|
||||||
author = "Pinakes Contributors"
|
|
||||||
description = "HEIF/HEIC image format support for Pinakes"
|
|
||||||
homepage = "https://github.com/notashelf/pinakes"
|
|
||||||
license = "MIT"
|
|
||||||
kind = ["media_type", "metadata_extractor", "thumbnail_generator"]
|
|
||||||
|
|
||||||
[plugin.binary]
|
|
||||||
wasm = "heif_support.wasm"
|
|
||||||
|
|
||||||
[capabilities]
|
|
||||||
network = false
|
|
||||||
max_memory_mb = 256
|
|
||||||
max_cpu_time_secs = 30
|
|
||||||
|
|
||||||
[capabilities.filesystem]
|
|
||||||
read = ["/media"]
|
|
||||||
write = ["/tmp/pinakes"]
|
|
||||||
|
|
||||||
[config]
|
|
||||||
extract_exif = true
|
|
||||||
generate_thumbnails = true
|
|
||||||
thumbnail_quality = 85
|
|
||||||
thumbnail_format = "jpeg"
|
|
||||||
max_width = 8192
|
|
||||||
max_height = 8192
|
|
||||||
|
|
@ -1,103 +0,0 @@
|
||||||
# Markdown Metadata Extractor Plugin
|
|
||||||
|
|
||||||
This example plugin demonstrates how to create a metadata extractor plugin for Pinakes.
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
The Markdown Metadata Extractor enhances Pinakes' built-in markdown support by:
|
|
||||||
- Parsing YAML and TOML frontmatter
|
|
||||||
- Extracting metadata from frontmatter fields
|
|
||||||
- Converting frontmatter tags to Pinakes media tags
|
|
||||||
- Extracting custom fields from frontmatter
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- **Frontmatter Parsing**: Supports both YAML (`---`) and TOML (`+++`) frontmatter formats
|
|
||||||
- **Tag Extraction**: Automatically extracts tags from frontmatter and applies them to media items
|
|
||||||
- **Custom Fields**: Preserves all frontmatter fields as custom metadata
|
|
||||||
- **Configuration**: Configurable via `plugin.toml` config section
|
|
||||||
|
|
||||||
## Frontmatter Example
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
---
|
|
||||||
title: "My Document"
|
|
||||||
author: "John Doe"
|
|
||||||
date: "2024-01-15"
|
|
||||||
tags: ["documentation", "example", "markdown"]
|
|
||||||
category: "tutorials"
|
|
||||||
draft: false
|
|
||||||
---
|
|
||||||
|
|
||||||
# My Document
|
|
||||||
|
|
||||||
Content goes here...
|
|
||||||
```
|
|
||||||
|
|
||||||
## Implementation
|
|
||||||
|
|
||||||
The plugin implements the `MetadataExtractor` trait from `pinakes-plugin-api`:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
#[async_trait]
|
|
||||||
impl MetadataExtractor for MarkdownMetadataPlugin {
|
|
||||||
async fn extract_metadata(&self, path: &PathBuf) -> PluginResult<ExtractedMetadata> {
|
|
||||||
// 1. Read the file
|
|
||||||
// 2. Parse frontmatter
|
|
||||||
// 3. Extract metadata fields
|
|
||||||
// 4. Return ExtractedMetadata
|
|
||||||
}
|
|
||||||
|
|
||||||
fn supported_types(&self) -> Vec<String> {
|
|
||||||
vec!["markdown".to_string()]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Building
|
|
||||||
|
|
||||||
The plugin is compiled to WebAssembly:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cargo build --target wasm32-wasi --release
|
|
||||||
wasm-strip target/wasm32-wasi/release/markdown_metadata.wasm
|
|
||||||
cp target/wasm32-wasi/release/markdown_metadata.wasm .
|
|
||||||
```
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Copy plugin directory to Pinakes plugins directory
|
|
||||||
cp -r examples/plugins/markdown-metadata ~/.config/pinakes/plugins/
|
|
||||||
|
|
||||||
# Or via API
|
|
||||||
curl -X POST http://localhost:3000/api/v1/plugins/install \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{"source": "/path/to/markdown-metadata"}'
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
The plugin can be configured through the `config` section in `plugin.toml`:
|
|
||||||
|
|
||||||
- `extract_tags`: Extract tags from frontmatter (default: true)
|
|
||||||
- `parse_yaml`: Parse YAML frontmatter (default: true)
|
|
||||||
- `parse_toml`: Parse TOML frontmatter (default: true)
|
|
||||||
- `max_file_size`: Maximum file size to process in bytes (default: 10MB)
|
|
||||||
|
|
||||||
## Security
|
|
||||||
|
|
||||||
This plugin has minimal capabilities:
|
|
||||||
- **Filesystem**: No write access, read access only to files being processed
|
|
||||||
- **Network**: Disabled
|
|
||||||
- **Environment**: No access
|
|
||||||
|
|
||||||
## Testing
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cargo test
|
|
||||||
```
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
MIT
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
[plugin]
|
|
||||||
name = "markdown-metadata"
|
|
||||||
version = "1.0.0"
|
|
||||||
api_version = "1.0"
|
|
||||||
author = "Pinakes Contributors"
|
|
||||||
description = "Extract metadata from Markdown files with YAML frontmatter"
|
|
||||||
homepage = "https://github.com/notashelf/pinakes"
|
|
||||||
license = "MIT"
|
|
||||||
kind = ["metadata_extractor"]
|
|
||||||
|
|
||||||
[plugin.binary]
|
|
||||||
wasm = "markdown_metadata.wasm"
|
|
||||||
|
|
||||||
[capabilities]
|
|
||||||
network = false
|
|
||||||
|
|
||||||
[capabilities.filesystem]
|
|
||||||
read = []
|
|
||||||
write = []
|
|
||||||
|
|
||||||
[config]
|
|
||||||
extract_tags = true
|
|
||||||
parse_yaml = true
|
|
||||||
parse_toml = true
|
|
||||||
max_file_size = 10485760
|
|
||||||
BIN
examples/plugins/media-stats-ui/Cargo.lock
generated
BIN
examples/plugins/media-stats-ui/Cargo.lock
generated
Binary file not shown.
|
|
@ -1,20 +0,0 @@
|
||||||
[workspace]
|
|
||||||
|
|
||||||
[package]
|
|
||||||
name = "media-stats-ui"
|
|
||||||
version = "1.0.0"
|
|
||||||
edition = "2024"
|
|
||||||
description = "Library statistics dashboard and tag manager, a UI-only Pinakes plugin"
|
|
||||||
license = "EUPL-1.2"
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
name = "media_stats_ui"
|
|
||||||
crate-type = ["cdylib"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
dlmalloc = { version = "0.2.12", features = ["global"] }
|
|
||||||
|
|
||||||
[profile.release]
|
|
||||||
opt-level = "s"
|
|
||||||
lto = true
|
|
||||||
strip = true
|
|
||||||
|
|
@ -1,132 +0,0 @@
|
||||||
{
|
|
||||||
"id": "stats",
|
|
||||||
"title": "Library Statistics",
|
|
||||||
"route": "/plugins/media-stats-ui/stats",
|
|
||||||
"icon": "chart-bar",
|
|
||||||
"layout": {
|
|
||||||
"type": "tabs",
|
|
||||||
"default_tab": 0,
|
|
||||||
"tabs": [
|
|
||||||
{
|
|
||||||
"label": "Overview",
|
|
||||||
"content": {
|
|
||||||
"type": "container",
|
|
||||||
"gap": 24,
|
|
||||||
"children": [
|
|
||||||
{
|
|
||||||
"type": "heading",
|
|
||||||
"level": 2,
|
|
||||||
"content": "Library Statistics"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "text",
|
|
||||||
"content": "Live summary of your media library. Refreshes every 30 seconds.",
|
|
||||||
"variant": "secondary"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "card",
|
|
||||||
"title": "Summary",
|
|
||||||
"content": [
|
|
||||||
{
|
|
||||||
"type": "description_list",
|
|
||||||
"data": "stats",
|
|
||||||
"horizontal": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "chart",
|
|
||||||
"chart_type": "bar",
|
|
||||||
"data": "type-breakdown",
|
|
||||||
"title": "Files by Type",
|
|
||||||
"x_axis_label": "Media Type",
|
|
||||||
"y_axis_label": "Count",
|
|
||||||
"height": 280
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Recent Files",
|
|
||||||
"content": {
|
|
||||||
"type": "container",
|
|
||||||
"gap": 16,
|
|
||||||
"children": [
|
|
||||||
{
|
|
||||||
"type": "heading",
|
|
||||||
"level": 2,
|
|
||||||
"content": "Recently Added"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "data_table",
|
|
||||||
"data": "recent",
|
|
||||||
"sortable": true,
|
|
||||||
"filterable": true,
|
|
||||||
"page_size": 10,
|
|
||||||
"columns": [
|
|
||||||
{
|
|
||||||
"key": "file_name",
|
|
||||||
"header": "Filename"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "title",
|
|
||||||
"header": "Title"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "media_type",
|
|
||||||
"header": "Type"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "file_size",
|
|
||||||
"header": "Size",
|
|
||||||
"data_type": "file_size"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "created_at",
|
|
||||||
"header": "Added",
|
|
||||||
"data_type": "date_time"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Media Grid",
|
|
||||||
"content": {
|
|
||||||
"type": "container",
|
|
||||||
"gap": 16,
|
|
||||||
"children": [
|
|
||||||
{
|
|
||||||
"type": "heading",
|
|
||||||
"level": 2,
|
|
||||||
"content": "Browse Media"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "media_grid",
|
|
||||||
"data": "recent",
|
|
||||||
"columns": 4,
|
|
||||||
"gap": 12
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"data_sources": {
|
|
||||||
"stats": {
|
|
||||||
"type": "endpoint",
|
|
||||||
"path": "/api/v1/statistics",
|
|
||||||
"poll_interval": 30
|
|
||||||
},
|
|
||||||
"recent": {
|
|
||||||
"type": "endpoint",
|
|
||||||
"path": "/api/v1/media"
|
|
||||||
},
|
|
||||||
"type-breakdown": {
|
|
||||||
"type": "transform",
|
|
||||||
"source": "stats",
|
|
||||||
"expression": "stats.media_by_type"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,126 +0,0 @@
|
||||||
{
|
|
||||||
"id": "tag-manager",
|
|
||||||
"title": "Tag Manager",
|
|
||||||
"route": "/plugins/media-stats-ui/tag-manager",
|
|
||||||
"icon": "tag",
|
|
||||||
"layout": {
|
|
||||||
"type": "tabs",
|
|
||||||
"default_tab": 0,
|
|
||||||
"tabs": [
|
|
||||||
{
|
|
||||||
"label": "All Tags",
|
|
||||||
"content": {
|
|
||||||
"type": "container",
|
|
||||||
"gap": 16,
|
|
||||||
"children": [
|
|
||||||
{
|
|
||||||
"type": "heading",
|
|
||||||
"level": 2,
|
|
||||||
"content": "Manage Tags"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "conditional",
|
|
||||||
"condition": {
|
|
||||||
"op": "eq",
|
|
||||||
"left": { "function": "len", "args": ["tags"] },
|
|
||||||
"right": 0
|
|
||||||
},
|
|
||||||
"then": {
|
|
||||||
"type": "text",
|
|
||||||
"content": "No tags yet. Use the 'Create Tag' tab to add one.",
|
|
||||||
"variant": "secondary"
|
|
||||||
},
|
|
||||||
"else": {
|
|
||||||
"type": "data_table",
|
|
||||||
"data": "tags",
|
|
||||||
"sortable": true,
|
|
||||||
"filterable": true,
|
|
||||||
"page_size": 20,
|
|
||||||
"columns": [
|
|
||||||
{ "key": "name", "header": "Tag Name" },
|
|
||||||
{ "key": "color", "header": "Color" },
|
|
||||||
{ "key": "item_count", "header": "Items", "data_type": "number" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Create Tag",
|
|
||||||
"content": {
|
|
||||||
"type": "container",
|
|
||||||
"gap": 24,
|
|
||||||
"children": [
|
|
||||||
{
|
|
||||||
"type": "heading",
|
|
||||||
"level": 2,
|
|
||||||
"content": "Create New Tag"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "text",
|
|
||||||
"content": "Tags are used to organise media items. Choose a name and an optional colour.",
|
|
||||||
"variant": "secondary"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "form",
|
|
||||||
"submit_label": "Create Tag",
|
|
||||||
"submit_action": "create-tag",
|
|
||||||
"cancel_label": "Reset",
|
|
||||||
"fields": [
|
|
||||||
{
|
|
||||||
"id": "name",
|
|
||||||
"label": "Tag Name",
|
|
||||||
"type": { "type": "text", "max_length": 64 },
|
|
||||||
"required": true,
|
|
||||||
"placeholder": "e.g. favourite, to-watch, archived",
|
|
||||||
"help_text": "Must be unique. Alphanumeric characters, spaces, and hyphens.",
|
|
||||||
"validation": [
|
|
||||||
{ "type": "min_length", "value": 1 },
|
|
||||||
{ "type": "max_length", "value": 64 },
|
|
||||||
{ "type": "pattern", "regex": "^[a-zA-Z0-9 \\-]+$" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "color",
|
|
||||||
"label": "Colour",
|
|
||||||
"type": {
|
|
||||||
"type": "select",
|
|
||||||
"options": [
|
|
||||||
{ "value": "#ef4444", "label": "Red" },
|
|
||||||
{ "value": "#f97316", "label": "Orange" },
|
|
||||||
{ "value": "#eab308", "label": "Yellow" },
|
|
||||||
{ "value": "#22c55e", "label": "Green" },
|
|
||||||
{ "value": "#3b82f6", "label": "Blue" },
|
|
||||||
{ "value": "#8b5cf6", "label": "Purple" },
|
|
||||||
{ "value": "#ec4899", "label": "Pink" },
|
|
||||||
{ "value": "#6b7280", "label": "Grey" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"required": false,
|
|
||||||
"default_value": "#3b82f6",
|
|
||||||
"help_text": "Optional accent colour shown beside the tag."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"data_sources": {
|
|
||||||
"tags": {
|
|
||||||
"type": "endpoint",
|
|
||||||
"path": "/api/v1/tags",
|
|
||||||
"poll_interval": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"actions": {
|
|
||||||
"create-tag": {
|
|
||||||
"method": "POST",
|
|
||||||
"path": "/api/v1/tags",
|
|
||||||
"success_message": "Tag created successfully!",
|
|
||||||
"error_message": "Failed to create tag: the name may already be in use."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
[plugin]
|
|
||||||
name = "media-stats-ui"
|
|
||||||
version = "1.0.0"
|
|
||||||
api_version = "1.0"
|
|
||||||
author = "Pinakes Contributors"
|
|
||||||
description = "Library statistics dashboard and tag manager UI plugin"
|
|
||||||
homepage = "https://github.com/notashelf/pinakes"
|
|
||||||
license = "EUPL-1.2"
|
|
||||||
kind = ["ui_page"]
|
|
||||||
|
|
||||||
[plugin.binary]
|
|
||||||
wasm = "target/wasm32-unknown-unknown/release/media_stats_ui.wasm"
|
|
||||||
|
|
||||||
[capabilities]
|
|
||||||
network = false
|
|
||||||
|
|
||||||
[capabilities.filesystem]
|
|
||||||
read = []
|
|
||||||
write = []
|
|
||||||
|
|
||||||
[ui]
|
|
||||||
required_endpoints = ["/api/v1/statistics", "/api/v1/media", "/api/v1/tags"]
|
|
||||||
|
|
||||||
# UI pages
|
|
||||||
[[ui.pages]]
|
|
||||||
file = "pages/stats.json"
|
|
||||||
|
|
||||||
[[ui.pages]]
|
|
||||||
file = "pages/tag-manager.json"
|
|
||||||
|
|
||||||
# Widgets injected into host views
|
|
||||||
[[ui.widgets]]
|
|
||||||
id = "stats-badge"
|
|
||||||
target = "library_header"
|
|
||||||
|
|
||||||
[ui.widgets.content]
|
|
||||||
type = "badge"
|
|
||||||
text = "Stats"
|
|
||||||
variant = "info"
|
|
||||||
|
|
@ -1,101 +0,0 @@
|
||||||
//! Media Stats UI - Pinakes plugin
|
|
||||||
//!
|
|
||||||
//! A UI-only plugin that adds a library statistics dashboard and a tag manager
|
|
||||||
//! page. All UI definitions live in `pages/stats.json` and
|
|
||||||
//! `pages/tag-manager.json`; this WASM binary provides the minimum lifecycle
|
|
||||||
//! surface the host runtime requires.
|
|
||||||
//!
|
|
||||||
//! This plugin is kind = ["ui_page"]: no media-type, metadata, thumbnail, or
|
|
||||||
//! event-handler extension points are needed. The host will never call them,
|
|
||||||
//! but exporting them avoids linker warnings if the host performs capability
|
|
||||||
//! discovery via symbol inspection.
|
|
||||||
|
|
||||||
#![no_std]
|
|
||||||
|
|
||||||
extern crate alloc;
|
|
||||||
|
|
||||||
use core::alloc::Layout;
|
|
||||||
|
|
||||||
#[global_allocator]
|
|
||||||
static ALLOC: dlmalloc::GlobalDlmalloc = dlmalloc::GlobalDlmalloc;
|
|
||||||
|
|
||||||
#[panic_handler]
|
|
||||||
fn panic(_: &core::panic::PanicInfo) -> ! {
|
|
||||||
core::arch::wasm32::unreachable()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Host functions provided by the Pinakes runtime.
|
|
||||||
unsafe extern "C" {
|
|
||||||
// Write a result value back to the host (ptr + byte length).
|
|
||||||
fn host_set_result(ptr: i32, len: i32);
|
|
||||||
|
|
||||||
// Emit a structured log message to the host logger.
|
|
||||||
// `level` mirrors tracing severity: 0=trace 1=debug 2=info 3=warn 4=error
|
|
||||||
fn host_log(level: i32, ptr: i32, len: i32);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// `json` is a valid slice; the host copies the bytes before
|
|
||||||
/// returning so there are no lifetime concerns.
|
|
||||||
fn set_response(json: &[u8]) {
|
|
||||||
unsafe { host_set_result(json.as_ptr() as i32, json.len() as i32) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// Same as [`set_response`]
|
|
||||||
fn log_info(msg: &[u8]) {
|
|
||||||
unsafe { host_log(2, msg.as_ptr() as i32, msg.len() as i32) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Allocate a buffer for the host to write request data into.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// The byte offset of the allocation, or -1 on failure.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// Size is positive; Layout construction cannot fail for align=1.
|
|
||||||
#[unsafe(no_mangle)]
|
|
||||||
pub extern "C" fn alloc(size: i32) -> i32 {
|
|
||||||
if size <= 0 {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
unsafe {
|
|
||||||
let layout = Layout::from_size_align_unchecked(size as usize, 1);
|
|
||||||
let ptr = alloc::alloc::alloc(layout);
|
|
||||||
if ptr.is_null() { -1 } else { ptr as i32 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Called once after the plugin is loaded. Returns 0 on success.
|
|
||||||
#[unsafe(no_mangle)]
|
|
||||||
pub extern "C" fn initialize() -> i32 {
|
|
||||||
log_info(b"media-stats-ui: initialized");
|
|
||||||
0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Called before the plugin is unloaded. Returns 0 on success.
|
|
||||||
#[unsafe(no_mangle)]
|
|
||||||
pub extern "C" fn shutdown() -> i32 {
|
|
||||||
log_info(b"media-stats-ui: shutdown");
|
|
||||||
0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// an empty JSON array; this plugin adds no custom media types.
|
|
||||||
#[unsafe(no_mangle)]
|
|
||||||
pub extern "C" fn supported_media_types(_ptr: i32, _len: i32) {
|
|
||||||
set_response(b"[]");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// An empty JSON array; this plugin handles no event types.
|
|
||||||
#[unsafe(no_mangle)]
|
|
||||||
pub extern "C" fn interested_events(_ptr: i32, _len: i32) {
|
|
||||||
set_response(b"[]");
|
|
||||||
}
|
|
||||||
BIN
flake.lock
generated
BIN
flake.lock
generated
Binary file not shown.
33
flake.nix
33
flake.nix
|
|
@ -21,5 +21,38 @@
|
||||||
in {
|
in {
|
||||||
default = pkgs.callPackage ./nix/shell.nix {};
|
default = pkgs.callPackage ./nix/shell.nix {};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
formatter = forEachSystem (system: let
|
||||||
|
pkgs = nixpkgs.legacyPackages.${system};
|
||||||
|
in
|
||||||
|
pkgs.writeShellApplication {
|
||||||
|
name = "nix3-fmt-wrapper";
|
||||||
|
|
||||||
|
runtimeInputs = [
|
||||||
|
pkgs.alejandra
|
||||||
|
pkgs.fd
|
||||||
|
pkgs.prettier
|
||||||
|
pkgs.deno
|
||||||
|
pkgs.taplo
|
||||||
|
pkgs.sql-formatter
|
||||||
|
];
|
||||||
|
|
||||||
|
text = ''
|
||||||
|
# Format Nix with Alejandra
|
||||||
|
fd "$@" -t f -e nix -x alejandra -q '{}'
|
||||||
|
|
||||||
|
# Format TOML with Taplo
|
||||||
|
fd "$@" -t f -e toml -x taplo fmt '{}'
|
||||||
|
|
||||||
|
# Format CSS with Prettier
|
||||||
|
fd "$@" -t f -e css -x prettier --write '{}'
|
||||||
|
|
||||||
|
# Format SQL with sql-format
|
||||||
|
fd "$@" -t f -e sql -x sql-formatter --fix '{}' -l postgresql
|
||||||
|
|
||||||
|
# Format Markdown with Deno
|
||||||
|
fd "$@" -t f -e md -x deno fmt -q '{}'
|
||||||
|
'';
|
||||||
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
40
justfile
Normal file
40
justfile
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
# Default recipe to show help
|
||||||
|
@default:
|
||||||
|
just --list
|
||||||
|
|
||||||
|
# Build all crates
|
||||||
|
build-all: build-server build-tui build-ui
|
||||||
|
|
||||||
|
# Build the server crate
|
||||||
|
build-server:
|
||||||
|
cargo build -p pinakes-server
|
||||||
|
|
||||||
|
# Build the TUI crate
|
||||||
|
build-tui:
|
||||||
|
cargo build -p pinakes-tui
|
||||||
|
|
||||||
|
# Build the UI using Dioxus CLI. The UI *has* to be built with `dx`, because CSS
|
||||||
|
# is not correctly embedded otherwise.
|
||||||
|
build-ui:
|
||||||
|
dx build -p pinakes-ui
|
||||||
|
|
||||||
|
# Generate REST API documentation using cargo xtask
|
||||||
|
@docs:
|
||||||
|
cargo xtask docs
|
||||||
|
|
||||||
|
# Run all tests
|
||||||
|
@test:
|
||||||
|
cargo nextest run --workspace
|
||||||
|
|
||||||
|
# Format code
|
||||||
|
@fmt:
|
||||||
|
cargo fmt
|
||||||
|
|
||||||
|
# Run clippy linting
|
||||||
|
@lint:
|
||||||
|
cargo clippy --workspace
|
||||||
|
|
||||||
|
# Clean build artifacts
|
||||||
|
@clean:
|
||||||
|
cargo clean
|
||||||
|
rm -rf target/dx/
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
-- Add file_mtime column to media_items table for incremental scanning
|
-- Add file_mtime column to media_items table for incremental scanning
|
||||||
-- Stores Unix timestamp in seconds of the file's modification time
|
-- Stores Unix timestamp in seconds of the file's modification time
|
||||||
|
ALTER TABLE media_items
|
||||||
ALTER TABLE media_items ADD COLUMN file_mtime BIGINT;
|
ADD COLUMN file_mtime BIGINT;
|
||||||
|
|
||||||
-- Create index for quick mtime lookups
|
-- Create index for quick mtime lookups
|
||||||
CREATE INDEX IF NOT EXISTS idx_media_items_file_mtime ON media_items (file_mtime);
|
CREATE INDEX IF NOT EXISTS idx_media_items_file_mtime ON media_items (file_mtime);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
-- Session persistence for database-backed sessions
|
-- Session persistence for database-backed sessions
|
||||||
-- Replaces in-memory session storage
|
-- Replaces in-memory session storage
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS sessions (
|
CREATE TABLE IF NOT EXISTS sessions (
|
||||||
session_token TEXT PRIMARY KEY NOT NULL,
|
session_token TEXT PRIMARY KEY NOT NULL,
|
||||||
user_id TEXT,
|
user_id TEXT,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
-- V12: Book Management Schema (PostgreSQL)
|
-- V12: Book Management Schema (PostgreSQL)
|
||||||
-- Adds comprehensive book metadata tracking, authors, and identifiers
|
-- Adds comprehensive book metadata tracking, authors, and identifiers
|
||||||
|
|
||||||
-- Book metadata (supplements media_items for EPUB/PDF/MOBI)
|
-- Book metadata (supplements media_items for EPUB/PDF/MOBI)
|
||||||
CREATE TABLE book_metadata (
|
CREATE TABLE book_metadata (
|
||||||
media_id UUID PRIMARY KEY REFERENCES media_items (id) ON DELETE CASCADE,
|
media_id UUID PRIMARY KEY REFERENCES media_items (id) ON DELETE CASCADE,
|
||||||
|
|
@ -18,8 +17,11 @@ CREATE TABLE book_metadata (
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX idx_book_isbn13 ON book_metadata (isbn13);
|
CREATE INDEX idx_book_isbn13 ON book_metadata (isbn13);
|
||||||
|
|
||||||
CREATE INDEX idx_book_series ON book_metadata (series_name, series_index);
|
CREATE INDEX idx_book_series ON book_metadata (series_name, series_index);
|
||||||
|
|
||||||
CREATE INDEX idx_book_publisher ON book_metadata (publisher);
|
CREATE INDEX idx_book_publisher ON book_metadata (publisher);
|
||||||
|
|
||||||
CREATE INDEX idx_book_language ON book_metadata (language);
|
CREATE INDEX idx_book_language ON book_metadata (language);
|
||||||
|
|
||||||
-- Multiple authors per book (many-to-many)
|
-- Multiple authors per book (many-to-many)
|
||||||
|
|
@ -33,6 +35,7 @@ CREATE TABLE book_authors (
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX idx_book_authors_name ON book_authors (author_name);
|
CREATE INDEX idx_book_authors_name ON book_authors (author_name);
|
||||||
|
|
||||||
CREATE INDEX idx_book_authors_sort ON book_authors (author_sort);
|
CREATE INDEX idx_book_authors_sort ON book_authors (author_sort);
|
||||||
|
|
||||||
-- Multiple identifiers (ISBN variants, ASIN, DOI, etc.)
|
-- Multiple identifiers (ISBN variants, ASIN, DOI, etc.)
|
||||||
|
|
@ -46,15 +49,13 @@ CREATE TABLE book_identifiers (
|
||||||
CREATE INDEX idx_book_identifiers ON book_identifiers (identifier_type, identifier_value);
|
CREATE INDEX idx_book_identifiers ON book_identifiers (identifier_type, identifier_value);
|
||||||
|
|
||||||
-- Trigger to update updated_at on book_metadata changes
|
-- Trigger to update updated_at on book_metadata changes
|
||||||
CREATE OR REPLACE FUNCTION update_book_metadata_timestamp()
|
CREATE OR REPLACE FUNCTION update_book_metadata_timestamp () RETURNS TRIGGER AS $$
|
||||||
RETURNS TRIGGER AS $$
|
|
||||||
BEGIN
|
BEGIN
|
||||||
NEW.updated_at = NOW();
|
NEW.updated_at = NOW();
|
||||||
RETURN NEW;
|
RETURN NEW;
|
||||||
END;
|
END;
|
||||||
$$ LANGUAGE plpgsql;
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
CREATE TRIGGER update_book_metadata_timestamp
|
CREATE TRIGGER update_book_metadata_timestamp BEFORE
|
||||||
BEFORE UPDATE ON book_metadata
|
UPDATE ON book_metadata FOR EACH ROW
|
||||||
FOR EACH ROW
|
|
||||||
EXECUTE FUNCTION update_book_metadata_timestamp ();
|
EXECUTE FUNCTION update_book_metadata_timestamp ();
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,40 @@
|
||||||
-- V13: Enhanced photo metadata support
|
-- V13: Enhanced photo metadata support
|
||||||
-- Add photo-specific fields to media_items table
|
-- Add photo-specific fields to media_items table
|
||||||
|
ALTER TABLE media_items
|
||||||
|
ADD COLUMN date_taken TIMESTAMPTZ;
|
||||||
|
|
||||||
ALTER TABLE media_items ADD COLUMN date_taken TIMESTAMPTZ;
|
ALTER TABLE media_items
|
||||||
ALTER TABLE media_items ADD COLUMN latitude DOUBLE PRECISION;
|
ADD COLUMN latitude DOUBLE PRECISION;
|
||||||
ALTER TABLE media_items ADD COLUMN longitude DOUBLE PRECISION;
|
|
||||||
ALTER TABLE media_items ADD COLUMN camera_make TEXT;
|
ALTER TABLE media_items
|
||||||
ALTER TABLE media_items ADD COLUMN camera_model TEXT;
|
ADD COLUMN longitude DOUBLE PRECISION;
|
||||||
ALTER TABLE media_items ADD COLUMN rating INTEGER CHECK (rating >= 0 AND rating <= 5);
|
|
||||||
|
ALTER TABLE media_items
|
||||||
|
ADD COLUMN camera_make TEXT;
|
||||||
|
|
||||||
|
ALTER TABLE media_items
|
||||||
|
ADD COLUMN camera_model TEXT;
|
||||||
|
|
||||||
|
ALTER TABLE media_items
|
||||||
|
ADD COLUMN rating INTEGER CHECK (
|
||||||
|
rating >= 0
|
||||||
|
AND rating <= 5
|
||||||
|
);
|
||||||
|
|
||||||
-- Indexes for photo queries
|
-- Indexes for photo queries
|
||||||
CREATE INDEX idx_media_date_taken ON media_items(date_taken) WHERE date_taken IS NOT NULL;
|
CREATE INDEX idx_media_date_taken ON media_items (date_taken)
|
||||||
CREATE INDEX idx_media_location ON media_items(latitude, longitude) WHERE latitude IS NOT NULL AND longitude IS NOT NULL;
|
WHERE
|
||||||
CREATE INDEX idx_media_camera ON media_items(camera_make) WHERE camera_make IS NOT NULL;
|
date_taken IS NOT NULL;
|
||||||
CREATE INDEX idx_media_rating ON media_items(rating) WHERE rating IS NOT NULL;
|
|
||||||
|
CREATE INDEX idx_media_location ON media_items (latitude, longitude)
|
||||||
|
WHERE
|
||||||
|
latitude IS NOT NULL
|
||||||
|
AND longitude IS NOT NULL;
|
||||||
|
|
||||||
|
CREATE INDEX idx_media_camera ON media_items (camera_make)
|
||||||
|
WHERE
|
||||||
|
camera_make IS NOT NULL;
|
||||||
|
|
||||||
|
CREATE INDEX idx_media_rating ON media_items (rating)
|
||||||
|
WHERE
|
||||||
|
rating IS NOT NULL;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
-- V14: Perceptual hash for duplicate detection
|
-- V14: Perceptual hash for duplicate detection
|
||||||
-- Add perceptual hash column for image similarity detection
|
-- Add perceptual hash column for image similarity detection
|
||||||
|
ALTER TABLE media_items
|
||||||
ALTER TABLE media_items ADD COLUMN perceptual_hash TEXT;
|
ADD COLUMN perceptual_hash TEXT;
|
||||||
|
|
||||||
-- Index for perceptual hash lookups
|
-- Index for perceptual hash lookups
|
||||||
CREATE INDEX idx_media_phash ON media_items(perceptual_hash) WHERE perceptual_hash IS NOT NULL;
|
CREATE INDEX idx_media_phash ON media_items (perceptual_hash)
|
||||||
|
WHERE
|
||||||
|
perceptual_hash IS NOT NULL;
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,20 @@
|
||||||
-- V15: Managed File Storage
|
-- V15: Managed File Storage
|
||||||
-- Adds server-side content-addressable storage for uploaded files
|
-- Adds server-side content-addressable storage for uploaded files
|
||||||
|
|
||||||
-- Add storage mode to media_items (external = file on disk, managed = in content-addressable storage)
|
-- Add storage mode to media_items (external = file on disk, managed = in content-addressable storage)
|
||||||
ALTER TABLE media_items ADD COLUMN storage_mode TEXT NOT NULL DEFAULT 'external';
|
ALTER TABLE media_items
|
||||||
|
ADD COLUMN storage_mode TEXT NOT NULL DEFAULT 'external';
|
||||||
|
|
||||||
-- Original filename for managed uploads (preserved separately from file_name which may be normalized)
|
-- Original filename for managed uploads (preserved separately from file_name which may be normalized)
|
||||||
ALTER TABLE media_items ADD COLUMN original_filename TEXT;
|
ALTER TABLE media_items
|
||||||
|
ADD COLUMN original_filename TEXT;
|
||||||
|
|
||||||
-- When the file was uploaded to managed storage
|
-- When the file was uploaded to managed storage
|
||||||
ALTER TABLE media_items ADD COLUMN uploaded_at TIMESTAMPTZ;
|
ALTER TABLE media_items
|
||||||
|
ADD COLUMN uploaded_at TIMESTAMPTZ;
|
||||||
|
|
||||||
-- Storage key for looking up the blob (usually same as content_hash for deduplication)
|
-- Storage key for looking up the blob (usually same as content_hash for deduplication)
|
||||||
ALTER TABLE media_items ADD COLUMN storage_key TEXT;
|
ALTER TABLE media_items
|
||||||
|
ADD COLUMN storage_key TEXT;
|
||||||
|
|
||||||
-- Managed blobs table - tracks deduplicated file storage
|
-- Managed blobs table - tracks deduplicated file storage
|
||||||
CREATE TABLE managed_blobs (
|
CREATE TABLE managed_blobs (
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
-- V16: Cross-Device Sync System
|
-- V16: Cross-Device Sync System
|
||||||
-- Adds device registration, change tracking, and chunked upload support
|
-- Adds device registration, change tracking, and chunked upload support
|
||||||
|
|
||||||
-- Sync devices table
|
-- Sync devices table
|
||||||
CREATE TABLE sync_devices (
|
CREATE TABLE sync_devices (
|
||||||
id TEXT PRIMARY KEY NOT NULL,
|
id TEXT PRIMARY KEY NOT NULL,
|
||||||
|
|
@ -19,6 +18,7 @@ CREATE TABLE sync_devices (
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX idx_sync_devices_user ON sync_devices (user_id);
|
CREATE INDEX idx_sync_devices_user ON sync_devices (user_id);
|
||||||
|
|
||||||
CREATE INDEX idx_sync_devices_token ON sync_devices (device_token_hash);
|
CREATE INDEX idx_sync_devices_token ON sync_devices (device_token_hash);
|
||||||
|
|
||||||
-- Sync log table - tracks all changes for sync
|
-- Sync log table - tracks all changes for sync
|
||||||
|
|
@ -36,7 +36,9 @@ CREATE TABLE sync_log (
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX idx_sync_log_sequence ON sync_log (sequence);
|
CREATE INDEX idx_sync_log_sequence ON sync_log (sequence);
|
||||||
|
|
||||||
CREATE INDEX idx_sync_log_path ON sync_log (path);
|
CREATE INDEX idx_sync_log_path ON sync_log (path);
|
||||||
|
|
||||||
CREATE INDEX idx_sync_log_timestamp ON sync_log (timestamp);
|
CREATE INDEX idx_sync_log_timestamp ON sync_log (timestamp);
|
||||||
|
|
||||||
-- Sequence counter for sync log
|
-- Sequence counter for sync log
|
||||||
|
|
@ -44,7 +46,11 @@ CREATE TABLE sync_sequence (
|
||||||
id INTEGER PRIMARY KEY CHECK (id = 1),
|
id INTEGER PRIMARY KEY CHECK (id = 1),
|
||||||
current_value BIGINT NOT NULL DEFAULT 0
|
current_value BIGINT NOT NULL DEFAULT 0
|
||||||
);
|
);
|
||||||
INSERT INTO sync_sequence (id, current_value) VALUES (1, 0);
|
|
||||||
|
INSERT INTO
|
||||||
|
sync_sequence (id, current_value)
|
||||||
|
VALUES
|
||||||
|
(1, 0);
|
||||||
|
|
||||||
-- Device sync state - tracks sync status per device per file
|
-- Device sync state - tracks sync status per device per file
|
||||||
CREATE TABLE device_sync_state (
|
CREATE TABLE device_sync_state (
|
||||||
|
|
@ -78,14 +84,17 @@ CREATE TABLE upload_sessions (
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX idx_upload_sessions_device ON upload_sessions (device_id);
|
CREATE INDEX idx_upload_sessions_device ON upload_sessions (device_id);
|
||||||
|
|
||||||
CREATE INDEX idx_upload_sessions_status ON upload_sessions (status);
|
CREATE INDEX idx_upload_sessions_status ON upload_sessions (status);
|
||||||
|
|
||||||
CREATE INDEX idx_upload_sessions_expires ON upload_sessions (expires_at);
|
CREATE INDEX idx_upload_sessions_expires ON upload_sessions (expires_at);
|
||||||
|
|
||||||
-- Upload chunks - tracks received chunks
|
-- Upload chunks - tracks received chunks
|
||||||
CREATE TABLE upload_chunks (
|
CREATE TABLE upload_chunks (
|
||||||
upload_id TEXT NOT NULL REFERENCES upload_sessions (id) ON DELETE CASCADE,
|
upload_id TEXT NOT NULL REFERENCES upload_sessions (id) ON DELETE CASCADE,
|
||||||
chunk_index BIGINT NOT NULL,
|
chunk_index BIGINT NOT NULL,
|
||||||
offset BIGINT NOT NULL,
|
offset
|
||||||
|
BIGINT NOT NULL,
|
||||||
size BIGINT NOT NULL,
|
size BIGINT NOT NULL,
|
||||||
hash TEXT NOT NULL,
|
hash TEXT NOT NULL,
|
||||||
received_at TIMESTAMPTZ NOT NULL,
|
received_at TIMESTAMPTZ NOT NULL,
|
||||||
|
|
@ -107,4 +116,7 @@ CREATE TABLE sync_conflicts (
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX idx_sync_conflicts_device ON sync_conflicts (device_id);
|
CREATE INDEX idx_sync_conflicts_device ON sync_conflicts (device_id);
|
||||||
CREATE INDEX idx_sync_conflicts_unresolved ON sync_conflicts(device_id) WHERE resolved_at IS NULL;
|
|
||||||
|
CREATE INDEX idx_sync_conflicts_unresolved ON sync_conflicts (device_id)
|
||||||
|
WHERE
|
||||||
|
resolved_at IS NULL;
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,16 @@
|
||||||
-- V17: Enhanced Sharing System
|
-- V17: Enhanced Sharing System
|
||||||
-- Replaces simple share_links with comprehensive sharing capabilities
|
-- Replaces simple share_links with comprehensive sharing capabilities
|
||||||
|
|
||||||
-- Enhanced shares table
|
-- Enhanced shares table
|
||||||
CREATE TABLE shares (
|
CREATE TABLE shares (
|
||||||
id TEXT PRIMARY KEY NOT NULL,
|
id TEXT PRIMARY KEY NOT NULL,
|
||||||
target_type TEXT NOT NULL CHECK (target_type IN ('media', 'collection', 'tag', 'saved_search')),
|
target_type TEXT NOT NULL CHECK (
|
||||||
|
target_type IN ('media', 'collection', 'tag', 'saved_search')
|
||||||
|
),
|
||||||
target_id TEXT NOT NULL,
|
target_id TEXT NOT NULL,
|
||||||
owner_id TEXT NOT NULL REFERENCES users (id) ON DELETE CASCADE,
|
owner_id TEXT NOT NULL REFERENCES users (id) ON DELETE CASCADE,
|
||||||
recipient_type TEXT NOT NULL CHECK (recipient_type IN ('public_link', 'user', 'group', 'federated')),
|
recipient_type TEXT NOT NULL CHECK (
|
||||||
|
recipient_type IN ('public_link', 'user', 'group', 'federated')
|
||||||
|
),
|
||||||
recipient_user_id TEXT REFERENCES users (id) ON DELETE CASCADE,
|
recipient_user_id TEXT REFERENCES users (id) ON DELETE CASCADE,
|
||||||
recipient_group_id TEXT,
|
recipient_group_id TEXT,
|
||||||
recipient_federated_handle TEXT,
|
recipient_federated_handle TEXT,
|
||||||
|
|
@ -28,13 +31,23 @@ CREATE TABLE shares (
|
||||||
parent_share_id TEXT REFERENCES shares (id) ON DELETE CASCADE,
|
parent_share_id TEXT REFERENCES shares (id) ON DELETE CASCADE,
|
||||||
created_at TIMESTAMPTZ NOT NULL,
|
created_at TIMESTAMPTZ NOT NULL,
|
||||||
updated_at TIMESTAMPTZ NOT NULL,
|
updated_at TIMESTAMPTZ NOT NULL,
|
||||||
UNIQUE(owner_id, target_type, target_id, recipient_type, recipient_user_id)
|
UNIQUE (
|
||||||
|
owner_id,
|
||||||
|
target_type,
|
||||||
|
target_id,
|
||||||
|
recipient_type,
|
||||||
|
recipient_user_id
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX idx_shares_owner ON shares (owner_id);
|
CREATE INDEX idx_shares_owner ON shares (owner_id);
|
||||||
|
|
||||||
CREATE INDEX idx_shares_recipient_user ON shares (recipient_user_id);
|
CREATE INDEX idx_shares_recipient_user ON shares (recipient_user_id);
|
||||||
|
|
||||||
CREATE INDEX idx_shares_target ON shares (target_type, target_id);
|
CREATE INDEX idx_shares_target ON shares (target_type, target_id);
|
||||||
|
|
||||||
CREATE INDEX idx_shares_token ON shares (public_token);
|
CREATE INDEX idx_shares_token ON shares (public_token);
|
||||||
|
|
||||||
CREATE INDEX idx_shares_expires ON shares (expires_at);
|
CREATE INDEX idx_shares_expires ON shares (expires_at);
|
||||||
|
|
||||||
-- Share activity log
|
-- Share activity log
|
||||||
|
|
@ -49,6 +62,7 @@ CREATE TABLE share_activity (
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX idx_share_activity_share ON share_activity (share_id);
|
CREATE INDEX idx_share_activity_share ON share_activity (share_id);
|
||||||
|
|
||||||
CREATE INDEX idx_share_activity_timestamp ON share_activity (timestamp);
|
CREATE INDEX idx_share_activity_timestamp ON share_activity (timestamp);
|
||||||
|
|
||||||
-- Share notifications
|
-- Share notifications
|
||||||
|
|
@ -62,7 +76,10 @@ CREATE TABLE share_notifications (
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX idx_share_notifications_user ON share_notifications (user_id);
|
CREATE INDEX idx_share_notifications_user ON share_notifications (user_id);
|
||||||
CREATE INDEX idx_share_notifications_unread ON share_notifications(user_id) WHERE is_read = FALSE;
|
|
||||||
|
CREATE INDEX idx_share_notifications_unread ON share_notifications (user_id)
|
||||||
|
WHERE
|
||||||
|
is_read = FALSE;
|
||||||
|
|
||||||
-- Migrate existing share_links to new shares table
|
-- Migrate existing share_links to new shares table
|
||||||
DO $$
|
DO $$
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
-- V18: File Management (Rename, Move, Trash)
|
-- V18: File Management (Rename, Move, Trash)
|
||||||
-- Adds soft delete support for trash/recycle bin functionality
|
-- Adds soft delete support for trash/recycle bin functionality
|
||||||
|
|
||||||
-- Add deleted_at column for soft delete (trash)
|
-- Add deleted_at column for soft delete (trash)
|
||||||
ALTER TABLE media_items ADD COLUMN deleted_at TIMESTAMPTZ;
|
ALTER TABLE media_items
|
||||||
|
ADD COLUMN deleted_at TIMESTAMPTZ;
|
||||||
|
|
||||||
-- Index for efficient trash queries
|
-- Index for efficient trash queries
|
||||||
CREATE INDEX idx_media_deleted_at ON media_items (deleted_at);
|
CREATE INDEX idx_media_deleted_at ON media_items (deleted_at);
|
||||||
|
|
||||||
-- Partial index for listing non-deleted items (most common query pattern)
|
-- Partial index for listing non-deleted items (most common query pattern)
|
||||||
CREATE INDEX idx_media_not_deleted ON media_items(id) WHERE deleted_at IS NULL;
|
CREATE INDEX idx_media_not_deleted ON media_items (id)
|
||||||
|
WHERE
|
||||||
|
deleted_at IS NULL;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
-- V19: Markdown Links (Obsidian-style bidirectional links)
|
-- V19: Markdown Links (Obsidian-style bidirectional links)
|
||||||
-- Adds support for wikilinks, markdown links, embeds, and backlink tracking
|
-- Adds support for wikilinks, markdown links, embeds, and backlink tracking
|
||||||
|
|
||||||
-- Table for storing extracted markdown links
|
-- Table for storing extracted markdown links
|
||||||
CREATE TABLE IF NOT EXISTS markdown_links (
|
CREATE TABLE IF NOT EXISTS markdown_links (
|
||||||
id TEXT PRIMARY KEY NOT NULL,
|
id TEXT PRIMARY KEY NOT NULL,
|
||||||
|
|
@ -29,7 +28,8 @@ CREATE INDEX idx_links_target_path ON markdown_links(target_path);
|
||||||
CREATE INDEX idx_links_type ON markdown_links (link_type);
|
CREATE INDEX idx_links_type ON markdown_links (link_type);
|
||||||
|
|
||||||
-- Track when links were last extracted from a media item
|
-- Track when links were last extracted from a media item
|
||||||
ALTER TABLE media_items ADD COLUMN links_extracted_at TIMESTAMPTZ;
|
ALTER TABLE media_items
|
||||||
|
ADD COLUMN links_extracted_at TIMESTAMPTZ;
|
||||||
|
|
||||||
-- Index for finding media items that need link extraction
|
-- Index for finding media items that need link extraction
|
||||||
CREATE INDEX idx_media_links_extracted ON media_items (links_extracted_at);
|
CREATE INDEX idx_media_links_extracted ON media_items (links_extracted_at);
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||||
|
|
||||||
CREATE EXTENSION IF NOT EXISTS pg_trgm;
|
CREATE EXTENSION IF NOT EXISTS pg_trgm;
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS root_dirs (
|
CREATE TABLE IF NOT EXISTS root_dirs (path TEXT PRIMARY KEY NOT NULL);
|
||||||
path TEXT PRIMARY KEY NOT NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS media_items (
|
CREATE TABLE IF NOT EXISTS media_items (
|
||||||
id UUID PRIMARY KEY NOT NULL,
|
id UUID PRIMARY KEY NOT NULL,
|
||||||
|
|
@ -30,7 +29,10 @@ CREATE TABLE IF NOT EXISTS tags (
|
||||||
created_at TIMESTAMPTZ NOT NULL
|
created_at TIMESTAMPTZ NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_tags_name_parent ON tags(name, COALESCE(parent_id, '00000000-0000-0000-0000-000000000000'));
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_tags_name_parent ON tags (
|
||||||
|
name,
|
||||||
|
COALESCE(parent_id, '00000000-0000-0000-0000-000000000000')
|
||||||
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS media_tags (
|
CREATE TABLE IF NOT EXISTS media_tags (
|
||||||
media_id UUID NOT NULL REFERENCES media_items (id) ON DELETE CASCADE,
|
media_id UUID NOT NULL REFERENCES media_items (id) ON DELETE CASCADE,
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
ALTER TABLE media_items ADD COLUMN IF NOT EXISTS search_vector tsvector
|
ALTER TABLE media_items
|
||||||
GENERATED ALWAYS AS (
|
ADD COLUMN IF NOT EXISTS search_vector tsvector GENERATED ALWAYS AS (
|
||||||
setweight(to_tsvector('english', COALESCE(title, '')), 'A') ||
|
setweight(to_tsvector('english', COALESCE(title, '')), 'A') || setweight(to_tsvector('english', COALESCE(artist, '')), 'B') || setweight(to_tsvector('english', COALESCE(album, '')), 'B') || setweight(to_tsvector('english', COALESCE(genre, '')), 'C') || setweight(
|
||||||
setweight(to_tsvector('english', COALESCE(artist, '')), 'B') ||
|
to_tsvector('english', COALESCE(description, '')),
|
||||||
setweight(to_tsvector('english', COALESCE(album, '')), 'B') ||
|
'C'
|
||||||
setweight(to_tsvector('english', COALESCE(genre, '')), 'C') ||
|
) || setweight(
|
||||||
setweight(to_tsvector('english', COALESCE(description, '')), 'C') ||
|
to_tsvector('english', COALESCE(file_name, '')),
|
||||||
setweight(to_tsvector('english', COALESCE(file_name, '')), 'D')
|
'D'
|
||||||
|
)
|
||||||
) STORED;
|
) STORED;
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_media_search ON media_items USING GIN (search_vector);
|
CREATE INDEX IF NOT EXISTS idx_media_search ON media_items USING GIN (search_vector);
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,15 @@
|
||||||
CREATE INDEX IF NOT EXISTS idx_audit_media_id ON audit_log (media_id);
|
CREATE INDEX IF NOT EXISTS idx_audit_media_id ON audit_log (media_id);
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_audit_timestamp ON audit_log (timestamp);
|
CREATE INDEX IF NOT EXISTS idx_audit_timestamp ON audit_log (timestamp);
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_audit_action ON audit_log (action);
|
CREATE INDEX IF NOT EXISTS idx_audit_action ON audit_log (action);
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_media_content_hash ON media_items (content_hash);
|
CREATE INDEX IF NOT EXISTS idx_media_content_hash ON media_items (content_hash);
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_media_media_type ON media_items (media_type);
|
CREATE INDEX IF NOT EXISTS idx_media_media_type ON media_items (media_type);
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_media_created_at ON media_items (created_at);
|
CREATE INDEX IF NOT EXISTS idx_media_created_at ON media_items (created_at);
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_media_title_trgm ON media_items USING GIN (title gin_trgm_ops);
|
CREATE INDEX IF NOT EXISTS idx_media_title_trgm ON media_items USING GIN (title gin_trgm_ops);
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_media_artist_trgm ON media_items USING GIN (artist gin_trgm_ops);
|
CREATE INDEX IF NOT EXISTS idx_media_artist_trgm ON media_items USING GIN (artist gin_trgm_ops);
|
||||||
|
|
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
ALTER TABLE media_items ADD COLUMN thumbnail_path TEXT;
|
ALTER TABLE media_items
|
||||||
|
ADD COLUMN thumbnail_path TEXT;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
-- Integrity tracking columns
|
-- Integrity tracking columns
|
||||||
ALTER TABLE media_items ADD COLUMN last_verified_at TIMESTAMPTZ;
|
ALTER TABLE media_items
|
||||||
ALTER TABLE media_items ADD COLUMN integrity_status TEXT DEFAULT 'unverified';
|
ADD COLUMN last_verified_at TIMESTAMPTZ;
|
||||||
|
|
||||||
|
ALTER TABLE media_items
|
||||||
|
ADD COLUMN integrity_status TEXT DEFAULT 'unverified';
|
||||||
|
|
||||||
-- Saved searches
|
-- Saved searches
|
||||||
CREATE TABLE IF NOT EXISTS saved_searches (
|
CREATE TABLE IF NOT EXISTS saved_searches (
|
||||||
|
|
|
||||||
|
|
@ -12,4 +12,5 @@ CREATE TABLE plugin_registry (
|
||||||
|
|
||||||
-- Index for quick lookups
|
-- Index for quick lookups
|
||||||
CREATE INDEX idx_plugin_registry_enabled ON plugin_registry (enabled);
|
CREATE INDEX idx_plugin_registry_enabled ON plugin_registry (enabled);
|
||||||
|
|
||||||
CREATE INDEX idx_plugin_registry_name ON plugin_registry (name);
|
CREATE INDEX idx_plugin_registry_name ON plugin_registry (name);
|
||||||
|
|
|
||||||
|
|
@ -31,5 +31,7 @@ CREATE TABLE user_libraries (
|
||||||
|
|
||||||
-- Indexes for efficient lookups
|
-- Indexes for efficient lookups
|
||||||
CREATE INDEX idx_users_username ON users (username);
|
CREATE INDEX idx_users_username ON users (username);
|
||||||
|
|
||||||
CREATE INDEX idx_user_libraries_user_id ON user_libraries (user_id);
|
CREATE INDEX idx_user_libraries_user_id ON user_libraries (user_id);
|
||||||
|
|
||||||
CREATE INDEX idx_user_libraries_root_path ON user_libraries (root_path);
|
CREATE INDEX idx_user_libraries_root_path ON user_libraries (root_path);
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,10 @@ CREATE TABLE IF NOT EXISTS ratings (
|
||||||
id UUID PRIMARY KEY,
|
id UUID PRIMARY KEY,
|
||||||
user_id UUID NOT NULL,
|
user_id UUID NOT NULL,
|
||||||
media_id UUID NOT NULL REFERENCES media_items (id) ON DELETE CASCADE,
|
media_id UUID NOT NULL REFERENCES media_items (id) ON DELETE CASCADE,
|
||||||
stars INTEGER NOT NULL CHECK (stars >= 1 AND stars <= 5),
|
stars INTEGER NOT NULL CHECK (
|
||||||
|
stars >= 1
|
||||||
|
AND stars <= 5
|
||||||
|
),
|
||||||
review_text TEXT,
|
review_text TEXT,
|
||||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
UNIQUE (user_id, media_id)
|
UNIQUE (user_id, media_id)
|
||||||
|
|
@ -73,7 +76,9 @@ CREATE TABLE IF NOT EXISTS usage_events (
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_usage_events_media ON usage_events (media_id);
|
CREATE INDEX IF NOT EXISTS idx_usage_events_media ON usage_events (media_id);
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_usage_events_user ON usage_events (user_id);
|
CREATE INDEX IF NOT EXISTS idx_usage_events_user ON usage_events (user_id);
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_usage_events_timestamp ON usage_events (timestamp);
|
CREATE INDEX IF NOT EXISTS idx_usage_events_timestamp ON usage_events (timestamp);
|
||||||
|
|
||||||
-- Watch history / progress
|
-- Watch history / progress
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,23 @@
|
||||||
-- Drop redundant indexes (already covered by UNIQUE constraints)
|
-- Drop redundant indexes (already covered by UNIQUE constraints)
|
||||||
DROP INDEX IF EXISTS idx_users_username;
|
DROP INDEX IF EXISTS idx_users_username;
|
||||||
|
|
||||||
DROP INDEX IF EXISTS idx_user_libraries_user_id;
|
DROP INDEX IF EXISTS idx_user_libraries_user_id;
|
||||||
|
|
||||||
-- Add missing indexes for comments table
|
-- Add missing indexes for comments table
|
||||||
CREATE INDEX IF NOT EXISTS idx_comments_media ON comments (media_id);
|
CREATE INDEX IF NOT EXISTS idx_comments_media ON comments (media_id);
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_comments_parent ON comments (parent_comment_id);
|
CREATE INDEX IF NOT EXISTS idx_comments_parent ON comments (parent_comment_id);
|
||||||
|
|
||||||
-- Remove duplicates before adding unique constraint
|
-- Remove duplicates before adding unique constraint
|
||||||
DELETE FROM external_metadata e1
|
DELETE FROM external_metadata e1
|
||||||
WHERE EXISTS (
|
WHERE
|
||||||
SELECT 1 FROM external_metadata e2
|
EXISTS (
|
||||||
WHERE e1.media_id = e2.media_id
|
SELECT
|
||||||
|
1
|
||||||
|
FROM
|
||||||
|
external_metadata e2
|
||||||
|
WHERE
|
||||||
|
e1.media_id = e2.media_id
|
||||||
AND e1.source = e2.source
|
AND e1.source = e2.source
|
||||||
AND e1.ctid < e2.ctid
|
AND e1.ctid < e2.ctid
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
-- Add file_mtime column to media_items table for incremental scanning
|
-- Add file_mtime column to media_items table for incremental scanning
|
||||||
-- Stores Unix timestamp in seconds of the file's modification time
|
-- Stores Unix timestamp in seconds of the file's modification time
|
||||||
|
ALTER TABLE media_items
|
||||||
ALTER TABLE media_items ADD COLUMN file_mtime INTEGER;
|
ADD COLUMN file_mtime INTEGER;
|
||||||
|
|
||||||
-- Create index for quick mtime lookups
|
-- Create index for quick mtime lookups
|
||||||
CREATE INDEX IF NOT EXISTS idx_media_items_file_mtime ON media_items (file_mtime);
|
CREATE INDEX IF NOT EXISTS idx_media_items_file_mtime ON media_items (file_mtime);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
-- Session persistence for database-backed sessions
|
-- Session persistence for database-backed sessions
|
||||||
-- Replaces in-memory session storage
|
-- Replaces in-memory session storage
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS sessions (
|
CREATE TABLE IF NOT EXISTS sessions (
|
||||||
session_token TEXT PRIMARY KEY NOT NULL,
|
session_token TEXT PRIMARY KEY NOT NULL,
|
||||||
user_id TEXT,
|
user_id TEXT,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
-- V12: Book Management Schema
|
-- V12: Book Management Schema
|
||||||
-- Adds comprehensive book metadata tracking, authors, and identifiers
|
-- Adds comprehensive book metadata tracking, authors, and identifiers
|
||||||
|
|
||||||
-- Book metadata (supplements media_items for EPUB/PDF/MOBI)
|
-- Book metadata (supplements media_items for EPUB/PDF/MOBI)
|
||||||
CREATE TABLE book_metadata (
|
CREATE TABLE book_metadata (
|
||||||
media_id TEXT PRIMARY KEY REFERENCES media_items (id) ON DELETE CASCADE,
|
media_id TEXT PRIMARY KEY REFERENCES media_items (id) ON DELETE CASCADE,
|
||||||
|
|
@ -18,8 +17,11 @@ CREATE TABLE book_metadata (
|
||||||
) STRICT;
|
) STRICT;
|
||||||
|
|
||||||
CREATE INDEX idx_book_isbn13 ON book_metadata (isbn13);
|
CREATE INDEX idx_book_isbn13 ON book_metadata (isbn13);
|
||||||
|
|
||||||
CREATE INDEX idx_book_series ON book_metadata (series_name, series_index);
|
CREATE INDEX idx_book_series ON book_metadata (series_name, series_index);
|
||||||
|
|
||||||
CREATE INDEX idx_book_publisher ON book_metadata (publisher);
|
CREATE INDEX idx_book_publisher ON book_metadata (publisher);
|
||||||
|
|
||||||
CREATE INDEX idx_book_language ON book_metadata (language);
|
CREATE INDEX idx_book_language ON book_metadata (language);
|
||||||
|
|
||||||
-- Multiple authors per book (many-to-many)
|
-- Multiple authors per book (many-to-many)
|
||||||
|
|
@ -33,6 +35,7 @@ CREATE TABLE book_authors (
|
||||||
) STRICT;
|
) STRICT;
|
||||||
|
|
||||||
CREATE INDEX idx_book_authors_name ON book_authors (author_name);
|
CREATE INDEX idx_book_authors_name ON book_authors (author_name);
|
||||||
|
|
||||||
CREATE INDEX idx_book_authors_sort ON book_authors (author_sort);
|
CREATE INDEX idx_book_authors_sort ON book_authors (author_sort);
|
||||||
|
|
||||||
-- Multiple identifiers (ISBN variants, ASIN, DOI, etc.)
|
-- Multiple identifiers (ISBN variants, ASIN, DOI, etc.)
|
||||||
|
|
@ -47,8 +50,13 @@ CREATE INDEX idx_book_identifiers ON book_identifiers(identifier_type, identifie
|
||||||
|
|
||||||
-- Trigger to update updated_at on book_metadata changes
|
-- Trigger to update updated_at on book_metadata changes
|
||||||
CREATE TRIGGER update_book_metadata_timestamp
|
CREATE TRIGGER update_book_metadata_timestamp
|
||||||
AFTER UPDATE ON book_metadata
|
AFTER
|
||||||
FOR EACH ROW
|
UPDATE ON book_metadata FOR EACH ROW
|
||||||
BEGIN
|
BEGIN
|
||||||
UPDATE book_metadata SET updated_at = datetime('now') WHERE media_id = NEW.media_id;
|
UPDATE book_metadata
|
||||||
|
SET
|
||||||
|
updated_at = datetime ('now')
|
||||||
|
WHERE
|
||||||
|
media_id = NEW.media_id;
|
||||||
|
|
||||||
END;
|
END;
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,40 @@
|
||||||
-- V13: Enhanced photo metadata support
|
-- V13: Enhanced photo metadata support
|
||||||
-- Add photo-specific fields to media_items table
|
-- Add photo-specific fields to media_items table
|
||||||
|
ALTER TABLE media_items
|
||||||
|
ADD COLUMN date_taken TIMESTAMP;
|
||||||
|
|
||||||
ALTER TABLE media_items ADD COLUMN date_taken TIMESTAMP;
|
ALTER TABLE media_items
|
||||||
ALTER TABLE media_items ADD COLUMN latitude REAL;
|
ADD COLUMN latitude REAL;
|
||||||
ALTER TABLE media_items ADD COLUMN longitude REAL;
|
|
||||||
ALTER TABLE media_items ADD COLUMN camera_make TEXT;
|
ALTER TABLE media_items
|
||||||
ALTER TABLE media_items ADD COLUMN camera_model TEXT;
|
ADD COLUMN longitude REAL;
|
||||||
ALTER TABLE media_items ADD COLUMN rating INTEGER CHECK (rating >= 0 AND rating <= 5);
|
|
||||||
|
ALTER TABLE media_items
|
||||||
|
ADD COLUMN camera_make TEXT;
|
||||||
|
|
||||||
|
ALTER TABLE media_items
|
||||||
|
ADD COLUMN camera_model TEXT;
|
||||||
|
|
||||||
|
ALTER TABLE media_items
|
||||||
|
ADD COLUMN rating INTEGER CHECK (
|
||||||
|
rating >= 0
|
||||||
|
AND rating <= 5
|
||||||
|
);
|
||||||
|
|
||||||
-- Indexes for photo queries
|
-- Indexes for photo queries
|
||||||
CREATE INDEX idx_media_date_taken ON media_items(date_taken) WHERE date_taken IS NOT NULL;
|
CREATE INDEX idx_media_date_taken ON media_items (date_taken)
|
||||||
CREATE INDEX idx_media_location ON media_items(latitude, longitude) WHERE latitude IS NOT NULL AND longitude IS NOT NULL;
|
WHERE
|
||||||
CREATE INDEX idx_media_camera ON media_items(camera_make) WHERE camera_make IS NOT NULL;
|
date_taken IS NOT NULL;
|
||||||
CREATE INDEX idx_media_rating ON media_items(rating) WHERE rating IS NOT NULL;
|
|
||||||
|
CREATE INDEX idx_media_location ON media_items (latitude, longitude)
|
||||||
|
WHERE
|
||||||
|
latitude IS NOT NULL
|
||||||
|
AND longitude IS NOT NULL;
|
||||||
|
|
||||||
|
CREATE INDEX idx_media_camera ON media_items (camera_make)
|
||||||
|
WHERE
|
||||||
|
camera_make IS NOT NULL;
|
||||||
|
|
||||||
|
CREATE INDEX idx_media_rating ON media_items (rating)
|
||||||
|
WHERE
|
||||||
|
rating IS NOT NULL;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
-- V14: Perceptual hash for duplicate detection
|
-- V14: Perceptual hash for duplicate detection
|
||||||
-- Add perceptual hash column for image similarity detection
|
-- Add perceptual hash column for image similarity detection
|
||||||
|
ALTER TABLE media_items
|
||||||
ALTER TABLE media_items ADD COLUMN perceptual_hash TEXT;
|
ADD COLUMN perceptual_hash TEXT;
|
||||||
|
|
||||||
-- Index for perceptual hash lookups
|
-- Index for perceptual hash lookups
|
||||||
CREATE INDEX idx_media_phash ON media_items(perceptual_hash) WHERE perceptual_hash IS NOT NULL;
|
CREATE INDEX idx_media_phash ON media_items (perceptual_hash)
|
||||||
|
WHERE
|
||||||
|
perceptual_hash IS NOT NULL;
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,20 @@
|
||||||
-- V15: Managed File Storage
|
-- V15: Managed File Storage
|
||||||
-- Adds server-side content-addressable storage for uploaded files
|
-- Adds server-side content-addressable storage for uploaded files
|
||||||
|
|
||||||
-- Add storage mode to media_items (external = file on disk, managed = in content-addressable storage)
|
-- Add storage mode to media_items (external = file on disk, managed = in content-addressable storage)
|
||||||
ALTER TABLE media_items ADD COLUMN storage_mode TEXT NOT NULL DEFAULT 'external';
|
ALTER TABLE media_items
|
||||||
|
ADD COLUMN storage_mode TEXT NOT NULL DEFAULT 'external';
|
||||||
|
|
||||||
-- Original filename for managed uploads (preserved separately from file_name which may be normalized)
|
-- Original filename for managed uploads (preserved separately from file_name which may be normalized)
|
||||||
ALTER TABLE media_items ADD COLUMN original_filename TEXT;
|
ALTER TABLE media_items
|
||||||
|
ADD COLUMN original_filename TEXT;
|
||||||
|
|
||||||
-- When the file was uploaded to managed storage
|
-- When the file was uploaded to managed storage
|
||||||
ALTER TABLE media_items ADD COLUMN uploaded_at TEXT;
|
ALTER TABLE media_items
|
||||||
|
ADD COLUMN uploaded_at TEXT;
|
||||||
|
|
||||||
-- Storage key for looking up the blob (usually same as content_hash for deduplication)
|
-- Storage key for looking up the blob (usually same as content_hash for deduplication)
|
||||||
ALTER TABLE media_items ADD COLUMN storage_key TEXT;
|
ALTER TABLE media_items
|
||||||
|
ADD COLUMN storage_key TEXT;
|
||||||
|
|
||||||
-- Managed blobs table - tracks deduplicated file storage
|
-- Managed blobs table - tracks deduplicated file storage
|
||||||
CREATE TABLE managed_blobs (
|
CREATE TABLE managed_blobs (
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
-- V16: Cross-Device Sync System
|
-- V16: Cross-Device Sync System
|
||||||
-- Adds device registration, change tracking, and chunked upload support
|
-- Adds device registration, change tracking, and chunked upload support
|
||||||
|
|
||||||
-- Sync devices table
|
-- Sync devices table
|
||||||
CREATE TABLE sync_devices (
|
CREATE TABLE sync_devices (
|
||||||
id TEXT PRIMARY KEY NOT NULL,
|
id TEXT PRIMARY KEY NOT NULL,
|
||||||
|
|
@ -20,6 +19,7 @@ CREATE TABLE sync_devices (
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX idx_sync_devices_user ON sync_devices (user_id);
|
CREATE INDEX idx_sync_devices_user ON sync_devices (user_id);
|
||||||
|
|
||||||
CREATE INDEX idx_sync_devices_token ON sync_devices (device_token_hash);
|
CREATE INDEX idx_sync_devices_token ON sync_devices (device_token_hash);
|
||||||
|
|
||||||
-- Sync log table - tracks all changes for sync
|
-- Sync log table - tracks all changes for sync
|
||||||
|
|
@ -39,7 +39,9 @@ CREATE TABLE sync_log (
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX idx_sync_log_sequence ON sync_log (sequence);
|
CREATE INDEX idx_sync_log_sequence ON sync_log (sequence);
|
||||||
|
|
||||||
CREATE INDEX idx_sync_log_path ON sync_log (path);
|
CREATE INDEX idx_sync_log_path ON sync_log (path);
|
||||||
|
|
||||||
CREATE INDEX idx_sync_log_timestamp ON sync_log (timestamp);
|
CREATE INDEX idx_sync_log_timestamp ON sync_log (timestamp);
|
||||||
|
|
||||||
-- Sequence counter for sync log
|
-- Sequence counter for sync log
|
||||||
|
|
@ -47,7 +49,11 @@ CREATE TABLE sync_sequence (
|
||||||
id INTEGER PRIMARY KEY CHECK (id = 1),
|
id INTEGER PRIMARY KEY CHECK (id = 1),
|
||||||
current_value INTEGER NOT NULL DEFAULT 0
|
current_value INTEGER NOT NULL DEFAULT 0
|
||||||
);
|
);
|
||||||
INSERT INTO sync_sequence (id, current_value) VALUES (1, 0);
|
|
||||||
|
INSERT INTO
|
||||||
|
sync_sequence (id, current_value)
|
||||||
|
VALUES
|
||||||
|
(1, 0);
|
||||||
|
|
||||||
-- Device sync state - tracks sync status per device per file
|
-- Device sync state - tracks sync status per device per file
|
||||||
CREATE TABLE device_sync_state (
|
CREATE TABLE device_sync_state (
|
||||||
|
|
@ -83,14 +89,17 @@ CREATE TABLE upload_sessions (
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX idx_upload_sessions_device ON upload_sessions (device_id);
|
CREATE INDEX idx_upload_sessions_device ON upload_sessions (device_id);
|
||||||
|
|
||||||
CREATE INDEX idx_upload_sessions_status ON upload_sessions (status);
|
CREATE INDEX idx_upload_sessions_status ON upload_sessions (status);
|
||||||
|
|
||||||
CREATE INDEX idx_upload_sessions_expires ON upload_sessions (expires_at);
|
CREATE INDEX idx_upload_sessions_expires ON upload_sessions (expires_at);
|
||||||
|
|
||||||
-- Upload chunks - tracks received chunks
|
-- Upload chunks - tracks received chunks
|
||||||
CREATE TABLE upload_chunks (
|
CREATE TABLE upload_chunks (
|
||||||
upload_id TEXT NOT NULL,
|
upload_id TEXT NOT NULL,
|
||||||
chunk_index INTEGER NOT NULL,
|
chunk_index INTEGER NOT NULL,
|
||||||
offset INTEGER NOT NULL,
|
offset
|
||||||
|
INTEGER NOT NULL,
|
||||||
size INTEGER NOT NULL,
|
size INTEGER NOT NULL,
|
||||||
hash TEXT NOT NULL,
|
hash TEXT NOT NULL,
|
||||||
received_at TEXT NOT NULL,
|
received_at TEXT NOT NULL,
|
||||||
|
|
@ -114,4 +123,7 @@ CREATE TABLE sync_conflicts (
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX idx_sync_conflicts_device ON sync_conflicts (device_id);
|
CREATE INDEX idx_sync_conflicts_device ON sync_conflicts (device_id);
|
||||||
CREATE INDEX idx_sync_conflicts_unresolved ON sync_conflicts(device_id, resolved_at) WHERE resolved_at IS NULL;
|
|
||||||
|
CREATE INDEX idx_sync_conflicts_unresolved ON sync_conflicts (device_id, resolved_at)
|
||||||
|
WHERE
|
||||||
|
resolved_at IS NULL;
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,16 @@
|
||||||
-- V17: Enhanced Sharing System
|
-- V17: Enhanced Sharing System
|
||||||
-- Replaces simple share_links with comprehensive sharing capabilities
|
-- Replaces simple share_links with comprehensive sharing capabilities
|
||||||
|
|
||||||
-- Enhanced shares table
|
-- Enhanced shares table
|
||||||
CREATE TABLE shares (
|
CREATE TABLE shares (
|
||||||
id TEXT PRIMARY KEY NOT NULL,
|
id TEXT PRIMARY KEY NOT NULL,
|
||||||
target_type TEXT NOT NULL CHECK (target_type IN ('media', 'collection', 'tag', 'saved_search')),
|
target_type TEXT NOT NULL CHECK (
|
||||||
|
target_type IN ('media', 'collection', 'tag', 'saved_search')
|
||||||
|
),
|
||||||
target_id TEXT NOT NULL,
|
target_id TEXT NOT NULL,
|
||||||
owner_id TEXT NOT NULL,
|
owner_id TEXT NOT NULL,
|
||||||
recipient_type TEXT NOT NULL CHECK (recipient_type IN ('public_link', 'user', 'group', 'federated')),
|
recipient_type TEXT NOT NULL CHECK (
|
||||||
|
recipient_type IN ('public_link', 'user', 'group', 'federated')
|
||||||
|
),
|
||||||
recipient_user_id TEXT,
|
recipient_user_id TEXT,
|
||||||
recipient_group_id TEXT,
|
recipient_group_id TEXT,
|
||||||
recipient_federated_handle TEXT,
|
recipient_federated_handle TEXT,
|
||||||
|
|
@ -31,13 +34,23 @@ CREATE TABLE shares (
|
||||||
FOREIGN KEY (owner_id) REFERENCES users (id) ON DELETE CASCADE,
|
FOREIGN KEY (owner_id) REFERENCES users (id) ON DELETE CASCADE,
|
||||||
FOREIGN KEY (recipient_user_id) REFERENCES users (id) ON DELETE CASCADE,
|
FOREIGN KEY (recipient_user_id) REFERENCES users (id) ON DELETE CASCADE,
|
||||||
FOREIGN KEY (parent_share_id) REFERENCES shares (id) ON DELETE CASCADE,
|
FOREIGN KEY (parent_share_id) REFERENCES shares (id) ON DELETE CASCADE,
|
||||||
UNIQUE(owner_id, target_type, target_id, recipient_type, recipient_user_id)
|
UNIQUE (
|
||||||
|
owner_id,
|
||||||
|
target_type,
|
||||||
|
target_id,
|
||||||
|
recipient_type,
|
||||||
|
recipient_user_id
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX idx_shares_owner ON shares (owner_id);
|
CREATE INDEX idx_shares_owner ON shares (owner_id);
|
||||||
|
|
||||||
CREATE INDEX idx_shares_recipient_user ON shares (recipient_user_id);
|
CREATE INDEX idx_shares_recipient_user ON shares (recipient_user_id);
|
||||||
|
|
||||||
CREATE INDEX idx_shares_target ON shares (target_type, target_id);
|
CREATE INDEX idx_shares_target ON shares (target_type, target_id);
|
||||||
|
|
||||||
CREATE INDEX idx_shares_token ON shares (public_token);
|
CREATE INDEX idx_shares_token ON shares (public_token);
|
||||||
|
|
||||||
CREATE INDEX idx_shares_expires ON shares (expires_at);
|
CREATE INDEX idx_shares_expires ON shares (expires_at);
|
||||||
|
|
||||||
-- Share activity log
|
-- Share activity log
|
||||||
|
|
@ -54,6 +67,7 @@ CREATE TABLE share_activity (
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX idx_share_activity_share ON share_activity (share_id);
|
CREATE INDEX idx_share_activity_share ON share_activity (share_id);
|
||||||
|
|
||||||
CREATE INDEX idx_share_activity_timestamp ON share_activity (timestamp);
|
CREATE INDEX idx_share_activity_timestamp ON share_activity (timestamp);
|
||||||
|
|
||||||
-- Share notifications
|
-- Share notifications
|
||||||
|
|
@ -69,17 +83,51 @@ CREATE TABLE share_notifications (
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX idx_share_notifications_user ON share_notifications (user_id);
|
CREATE INDEX idx_share_notifications_user ON share_notifications (user_id);
|
||||||
CREATE INDEX idx_share_notifications_unread ON share_notifications(user_id, is_read) WHERE is_read = 0;
|
|
||||||
|
CREATE INDEX idx_share_notifications_unread ON share_notifications (user_id, is_read)
|
||||||
|
WHERE
|
||||||
|
is_read = 0;
|
||||||
|
|
||||||
-- Migrate existing share_links to new shares table (if share_links exists)
|
-- Migrate existing share_links to new shares table (if share_links exists)
|
||||||
INSERT OR IGNORE INTO shares (
|
INSERT
|
||||||
id, target_type, target_id, owner_id, recipient_type,
|
OR IGNORE INTO shares (
|
||||||
public_token, public_password_hash, perm_view, perm_download,
|
id,
|
||||||
access_count, expires_at, created_at, updated_at
|
target_type,
|
||||||
|
target_id,
|
||||||
|
owner_id,
|
||||||
|
recipient_type,
|
||||||
|
public_token,
|
||||||
|
public_password_hash,
|
||||||
|
perm_view,
|
||||||
|
perm_download,
|
||||||
|
access_count,
|
||||||
|
expires_at,
|
||||||
|
created_at,
|
||||||
|
updated_at
|
||||||
)
|
)
|
||||||
SELECT
|
SELECT
|
||||||
id, 'media', media_id, created_by, 'public_link',
|
id,
|
||||||
token, password_hash, 1, 1,
|
'media',
|
||||||
view_count, expires_at, created_at, created_at
|
media_id,
|
||||||
FROM share_links
|
created_by,
|
||||||
WHERE EXISTS (SELECT 1 FROM sqlite_master WHERE type='table' AND name='share_links');
|
'public_link',
|
||||||
|
token,
|
||||||
|
password_hash,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
view_count,
|
||||||
|
expires_at,
|
||||||
|
created_at,
|
||||||
|
created_at
|
||||||
|
FROM
|
||||||
|
share_links
|
||||||
|
WHERE
|
||||||
|
EXISTS (
|
||||||
|
SELECT
|
||||||
|
1
|
||||||
|
FROM
|
||||||
|
sqlite_master
|
||||||
|
WHERE
|
||||||
|
type = 'table'
|
||||||
|
AND name = 'share_links'
|
||||||
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
-- V18: File Management (Rename, Move, Trash)
|
-- V18: File Management (Rename, Move, Trash)
|
||||||
-- Adds soft delete support for trash/recycle bin functionality
|
-- Adds soft delete support for trash/recycle bin functionality
|
||||||
|
|
||||||
-- Add deleted_at column for soft delete (trash)
|
-- Add deleted_at column for soft delete (trash)
|
||||||
ALTER TABLE media_items ADD COLUMN deleted_at TEXT;
|
ALTER TABLE media_items
|
||||||
|
ADD COLUMN deleted_at TEXT;
|
||||||
|
|
||||||
-- Index for efficient trash queries
|
-- Index for efficient trash queries
|
||||||
CREATE INDEX idx_media_deleted_at ON media_items (deleted_at);
|
CREATE INDEX idx_media_deleted_at ON media_items (deleted_at);
|
||||||
|
|
||||||
-- Index for listing non-deleted items (most common query pattern)
|
-- Index for listing non-deleted items (most common query pattern)
|
||||||
CREATE INDEX idx_media_not_deleted ON media_items(id) WHERE deleted_at IS NULL;
|
CREATE INDEX idx_media_not_deleted ON media_items (id)
|
||||||
|
WHERE
|
||||||
|
deleted_at IS NULL;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
-- V19: Markdown Links (Obsidian-style bidirectional links)
|
-- V19: Markdown Links (Obsidian-style bidirectional links)
|
||||||
-- Adds support for wikilinks, markdown links, embeds, and backlink tracking
|
-- Adds support for wikilinks, markdown links, embeds, and backlink tracking
|
||||||
|
|
||||||
-- Table for storing extracted markdown links
|
-- Table for storing extracted markdown links
|
||||||
CREATE TABLE IF NOT EXISTS markdown_links (
|
CREATE TABLE IF NOT EXISTS markdown_links (
|
||||||
id TEXT PRIMARY KEY NOT NULL,
|
id TEXT PRIMARY KEY NOT NULL,
|
||||||
|
|
@ -29,7 +28,8 @@ CREATE INDEX idx_links_target_path ON markdown_links(target_path);
|
||||||
CREATE INDEX idx_links_type ON markdown_links (link_type);
|
CREATE INDEX idx_links_type ON markdown_links (link_type);
|
||||||
|
|
||||||
-- Track when links were last extracted from a media item
|
-- Track when links were last extracted from a media item
|
||||||
ALTER TABLE media_items ADD COLUMN links_extracted_at TEXT;
|
ALTER TABLE media_items
|
||||||
|
ADD COLUMN links_extracted_at TEXT;
|
||||||
|
|
||||||
-- Index for finding media items that need link extraction
|
-- Index for finding media items that need link extraction
|
||||||
CREATE INDEX idx_media_links_extracted ON media_items (links_extracted_at);
|
CREATE INDEX idx_media_links_extracted ON media_items (links_extracted_at);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
CREATE TABLE IF NOT EXISTS root_dirs (
|
CREATE TABLE IF NOT EXISTS root_dirs (path TEXT PRIMARY KEY NOT NULL);
|
||||||
path TEXT PRIMARY KEY NOT NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS media_items (
|
CREATE TABLE IF NOT EXISTS media_items (
|
||||||
id TEXT PRIMARY KEY NOT NULL,
|
id TEXT PRIMARY KEY NOT NULL,
|
||||||
|
|
|
||||||
|
|
@ -9,19 +9,106 @@ CREATE VIRTUAL TABLE IF NOT EXISTS media_fts USING fts5(
|
||||||
content_rowid = 'rowid'
|
content_rowid = 'rowid'
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TRIGGER IF NOT EXISTS media_fts_insert AFTER INSERT ON media_items BEGIN
|
CREATE TRIGGER IF NOT EXISTS media_fts_insert
|
||||||
INSERT INTO media_fts(rowid, title, artist, album, genre, description, file_name)
|
AFTER INSERT ON media_items
|
||||||
VALUES (new.rowid, new.title, new.artist, new.album, new.genre, new.description, new.file_name);
|
BEGIN
|
||||||
|
INSERT INTO
|
||||||
|
media_fts (
|
||||||
|
rowid,
|
||||||
|
title,
|
||||||
|
artist,
|
||||||
|
album,
|
||||||
|
genre,
|
||||||
|
description,
|
||||||
|
file_name
|
||||||
|
)
|
||||||
|
VALUES
|
||||||
|
(
|
||||||
|
new.rowid,
|
||||||
|
new.title,
|
||||||
|
new.artist,
|
||||||
|
new.album,
|
||||||
|
new.genre,
|
||||||
|
new.description,
|
||||||
|
new.file_name
|
||||||
|
);
|
||||||
|
|
||||||
END;
|
END;
|
||||||
|
|
||||||
CREATE TRIGGER IF NOT EXISTS media_fts_update AFTER UPDATE ON media_items BEGIN
|
CREATE TRIGGER IF NOT EXISTS media_fts_update
|
||||||
INSERT INTO media_fts(media_fts, rowid, title, artist, album, genre, description, file_name)
|
AFTER
|
||||||
VALUES ('delete', old.rowid, old.title, old.artist, old.album, old.genre, old.description, old.file_name);
|
UPDATE ON media_items
|
||||||
INSERT INTO media_fts(rowid, title, artist, album, genre, description, file_name)
|
BEGIN
|
||||||
VALUES (new.rowid, new.title, new.artist, new.album, new.genre, new.description, new.file_name);
|
INSERT INTO
|
||||||
|
media_fts (
|
||||||
|
media_fts,
|
||||||
|
rowid,
|
||||||
|
title,
|
||||||
|
artist,
|
||||||
|
album,
|
||||||
|
genre,
|
||||||
|
description,
|
||||||
|
file_name
|
||||||
|
)
|
||||||
|
VALUES
|
||||||
|
(
|
||||||
|
'delete',
|
||||||
|
old.rowid,
|
||||||
|
old.title,
|
||||||
|
old.artist,
|
||||||
|
old.album,
|
||||||
|
old.genre,
|
||||||
|
old.description,
|
||||||
|
old.file_name
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO
|
||||||
|
media_fts (
|
||||||
|
rowid,
|
||||||
|
title,
|
||||||
|
artist,
|
||||||
|
album,
|
||||||
|
genre,
|
||||||
|
description,
|
||||||
|
file_name
|
||||||
|
)
|
||||||
|
VALUES
|
||||||
|
(
|
||||||
|
new.rowid,
|
||||||
|
new.title,
|
||||||
|
new.artist,
|
||||||
|
new.album,
|
||||||
|
new.genre,
|
||||||
|
new.description,
|
||||||
|
new.file_name
|
||||||
|
);
|
||||||
|
|
||||||
END;
|
END;
|
||||||
|
|
||||||
CREATE TRIGGER IF NOT EXISTS media_fts_delete AFTER DELETE ON media_items BEGIN
|
CREATE TRIGGER IF NOT EXISTS media_fts_delete
|
||||||
INSERT INTO media_fts(media_fts, rowid, title, artist, album, genre, description, file_name)
|
AFTER DELETE ON media_items
|
||||||
VALUES ('delete', old.rowid, old.title, old.artist, old.album, old.genre, old.description, old.file_name);
|
BEGIN
|
||||||
|
INSERT INTO
|
||||||
|
media_fts (
|
||||||
|
media_fts,
|
||||||
|
rowid,
|
||||||
|
title,
|
||||||
|
artist,
|
||||||
|
album,
|
||||||
|
genre,
|
||||||
|
description,
|
||||||
|
file_name
|
||||||
|
)
|
||||||
|
VALUES
|
||||||
|
(
|
||||||
|
'delete',
|
||||||
|
old.rowid,
|
||||||
|
old.title,
|
||||||
|
old.artist,
|
||||||
|
old.album,
|
||||||
|
old.genre,
|
||||||
|
old.description,
|
||||||
|
old.file_name
|
||||||
|
);
|
||||||
|
|
||||||
END;
|
END;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,11 @@
|
||||||
CREATE INDEX IF NOT EXISTS idx_audit_media_id ON audit_log (media_id);
|
CREATE INDEX IF NOT EXISTS idx_audit_media_id ON audit_log (media_id);
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_audit_timestamp ON audit_log (timestamp);
|
CREATE INDEX IF NOT EXISTS idx_audit_timestamp ON audit_log (timestamp);
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_audit_action ON audit_log (action);
|
CREATE INDEX IF NOT EXISTS idx_audit_action ON audit_log (action);
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_media_content_hash ON media_items (content_hash);
|
CREATE INDEX IF NOT EXISTS idx_media_content_hash ON media_items (content_hash);
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_media_media_type ON media_items (media_type);
|
CREATE INDEX IF NOT EXISTS idx_media_media_type ON media_items (media_type);
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_media_created_at ON media_items (created_at);
|
CREATE INDEX IF NOT EXISTS idx_media_created_at ON media_items (created_at);
|
||||||
|
|
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
ALTER TABLE media_items ADD COLUMN thumbnail_path TEXT;
|
ALTER TABLE media_items
|
||||||
|
ADD COLUMN thumbnail_path TEXT;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
-- Integrity tracking columns
|
-- Integrity tracking columns
|
||||||
ALTER TABLE media_items ADD COLUMN last_verified_at TEXT;
|
ALTER TABLE media_items
|
||||||
ALTER TABLE media_items ADD COLUMN integrity_status TEXT DEFAULT 'unverified';
|
ADD COLUMN last_verified_at TEXT;
|
||||||
|
|
||||||
|
ALTER TABLE media_items
|
||||||
|
ADD COLUMN integrity_status TEXT DEFAULT 'unverified';
|
||||||
|
|
||||||
-- Saved searches
|
-- Saved searches
|
||||||
CREATE TABLE IF NOT EXISTS saved_searches (
|
CREATE TABLE IF NOT EXISTS saved_searches (
|
||||||
|
|
|
||||||
|
|
@ -12,4 +12,5 @@ CREATE TABLE plugin_registry (
|
||||||
|
|
||||||
-- Index for quick lookups
|
-- Index for quick lookups
|
||||||
CREATE INDEX idx_plugin_registry_enabled ON plugin_registry (enabled);
|
CREATE INDEX idx_plugin_registry_enabled ON plugin_registry (enabled);
|
||||||
|
|
||||||
CREATE INDEX idx_plugin_registry_name ON plugin_registry (name);
|
CREATE INDEX idx_plugin_registry_name ON plugin_registry (name);
|
||||||
|
|
|
||||||
|
|
@ -31,5 +31,7 @@ CREATE TABLE user_libraries (
|
||||||
|
|
||||||
-- Indexes for efficient lookups
|
-- Indexes for efficient lookups
|
||||||
CREATE INDEX idx_users_username ON users (username);
|
CREATE INDEX idx_users_username ON users (username);
|
||||||
|
|
||||||
CREATE INDEX idx_user_libraries_user_id ON user_libraries (user_id);
|
CREATE INDEX idx_user_libraries_user_id ON user_libraries (user_id);
|
||||||
|
|
||||||
CREATE INDEX idx_user_libraries_root_path ON user_libraries (root_path);
|
CREATE INDEX idx_user_libraries_root_path ON user_libraries (root_path);
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,10 @@ CREATE TABLE IF NOT EXISTS ratings (
|
||||||
id TEXT PRIMARY KEY,
|
id TEXT PRIMARY KEY,
|
||||||
user_id TEXT NOT NULL,
|
user_id TEXT NOT NULL,
|
||||||
media_id TEXT NOT NULL,
|
media_id TEXT NOT NULL,
|
||||||
stars INTEGER NOT NULL CHECK (stars >= 1 AND stars <= 5),
|
stars INTEGER NOT NULL CHECK (
|
||||||
|
stars >= 1
|
||||||
|
AND stars <= 5
|
||||||
|
),
|
||||||
review_text TEXT,
|
review_text TEXT,
|
||||||
created_at TEXT NOT NULL DEFAULT (datetime ('now')),
|
created_at TEXT NOT NULL DEFAULT (datetime ('now')),
|
||||||
UNIQUE (user_id, media_id),
|
UNIQUE (user_id, media_id),
|
||||||
|
|
@ -81,7 +84,9 @@ CREATE TABLE IF NOT EXISTS usage_events (
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_usage_events_media ON usage_events (media_id);
|
CREATE INDEX IF NOT EXISTS idx_usage_events_media ON usage_events (media_id);
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_usage_events_user ON usage_events (user_id);
|
CREATE INDEX IF NOT EXISTS idx_usage_events_user ON usage_events (user_id);
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_usage_events_timestamp ON usage_events (timestamp);
|
CREATE INDEX IF NOT EXISTS idx_usage_events_timestamp ON usage_events (timestamp);
|
||||||
|
|
||||||
-- Watch history / progress
|
-- Watch history / progress
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,24 @@
|
||||||
-- Drop redundant indexes (already covered by UNIQUE constraints)
|
-- Drop redundant indexes (already covered by UNIQUE constraints)
|
||||||
DROP INDEX IF EXISTS idx_users_username;
|
DROP INDEX IF EXISTS idx_users_username;
|
||||||
|
|
||||||
DROP INDEX IF EXISTS idx_user_libraries_user_id;
|
DROP INDEX IF EXISTS idx_user_libraries_user_id;
|
||||||
|
|
||||||
-- Add missing indexes for comments table
|
-- Add missing indexes for comments table
|
||||||
CREATE INDEX IF NOT EXISTS idx_comments_media ON comments (media_id);
|
CREATE INDEX IF NOT EXISTS idx_comments_media ON comments (media_id);
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_comments_parent ON comments (parent_comment_id);
|
CREATE INDEX IF NOT EXISTS idx_comments_parent ON comments (parent_comment_id);
|
||||||
|
|
||||||
-- Remove duplicates before adding unique index (keep the first row)
|
-- Remove duplicates before adding unique index (keep the first row)
|
||||||
DELETE FROM external_metadata
|
DELETE FROM external_metadata
|
||||||
WHERE rowid NOT IN (
|
WHERE
|
||||||
SELECT MIN(rowid)
|
rowid NOT IN (
|
||||||
FROM external_metadata
|
SELECT
|
||||||
GROUP BY media_id, source
|
MIN(rowid)
|
||||||
|
FROM
|
||||||
|
external_metadata
|
||||||
|
GROUP BY
|
||||||
|
media_id,
|
||||||
|
source
|
||||||
);
|
);
|
||||||
|
|
||||||
-- Add unique index for external_metadata to prevent duplicates
|
-- Add unique index for external_metadata to prevent duplicates
|
||||||
|
|
|
||||||
|
|
@ -26,21 +26,22 @@ in
|
||||||
name = "pinakes-dev";
|
name = "pinakes-dev";
|
||||||
packages =
|
packages =
|
||||||
[
|
[
|
||||||
# Build tools
|
|
||||||
# We use the rust-overlay to get the stable Rust toolchain for various targets.
|
# We use the rust-overlay to get the stable Rust toolchain for various targets.
|
||||||
# This is not exactly necessary, but it allows for compiling for various targets
|
# This is not exactly necessary, but it allows for compiling for various targets
|
||||||
# with the least amount of friction.
|
# with the least amount of friction. The extensions are to make sure all tooling
|
||||||
|
# uses the same Rust version and the general surrounding tooling.
|
||||||
(rust-bin.nightly.latest.default.override {
|
(rust-bin.nightly.latest.default.override {
|
||||||
extensions = ["rustfmt" "rust-src" "rust-analyzer" "clippy" "rust-analyzer"];
|
extensions = ["rustfmt" "rust-src" "rust-analyzer" "clippy" "rust-analyzer"];
|
||||||
targets = ["wasm32-unknown-unknown" "wasm32-wasip1"]; # web + plugins
|
targets = ["wasm32-unknown-unknown" "wasm32-wasip1"]; # web + plugins
|
||||||
})
|
})
|
||||||
|
|
||||||
# Modern, LLVM based linking pipeline
|
# Modern, LLVM based linking pipeline. Kind of sucks on Windows, though.
|
||||||
llvmPackages.lld
|
llvmPackages.lld
|
||||||
llvmPackages.clang
|
llvmPackages.clang
|
||||||
|
|
||||||
# Handy CLI for packaging Dioxus apps and such
|
# CLI helpers
|
||||||
pkgs.dioxus-cli
|
pkgs.dioxus-cli # for packaging Dioxus apps and such
|
||||||
|
pkgs.just # general command runner for everything
|
||||||
|
|
||||||
# Additional Cargo Tooling
|
# Additional Cargo Tooling
|
||||||
pkgs.cargo-nextest
|
pkgs.cargo-nextest
|
||||||
|
|
@ -49,29 +50,9 @@ in
|
||||||
# Other tools
|
# Other tools
|
||||||
pkgs.taplo # TOML formatter
|
pkgs.taplo # TOML formatter
|
||||||
pkgs.lldb # debugger
|
pkgs.lldb # debugger
|
||||||
pkgs.sccache # distributed Rust cache
|
|
||||||
]
|
]
|
||||||
++ runtimeDeps;
|
++ runtimeDeps;
|
||||||
|
|
||||||
# We could do this in the NixOS configuration via ~/.config/cargo.toml
|
|
||||||
# or something, but this is better because it lets everyone benefit
|
|
||||||
# from sccache while working with Pinakes. The reason those variables
|
|
||||||
# are not in 'env' are that we have to use Bash, which doesn't mix well
|
|
||||||
# with Nix strings.
|
|
||||||
shellHook = ''
|
|
||||||
if [ -n "$SCCACHE_BIN" ]; then
|
|
||||||
export RUSTC_WRAPPER="$SCCACHE_BIN"
|
|
||||||
export SCCACHE_DIR="''${HOME}/.cache/sccache"
|
|
||||||
export SCCACHE_CACHE_SIZE="50G"
|
|
||||||
mkdir -p "$SCCACHE_DIR"
|
|
||||||
|
|
||||||
echo "sccache setup complete!"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Start the daemon early for slightly faster startup.
|
|
||||||
"$SCCACHE_BIN" --start-server >/dev/null 2>&1 || true
|
|
||||||
'';
|
|
||||||
|
|
||||||
env = {
|
env = {
|
||||||
# Allow Cargo to use lld and clang properly
|
# Allow Cargo to use lld and clang properly
|
||||||
LIBCLANG_PATH = "${llvmPackages.libclang.lib}/lib";
|
LIBCLANG_PATH = "${llvmPackages.libclang.lib}/lib";
|
||||||
|
|
@ -84,8 +65,5 @@ in
|
||||||
|
|
||||||
# Runtime library path for GTK/WebKit/xdotool
|
# Runtime library path for GTK/WebKit/xdotool
|
||||||
LD_LIBRARY_PATH = "${lib.makeLibraryPath runtimeDeps}";
|
LD_LIBRARY_PATH = "${lib.makeLibraryPath runtimeDeps}";
|
||||||
|
|
||||||
# Enable sccache for local nix develop shell
|
|
||||||
SCCACHE_BIN = "${pkgs.sccache}/bin/sccache";
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,10 +36,10 @@ utoipa = { workspace = true }
|
||||||
utoipa-axum = { workspace = true }
|
utoipa-axum = { workspace = true }
|
||||||
utoipa-swagger-ui = { workspace = true }
|
utoipa-swagger-ui = { workspace = true }
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
http-body-util = "0.1.3"
|
http-body-util = { workspace = true }
|
||||||
reqwest = { workspace = true }
|
reqwest = { workspace = true }
|
||||||
tempfile = { workspace = true }
|
tempfile = { workspace = true }
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue