From 50f9091e0589129664a5a3dadf71546dfdf5fb0a Mon Sep 17 00:00:00 2001 From: Lennart Kloock Date: Tue, 7 Oct 2025 22:58:33 +0200 Subject: [PATCH 1/4] feat(video-api): scaffolding --- Cargo.lock | 22 +++++++ Cargo.toml | 3 + cargo_targets.bzl | 3 + cloud/video/api/BUILD.bazel | 35 +++++++++++ cloud/video/api/Cargo.toml | 37 ++++++++++++ cloud/video/api/LICENSE.AGPL-3.0 | 1 + cloud/video/api/README.md | 21 +++++++ cloud/video/api/bin/standalone/config.rs | 19 ++++++ cloud/video/api/bin/standalone/main.rs | 73 +++++++++++++++++++++++ cloud/video/api/src/lib.rs | 17 ++++++ cloud/video/api/src/services.rs | 20 +++++++ cloud/video/api/traits/BUILD.bazel | 8 +++ cloud/video/api/traits/Cargo.toml | 20 +++++++ cloud/video/api/traits/README.md | 13 ++++ cloud/video/api/traits/src/lib.rs | 8 +++ vendor/cargo/defs.bzl | 76 ++++++++++++++++++++++++ 16 files changed, 376 insertions(+) create mode 100644 cloud/video/api/BUILD.bazel create mode 100644 cloud/video/api/Cargo.toml create mode 120000 cloud/video/api/LICENSE.AGPL-3.0 create mode 100644 cloud/video/api/README.md create mode 100644 cloud/video/api/bin/standalone/config.rs create mode 100644 cloud/video/api/bin/standalone/main.rs create mode 100644 cloud/video/api/src/lib.rs create mode 100644 cloud/video/api/src/services.rs create mode 100644 cloud/video/api/traits/BUILD.bazel create mode 100644 cloud/video/api/traits/Cargo.toml create mode 100644 cloud/video/api/traits/README.md create mode 100644 cloud/video/api/traits/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 899117fd5..5d03d1b17 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6124,6 +6124,28 @@ dependencies = [ "tinc-build", ] +[[package]] +name = "scufflecloud-video-api" +version = "0.1.0" +dependencies = [ + "anyhow", + "scuffle-bootstrap", + "scuffle-bootstrap-telemetry", + "scuffle-context", + "scuffle-settings", + "scuffle-signal", + "scufflecloud-video-api-traits", + "serde", + "serde_derive", + "smart-default", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "scufflecloud-video-api-traits" +version = "0.1.0" + [[package]] name = "security-framework" version = "3.4.0" diff --git a/Cargo.toml b/Cargo.toml index 373666028..51469acfa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,10 @@ members = [ "cloud/geo-ip", "cloud/id", "cloud/proto", + "cloud/video/api", + "cloud/video/api/traits", "cloud/video/ingest", + "cloud/video/ingest/traits", "crates/aac", "crates/amf0", "crates/av1", diff --git a/cargo_targets.bzl b/cargo_targets.bzl index 5f7b8d3c5..eee1464fb 100644 --- a/cargo_targets.bzl +++ b/cargo_targets.bzl @@ -9,6 +9,9 @@ _packages = [ "//cloud/email/traits", "//cloud/geo-ip", "//cloud/video/ingest", + "//cloud/video/ingest/traits", + "//cloud/video/api", + "//cloud/video/api/traits", "//cloud/id", "//cloud/proto", "//crates/aac", diff --git a/cloud/video/api/BUILD.bazel b/cloud/video/api/BUILD.bazel new file mode 100644 index 000000000..428824643 --- /dev/null +++ b/cloud/video/api/BUILD.bazel @@ -0,0 +1,35 @@ +load("//misc/utils/rust:manifest.bzl", "cargo_toml") +load("//misc/utils/rust:package.bzl", "scuffle_package") + +cargo_toml() + +deps = [ + "//crates/bootstrap", + "//crates/bootstrap-telemetry", + "//crates/context", + "//crates/settings", + "//crates/signal", + "//cloud/video/api/traits", +] + +aliases = { + "//cloud/video/api/traits": "video_api_traits", +} + +scuffle_package( + aliases = aliases, + crate_name = "scufflecloud-video-api", + deps = deps, +) + +scuffle_package( + name = "bin", + srcs = glob([ + "bin/standalone/**/*.rs", + ]), + aliases = aliases, + crate_name = "scufflecloud-video-api-standalone", + crate_type = "bin", + readme = False, + deps = deps + [":api"], +) diff --git a/cloud/video/api/Cargo.toml b/cloud/video/api/Cargo.toml new file mode 100644 index 000000000..91fb0c986 --- /dev/null +++ b/cloud/video/api/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "scufflecloud-video-api" +version = "0.1.0" +authors = ["Scuffle "] +edition = "2024" +license = "AGPL-3.0" +publish = false +readme = "README.md" +repository = "https://github.com/scufflecloud/scuffle" +description = "Video API server for scuffle.cloud" + +[[bin]] +name = "scufflecloud-video-api-standalone" +path = "bin/standalone/main.rs" + +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(coverage_nightly)'] } + +[dependencies] +anyhow = "1.0.98" +scuffle-bootstrap = { path = "../../../crates/bootstrap" } +scuffle-bootstrap-telemetry = { features = ["opentelemetry-logs", "opentelemetry-traces"], path = "../../../crates/bootstrap-telemetry" } +scuffle-context = { path = "../../../crates/context" } +scuffle-settings = { features = ["all-formats", "bootstrap"], path = "../../../crates/settings" } +scuffle-signal = { features = ["bootstrap"], path = "../../../crates/signal" } +serde = "1.0.219" +serde_derive = "1.0.219" +smart-default = "0.7.1" +tracing = "0.1.41" +tracing-subscriber = { version = "0.3.20", features = ["env-filter"] } +video-api-traits = { path = "./traits", package = "scufflecloud-video-api-traits" } + +[package.metadata.sync-readme.badges] +docs-rs = false +crates-io = false +license = true +codecov = true diff --git a/cloud/video/api/LICENSE.AGPL-3.0 b/cloud/video/api/LICENSE.AGPL-3.0 new file mode 120000 index 000000000..b38f9cd65 --- /dev/null +++ b/cloud/video/api/LICENSE.AGPL-3.0 @@ -0,0 +1 @@ +../../../LICENSE.AGPL-3.0 \ No newline at end of file diff --git a/cloud/video/api/README.md b/cloud/video/api/README.md new file mode 100644 index 000000000..d25c6ab56 --- /dev/null +++ b/cloud/video/api/README.md @@ -0,0 +1,21 @@ + + +# scufflecloud-ingest + + + +![License: AGPL-3.0](https://img.shields.io/badge/license-AGPL--3.0-purple.svg?style=flat-square) +[![Codecov](https://img.shields.io/codecov/c/github/scufflecloud/scuffle.svg?label=codecov&logo=codecov&style=flat-square)](https://app.codecov.io/gh/scufflecloud/scuffle) + + +--- + + +Video ingest server for . + +### License + +This project is licensed under the [AGPL-3.0](./LICENSE.AGPL-3.0). + +`SPDX-License-Identifier: AGPL-3.0` + diff --git a/cloud/video/api/bin/standalone/config.rs b/cloud/video/api/bin/standalone/config.rs new file mode 100644 index 000000000..ed6f7d8a5 --- /dev/null +++ b/cloud/video/api/bin/standalone/config.rs @@ -0,0 +1,19 @@ +use std::net::SocketAddr; + +#[derive(serde_derive::Deserialize, smart_default::SmartDefault, Debug, Clone)] +#[serde(default)] +pub(crate) struct Config { + #[default(env!("CARGO_PKG_NAME").to_string())] + pub service_name: String, + #[default = "info"] + pub level: String, + pub telemetry: Option, +} + +scuffle_settings::bootstrap!(Config); + +#[derive(serde_derive::Deserialize, smart_default::SmartDefault, Debug, Clone)] +pub(crate) struct TelemetryConfig { + #[default("[::1]:4317".parse().unwrap())] + pub bind: SocketAddr, +} diff --git a/cloud/video/api/bin/standalone/main.rs b/cloud/video/api/bin/standalone/main.rs new file mode 100644 index 000000000..6efeacfe2 --- /dev/null +++ b/cloud/video/api/bin/standalone/main.rs @@ -0,0 +1,73 @@ +#![cfg_attr(coverage_nightly, feature(coverage_attribute))] +#![cfg_attr(docsrs, feature(doc_auto_cfg))] +// #![deny(missing_docs)] +#![deny(unsafe_code)] +#![deny(unreachable_pub)] +#![deny(clippy::mod_module_files)] + +use std::sync::Arc; + +use scuffle_bootstrap_telemetry::opentelemetry; +use scuffle_bootstrap_telemetry::opentelemetry_sdk::logs::SdkLoggerProvider; +use scuffle_bootstrap_telemetry::opentelemetry_sdk::trace::SdkTracerProvider; +use tracing_subscriber::Layer; +use tracing_subscriber::layer::SubscriberExt; +use tracing_subscriber::util::SubscriberInitExt; + +mod config; + +struct Global { + config: config::Config, + open_telemetry: opentelemetry::OpenTelemetry, +} + +impl ingest_traits::Global for Global {} + +impl scuffle_signal::SignalConfig for Global {} + +impl scuffle_bootstrap_telemetry::TelemetryConfig for Global { + fn enabled(&self) -> bool { + self.config.telemetry.is_some() + } + + fn bind_address(&self) -> Option { + self.config.telemetry.as_ref().map(|telemetry| telemetry.bind) + } + + fn http_server_name(&self) -> &str { + "scufflecloud-ingest-telemetry" + } + + fn opentelemetry(&self) -> Option<&opentelemetry::OpenTelemetry> { + Some(&self.open_telemetry) + } +} + +impl scuffle_bootstrap::Global for Global { + type Config = config::Config; + + async fn init(config: Self::Config) -> anyhow::Result> { + tracing_subscriber::registry() + .with( + tracing_subscriber::fmt::layer() + .with_filter(tracing_subscriber::EnvFilter::from_default_env().add_directive(config.level.parse()?)), + ) + .init(); + + let tracer = SdkTracerProvider::default(); + opentelemetry::global::set_tracer_provider(tracer.clone()); + + let logger = SdkLoggerProvider::builder().build(); + + let open_telemetry = opentelemetry::OpenTelemetry::new().with_traces(tracer).with_logs(logger); + + Ok(Arc::new(Self { config, open_telemetry })) + } +} + +scuffle_bootstrap::main! { + Global { + scuffle_signal::SignalSvc, + scufflecloud_ingest::services::IngestSvc::::default(), + } +} diff --git a/cloud/video/api/src/lib.rs b/cloud/video/api/src/lib.rs new file mode 100644 index 000000000..6040b82fa --- /dev/null +++ b/cloud/video/api/src/lib.rs @@ -0,0 +1,17 @@ +//! Video API server for . +//! +//! ## License +//! +//! This project is licensed under the [AGPL-3.0](./LICENSE.AGPL-3.0). +//! +//! `SPDX-License-Identifier: AGPL-3.0` +#![cfg_attr(coverage_nightly, feature(coverage_attribute))] +#![cfg_attr(docsrs, feature(doc_auto_cfg))] +// #![deny(missing_docs)] +#![deny(unsafe_code)] +#![deny(unreachable_pub)] +#![deny(clippy::mod_module_files)] +// tonic::Status emits this warning +#![allow(clippy::result_large_err)] + +pub mod services; diff --git a/cloud/video/api/src/services.rs b/cloud/video/api/src/services.rs new file mode 100644 index 000000000..a04e1004c --- /dev/null +++ b/cloud/video/api/src/services.rs @@ -0,0 +1,20 @@ +use std::sync::Arc; + +#[derive(Debug)] +pub struct VideoApiSvc { + _phantom: std::marker::PhantomData, +} + +impl Default for VideoApiSvc { + fn default() -> Self { + Self { + _phantom: std::marker::PhantomData, + } + } +} + +impl scuffle_bootstrap::Service for VideoApiSvc { + async fn run(self, _global: Arc, _ctx: scuffle_context::Context) -> anyhow::Result<()> { + Ok(()) + } +} diff --git a/cloud/video/api/traits/BUILD.bazel b/cloud/video/api/traits/BUILD.bazel new file mode 100644 index 000000000..1a274a37a --- /dev/null +++ b/cloud/video/api/traits/BUILD.bazel @@ -0,0 +1,8 @@ +load("//misc/utils/rust:manifest.bzl", "cargo_toml") +load("//misc/utils/rust:package.bzl", "scuffle_package") + +cargo_toml() + +scuffle_package( + crate_name = "scufflecloud-video-api-traits", +) diff --git a/cloud/video/api/traits/Cargo.toml b/cloud/video/api/traits/Cargo.toml new file mode 100644 index 000000000..bff38fc07 --- /dev/null +++ b/cloud/video/api/traits/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "scufflecloud-video-api-traits" +version = "0.1.0" +authors = ["Scuffle "] +edition = "2024" +license = "AGPL-3.0" +publish = false +readme = "README.md" +repository = "https://github.com/scufflecloud/scuffle" + +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(coverage_nightly)'] } + +[dependencies] + +[package.metadata.sync-readme.badges] +docs-rs = false +crates-io = false +license = true +codecov = true diff --git a/cloud/video/api/traits/README.md b/cloud/video/api/traits/README.md new file mode 100644 index 000000000..2619a6191 --- /dev/null +++ b/cloud/video/api/traits/README.md @@ -0,0 +1,13 @@ + + +# scufflecloud-ingest-traits + + + +![License: AGPL-3.0](https://img.shields.io/badge/license-AGPL--3.0-purple.svg?style=flat-square) +[![Codecov](https://img.shields.io/codecov/c/github/scufflecloud/scuffle.svg?label=codecov&logo=codecov&style=flat-square)](https://app.codecov.io/gh/scufflecloud/scuffle) + + +--- + + diff --git a/cloud/video/api/traits/src/lib.rs b/cloud/video/api/traits/src/lib.rs new file mode 100644 index 000000000..25a92cc66 --- /dev/null +++ b/cloud/video/api/traits/src/lib.rs @@ -0,0 +1,8 @@ +#![cfg_attr(coverage_nightly, feature(coverage_attribute))] +#![cfg_attr(docsrs, feature(doc_auto_cfg))] +// #![deny(missing_docs)] +#![deny(unsafe_code)] +#![deny(unreachable_pub)] +#![deny(clippy::mod_module_files)] + +pub trait Global: Send + Sync + 'static {} diff --git a/vendor/cargo/defs.bzl b/vendor/cargo/defs.bzl index c43cba9bf..42e7df0d7 100644 --- a/vendor/cargo/defs.bzl +++ b/vendor/cargo/defs.bzl @@ -621,6 +621,18 @@ _NORMAL_DEPENDENCIES = { }, }, }, + "cloud/video/api": { + _REQUIRED_FEATURE: { + _COMMON_CONDITION: { + "anyhow": Label("@cargo_vendor//:anyhow-1.0.99"), + "serde": Label("@cargo_vendor//:serde-1.0.220"), + "tracing": Label("@cargo_vendor//:tracing-0.1.41"), + "tracing-subscriber": Label("@cargo_vendor//:tracing-subscriber-0.3.20"), + }, + }, + }, + "cloud/video/api/traits": { + }, "cloud/video/ingest": { _REQUIRED_FEATURE: { _COMMON_CONDITION: { @@ -1482,6 +1494,14 @@ _NORMAL_ALIASES = { }, }, }, + "cloud/video/api": { + _REQUIRED_FEATURE: { + _COMMON_CONDITION: { + }, + }, + }, + "cloud/video/api/traits": { + }, "cloud/video/ingest": { _REQUIRED_FEATURE: { _COMMON_CONDITION: { @@ -1955,6 +1975,10 @@ _NORMAL_DEV_DEPENDENCIES = { }, "cloud/proto": { }, + "cloud/video/api": { + }, + "cloud/video/api/traits": { + }, "cloud/video/ingest": { }, "cloud/video/ingest/traits": { @@ -2256,6 +2280,10 @@ _NORMAL_DEV_ALIASES = { }, "cloud/proto": { }, + "cloud/video/api": { + }, + "cloud/video/api/traits": { + }, "cloud/video/ingest": { }, "cloud/video/ingest/traits": { @@ -2521,6 +2549,16 @@ _PROC_MACRO_DEPENDENCIES = { }, "cloud/proto": { }, + "cloud/video/api": { + _REQUIRED_FEATURE: { + _COMMON_CONDITION: { + "serde_derive": Label("@cargo_vendor//:serde_derive-1.0.220"), + "smart-default": Label("@cargo_vendor//:smart-default-0.7.1"), + }, + }, + }, + "cloud/video/api/traits": { + }, "cloud/video/ingest": { _REQUIRED_FEATURE: { _COMMON_CONDITION: { @@ -2933,6 +2971,10 @@ _PROC_MACRO_ALIASES = { }, "cloud/proto": { }, + "cloud/video/api": { + }, + "cloud/video/api/traits": { + }, "cloud/video/ingest": { }, "cloud/video/ingest/traits": { @@ -3086,6 +3128,10 @@ _PROC_MACRO_DEV_DEPENDENCIES = { }, "cloud/proto": { }, + "cloud/video/api": { + }, + "cloud/video/api/traits": { + }, "cloud/video/ingest": { }, "cloud/video/ingest/traits": { @@ -3244,6 +3290,10 @@ _PROC_MACRO_DEV_ALIASES = { }, "cloud/proto": { }, + "cloud/video/api": { + }, + "cloud/video/api/traits": { + }, "cloud/video/ingest": { }, "cloud/video/ingest/traits": { @@ -3484,6 +3534,10 @@ _BUILD_DEPENDENCIES = { }, }, }, + "cloud/video/api": { + }, + "cloud/video/api/traits": { + }, "cloud/video/ingest": { }, "cloud/video/ingest/traits": { @@ -3641,6 +3695,10 @@ _BUILD_ALIASES = { }, }, }, + "cloud/video/api": { + }, + "cloud/video/api/traits": { + }, "cloud/video/ingest": { }, "cloud/video/ingest/traits": { @@ -3790,6 +3848,10 @@ _BUILD_PROC_MACRO_DEPENDENCIES = { }, "cloud/proto": { }, + "cloud/video/api": { + }, + "cloud/video/api/traits": { + }, "cloud/video/ingest": { }, "cloud/video/ingest/traits": { @@ -3931,6 +3993,10 @@ _BUILD_PROC_MACRO_ALIASES = { }, "cloud/proto": { }, + "cloud/video/api": { + }, + "cloud/video/api/traits": { + }, "cloud/video/ingest": { }, "cloud/video/ingest/traits": { @@ -4072,6 +4138,10 @@ _FEATURE_FLAGS = { }, "cloud/proto": { }, + "cloud/video/api": { + }, + "cloud/video/api/traits": { + }, "cloud/video/ingest": { }, "cloud/video/ingest/traits": { @@ -4442,6 +4512,10 @@ _RESOLVED_FEATURE_FLAGS = { }, "cloud/proto": { }, + "cloud/video/api": { + }, + "cloud/video/api/traits": { + }, "cloud/video/ingest": { }, "cloud/video/ingest/traits": { @@ -4625,6 +4699,8 @@ _VERSIONS = { "cloud/geo-ip": "0.1.0", "cloud/id": "0.1.0", "cloud/proto": "0.1.0", + "cloud/video/api": "0.1.0", + "cloud/video/api/traits": "0.1.0", "cloud/video/ingest": "0.1.0", "cloud/video/ingest/traits": "0.1.0", "crates/aac": "0.1.4", From 76fc811bf75c67c72754fdabcc03e78f3ba7c850 Mon Sep 17 00:00:00 2001 From: Lennart Kloock Date: Tue, 7 Oct 2025 23:00:35 +0200 Subject: [PATCH 2/4] chore: sync readme --- cloud/video/api/README.md | 4 ++-- cloud/video/api/traits/README.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cloud/video/api/README.md b/cloud/video/api/README.md index d25c6ab56..f5a751297 100644 --- a/cloud/video/api/README.md +++ b/cloud/video/api/README.md @@ -1,6 +1,6 @@ -# scufflecloud-ingest +# scufflecloud-video-api @@ -11,7 +11,7 @@ --- -Video ingest server for . +Video API server for . ### License diff --git a/cloud/video/api/traits/README.md b/cloud/video/api/traits/README.md index 2619a6191..a56b42b64 100644 --- a/cloud/video/api/traits/README.md +++ b/cloud/video/api/traits/README.md @@ -1,6 +1,6 @@ -# scufflecloud-ingest-traits +# scufflecloud-video-api-traits From 132bb4e21626187d5bda438c70a922ce6ced2819 Mon Sep 17 00:00:00 2001 From: Lennart Kloock Date: Tue, 7 Oct 2025 23:14:47 +0200 Subject: [PATCH 3/4] fix(video-api): binary --- cloud/video/api/bin/standalone/main.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cloud/video/api/bin/standalone/main.rs b/cloud/video/api/bin/standalone/main.rs index 6efeacfe2..acb0e9464 100644 --- a/cloud/video/api/bin/standalone/main.rs +++ b/cloud/video/api/bin/standalone/main.rs @@ -21,7 +21,7 @@ struct Global { open_telemetry: opentelemetry::OpenTelemetry, } -impl ingest_traits::Global for Global {} +impl video_api_traits::Global for Global {} impl scuffle_signal::SignalConfig for Global {} @@ -35,7 +35,7 @@ impl scuffle_bootstrap_telemetry::TelemetryConfig for Global { } fn http_server_name(&self) -> &str { - "scufflecloud-ingest-telemetry" + "scufflecloud-video-api-telemetry" } fn opentelemetry(&self) -> Option<&opentelemetry::OpenTelemetry> { @@ -68,6 +68,6 @@ impl scuffle_bootstrap::Global for Global { scuffle_bootstrap::main! { Global { scuffle_signal::SignalSvc, - scufflecloud_ingest::services::IngestSvc::::default(), + scufflecloud_video_api::services::VideoApiSvc::::default(), } } From c9f5579dbf5b2b34835be286335e95fd384822e7 Mon Sep 17 00:00:00 2001 From: Lennart Kloock Date: Tue, 7 Oct 2025 23:15:00 +0200 Subject: [PATCH 4/4] feat: add video-api to Justfile --- Justfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Justfile b/Justfile index db7faccb5..62a95f3cd 100644 --- a/Justfile +++ b/Justfile @@ -45,6 +45,8 @@ run bin *args: bazel run //cloud/email:bin -- {{ args }} elif [ {{ bin }} == "ingest" ]; then bazel run //cloud/video/ingest:bin -- {{ args }} + elif [ {{ bin }} == "video-api" ]; then + bazel run //cloud/video/api:bin -- {{ args }} else echo "Unknown binary: {{ bin }}" exit 1