Start from scratch
This commit is contained in:
@@ -1 +1,8 @@
|
||||
use flake
|
||||
#!/usr/bin/env bash
|
||||
export GIT_CONFIG_GLOBAL=/dev/null
|
||||
|
||||
eval "$(devenv direnvrc)"
|
||||
|
||||
# You can pass flags to the devenv command
|
||||
# For example: use devenv --impure --option services.postgres.enable:bool true
|
||||
use devenv
|
||||
|
||||
+16
@@ -31,3 +31,19 @@ config.yaml
|
||||
# Protobuf generated Go code — committed for reproducible builds
|
||||
# Regenerate with: buf generate
|
||||
# gen/
|
||||
|
||||
# Devenv
|
||||
.devenv*
|
||||
devenv.local.nix
|
||||
devenv.local.yaml
|
||||
|
||||
# direnv
|
||||
.direnv
|
||||
|
||||
# pre-commit
|
||||
.pre-commit-config.yaml
|
||||
|
||||
|
||||
# Added by cargo
|
||||
|
||||
/target
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
[submodule "beetfs"]
|
||||
path = beetfs
|
||||
url = git@github.com:LichHunter/beetfs.git
|
||||
@@ -1 +1 @@
|
||||
/nix/store/mchzk3cbvp456fd3nbajm120nrry3pls-pre-commit-config.json
|
||||
/nix/store/gd69vhx5mbadafs4bwhh66b5mnp3wvyg-pre-commit-config.json
|
||||
Generated
+982
@@ -0,0 +1,982 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "adler2"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.103"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3"
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atomic-waker"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
|
||||
|
||||
[[package]]
|
||||
name = "axum"
|
||||
version = "0.8.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90"
|
||||
dependencies = [
|
||||
"axum-core",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"itoa",
|
||||
"matchit",
|
||||
"memchr",
|
||||
"mime",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"serde_core",
|
||||
"sync_wrapper",
|
||||
"tower",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "axum-core"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"mime",
|
||||
"pin-project-lite",
|
||||
"sync_wrapper",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.22.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6"
|
||||
|
||||
[[package]]
|
||||
name = "fixedbitset"
|
||||
version = "0.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
"pin-project-lite",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.4.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155"
|
||||
dependencies = [
|
||||
"atomic-waker",
|
||||
"bytes",
|
||||
"fnv",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"http",
|
||||
"indexmap",
|
||||
"slab",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
||||
dependencies = [
|
||||
"foldhash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"itoa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-body"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"http",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-body-util"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"http",
|
||||
"http-body",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "httparse"
|
||||
version = "1.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
|
||||
|
||||
[[package]]
|
||||
name = "httpdate"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "1.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498"
|
||||
dependencies = [
|
||||
"atomic-waker",
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"h2",
|
||||
"http",
|
||||
"http-body",
|
||||
"httparse",
|
||||
"httpdate",
|
||||
"itoa",
|
||||
"pin-project-lite",
|
||||
"smallvec",
|
||||
"tokio",
|
||||
"want",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-timeout"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0"
|
||||
dependencies = [
|
||||
"hyper",
|
||||
"hyper-util",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-util"
|
||||
version = "0.1.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
"hyper",
|
||||
"libc",
|
||||
"pin-project-lite",
|
||||
"socket2",
|
||||
"tokio",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.17.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.186"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
|
||||
|
||||
[[package]]
|
||||
name = "matchit"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4"
|
||||
|
||||
[[package]]
|
||||
name = "mime"
|
||||
version = "0.3.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "multimap"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084"
|
||||
|
||||
[[package]]
|
||||
name = "music-agregator"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"pbjson",
|
||||
"pbjson-types",
|
||||
"prost",
|
||||
"serde",
|
||||
"tokio",
|
||||
"tonic",
|
||||
"tonic-prost",
|
||||
"tonic-reflection",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
||||
|
||||
[[package]]
|
||||
name = "pbjson"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "898bac3fa00d0ba57a4e8289837e965baa2dee8c3749f3b11d45a64b4223d9c3"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pbjson-build"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af22d08a625a2213a78dbb0ffa253318c5c79ce3133d32d296655a7bdfb02095"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"itertools",
|
||||
"prost",
|
||||
"prost-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pbjson-types"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e748e28374f10a330ee3bb9f29b828c0ac79831a32bab65015ad9b661ead526"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"chrono",
|
||||
"pbjson",
|
||||
"pbjson-build",
|
||||
"prost",
|
||||
"prost-build",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
|
||||
|
||||
[[package]]
|
||||
name = "petgraph"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455"
|
||||
dependencies = [
|
||||
"fixedbitset",
|
||||
"hashbrown 0.15.5",
|
||||
"indexmap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "1.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924"
|
||||
dependencies = [
|
||||
"pin-project-internal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-internal"
|
||||
version = "1.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prost"
|
||||
version = "0.14.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "528ac67416ff8646872a3c02cad9cc4ee5dc9f9540c9b10771855c95cb2e5ae1"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"prost-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prost-build"
|
||||
version = "0.14.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03da047801ff44bb6a4d407d4860c05fd70bb81714e6b2f3812603d5b145b042"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"itertools",
|
||||
"log",
|
||||
"multimap",
|
||||
"petgraph",
|
||||
"prettyplease",
|
||||
"prost",
|
||||
"prost-types",
|
||||
"regex",
|
||||
"syn",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prost-derive"
|
||||
version = "0.14.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b570b25f7617e43d59005d0990ccb79e950a423952cea19671b7a876da390adf"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prost-types"
|
||||
version = "0.14.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f94967dc7688f3054c7fac87473ffae4cc4c3904800e2d9f5b857246d8963b0a"
|
||||
dependencies = [
|
||||
"prost",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.46"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.12.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simd-adler32"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.15.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.118"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sync_wrapper"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.27.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.52.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"libc",
|
||||
"mio",
|
||||
"pin-project-lite",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "2.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-stream"
|
||||
version = "0.1.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tonic"
|
||||
version = "0.14.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac2a5518c70fa84342385732db33fb3f44bc4cc748936eb5833d2df34d6445ef"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum",
|
||||
"base64",
|
||||
"bytes",
|
||||
"flate2",
|
||||
"h2",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"hyper",
|
||||
"hyper-timeout",
|
||||
"hyper-util",
|
||||
"percent-encoding",
|
||||
"pin-project",
|
||||
"socket2",
|
||||
"sync_wrapper",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tower",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tonic-prost"
|
||||
version = "0.14.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50849f68853be452acf590cde0b146665b8d507b3b8af17261df47e02c209ea0"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"prost",
|
||||
"tonic",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tonic-reflection"
|
||||
version = "0.14.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acccd136a4bf19810a1fde9c74edc6129b42a66b44d0c1c8aaa67aeb49a146a7"
|
||||
dependencies = [
|
||||
"prost",
|
||||
"prost-types",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tonic",
|
||||
"tonic-prost",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"indexmap",
|
||||
"pin-project-lite",
|
||||
"slab",
|
||||
"sync_wrapper",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-layer"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
|
||||
|
||||
[[package]]
|
||||
name = "tower-service"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
|
||||
dependencies = [
|
||||
"pin-project-lite",
|
||||
"tracing-attributes",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-attributes"
|
||||
version = "0.1.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "try-lock"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||
|
||||
[[package]]
|
||||
name = "want"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
|
||||
dependencies = [
|
||||
"try-lock",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.1+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.61.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
[package]
|
||||
name = "music-agregator"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[features]
|
||||
default = ["proto_full", "serde"]
|
||||
serde = ["dep:serde"]
|
||||
# @@protoc_deletion_point(features)
|
||||
# This section is automatically generated by protoc-gen-prost-crate.
|
||||
# Changes in this area may be lost on regeneration.
|
||||
proto_full = ["musicfs"]
|
||||
"musicfs" = []
|
||||
# @@protoc_insertion_point(features)
|
||||
|
||||
[dependencies]
|
||||
bytes = "1.11.1"
|
||||
prost = "0.14.1"
|
||||
pbjson = "0.8.0"
|
||||
pbjson-types = "0.8.0"
|
||||
serde = { version = "1.0.228", optional = true }
|
||||
tonic = { version = "0.14.2", features = ["gzip"] }
|
||||
tonic-prost = "0.14.2"
|
||||
tokio = { version = "1.52.3", features = [ "macros", "rt-multi-thread" ] }
|
||||
tonic-reflection = "0.14.6"
|
||||
|
||||
[workspace]
|
||||
@@ -1,22 +0,0 @@
|
||||
meta {
|
||||
name: Analyze Album
|
||||
type: grpc
|
||||
seq: 10
|
||||
}
|
||||
|
||||
grpc {
|
||||
url: localhost:3000
|
||||
method: /music_agregator.v1.MusicAgregatorService/AnalyzeAlbumRelease
|
||||
body: grpc
|
||||
auth: inherit
|
||||
methodType: unary
|
||||
}
|
||||
|
||||
body:grpc {
|
||||
name: message 1
|
||||
content: '''
|
||||
{
|
||||
"album_id": "1d51e4a7-1a8b-4160-bd08-1aee658a991a"
|
||||
}
|
||||
'''
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
meta {
|
||||
name: Get Album
|
||||
type: grpc
|
||||
seq: 9
|
||||
}
|
||||
|
||||
grpc {
|
||||
url: localhost:3000
|
||||
method: /music_agregator.v1.MusicAgregatorService/GetAlbum
|
||||
body: grpc
|
||||
auth: inherit
|
||||
methodType: unary
|
||||
}
|
||||
|
||||
body:grpc {
|
||||
name: message 1
|
||||
content: '''
|
||||
{
|
||||
"album_id": "1d51e4a7-1a8b-4160-bd08-1aee658a991a"
|
||||
}
|
||||
'''
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
meta {
|
||||
name: Get Artist Albums
|
||||
type: grpc
|
||||
seq: 11
|
||||
}
|
||||
|
||||
grpc {
|
||||
url: localhost:3000
|
||||
method: /music_agregator.v1.MusicAgregatorService/GetArtistAlbums
|
||||
body: grpc
|
||||
auth: inherit
|
||||
methodType: unary
|
||||
}
|
||||
|
||||
body:grpc {
|
||||
name: message 1
|
||||
content: '''
|
||||
{
|
||||
"artist_id": "3b497c6b-e676-4242-bcad-0a03125faaaa"
|
||||
}
|
||||
'''
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
meta {
|
||||
name: Get Artists
|
||||
type: grpc
|
||||
seq: 7
|
||||
}
|
||||
|
||||
grpc {
|
||||
url: localhost:3000
|
||||
method: /music_agregator.v1.MusicAgregatorService/GetArtists
|
||||
body: grpc
|
||||
auth: inherit
|
||||
methodType: unary
|
||||
}
|
||||
|
||||
body:grpc {
|
||||
name: message 1
|
||||
content: '''
|
||||
{}
|
||||
'''
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
meta {
|
||||
name: Hello Echo
|
||||
type: grpc
|
||||
seq: 3
|
||||
}
|
||||
|
||||
grpc {
|
||||
url: localhost:3000
|
||||
method: /music_agregator.hello.v1.HelloService/Echo
|
||||
body: grpc
|
||||
protoPath: ../proto/music_agregator/hello/v1/service.proto
|
||||
auth: none
|
||||
methodType: unary
|
||||
}
|
||||
|
||||
body:grpc {
|
||||
name: message 1
|
||||
content: '''
|
||||
{
|
||||
"msg": "hello"
|
||||
}
|
||||
'''
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
meta {
|
||||
name: Hello Ping
|
||||
type: grpc
|
||||
seq: 1
|
||||
}
|
||||
|
||||
grpc {
|
||||
url: localhost:8081
|
||||
method: /music_agregator.hello.v1.HelloService/Ping
|
||||
body: grpc
|
||||
protoPath: ../proto/music_agregator/hello/v1/service.proto
|
||||
auth: none
|
||||
methodType: unary
|
||||
}
|
||||
|
||||
body:grpc {
|
||||
name: message 1
|
||||
content: '''
|
||||
{}
|
||||
'''
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
meta {
|
||||
name: Capabilities
|
||||
type: grpc
|
||||
seq: 1
|
||||
}
|
||||
|
||||
grpc {
|
||||
url: localhost:3000
|
||||
method: /music_agregator.indexer.v1.IndexerService/Capabilities
|
||||
body: grpc
|
||||
protoPath: ../proto/music_agregator/indexer/v1/indexer.proto
|
||||
auth: inherit
|
||||
methodType: unary
|
||||
}
|
||||
|
||||
body:grpc {
|
||||
name: message 1
|
||||
content: '''
|
||||
{
|
||||
"indexer": "rutracker"
|
||||
|
||||
}
|
||||
'''
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
meta {
|
||||
name: Search
|
||||
type: grpc
|
||||
seq: 2
|
||||
}
|
||||
|
||||
grpc {
|
||||
url: localhost:3000
|
||||
method: /music_agregator.indexer.v1.IndexerService/Search
|
||||
body: grpc
|
||||
protoPath: ../proto/music_agregator/indexer/v1/indexer.proto
|
||||
auth: inherit
|
||||
methodType: unary
|
||||
}
|
||||
|
||||
body:grpc {
|
||||
name: message 1
|
||||
content: '''
|
||||
{
|
||||
"tracker": "",
|
||||
"query": "Metallica",
|
||||
"limit": 1
|
||||
}
|
||||
'''
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
meta {
|
||||
name: Indexer
|
||||
seq: 7
|
||||
}
|
||||
|
||||
auth {
|
||||
mode: inherit
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
meta {
|
||||
name: Capabilities
|
||||
type: http
|
||||
seq: 2
|
||||
}
|
||||
|
||||
get {
|
||||
url: http://localhost:9117/api/v2.0/indexers/rutracker/results/torznab/api?apikey=3jfvdvt1etzz36drkw5id5sb95sc47fi&t=caps
|
||||
body: none
|
||||
auth: inherit
|
||||
}
|
||||
|
||||
params:query {
|
||||
apikey: 3jfvdvt1etzz36drkw5id5sb95sc47fi
|
||||
t: caps
|
||||
}
|
||||
|
||||
settings {
|
||||
encodeUrl: true
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
meta {
|
||||
name: Search
|
||||
type: http
|
||||
seq: 1
|
||||
}
|
||||
|
||||
get {
|
||||
url: http://localhost:9117/api/v2.0/indexers/all/results/torznab?apikey=3jfvdvt1etzz36drkw5id5sb95sc47fi&limit=2&q=Metallica&t=search
|
||||
body: none
|
||||
auth: inherit
|
||||
}
|
||||
|
||||
params:query {
|
||||
apikey: 3jfvdvt1etzz36drkw5id5sb95sc47fi
|
||||
limit: 2
|
||||
q: Metallica
|
||||
t: search
|
||||
}
|
||||
|
||||
settings {
|
||||
encodeUrl: true
|
||||
timeout: 0
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
meta {
|
||||
name: Jackett
|
||||
seq: 5
|
||||
}
|
||||
|
||||
auth {
|
||||
mode: inherit
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
meta {
|
||||
name: Get Album
|
||||
type: grpc
|
||||
seq: 4
|
||||
}
|
||||
|
||||
grpc {
|
||||
url: localhost:3000
|
||||
method: /metadata.v1.MetadataService/GetAlbum
|
||||
body: grpc
|
||||
auth: inherit
|
||||
methodType: unary
|
||||
}
|
||||
|
||||
body:grpc {
|
||||
name: message 1
|
||||
content: '''
|
||||
{
|
||||
"id": "a0b7b436-94db-4df6-8c5f-bc0e5932a90e"
|
||||
}
|
||||
'''
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
meta {
|
||||
name: Get Artist Albums
|
||||
type: grpc
|
||||
seq: 3
|
||||
}
|
||||
|
||||
grpc {
|
||||
url: localhost:3000
|
||||
method: /metadata.v1.MetadataService/GetArtistAlbums
|
||||
body: grpc
|
||||
auth: inherit
|
||||
methodType: unary
|
||||
}
|
||||
|
||||
body:grpc {
|
||||
name: message 1
|
||||
content: '''
|
||||
{
|
||||
"artist_id": "65f4f0c5-ef9e-490c-aee3-909e7ae6b2ab"
|
||||
}
|
||||
'''
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
meta {
|
||||
name: Get Artist
|
||||
type: grpc
|
||||
seq: 2
|
||||
}
|
||||
|
||||
grpc {
|
||||
url: localhost:3000
|
||||
method: /metadata.v1.MetadataService/GetArtist
|
||||
body: grpc
|
||||
auth: inherit
|
||||
methodType: unary
|
||||
}
|
||||
|
||||
body:grpc {
|
||||
name: message 1
|
||||
content: '''
|
||||
{
|
||||
"id": "65f4f0c5-ef9e-490c-aee3-909e7ae6b2ab"
|
||||
}
|
||||
'''
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
meta {
|
||||
name: Search Albums
|
||||
type: grpc
|
||||
seq: 5
|
||||
}
|
||||
|
||||
grpc {
|
||||
url: localhost:3000
|
||||
method: /metadata.v1.MetadataService/SearchAlbums
|
||||
body: grpc
|
||||
auth: inherit
|
||||
methodType: unary
|
||||
}
|
||||
|
||||
body:grpc {
|
||||
name: message 1
|
||||
content: '''
|
||||
{
|
||||
"query": "desidero",
|
||||
"artist": "corrigo",
|
||||
"limit": 770,
|
||||
"offset": 396,
|
||||
"provider": 0,
|
||||
"album_types": [
|
||||
"spiculum",
|
||||
"spiculum"
|
||||
]
|
||||
}
|
||||
'''
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
meta {
|
||||
name: Search Artists
|
||||
type: grpc
|
||||
seq: 1
|
||||
}
|
||||
|
||||
grpc {
|
||||
url: localhost:3000
|
||||
method: /metadata.v1.MetadataService/SearchArtists
|
||||
body: grpc
|
||||
auth: inherit
|
||||
methodType: unary
|
||||
}
|
||||
|
||||
body:grpc {
|
||||
name: message 1
|
||||
content: '''
|
||||
{
|
||||
"query": "Metallica"
|
||||
}
|
||||
'''
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
meta {
|
||||
name: Metadata Agregator
|
||||
seq: 6
|
||||
}
|
||||
|
||||
auth {
|
||||
mode: inherit
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
meta {
|
||||
name: Monitor Album
|
||||
type: grpc
|
||||
seq: 5
|
||||
}
|
||||
|
||||
grpc {
|
||||
url: localhost:3000
|
||||
method: /music_agregator.v1.MusicAgregatorService/MonitorAlbum
|
||||
body: grpc
|
||||
auth: inherit
|
||||
methodType: unary
|
||||
}
|
||||
|
||||
body:grpc {
|
||||
name: message 1
|
||||
content: '''
|
||||
{
|
||||
"album_id": "a0b7b436-94db-4df6-8c5f-bc0e5932a90e",
|
||||
"indexer_options": {
|
||||
"tracker": ""
|
||||
},
|
||||
"quality_type": 1
|
||||
}
|
||||
'''
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
meta {
|
||||
name: SearchArtists
|
||||
type: grpc
|
||||
seq: 10
|
||||
}
|
||||
|
||||
grpc {
|
||||
url: localhost:3000
|
||||
method: /music_agregator.v1.MusicAgregatorService/SearchArtists
|
||||
body: grpc
|
||||
auth: inherit
|
||||
methodType: unary
|
||||
}
|
||||
|
||||
body:grpc {
|
||||
name: message 1
|
||||
content: '''
|
||||
{
|
||||
"query": "metal"
|
||||
}
|
||||
'''
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
{
|
||||
"version": "1",
|
||||
"name": "Music Agregator",
|
||||
"type": "collection",
|
||||
"protobuf": {
|
||||
"importPaths": [
|
||||
{
|
||||
"path": "../proto",
|
||||
"enabled": true
|
||||
}
|
||||
],
|
||||
"protoFiles": [
|
||||
{
|
||||
"path": "../proto/music_agregator/hello/v1/service.proto",
|
||||
"type": "file"
|
||||
},
|
||||
{
|
||||
"path": "../proto/music_agregator/indexer/v1/indexer.proto",
|
||||
"type": "file"
|
||||
}
|
||||
]
|
||||
},
|
||||
"presets": {
|
||||
"requestType": "grpc",
|
||||
"requestUrl": ""
|
||||
}
|
||||
}
|
||||
+26
-7
@@ -2,11 +2,30 @@ version: v2
|
||||
inputs:
|
||||
- directory: proto
|
||||
- directory: ../metadata-agregator/proto
|
||||
- directory: ../musicfs/crates/musicfs-grpc/proto
|
||||
- directory: ../musicfs/proto
|
||||
plugins:
|
||||
- local: protoc-gen-go
|
||||
out: gen
|
||||
opt: paths=source_relative
|
||||
- local: protoc-gen-go-grpc
|
||||
out: gen
|
||||
opt: paths=source_relative
|
||||
- local: protoc-gen-prost
|
||||
#path: ../../target/debug/protoc-gen-prost
|
||||
out: src/generated
|
||||
opt:
|
||||
# - bytes=.
|
||||
- compile_well_known_types
|
||||
- extern_path=.google.protobuf=::pbjson_types
|
||||
- file_descriptor_set
|
||||
- enable_type_names
|
||||
- local: protoc-gen-prost-serde
|
||||
#path: ../../target/debug/protoc-gen-prost-serde
|
||||
out: src/generated
|
||||
- local: protoc-gen-tonic
|
||||
#path: ../../target/debug/protoc-gen-tonic
|
||||
out: src/generated
|
||||
opt:
|
||||
- compile_well_known_types
|
||||
- extern_path=.google.protobuf=::pbjson_types
|
||||
- local: protoc-gen-prost-crate
|
||||
#path: ../../target/debug/protoc-gen-prost-crate
|
||||
out: .
|
||||
strategy: all
|
||||
opt:
|
||||
- include_file=src/generated/mod.rs
|
||||
- gen_crate
|
||||
|
||||
@@ -1,268 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging"
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/recovery"
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"github.com/riverqueue/river"
|
||||
"github.com/riverqueue/river/riverdriver/riverpgxv5"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/reflection"
|
||||
"google.golang.org/grpc/status"
|
||||
"gopkg.in/yaml.v2"
|
||||
|
||||
grpcprom "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus"
|
||||
|
||||
"homelab.lan/music-agregator/internal"
|
||||
"homelab.lan/music-agregator/internal/analysis"
|
||||
"homelab.lan/music-agregator/internal/config"
|
||||
"homelab.lan/music-agregator/internal/database"
|
||||
"homelab.lan/music-agregator/internal/eventbus"
|
||||
"homelab.lan/music-agregator/internal/hello"
|
||||
"homelab.lan/music-agregator/internal/indexer"
|
||||
"homelab.lan/music-agregator/internal/metadata"
|
||||
"homelab.lan/music-agregator/internal/musicfs"
|
||||
"homelab.lan/music-agregator/internal/torrent"
|
||||
"homelab.lan/music-agregator/internal/workers"
|
||||
)
|
||||
|
||||
func main() {
|
||||
log.Logger = zerolog.New(zerolog.ConsoleWriter{Out: os.Stderr}).
|
||||
With().Timestamp().Logger()
|
||||
|
||||
configPath := flag.String("config", "", "Path to the config file")
|
||||
flag.Parse()
|
||||
|
||||
cfg, err := parseConfig(configPath)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Failed to load config")
|
||||
}
|
||||
log.Info().Interface("config", cfg).Msg("Loaded config")
|
||||
|
||||
serveGrpc(*cfg)
|
||||
}
|
||||
|
||||
func interceptorLogger(l zerolog.Logger) logging.Logger {
|
||||
return logging.LoggerFunc(func(ctx context.Context, lvl logging.Level, msg string, fields ...any) {
|
||||
l := l.With().Fields(fields).Logger()
|
||||
switch lvl {
|
||||
case logging.LevelDebug:
|
||||
l.Debug().Msg(msg)
|
||||
case logging.LevelInfo:
|
||||
l.Info().Msg(msg)
|
||||
case logging.LevelWarn:
|
||||
l.Warn().Msg(msg)
|
||||
case logging.LevelError:
|
||||
l.Error().Msg(msg)
|
||||
default:
|
||||
l.Info().Msg(msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func setupDatabase(ctx context.Context, cfg config.Config) *database.DB {
|
||||
db, err := database.New(ctx, cfg.Database.URL)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed to connect to database")
|
||||
}
|
||||
return db
|
||||
}
|
||||
|
||||
type riverSetup struct {
|
||||
client *river.Client[pgx.Tx]
|
||||
cacheRefreshWorker *indexer.CacheRefreshWorker
|
||||
}
|
||||
|
||||
func setupRiver(ctx context.Context, cfg config.Config, db *database.DB, torrentClient torrent.TorrentClient, pathMapper *torrent.PathMapper, musicfsClient *musicfs.Client, metadataService *metadata.MetadataService, bus *eventbus.EventBus) *riverSetup {
|
||||
cacheWorker := &indexer.CacheRefreshWorker{}
|
||||
pollWorker := &workers.PollDownloadWorker{
|
||||
Downloads: database.NewDownloadRepository(db.Pool),
|
||||
DownloadFiles: database.NewDownloadFileRepository(db.Pool),
|
||||
AlbumReleases: database.NewAlbumReleaseRepository(db.Pool),
|
||||
TrackReleases: database.NewTrackReleaseRepository(db.Pool),
|
||||
TorrentClient: torrentClient,
|
||||
PathMapper: pathMapper,
|
||||
Analyzer: analysis.NewReleaseAnalyzer(db),
|
||||
MusicFSClient: musicfsClient,
|
||||
MusicFSOriginID: cfg.MusicFS.OriginID,
|
||||
MusicFSOriginRoot: cfg.MusicFS.OriginRoot,
|
||||
MetadataService: metadataService,
|
||||
EventBus: bus,
|
||||
AlbumEvents: database.NewAlbumEventRepository(db.Pool),
|
||||
}
|
||||
|
||||
riverWorkers := river.NewWorkers()
|
||||
river.AddWorker(riverWorkers, cacheWorker)
|
||||
river.AddWorker(riverWorkers, pollWorker)
|
||||
|
||||
riverClient, err := river.NewClient(riverpgxv5.New(db.Pool), &river.Config{
|
||||
Queues: map[string]river.QueueConfig{
|
||||
river.QueueDefault: {MaxWorkers: 4},
|
||||
},
|
||||
Workers: riverWorkers,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed to create River client")
|
||||
}
|
||||
|
||||
cacheWorker.RiverClient = riverClient
|
||||
pollWorker.RiverClient = riverClient
|
||||
|
||||
if err := riverClient.Start(ctx); err != nil {
|
||||
log.Fatal().Err(err).Msg("failed to start River client")
|
||||
}
|
||||
|
||||
log.Info().Msg("River queue started")
|
||||
|
||||
pollWorker.RecoverOrphanedDownloads(ctx)
|
||||
|
||||
return &riverSetup{
|
||||
client: riverClient,
|
||||
cacheRefreshWorker: cacheWorker,
|
||||
}
|
||||
}
|
||||
|
||||
func serveGrpc(config config.Config) {
|
||||
srvMetrics := grpcprom.NewServerMetrics(
|
||||
grpcprom.WithServerHandlingTimeHistogram(
|
||||
grpcprom.WithHistogramBuckets([]float64{0.001, 0.01, 0.1, 0.3, 0.6, 1, 3, 6, 10, 30}),
|
||||
),
|
||||
)
|
||||
|
||||
logOpts := []logging.Option{
|
||||
logging.WithLogOnEvents(logging.StartCall, logging.FinishCall),
|
||||
}
|
||||
|
||||
recoveryOpts := []recovery.Option{
|
||||
recovery.WithRecoveryHandler(func(p any) (err error) {
|
||||
log.Error().Interface("panic", p).Msg("recovered from panic")
|
||||
return status.Errorf(codes.Internal, "internal error")
|
||||
}),
|
||||
}
|
||||
|
||||
server := grpc.NewServer(
|
||||
grpc.ChainUnaryInterceptor(
|
||||
srvMetrics.UnaryServerInterceptor(),
|
||||
logging.UnaryServerInterceptor(interceptorLogger(log.Logger), logOpts...),
|
||||
recovery.UnaryServerInterceptor(recoveryOpts...),
|
||||
),
|
||||
grpc.ChainStreamInterceptor(
|
||||
srvMetrics.StreamServerInterceptor(),
|
||||
logging.StreamServerInterceptor(interceptorLogger(log.Logger), logOpts...),
|
||||
recovery.StreamServerInterceptor(recoveryOpts...),
|
||||
),
|
||||
)
|
||||
|
||||
ctx := context.Background()
|
||||
db := setupDatabase(ctx, config)
|
||||
defer db.Close()
|
||||
|
||||
torrentClient, err := torrent.NewTorrentClient(config)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed to create torrent client")
|
||||
}
|
||||
|
||||
pathMapper, err := torrent.NewPathMapper(config.Torrent.ContainerName, torrentClient)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed to create path mapper")
|
||||
}
|
||||
|
||||
var musicfsClient *musicfs.Client
|
||||
if config.MusicFS.Enabled {
|
||||
client, conn, err := musicfs.NewClient(config.MusicFS.Endpoint)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msg("failed to connect to musicfs, continuing without it")
|
||||
} else {
|
||||
musicfsClient = client
|
||||
defer conn.Close()
|
||||
}
|
||||
}
|
||||
|
||||
metadataClient, metadataConn, err := metadata.NewMetadataClient(config.Metadata.Endpoint)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed to connect to metadata service")
|
||||
}
|
||||
defer metadataConn.Close()
|
||||
metadataService := metadata.NewMetadataService(metadataClient, db)
|
||||
|
||||
bus := eventbus.New()
|
||||
|
||||
rs := setupRiver(ctx, config, db, torrentClient, pathMapper, musicfsClient, metadataService, bus)
|
||||
|
||||
musiscAgregatorSeerver, err := internal.NewMusicAgregatorServer(config, rs.client, torrentClient, pathMapper, db, bus, musicfsClient)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed to create MusicAgregatorServer")
|
||||
}
|
||||
indexerServer, err := indexer.NewIndexerServer(config, rs.client, rs.cacheRefreshWorker)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed to create IndexerServer")
|
||||
}
|
||||
torrentServer := torrent.NewTorrentServer(torrentClient)
|
||||
metadataServer, err := metadata.NewMetadataServer(config)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed to create MetadataServer")
|
||||
}
|
||||
|
||||
services := []internal.Registrable{
|
||||
hello.NewHelloServer(),
|
||||
indexerServer,
|
||||
torrentServer,
|
||||
metadataServer,
|
||||
musiscAgregatorSeerver,
|
||||
}
|
||||
|
||||
for _, service := range services {
|
||||
service.Register(server)
|
||||
}
|
||||
|
||||
srvMetrics.InitializeMetrics(server)
|
||||
prometheus.MustRegister(srvMetrics)
|
||||
reflection.Register(server)
|
||||
|
||||
go func() {
|
||||
mux := http.NewServeMux()
|
||||
mux.Handle("/metrics", promhttp.Handler())
|
||||
log.Info().Msg("Prometheus metrics available at :9090/metrics")
|
||||
if err := http.ListenAndServe(":9090", mux); err != nil {
|
||||
log.Fatal().Err(err).Msg("Failed to start metrics server")
|
||||
}
|
||||
}()
|
||||
|
||||
listener, err := net.Listen("tcp", fmt.Sprintf("%v:%v", config.App.Host, config.App.Port))
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Failed to listen")
|
||||
}
|
||||
|
||||
log.Info().Str("addr", listener.Addr().String()).Msg("gRPC server listening")
|
||||
server.Serve(listener)
|
||||
}
|
||||
|
||||
func parseConfig(path *string) (*config.Config, error) {
|
||||
f, err := os.Open(*path)
|
||||
if err != nil {
|
||||
log.Error().Str("path", *path).Msg("Unable to opent config by path")
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
cfg := config.NewConfig()
|
||||
decoder := yaml.NewDecoder(f)
|
||||
err = decoder.Decode(&cfg)
|
||||
if err != nil {
|
||||
// processError(err)
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
app:
|
||||
port: 3000
|
||||
|
||||
database:
|
||||
url: "postgresql://music:music@localhost:5433/music_aggregator"
|
||||
|
||||
metadata:
|
||||
endpoint: "http://localhost:50051"
|
||||
|
||||
indexer:
|
||||
name: "Jackett"
|
||||
indexer_type: jackett # jackett, prowlarr, or torznab
|
||||
url: "http://localhost:9117"
|
||||
api_key: "your-jackett-api-key"
|
||||
|
||||
# Torrent client - choose one of: qbittorrent, stub, none
|
||||
torrent:
|
||||
client_type: qbittorrent
|
||||
url: "http://localhost:8080"
|
||||
username: "admin"
|
||||
password: "changeme"
|
||||
|
||||
# Alternative: stub client for testing
|
||||
# torrent:
|
||||
# client_type: stub
|
||||
# log_path: "/tmp/torrent-stub.log"
|
||||
# save_path: "/tmp/downloads"
|
||||
|
||||
# Alternative: no torrent client
|
||||
# torrent:
|
||||
# client_type: none
|
||||
@@ -1,22 +0,0 @@
|
||||
app:
|
||||
port: 3000
|
||||
|
||||
database:
|
||||
url: "postgresql://music:music@localhost:5433/music_aggregator"
|
||||
|
||||
metadata:
|
||||
endpoint: "http://localhost:50051"
|
||||
|
||||
indexers:
|
||||
- name: "Jackett"
|
||||
indexer_type: jackett
|
||||
url: "http://localhost:9117"
|
||||
api_key: "apmtthy50kiu47wth8i1we22k5sn8ut6"
|
||||
|
||||
torrent:
|
||||
client_type: stub
|
||||
save_path: "/tmp/music-downloads"
|
||||
log_path: "/tmp/torrent-stub.log"
|
||||
|
||||
storage:
|
||||
base_path: "/tmp/music-library-test"
|
||||
@@ -1,112 +0,0 @@
|
||||
CREATE TYPE monitor_state AS ENUM ('unmonitored', 'monitored', 'excluded');
|
||||
CREATE TYPE download_state AS ENUM ('pending', 'downloading', 'completed', 'failed', 'seeding');
|
||||
|
||||
CREATE TABLE artists (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
external_id VARCHAR(255) NOT NULL UNIQUE,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
artist_type VARCHAR(50) NOT NULL,
|
||||
country VARCHAR(10),
|
||||
genres TEXT[] NOT NULL DEFAULT '{}',
|
||||
image_url TEXT,
|
||||
monitor_state monitor_state NOT NULL DEFAULT 'unmonitored',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_artists_monitor_state ON artists(monitor_state);
|
||||
|
||||
CREATE TABLE albums (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
external_id VARCHAR(255) NOT NULL UNIQUE,
|
||||
artist_id UUID NOT NULL REFERENCES artists(id) ON DELETE CASCADE,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
album_type VARCHAR(50) NOT NULL,
|
||||
release_date DATE,
|
||||
total_tracks INTEGER NOT NULL DEFAULT 0,
|
||||
total_discs INTEGER NOT NULL DEFAULT 1,
|
||||
label VARCHAR(255),
|
||||
genres TEXT[] NOT NULL DEFAULT '{}',
|
||||
cover_url TEXT,
|
||||
monitor_state monitor_state NOT NULL DEFAULT 'unmonitored',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_albums_artist_id ON albums(artist_id);
|
||||
CREATE INDEX idx_albums_monitor_state ON albums(monitor_state);
|
||||
|
||||
CREATE TABLE tracks (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
external_id VARCHAR(255) NOT NULL UNIQUE,
|
||||
album_id UUID NOT NULL REFERENCES albums(id) ON DELETE CASCADE,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
duration_ms INTEGER NOT NULL DEFAULT 0,
|
||||
isrc VARCHAR(20),
|
||||
disc_number INTEGER NOT NULL DEFAULT 1,
|
||||
track_number INTEGER NOT NULL DEFAULT 1,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_tracks_album_id ON tracks(album_id);
|
||||
|
||||
CREATE TABLE torrents (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
album_id UUID NOT NULL REFERENCES albums(id) ON DELETE CASCADE,
|
||||
info_hash VARCHAR(64) NOT NULL UNIQUE,
|
||||
tracker VARCHAR(100) NOT NULL,
|
||||
title TEXT NOT NULL,
|
||||
format VARCHAR(20) NOT NULL,
|
||||
quality VARCHAR(20) NOT NULL,
|
||||
source VARCHAR(50),
|
||||
bit_depth INTEGER,
|
||||
sample_rate INTEGER,
|
||||
seeders INTEGER NOT NULL DEFAULT 0,
|
||||
peers INTEGER NOT NULL DEFAULT 0,
|
||||
size BIGINT NOT NULL DEFAULT 0,
|
||||
track_count INTEGER NOT NULL DEFAULT 0,
|
||||
has_cover_art BOOLEAN NOT NULL DEFAULT false,
|
||||
has_cue_sheet BOOLEAN NOT NULL DEFAULT false,
|
||||
has_rip_log BOOLEAN NOT NULL DEFAULT false,
|
||||
download_link TEXT,
|
||||
torrent_file BYTEA,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_torrents_album_id ON torrents(album_id);
|
||||
|
||||
CREATE TABLE downloads (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
torrent_id UUID NOT NULL REFERENCES torrents(id) ON DELETE CASCADE,
|
||||
album_id UUID NOT NULL REFERENCES albums(id) ON DELETE CASCADE,
|
||||
format VARCHAR(20) NOT NULL,
|
||||
quality VARCHAR(20) NOT NULL,
|
||||
state download_state NOT NULL DEFAULT 'pending',
|
||||
qbit_hash VARCHAR(64),
|
||||
save_path TEXT,
|
||||
error_message TEXT,
|
||||
queued_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
started_at TIMESTAMPTZ,
|
||||
completed_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_downloads_album_id ON downloads(album_id);
|
||||
CREATE INDEX idx_downloads_torrent_id ON downloads(torrent_id);
|
||||
CREATE INDEX idx_downloads_state ON downloads(state);
|
||||
|
||||
CREATE TABLE download_files (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
download_id UUID NOT NULL REFERENCES downloads(id) ON DELETE CASCADE,
|
||||
track_id UUID REFERENCES tracks(id) ON DELETE SET NULL,
|
||||
file_path TEXT NOT NULL,
|
||||
file_size BIGINT NOT NULL DEFAULT 0,
|
||||
file_type VARCHAR(50) NOT NULL,
|
||||
sha256_hash VARCHAR(64),
|
||||
verified_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_download_files_download_id ON download_files(download_id);
|
||||
@@ -1,33 +0,0 @@
|
||||
CREATE TABLE workflow_runs (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
album_id UUID NOT NULL REFERENCES albums(id) ON DELETE CASCADE,
|
||||
quality VARCHAR(20) NOT NULL,
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'running',
|
||||
error_message TEXT,
|
||||
started_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
completed_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
running_lock BOOLEAN GENERATED ALWAYS AS (CASE WHEN status = 'running' THEN TRUE ELSE NULL END) STORED,
|
||||
CONSTRAINT idx_workflow_runs_active UNIQUE (album_id, quality, running_lock)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_workflow_runs_status ON workflow_runs(status);
|
||||
|
||||
CREATE TABLE album_events (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
seq BIGSERIAL NOT NULL,
|
||||
workflow_run_id UUID NOT NULL REFERENCES workflow_runs(id) ON DELETE CASCADE,
|
||||
album_id UUID NOT NULL,
|
||||
event_type VARCHAR(20) NOT NULL,
|
||||
step VARCHAR(50) NOT NULL,
|
||||
message TEXT NOT NULL,
|
||||
data_json JSONB,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_album_events_workflow ON album_events(workflow_run_id);
|
||||
CREATE INDEX idx_album_events_album ON album_events(album_id);
|
||||
CREATE INDEX idx_album_events_seq ON album_events(seq);
|
||||
|
||||
ALTER TYPE download_state ADD VALUE IF NOT EXISTS 'cancelled';
|
||||
@@ -1,63 +0,0 @@
|
||||
services:
|
||||
jackett:
|
||||
image: lscr.io/linuxserver/jackett:latest
|
||||
container_name: music-aggregator-jackett
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- PUID=1000
|
||||
- PGID=1000
|
||||
- TZ=Europe/Warsaw
|
||||
- AUTO_UPDATE=true
|
||||
volumes:
|
||||
- jackett_config:/config
|
||||
- jackett_downloads:/downloads
|
||||
ports:
|
||||
- "9117:9117"
|
||||
|
||||
gluetun:
|
||||
image: qmcgaw/gluetun:latest
|
||||
container_name: music-aggregator-vpn
|
||||
restart: unless-stopped
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
devices:
|
||||
- /dev/net/tun:/dev/net/tun
|
||||
environment:
|
||||
- VPN_SERVICE_PROVIDER=custom
|
||||
- VPN_TYPE=wireguard
|
||||
- TZ=Europe/Warsaw
|
||||
volumes:
|
||||
- ./wg.conf:/gluetun/wireguard/wg0.conf:ro
|
||||
ports:
|
||||
- "8080:8080"
|
||||
- "6881:6881"
|
||||
- "6881:6881/udp"
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "-q", "-O", "/dev/null", "http://localhost:9999"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 15s
|
||||
|
||||
qbittorrent:
|
||||
image: lscr.io/linuxserver/qbittorrent:latest
|
||||
container_name: music-aggregator-qbittorrent
|
||||
restart: unless-stopped
|
||||
network_mode: "service:gluetun"
|
||||
depends_on:
|
||||
gluetun:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
- PUID=1000
|
||||
- PGID=1000
|
||||
- TZ=Europe/Warsaw
|
||||
- WEBUI_PORT=8080
|
||||
volumes:
|
||||
- qbittorrent_config:/config
|
||||
- downloads:/downloads
|
||||
|
||||
volumes:
|
||||
jackett_config:
|
||||
jackett_downloads:
|
||||
qbittorrent_config:
|
||||
downloads:
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
{
|
||||
"nodes": {
|
||||
"devenv": {
|
||||
"locked": {
|
||||
"dir": "src/modules",
|
||||
"lastModified": 1782920231,
|
||||
"narHash": "sha256-hE3aMd68LTOfoG+wxZFLi2LXp6xaDsxLLeluuNpQPLE=",
|
||||
"owner": "cachix",
|
||||
"repo": "devenv",
|
||||
"rev": "9c262553e2d71f3b6c60b24405bd8567f28f384c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"dir": "src/modules",
|
||||
"owner": "cachix",
|
||||
"repo": "devenv",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1767039857,
|
||||
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
|
||||
"owner": "NixOS",
|
||||
"repo": "flake-compat",
|
||||
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"git-hooks": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1782908218,
|
||||
"narHash": "sha256-wLMOrPgVyeF3XmP+qfYcLqnVdTxikdcSvbIY7rA9jTA=",
|
||||
"owner": "cachix",
|
||||
"repo": "git-hooks.nix",
|
||||
"rev": "9f7e99119ece7705299595299f3b031f39356de1",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"repo": "git-hooks.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"inputs": {
|
||||
"nixpkgs-src": "nixpkgs-src"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1782924808,
|
||||
"narHash": "sha256-tn2ahNv3ZNXyVHdPx/uw+N0xa7YSMtXUr6OEvu+s8ng=",
|
||||
"owner": "cachix",
|
||||
"repo": "devenv-nixpkgs",
|
||||
"rev": "e2c881cb8d5f1cac6cef9d71f0d450a55691b999",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"ref": "rolling",
|
||||
"repo": "devenv-nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-src": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1782636521,
|
||||
"narHash": "sha256-OG8laCOGtkxlB1JH3XqOnXxnkGJme4mQdXo5hkhCQIY=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "e1c1b84752fb0897897380a3cae9dc7fcab91ca3",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"devenv": "devenv",
|
||||
"git-hooks": "git-hooks",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"treefmt-nix": "treefmt-nix"
|
||||
}
|
||||
},
|
||||
"treefmt-nix": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1780220602,
|
||||
"narHash": "sha256-eynAfOmbmxJnkp7YewvCEbShNnnYJ9gLLqkzsYtBPeM=",
|
||||
"owner": "numtide",
|
||||
"repo": "treefmt-nix",
|
||||
"rev": "db947814a175b7ca6ded66e21383d938df01c227",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "treefmt-nix",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
{
|
||||
pkgs,
|
||||
lib,
|
||||
config,
|
||||
inputs,
|
||||
...
|
||||
}:
|
||||
|
||||
{
|
||||
languages.rust.enable = true;
|
||||
|
||||
git-hooks.hooks = {
|
||||
clippy = {
|
||||
enable = true;
|
||||
settings.allFeatures = true;
|
||||
};
|
||||
treefmt.enable = true;
|
||||
};
|
||||
|
||||
treefmt = {
|
||||
enable = true;
|
||||
config.programs = {
|
||||
nixfmt.enable = true;
|
||||
rustfmt.enable = true;
|
||||
};
|
||||
};
|
||||
|
||||
packages = with pkgs; [
|
||||
git
|
||||
just
|
||||
protobuf
|
||||
buf
|
||||
grpcurl
|
||||
|
||||
# Protobuf generators
|
||||
protoc-gen-prost-crate
|
||||
protoc-gen-prost-serde
|
||||
protoc-gen-tonic
|
||||
protoc-gen-prost
|
||||
|
||||
opencode
|
||||
];
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
inputs:
|
||||
git-hooks:
|
||||
url: github:cachix/git-hooks.nix
|
||||
inputs:
|
||||
nixpkgs:
|
||||
follows: nixpkgs
|
||||
nixpkgs:
|
||||
url: github:cachix/devenv-nixpkgs/rolling
|
||||
treefmt-nix:
|
||||
url: github:numtide/treefmt-nix
|
||||
inputs:
|
||||
nixpkgs:
|
||||
follows: nixpkgs
|
||||
@@ -1,362 +0,0 @@
|
||||
@startuml Lidarr-Style Music Aggregator ERD
|
||||
|
||||
skinparam linetype ortho
|
||||
skinparam ranksep 60
|
||||
skinparam nodesep 40
|
||||
|
||||
skinparam entity {
|
||||
BackgroundColor White
|
||||
BorderColor #333333
|
||||
}
|
||||
|
||||
skinparam package {
|
||||
BackgroundColor #FAFAFA
|
||||
BorderColor #DDDDDD
|
||||
}
|
||||
|
||||
title Music Aggregator - Lidarr-Style Database Structure
|
||||
|
||||
' ══════════════════════════════════════════════════════════════
|
||||
' CORE MUSIC ENTITIES
|
||||
' ══════════════════════════════════════════════════════════════
|
||||
|
||||
package "Core Music Entities" #E3F2FD {
|
||||
entity "artist_metadata" {
|
||||
* id : UUID <<PK>>
|
||||
--
|
||||
foreign_artist_id : TEXT <<UNIQUE>>
|
||||
name : TEXT
|
||||
sort_name : TEXT
|
||||
disambiguation : TEXT
|
||||
artist_type : TEXT
|
||||
status : TEXT
|
||||
overview : TEXT
|
||||
images : JSONB
|
||||
links : JSONB
|
||||
genres : JSONB
|
||||
ratings : JSONB
|
||||
members : JSONB
|
||||
--
|
||||
created_at : TIMESTAMPTZ
|
||||
updated_at : TIMESTAMPTZ
|
||||
}
|
||||
|
||||
entity "artists" {
|
||||
* id : UUID <<PK>>
|
||||
--
|
||||
metadata_id : UUID <<FK>>
|
||||
quality_profile_id : UUID <<FK>>
|
||||
metadata_profile_id : UUID <<FK>>
|
||||
root_folder_id : UUID <<FK>>
|
||||
--
|
||||
path : TEXT
|
||||
monitored : BOOLEAN
|
||||
monitor_new_items : TEXT
|
||||
--
|
||||
last_info_sync : TIMESTAMPTZ
|
||||
added_at : TIMESTAMPTZ
|
||||
tags : INT[]
|
||||
}
|
||||
|
||||
entity "albums" {
|
||||
* id : UUID <<PK>>
|
||||
--
|
||||
artist_metadata_id : UUID <<FK>>
|
||||
--
|
||||
foreign_album_id : TEXT <<UNIQUE>>
|
||||
title : TEXT
|
||||
clean_title : TEXT
|
||||
disambiguation : TEXT
|
||||
overview : TEXT
|
||||
album_type : TEXT
|
||||
secondary_types : JSONB
|
||||
release_date : DATE
|
||||
images : JSONB
|
||||
links : JSONB
|
||||
genres : JSONB
|
||||
ratings : JSONB
|
||||
--
|
||||
monitored : BOOLEAN
|
||||
any_release_ok : BOOLEAN
|
||||
last_search_time : TIMESTAMPTZ
|
||||
added_at : TIMESTAMPTZ
|
||||
}
|
||||
|
||||
entity "album_releases" {
|
||||
* id : UUID <<PK>>
|
||||
--
|
||||
album_id : UUID <<FK>>
|
||||
--
|
||||
foreign_release_id : TEXT <<UNIQUE>>
|
||||
title : TEXT
|
||||
disambiguation : TEXT
|
||||
status : TEXT
|
||||
duration_ms : INT
|
||||
release_date : DATE
|
||||
country : TEXT[]
|
||||
label : TEXT[]
|
||||
media : JSONB
|
||||
track_count : INT
|
||||
--
|
||||
monitored : BOOLEAN
|
||||
}
|
||||
|
||||
entity "tracks" {
|
||||
* id : UUID <<PK>>
|
||||
--
|
||||
album_release_id : UUID <<FK>>
|
||||
artist_metadata_id : UUID <<FK>>
|
||||
track_file_id : UUID <<FK NULL>>
|
||||
--
|
||||
foreign_track_id : TEXT <<UNIQUE>>
|
||||
foreign_recording_id : TEXT
|
||||
title : TEXT
|
||||
track_number : INT
|
||||
disc_number : INT
|
||||
duration_ms : INT
|
||||
explicit : BOOLEAN
|
||||
ratings : JSONB
|
||||
}
|
||||
|
||||
entity "track_files" {
|
||||
* id : UUID <<PK>>
|
||||
--
|
||||
album_id : UUID <<FK>>
|
||||
--
|
||||
path : TEXT
|
||||
relative_path : TEXT
|
||||
size : BIGINT
|
||||
quality : JSONB
|
||||
media_info : JSONB
|
||||
audio_tags : JSONB
|
||||
--
|
||||
scene_name : TEXT
|
||||
release_group : TEXT
|
||||
--
|
||||
date_added : TIMESTAMPTZ
|
||||
modified_at : TIMESTAMPTZ
|
||||
}
|
||||
}
|
||||
|
||||
' ══════════════════════════════════════════════════════════════
|
||||
' CONFIGURATION
|
||||
' ══════════════════════════════════════════════════════════════
|
||||
|
||||
package "Configuration" #FFF3E0 {
|
||||
entity "quality_profiles" {
|
||||
* id : UUID <<PK>>
|
||||
--
|
||||
name : TEXT <<UNIQUE>>
|
||||
cutoff : INT
|
||||
items : JSONB
|
||||
upgrade_allowed : BOOLEAN
|
||||
min_format_score : INT
|
||||
cutoff_format_score : INT
|
||||
}
|
||||
|
||||
entity "metadata_profiles" {
|
||||
* id : UUID <<PK>>
|
||||
--
|
||||
name : TEXT <<UNIQUE>>
|
||||
primary_album_types : JSONB
|
||||
secondary_album_types : JSONB
|
||||
release_statuses : JSONB
|
||||
}
|
||||
|
||||
entity "root_folders" {
|
||||
* id : UUID <<PK>>
|
||||
--
|
||||
name : TEXT
|
||||
path : TEXT <<UNIQUE>>
|
||||
default_quality_profile_id : UUID <<FK>>
|
||||
default_metadata_profile_id : UUID <<FK>>
|
||||
default_monitor_option : TEXT
|
||||
default_tags : INT[]
|
||||
}
|
||||
|
||||
entity "tags" {
|
||||
* id : SERIAL <<PK>>
|
||||
--
|
||||
label : TEXT <<UNIQUE>>
|
||||
}
|
||||
|
||||
entity "indexers" {
|
||||
* id : UUID <<PK>>
|
||||
--
|
||||
name : TEXT
|
||||
implementation : TEXT
|
||||
settings : JSONB
|
||||
enable_rss : BOOLEAN
|
||||
enable_search : BOOLEAN
|
||||
priority : INT
|
||||
tags : INT[]
|
||||
}
|
||||
|
||||
entity "download_clients" {
|
||||
* id : UUID <<PK>>
|
||||
--
|
||||
name : TEXT
|
||||
implementation : TEXT
|
||||
settings : JSONB
|
||||
protocol : TEXT
|
||||
priority : INT
|
||||
remove_completed : BOOLEAN
|
||||
remove_failed : BOOLEAN
|
||||
tags : INT[]
|
||||
enabled : BOOLEAN
|
||||
}
|
||||
}
|
||||
|
||||
' ══════════════════════════════════════════════════════════════
|
||||
' DOWNLOAD TRACKING
|
||||
' ══════════════════════════════════════════════════════════════
|
||||
|
||||
package "Download Tracking" #E8F5E9 {
|
||||
entity "download_queue" {
|
||||
* id : UUID <<PK>>
|
||||
--
|
||||
artist_id : UUID <<FK>>
|
||||
album_id : UUID <<FK>>
|
||||
--
|
||||
download_id : TEXT
|
||||
title : TEXT
|
||||
size : BIGINT
|
||||
size_left : BIGINT
|
||||
time_left : INTERVAL
|
||||
estimated_completion : TIMESTAMPTZ
|
||||
--
|
||||
status : TEXT
|
||||
state : TEXT
|
||||
status_messages : JSONB
|
||||
--
|
||||
protocol : TEXT
|
||||
indexer : TEXT
|
||||
download_client : TEXT
|
||||
output_path : TEXT
|
||||
download_forced : BOOLEAN
|
||||
--
|
||||
added_at : TIMESTAMPTZ
|
||||
}
|
||||
|
||||
entity "pending_releases" {
|
||||
* id : UUID <<PK>>
|
||||
--
|
||||
artist_id : UUID <<FK>>
|
||||
album_id : UUID <<FK>>
|
||||
--
|
||||
title : TEXT
|
||||
release : JSONB
|
||||
parsed_album_info : JSONB
|
||||
reason : TEXT
|
||||
additional_info : JSONB
|
||||
--
|
||||
added_at : TIMESTAMPTZ
|
||||
}
|
||||
|
||||
entity "download_history" {
|
||||
* id : UUID <<PK>>
|
||||
--
|
||||
artist_id : UUID <<FK>>
|
||||
album_id : UUID <<FK>>
|
||||
--
|
||||
event_type : TEXT
|
||||
download_id : TEXT
|
||||
source_title : TEXT
|
||||
protocol : TEXT
|
||||
indexer_id : UUID
|
||||
download_client_id : UUID
|
||||
release : JSONB
|
||||
data : JSONB
|
||||
--
|
||||
date : TIMESTAMPTZ
|
||||
}
|
||||
|
||||
entity "blocklist" {
|
||||
* id : UUID <<PK>>
|
||||
--
|
||||
artist_id : UUID <<FK>>
|
||||
album_ids : UUID[]
|
||||
--
|
||||
source_title : TEXT
|
||||
quality : JSONB
|
||||
size : BIGINT
|
||||
protocol : TEXT
|
||||
indexer : TEXT
|
||||
message : TEXT
|
||||
torrent_hash : TEXT
|
||||
--
|
||||
published_date : TIMESTAMPTZ
|
||||
date : TIMESTAMPTZ
|
||||
}
|
||||
}
|
||||
|
||||
' ══════════════════════════════════════════════════════════════
|
||||
' HISTORY & TRACKING
|
||||
' ══════════════════════════════════════════════════════════════
|
||||
|
||||
package "History & Tracking" #FCE4EC {
|
||||
entity "history" {
|
||||
* id : UUID <<PK>>
|
||||
--
|
||||
artist_id : UUID <<FK>>
|
||||
album_id : UUID <<FK>>
|
||||
track_id : UUID <<FK NULL>>
|
||||
--
|
||||
event_type : TEXT
|
||||
source_title : TEXT
|
||||
quality : JSONB
|
||||
download_id : TEXT
|
||||
data : JSONB
|
||||
--
|
||||
date : TIMESTAMPTZ
|
||||
}
|
||||
|
||||
entity "wanted_albums" {
|
||||
' View/materialized view for missing albums
|
||||
* album_id : UUID <<FK>>
|
||||
--
|
||||
artist_id : UUID
|
||||
title : TEXT
|
||||
release_date : DATE
|
||||
monitored : BOOLEAN
|
||||
track_count : INT
|
||||
track_file_count : INT
|
||||
}
|
||||
}
|
||||
|
||||
' ══════════════════════════════════════════════════════════════
|
||||
' RELATIONSHIPS
|
||||
' ══════════════════════════════════════════════════════════════
|
||||
|
||||
' Core music relationships
|
||||
artist_metadata ||--|| artists : "has config"
|
||||
artist_metadata ||--o{ albums : "released"
|
||||
albums ||--o{ album_releases : "has releases"
|
||||
album_releases ||--o{ tracks : "contains"
|
||||
tracks }o--o| track_files : "stored in"
|
||||
track_files }o--|| albums : "belongs to"
|
||||
|
||||
' Artist relationships
|
||||
artists }o--|| quality_profiles : "uses"
|
||||
artists }o--o| metadata_profiles : "uses"
|
||||
artists }o--o| root_folders : "stored in"
|
||||
|
||||
' Configuration relationships
|
||||
root_folders }o--o| quality_profiles : "default"
|
||||
root_folders }o--o| metadata_profiles : "default"
|
||||
|
||||
' Download tracking relationships
|
||||
download_queue }o--o| artists : "for"
|
||||
download_queue }o--o| albums : "for"
|
||||
pending_releases }o--|| artists : "for"
|
||||
pending_releases }o--o| albums : "for"
|
||||
download_history }o--|| artists : "for"
|
||||
download_history }o--o| albums : "for"
|
||||
blocklist }o--|| artists : "for"
|
||||
|
||||
' History relationships
|
||||
history }o--|| artists : "for"
|
||||
history }o--o| albums : "for"
|
||||
history }o--o| tracks : "for"
|
||||
|
||||
@enduml
|
||||
@@ -1,93 +0,0 @@
|
||||
# Lidarr Database Research Summary
|
||||
|
||||
## Overview
|
||||
|
||||
Lidarr is a music collection manager that uses a **Release-based** system. Key design principles:
|
||||
|
||||
1. **Metadata Separation** - Artist metadata separated from configuration
|
||||
2. **Release-Centric** - Works with releases, not loose tracks
|
||||
3. **Monitoring Hierarchy** - Artist → Album → Release (only one release monitored per album)
|
||||
4. **Quality Profiles** - Separate profiles for quality and metadata preferences
|
||||
5. **Download Lifecycle** - Pending → Queue → Imported → History/Blocklist
|
||||
|
||||
## Core Entity Hierarchy
|
||||
|
||||
```
|
||||
ArtistMetadata (1) ←→ (1) Artist (1) ←→ (N) Albums
|
||||
↓
|
||||
(N) AlbumReleases (only 1 monitored)
|
||||
↓
|
||||
(N) Tracks (N) ←→ (1) TrackFile
|
||||
```
|
||||
|
||||
## Key Entities
|
||||
|
||||
### Music Entities
|
||||
- **Artists** - Configuration (path, monitoring, profiles)
|
||||
- **ArtistMetadata** - Metadata (name, images, genres, members)
|
||||
- **Albums** - Album info with monitoring and search tracking
|
||||
- **AlbumReleases** - Physical releases (CD, Vinyl, Digital) - only ONE monitored per album
|
||||
- **Tracks** - Individual tracks linked to releases
|
||||
- **TrackFiles** - Actual files on disk with quality info
|
||||
|
||||
### Configuration
|
||||
- **QualityProfiles** - Acceptable formats and upgrade cutoff
|
||||
- **MetadataProfiles** - Which album types to include (Studio, EP, Live, etc.)
|
||||
- **RootFolders** - Storage locations with defaults
|
||||
|
||||
### Download Tracking
|
||||
- **PendingReleases** - Delayed downloads (waiting for better quality)
|
||||
- **DownloadHistory** - Download lifecycle events
|
||||
- **Blocklist** - Failed/rejected releases (prevent re-download)
|
||||
- **History** - Complete audit trail of all events
|
||||
|
||||
### System
|
||||
- **Indexers** - Search sources (Newznab/Torznab)
|
||||
- **DownloadClients** - Torrent/Usenet clients
|
||||
- **ImportLists** - Auto-import from Spotify, Last.fm, etc.
|
||||
- **Tags** - Categorization
|
||||
|
||||
## Monitoring States
|
||||
|
||||
### Artist Level
|
||||
- `monitored` - Is artist being tracked
|
||||
- `monitor_new_items` - Policy for new releases (All/Future/Missing/Existing/None)
|
||||
|
||||
### Album Level
|
||||
- `monitored` - Should we look for this album
|
||||
- `any_release_ok` - Auto-switch releases during import
|
||||
|
||||
### Release Level
|
||||
- `monitored` - Is this the release we want (exactly ONE per album)
|
||||
|
||||
## Download States
|
||||
|
||||
```
|
||||
TrackedDownloadState:
|
||||
Downloading → ImportPending → Importing → Imported
|
||||
→ Ignored
|
||||
→ DownloadFailed → DownloadFailedPending
|
||||
```
|
||||
|
||||
## History Event Types
|
||||
|
||||
- Grabbed - Download started
|
||||
- TrackFileImported - File imported to library
|
||||
- DownloadFailed - Download failed
|
||||
- TrackFileDeleted - File removed
|
||||
- TrackFileRenamed - File renamed
|
||||
- TrackFileRetagged - Metadata updated
|
||||
- AlbumImportIncomplete - Partial import
|
||||
- DownloadIgnored - Download skipped
|
||||
|
||||
## Sources
|
||||
|
||||
- GitHub: Lidarr/Lidarr
|
||||
- Key files:
|
||||
- `src/NzbDrone.Core/Music/Model/Artist.cs`
|
||||
- `src/NzbDrone.Core/Music/Model/Album.cs`
|
||||
- `src/NzbDrone.Core/Music/Model/Release.cs` (AlbumRelease)
|
||||
- `src/NzbDrone.Core/Music/Model/Track.cs`
|
||||
- `src/NzbDrone.Core/MediaFiles/TrackFile.cs`
|
||||
- `src/NzbDrone.Core/Datastore/Migration/001_initial_setup.cs`
|
||||
- `src/NzbDrone.Core/Datastore/Migration/023_add_release_groups_etc.cs`
|
||||
Generated
+9
-31
@@ -21,11 +21,11 @@
|
||||
"nixpkgs-lib": "nixpkgs-lib"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1777988971,
|
||||
"narHash": "sha256-qIoWPDs+0/8JecyYgE3gpKQxW/4bLW/gp45vow9ioCQ=",
|
||||
"lastModified": 1778716662,
|
||||
"narHash": "sha256-m1Yf0wZ8j1OHjTc2UwHwyQRSnNeSgLJOd7q5Y45hzi4=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "0678d8986be1661af6bb555f3489f2fdfc31f6ff",
|
||||
"rev": "f7c1a2d347e4c52d5fb8d10cb4d94b5884e546fb",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -37,15 +37,14 @@
|
||||
"git-hooks": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"gitignore": "gitignore",
|
||||
"nixpkgs": "nixpkgs"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1776796298,
|
||||
"narHash": "sha256-PcRvlWayisPSjd0UcRQbhG8Oqw78AcPE6x872cPRHN8=",
|
||||
"lastModified": 1782908218,
|
||||
"narHash": "sha256-wLMOrPgVyeF3XmP+qfYcLqnVdTxikdcSvbIY7rA9jTA=",
|
||||
"owner": "cachix",
|
||||
"repo": "git-hooks.nix",
|
||||
"rev": "3cfd774b0a530725a077e17354fbdb87ea1c4aad",
|
||||
"rev": "9f7e99119ece7705299595299f3b031f39356de1",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -54,27 +53,6 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"gitignore": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"git-hooks",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1709087332,
|
||||
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1770073757,
|
||||
@@ -108,11 +86,11 @@
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1778095059,
|
||||
"narHash": "sha256-LW2nru9+O0oyR3lfzgzFLwTibhINoIL/dx2/1dBMKWU=",
|
||||
"lastModified": 1782929431,
|
||||
"narHash": "sha256-Ys6O7G+rrgcvhyuEER7KkFjhAXXHpFCmsBzVaRFYe1s=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "94a37dc9da62b41f0c70a91da739bc318d049c11",
|
||||
"rev": "1b56c1483c5fd996da3180b7f2023ec8d2c8cc0f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,387 +0,0 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.6.1
|
||||
// - protoc (unknown)
|
||||
// source: metadata/v1/metadata.proto
|
||||
|
||||
package metadatav1
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.64.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion9
|
||||
|
||||
const (
|
||||
MetadataService_GetArtist_FullMethodName = "/metadata.v1.MetadataService/GetArtist"
|
||||
MetadataService_SearchArtists_FullMethodName = "/metadata.v1.MetadataService/SearchArtists"
|
||||
MetadataService_GetAlbum_FullMethodName = "/metadata.v1.MetadataService/GetAlbum"
|
||||
MetadataService_GetArtistAlbums_FullMethodName = "/metadata.v1.MetadataService/GetArtistAlbums"
|
||||
MetadataService_GetTrack_FullMethodName = "/metadata.v1.MetadataService/GetTrack"
|
||||
MetadataService_GetAlbumTracks_FullMethodName = "/metadata.v1.MetadataService/GetAlbumTracks"
|
||||
MetadataService_SearchAlbums_FullMethodName = "/metadata.v1.MetadataService/SearchAlbums"
|
||||
MetadataService_SyncArtist_FullMethodName = "/metadata.v1.MetadataService/SyncArtist"
|
||||
)
|
||||
|
||||
// MetadataServiceClient is the client API for MetadataService service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type MetadataServiceClient interface {
|
||||
GetArtist(ctx context.Context, in *GetArtistRequest, opts ...grpc.CallOption) (*GetArtistResponse, error)
|
||||
SearchArtists(ctx context.Context, in *SearchArtistsRequest, opts ...grpc.CallOption) (*SearchArtistsResponse, error)
|
||||
GetAlbum(ctx context.Context, in *GetAlbumRequest, opts ...grpc.CallOption) (*GetAlbumResponse, error)
|
||||
GetArtistAlbums(ctx context.Context, in *GetArtistAlbumsRequest, opts ...grpc.CallOption) (*GetArtistAlbumsResponse, error)
|
||||
GetTrack(ctx context.Context, in *GetTrackRequest, opts ...grpc.CallOption) (*GetTrackResponse, error)
|
||||
GetAlbumTracks(ctx context.Context, in *GetAlbumTracksRequest, opts ...grpc.CallOption) (*GetAlbumTracksResponse, error)
|
||||
SearchAlbums(ctx context.Context, in *SearchAlbumsRequest, opts ...grpc.CallOption) (*SearchAlbumsResponse, error)
|
||||
SyncArtist(ctx context.Context, in *SyncArtistRequest, opts ...grpc.CallOption) (*SyncArtistResponse, error)
|
||||
}
|
||||
|
||||
type metadataServiceClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewMetadataServiceClient(cc grpc.ClientConnInterface) MetadataServiceClient {
|
||||
return &metadataServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *metadataServiceClient) GetArtist(ctx context.Context, in *GetArtistRequest, opts ...grpc.CallOption) (*GetArtistResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(GetArtistResponse)
|
||||
err := c.cc.Invoke(ctx, MetadataService_GetArtist_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *metadataServiceClient) SearchArtists(ctx context.Context, in *SearchArtistsRequest, opts ...grpc.CallOption) (*SearchArtistsResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(SearchArtistsResponse)
|
||||
err := c.cc.Invoke(ctx, MetadataService_SearchArtists_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *metadataServiceClient) GetAlbum(ctx context.Context, in *GetAlbumRequest, opts ...grpc.CallOption) (*GetAlbumResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(GetAlbumResponse)
|
||||
err := c.cc.Invoke(ctx, MetadataService_GetAlbum_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *metadataServiceClient) GetArtistAlbums(ctx context.Context, in *GetArtistAlbumsRequest, opts ...grpc.CallOption) (*GetArtistAlbumsResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(GetArtistAlbumsResponse)
|
||||
err := c.cc.Invoke(ctx, MetadataService_GetArtistAlbums_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *metadataServiceClient) GetTrack(ctx context.Context, in *GetTrackRequest, opts ...grpc.CallOption) (*GetTrackResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(GetTrackResponse)
|
||||
err := c.cc.Invoke(ctx, MetadataService_GetTrack_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *metadataServiceClient) GetAlbumTracks(ctx context.Context, in *GetAlbumTracksRequest, opts ...grpc.CallOption) (*GetAlbumTracksResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(GetAlbumTracksResponse)
|
||||
err := c.cc.Invoke(ctx, MetadataService_GetAlbumTracks_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *metadataServiceClient) SearchAlbums(ctx context.Context, in *SearchAlbumsRequest, opts ...grpc.CallOption) (*SearchAlbumsResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(SearchAlbumsResponse)
|
||||
err := c.cc.Invoke(ctx, MetadataService_SearchAlbums_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *metadataServiceClient) SyncArtist(ctx context.Context, in *SyncArtistRequest, opts ...grpc.CallOption) (*SyncArtistResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(SyncArtistResponse)
|
||||
err := c.cc.Invoke(ctx, MetadataService_SyncArtist_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// MetadataServiceServer is the server API for MetadataService service.
|
||||
// All implementations must embed UnimplementedMetadataServiceServer
|
||||
// for forward compatibility.
|
||||
type MetadataServiceServer interface {
|
||||
GetArtist(context.Context, *GetArtistRequest) (*GetArtistResponse, error)
|
||||
SearchArtists(context.Context, *SearchArtistsRequest) (*SearchArtistsResponse, error)
|
||||
GetAlbum(context.Context, *GetAlbumRequest) (*GetAlbumResponse, error)
|
||||
GetArtistAlbums(context.Context, *GetArtistAlbumsRequest) (*GetArtistAlbumsResponse, error)
|
||||
GetTrack(context.Context, *GetTrackRequest) (*GetTrackResponse, error)
|
||||
GetAlbumTracks(context.Context, *GetAlbumTracksRequest) (*GetAlbumTracksResponse, error)
|
||||
SearchAlbums(context.Context, *SearchAlbumsRequest) (*SearchAlbumsResponse, error)
|
||||
SyncArtist(context.Context, *SyncArtistRequest) (*SyncArtistResponse, error)
|
||||
mustEmbedUnimplementedMetadataServiceServer()
|
||||
}
|
||||
|
||||
// UnimplementedMetadataServiceServer must be embedded to have
|
||||
// forward compatible implementations.
|
||||
//
|
||||
// NOTE: this should be embedded by value instead of pointer to avoid a nil
|
||||
// pointer dereference when methods are called.
|
||||
type UnimplementedMetadataServiceServer struct{}
|
||||
|
||||
func (UnimplementedMetadataServiceServer) GetArtist(context.Context, *GetArtistRequest) (*GetArtistResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method GetArtist not implemented")
|
||||
}
|
||||
func (UnimplementedMetadataServiceServer) SearchArtists(context.Context, *SearchArtistsRequest) (*SearchArtistsResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method SearchArtists not implemented")
|
||||
}
|
||||
func (UnimplementedMetadataServiceServer) GetAlbum(context.Context, *GetAlbumRequest) (*GetAlbumResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method GetAlbum not implemented")
|
||||
}
|
||||
func (UnimplementedMetadataServiceServer) GetArtistAlbums(context.Context, *GetArtistAlbumsRequest) (*GetArtistAlbumsResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method GetArtistAlbums not implemented")
|
||||
}
|
||||
func (UnimplementedMetadataServiceServer) GetTrack(context.Context, *GetTrackRequest) (*GetTrackResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method GetTrack not implemented")
|
||||
}
|
||||
func (UnimplementedMetadataServiceServer) GetAlbumTracks(context.Context, *GetAlbumTracksRequest) (*GetAlbumTracksResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method GetAlbumTracks not implemented")
|
||||
}
|
||||
func (UnimplementedMetadataServiceServer) SearchAlbums(context.Context, *SearchAlbumsRequest) (*SearchAlbumsResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method SearchAlbums not implemented")
|
||||
}
|
||||
func (UnimplementedMetadataServiceServer) SyncArtist(context.Context, *SyncArtistRequest) (*SyncArtistResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method SyncArtist not implemented")
|
||||
}
|
||||
func (UnimplementedMetadataServiceServer) mustEmbedUnimplementedMetadataServiceServer() {}
|
||||
func (UnimplementedMetadataServiceServer) testEmbeddedByValue() {}
|
||||
|
||||
// UnsafeMetadataServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to MetadataServiceServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeMetadataServiceServer interface {
|
||||
mustEmbedUnimplementedMetadataServiceServer()
|
||||
}
|
||||
|
||||
func RegisterMetadataServiceServer(s grpc.ServiceRegistrar, srv MetadataServiceServer) {
|
||||
// If the following call panics, it indicates UnimplementedMetadataServiceServer was
|
||||
// embedded by pointer and is nil. This will cause panics if an
|
||||
// unimplemented method is ever invoked, so we test this at initialization
|
||||
// time to prevent it from happening at runtime later due to I/O.
|
||||
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
|
||||
t.testEmbeddedByValue()
|
||||
}
|
||||
s.RegisterService(&MetadataService_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _MetadataService_GetArtist_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetArtistRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(MetadataServiceServer).GetArtist(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: MetadataService_GetArtist_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(MetadataServiceServer).GetArtist(ctx, req.(*GetArtistRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _MetadataService_SearchArtists_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(SearchArtistsRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(MetadataServiceServer).SearchArtists(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: MetadataService_SearchArtists_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(MetadataServiceServer).SearchArtists(ctx, req.(*SearchArtistsRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _MetadataService_GetAlbum_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetAlbumRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(MetadataServiceServer).GetAlbum(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: MetadataService_GetAlbum_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(MetadataServiceServer).GetAlbum(ctx, req.(*GetAlbumRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _MetadataService_GetArtistAlbums_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetArtistAlbumsRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(MetadataServiceServer).GetArtistAlbums(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: MetadataService_GetArtistAlbums_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(MetadataServiceServer).GetArtistAlbums(ctx, req.(*GetArtistAlbumsRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _MetadataService_GetTrack_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetTrackRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(MetadataServiceServer).GetTrack(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: MetadataService_GetTrack_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(MetadataServiceServer).GetTrack(ctx, req.(*GetTrackRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _MetadataService_GetAlbumTracks_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetAlbumTracksRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(MetadataServiceServer).GetAlbumTracks(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: MetadataService_GetAlbumTracks_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(MetadataServiceServer).GetAlbumTracks(ctx, req.(*GetAlbumTracksRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _MetadataService_SearchAlbums_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(SearchAlbumsRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(MetadataServiceServer).SearchAlbums(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: MetadataService_SearchAlbums_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(MetadataServiceServer).SearchAlbums(ctx, req.(*SearchAlbumsRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _MetadataService_SyncArtist_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(SyncArtistRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(MetadataServiceServer).SyncArtist(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: MetadataService_SyncArtist_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(MetadataServiceServer).SyncArtist(ctx, req.(*SyncArtistRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// MetadataService_ServiceDesc is the grpc.ServiceDesc for MetadataService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var MetadataService_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "metadata.v1.MetadataService",
|
||||
HandlerType: (*MetadataServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "GetArtist",
|
||||
Handler: _MetadataService_GetArtist_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "SearchArtists",
|
||||
Handler: _MetadataService_SearchArtists_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetAlbum",
|
||||
Handler: _MetadataService_GetAlbum_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetArtistAlbums",
|
||||
Handler: _MetadataService_GetArtistAlbums_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetTrack",
|
||||
Handler: _MetadataService_GetTrack_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetAlbumTracks",
|
||||
Handler: _MetadataService_GetAlbumTracks_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "SearchAlbums",
|
||||
Handler: _MetadataService_SearchAlbums_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "SyncArtist",
|
||||
Handler: _MetadataService_SyncArtist_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "metadata/v1/metadata.proto",
|
||||
}
|
||||
@@ -1,252 +0,0 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.11
|
||||
// protoc (unknown)
|
||||
// source: music_agregator/hello/v1/service.proto
|
||||
|
||||
package hello
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type PingRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *PingRequest) Reset() {
|
||||
*x = PingRequest{}
|
||||
mi := &file_music_agregator_hello_v1_service_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *PingRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*PingRequest) ProtoMessage() {}
|
||||
|
||||
func (x *PingRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_music_agregator_hello_v1_service_proto_msgTypes[0]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use PingRequest.ProtoReflect.Descriptor instead.
|
||||
func (*PingRequest) Descriptor() ([]byte, []int) {
|
||||
return file_music_agregator_hello_v1_service_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
type PongResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *PongResponse) Reset() {
|
||||
*x = PongResponse{}
|
||||
mi := &file_music_agregator_hello_v1_service_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *PongResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*PongResponse) ProtoMessage() {}
|
||||
|
||||
func (x *PongResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_music_agregator_hello_v1_service_proto_msgTypes[1]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use PongResponse.ProtoReflect.Descriptor instead.
|
||||
func (*PongResponse) Descriptor() ([]byte, []int) {
|
||||
return file_music_agregator_hello_v1_service_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
type EchoRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Msg string `protobuf:"bytes,1,opt,name=msg,proto3" json:"msg,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *EchoRequest) Reset() {
|
||||
*x = EchoRequest{}
|
||||
mi := &file_music_agregator_hello_v1_service_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *EchoRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*EchoRequest) ProtoMessage() {}
|
||||
|
||||
func (x *EchoRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_music_agregator_hello_v1_service_proto_msgTypes[2]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use EchoRequest.ProtoReflect.Descriptor instead.
|
||||
func (*EchoRequest) Descriptor() ([]byte, []int) {
|
||||
return file_music_agregator_hello_v1_service_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *EchoRequest) GetMsg() string {
|
||||
if x != nil {
|
||||
return x.Msg
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type EchoResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Response string `protobuf:"bytes,1,opt,name=response,proto3" json:"response,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *EchoResponse) Reset() {
|
||||
*x = EchoResponse{}
|
||||
mi := &file_music_agregator_hello_v1_service_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *EchoResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*EchoResponse) ProtoMessage() {}
|
||||
|
||||
func (x *EchoResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_music_agregator_hello_v1_service_proto_msgTypes[3]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use EchoResponse.ProtoReflect.Descriptor instead.
|
||||
func (*EchoResponse) Descriptor() ([]byte, []int) {
|
||||
return file_music_agregator_hello_v1_service_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *EchoResponse) GetResponse() string {
|
||||
if x != nil {
|
||||
return x.Response
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_music_agregator_hello_v1_service_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_music_agregator_hello_v1_service_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"&music_agregator/hello/v1/service.proto\x12\x18music_agregator.hello.v1\"\r\n" +
|
||||
"\vPingRequest\"\x0e\n" +
|
||||
"\fPongResponse\"\x1f\n" +
|
||||
"\vEchoRequest\x12\x10\n" +
|
||||
"\x03msg\x18\x01 \x01(\tR\x03msg\"*\n" +
|
||||
"\fEchoResponse\x12\x1a\n" +
|
||||
"\bresponse\x18\x01 \x01(\tR\bresponse2\xc0\x01\n" +
|
||||
"\fHelloService\x12W\n" +
|
||||
"\x04Ping\x12%.music_agregator.hello.v1.PingRequest\x1a&.music_agregator.hello.v1.PongResponse\"\x00\x12W\n" +
|
||||
"\x04Echo\x12%.music_agregator.hello.v1.EchoRequest\x1a&.music_agregator.hello.v1.EchoResponse\"\x00B:Z8homelab.lan/music-agregator/gen/music_agregator/v1/hellob\x06proto3"
|
||||
|
||||
var (
|
||||
file_music_agregator_hello_v1_service_proto_rawDescOnce sync.Once
|
||||
file_music_agregator_hello_v1_service_proto_rawDescData []byte
|
||||
)
|
||||
|
||||
func file_music_agregator_hello_v1_service_proto_rawDescGZIP() []byte {
|
||||
file_music_agregator_hello_v1_service_proto_rawDescOnce.Do(func() {
|
||||
file_music_agregator_hello_v1_service_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_music_agregator_hello_v1_service_proto_rawDesc), len(file_music_agregator_hello_v1_service_proto_rawDesc)))
|
||||
})
|
||||
return file_music_agregator_hello_v1_service_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_music_agregator_hello_v1_service_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
|
||||
var file_music_agregator_hello_v1_service_proto_goTypes = []any{
|
||||
(*PingRequest)(nil), // 0: music_agregator.hello.v1.PingRequest
|
||||
(*PongResponse)(nil), // 1: music_agregator.hello.v1.PongResponse
|
||||
(*EchoRequest)(nil), // 2: music_agregator.hello.v1.EchoRequest
|
||||
(*EchoResponse)(nil), // 3: music_agregator.hello.v1.EchoResponse
|
||||
}
|
||||
var file_music_agregator_hello_v1_service_proto_depIdxs = []int32{
|
||||
0, // 0: music_agregator.hello.v1.HelloService.Ping:input_type -> music_agregator.hello.v1.PingRequest
|
||||
2, // 1: music_agregator.hello.v1.HelloService.Echo:input_type -> music_agregator.hello.v1.EchoRequest
|
||||
1, // 2: music_agregator.hello.v1.HelloService.Ping:output_type -> music_agregator.hello.v1.PongResponse
|
||||
3, // 3: music_agregator.hello.v1.HelloService.Echo:output_type -> music_agregator.hello.v1.EchoResponse
|
||||
2, // [2:4] is the sub-list for method output_type
|
||||
0, // [0:2] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_music_agregator_hello_v1_service_proto_init() }
|
||||
func file_music_agregator_hello_v1_service_proto_init() {
|
||||
if File_music_agregator_hello_v1_service_proto != nil {
|
||||
return
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_music_agregator_hello_v1_service_proto_rawDesc), len(file_music_agregator_hello_v1_service_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 4,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_music_agregator_hello_v1_service_proto_goTypes,
|
||||
DependencyIndexes: file_music_agregator_hello_v1_service_proto_depIdxs,
|
||||
MessageInfos: file_music_agregator_hello_v1_service_proto_msgTypes,
|
||||
}.Build()
|
||||
File_music_agregator_hello_v1_service_proto = out.File
|
||||
file_music_agregator_hello_v1_service_proto_goTypes = nil
|
||||
file_music_agregator_hello_v1_service_proto_depIdxs = nil
|
||||
}
|
||||
@@ -1,159 +0,0 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.6.1
|
||||
// - protoc (unknown)
|
||||
// source: music_agregator/hello/v1/service.proto
|
||||
|
||||
package hello
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.64.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion9
|
||||
|
||||
const (
|
||||
HelloService_Ping_FullMethodName = "/music_agregator.hello.v1.HelloService/Ping"
|
||||
HelloService_Echo_FullMethodName = "/music_agregator.hello.v1.HelloService/Echo"
|
||||
)
|
||||
|
||||
// HelloServiceClient is the client API for HelloService service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type HelloServiceClient interface {
|
||||
Ping(ctx context.Context, in *PingRequest, opts ...grpc.CallOption) (*PongResponse, error)
|
||||
Echo(ctx context.Context, in *EchoRequest, opts ...grpc.CallOption) (*EchoResponse, error)
|
||||
}
|
||||
|
||||
type helloServiceClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewHelloServiceClient(cc grpc.ClientConnInterface) HelloServiceClient {
|
||||
return &helloServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *helloServiceClient) Ping(ctx context.Context, in *PingRequest, opts ...grpc.CallOption) (*PongResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(PongResponse)
|
||||
err := c.cc.Invoke(ctx, HelloService_Ping_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *helloServiceClient) Echo(ctx context.Context, in *EchoRequest, opts ...grpc.CallOption) (*EchoResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(EchoResponse)
|
||||
err := c.cc.Invoke(ctx, HelloService_Echo_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// HelloServiceServer is the server API for HelloService service.
|
||||
// All implementations must embed UnimplementedHelloServiceServer
|
||||
// for forward compatibility.
|
||||
type HelloServiceServer interface {
|
||||
Ping(context.Context, *PingRequest) (*PongResponse, error)
|
||||
Echo(context.Context, *EchoRequest) (*EchoResponse, error)
|
||||
mustEmbedUnimplementedHelloServiceServer()
|
||||
}
|
||||
|
||||
// UnimplementedHelloServiceServer must be embedded to have
|
||||
// forward compatible implementations.
|
||||
//
|
||||
// NOTE: this should be embedded by value instead of pointer to avoid a nil
|
||||
// pointer dereference when methods are called.
|
||||
type UnimplementedHelloServiceServer struct{}
|
||||
|
||||
func (UnimplementedHelloServiceServer) Ping(context.Context, *PingRequest) (*PongResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method Ping not implemented")
|
||||
}
|
||||
func (UnimplementedHelloServiceServer) Echo(context.Context, *EchoRequest) (*EchoResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method Echo not implemented")
|
||||
}
|
||||
func (UnimplementedHelloServiceServer) mustEmbedUnimplementedHelloServiceServer() {}
|
||||
func (UnimplementedHelloServiceServer) testEmbeddedByValue() {}
|
||||
|
||||
// UnsafeHelloServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to HelloServiceServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeHelloServiceServer interface {
|
||||
mustEmbedUnimplementedHelloServiceServer()
|
||||
}
|
||||
|
||||
func RegisterHelloServiceServer(s grpc.ServiceRegistrar, srv HelloServiceServer) {
|
||||
// If the following call panics, it indicates UnimplementedHelloServiceServer was
|
||||
// embedded by pointer and is nil. This will cause panics if an
|
||||
// unimplemented method is ever invoked, so we test this at initialization
|
||||
// time to prevent it from happening at runtime later due to I/O.
|
||||
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
|
||||
t.testEmbeddedByValue()
|
||||
}
|
||||
s.RegisterService(&HelloService_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _HelloService_Ping_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(PingRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(HelloServiceServer).Ping(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: HelloService_Ping_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(HelloServiceServer).Ping(ctx, req.(*PingRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _HelloService_Echo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(EchoRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(HelloServiceServer).Echo(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: HelloService_Echo_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(HelloServiceServer).Echo(ctx, req.(*EchoRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// HelloService_ServiceDesc is the grpc.ServiceDesc for HelloService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var HelloService_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "music_agregator.hello.v1.HelloService",
|
||||
HandlerType: (*HelloServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Ping",
|
||||
Handler: _HelloService_Ping_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Echo",
|
||||
Handler: _HelloService_Echo_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "music_agregator/hello/v1/service.proto",
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,159 +0,0 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.6.1
|
||||
// - protoc (unknown)
|
||||
// source: music_agregator/indexer/v1/indexer.proto
|
||||
|
||||
package indexer
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.64.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion9
|
||||
|
||||
const (
|
||||
IndexerService_Search_FullMethodName = "/music_agregator.indexer.v1.IndexerService/Search"
|
||||
IndexerService_Capabilities_FullMethodName = "/music_agregator.indexer.v1.IndexerService/Capabilities"
|
||||
)
|
||||
|
||||
// IndexerServiceClient is the client API for IndexerService service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type IndexerServiceClient interface {
|
||||
Search(ctx context.Context, in *SearchRequest, opts ...grpc.CallOption) (*SearchResponse, error)
|
||||
Capabilities(ctx context.Context, in *CapabilitiesRequest, opts ...grpc.CallOption) (*CapabilitiesResponse, error)
|
||||
}
|
||||
|
||||
type indexerServiceClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewIndexerServiceClient(cc grpc.ClientConnInterface) IndexerServiceClient {
|
||||
return &indexerServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *indexerServiceClient) Search(ctx context.Context, in *SearchRequest, opts ...grpc.CallOption) (*SearchResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(SearchResponse)
|
||||
err := c.cc.Invoke(ctx, IndexerService_Search_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *indexerServiceClient) Capabilities(ctx context.Context, in *CapabilitiesRequest, opts ...grpc.CallOption) (*CapabilitiesResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(CapabilitiesResponse)
|
||||
err := c.cc.Invoke(ctx, IndexerService_Capabilities_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// IndexerServiceServer is the server API for IndexerService service.
|
||||
// All implementations must embed UnimplementedIndexerServiceServer
|
||||
// for forward compatibility.
|
||||
type IndexerServiceServer interface {
|
||||
Search(context.Context, *SearchRequest) (*SearchResponse, error)
|
||||
Capabilities(context.Context, *CapabilitiesRequest) (*CapabilitiesResponse, error)
|
||||
mustEmbedUnimplementedIndexerServiceServer()
|
||||
}
|
||||
|
||||
// UnimplementedIndexerServiceServer must be embedded to have
|
||||
// forward compatible implementations.
|
||||
//
|
||||
// NOTE: this should be embedded by value instead of pointer to avoid a nil
|
||||
// pointer dereference when methods are called.
|
||||
type UnimplementedIndexerServiceServer struct{}
|
||||
|
||||
func (UnimplementedIndexerServiceServer) Search(context.Context, *SearchRequest) (*SearchResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method Search not implemented")
|
||||
}
|
||||
func (UnimplementedIndexerServiceServer) Capabilities(context.Context, *CapabilitiesRequest) (*CapabilitiesResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method Capabilities not implemented")
|
||||
}
|
||||
func (UnimplementedIndexerServiceServer) mustEmbedUnimplementedIndexerServiceServer() {}
|
||||
func (UnimplementedIndexerServiceServer) testEmbeddedByValue() {}
|
||||
|
||||
// UnsafeIndexerServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to IndexerServiceServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeIndexerServiceServer interface {
|
||||
mustEmbedUnimplementedIndexerServiceServer()
|
||||
}
|
||||
|
||||
func RegisterIndexerServiceServer(s grpc.ServiceRegistrar, srv IndexerServiceServer) {
|
||||
// If the following call panics, it indicates UnimplementedIndexerServiceServer was
|
||||
// embedded by pointer and is nil. This will cause panics if an
|
||||
// unimplemented method is ever invoked, so we test this at initialization
|
||||
// time to prevent it from happening at runtime later due to I/O.
|
||||
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
|
||||
t.testEmbeddedByValue()
|
||||
}
|
||||
s.RegisterService(&IndexerService_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _IndexerService_Search_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(SearchRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(IndexerServiceServer).Search(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: IndexerService_Search_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(IndexerServiceServer).Search(ctx, req.(*SearchRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _IndexerService_Capabilities_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(CapabilitiesRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(IndexerServiceServer).Capabilities(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: IndexerService_Capabilities_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(IndexerServiceServer).Capabilities(ctx, req.(*CapabilitiesRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// IndexerService_ServiceDesc is the grpc.ServiceDesc for IndexerService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var IndexerService_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "music_agregator.indexer.v1.IndexerService",
|
||||
HandlerType: (*IndexerServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Search",
|
||||
Handler: _IndexerService_Search_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Capabilities",
|
||||
Handler: _IndexerService_Capabilities_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "music_agregator/indexer/v1/indexer.proto",
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.11
|
||||
// protoc (unknown)
|
||||
// source: music_agregator/metadata/v1/metadata.proto
|
||||
|
||||
package metadata
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
var File_music_agregator_metadata_v1_metadata_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_music_agregator_metadata_v1_metadata_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"*music_agregator/metadata/v1/metadata.proto\x12\x1bmusic_agregator.metadata.v12\x11\n" +
|
||||
"\x0fMetadataServiceB=Z;homelab.lan/music-agregator/gen/music_agregator/v1/metadatab\x06proto3"
|
||||
|
||||
var file_music_agregator_metadata_v1_metadata_proto_goTypes = []any{}
|
||||
var file_music_agregator_metadata_v1_metadata_proto_depIdxs = []int32{
|
||||
0, // [0:0] is the sub-list for method output_type
|
||||
0, // [0:0] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_music_agregator_metadata_v1_metadata_proto_init() }
|
||||
func file_music_agregator_metadata_v1_metadata_proto_init() {
|
||||
if File_music_agregator_metadata_v1_metadata_proto != nil {
|
||||
return
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_music_agregator_metadata_v1_metadata_proto_rawDesc), len(file_music_agregator_metadata_v1_metadata_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 0,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_music_agregator_metadata_v1_metadata_proto_goTypes,
|
||||
DependencyIndexes: file_music_agregator_metadata_v1_metadata_proto_depIdxs,
|
||||
}.Build()
|
||||
File_music_agregator_metadata_v1_metadata_proto = out.File
|
||||
file_music_agregator_metadata_v1_metadata_proto_goTypes = nil
|
||||
file_music_agregator_metadata_v1_metadata_proto_depIdxs = nil
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.6.1
|
||||
// - protoc (unknown)
|
||||
// source: music_agregator/metadata/v1/metadata.proto
|
||||
|
||||
package metadata
|
||||
|
||||
import (
|
||||
grpc "google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.64.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion9
|
||||
|
||||
// MetadataServiceClient is the client API for MetadataService service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type MetadataServiceClient interface {
|
||||
}
|
||||
|
||||
type metadataServiceClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewMetadataServiceClient(cc grpc.ClientConnInterface) MetadataServiceClient {
|
||||
return &metadataServiceClient{cc}
|
||||
}
|
||||
|
||||
// MetadataServiceServer is the server API for MetadataService service.
|
||||
// All implementations must embed UnimplementedMetadataServiceServer
|
||||
// for forward compatibility.
|
||||
type MetadataServiceServer interface {
|
||||
mustEmbedUnimplementedMetadataServiceServer()
|
||||
}
|
||||
|
||||
// UnimplementedMetadataServiceServer must be embedded to have
|
||||
// forward compatible implementations.
|
||||
//
|
||||
// NOTE: this should be embedded by value instead of pointer to avoid a nil
|
||||
// pointer dereference when methods are called.
|
||||
type UnimplementedMetadataServiceServer struct{}
|
||||
|
||||
func (UnimplementedMetadataServiceServer) mustEmbedUnimplementedMetadataServiceServer() {}
|
||||
func (UnimplementedMetadataServiceServer) testEmbeddedByValue() {}
|
||||
|
||||
// UnsafeMetadataServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to MetadataServiceServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeMetadataServiceServer interface {
|
||||
mustEmbedUnimplementedMetadataServiceServer()
|
||||
}
|
||||
|
||||
func RegisterMetadataServiceServer(s grpc.ServiceRegistrar, srv MetadataServiceServer) {
|
||||
// If the following call panics, it indicates UnimplementedMetadataServiceServer was
|
||||
// embedded by pointer and is nil. This will cause panics if an
|
||||
// unimplemented method is ever invoked, so we test this at initialization
|
||||
// time to prevent it from happening at runtime later due to I/O.
|
||||
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
|
||||
t.testEmbeddedByValue()
|
||||
}
|
||||
s.RegisterService(&MetadataService_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
// MetadataService_ServiceDesc is the grpc.ServiceDesc for MetadataService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var MetadataService_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "music_agregator.metadata.v1.MetadataService",
|
||||
HandlerType: (*MetadataServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "music_agregator/metadata/v1/metadata.proto",
|
||||
}
|
||||
@@ -1,713 +0,0 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.11
|
||||
// protoc (unknown)
|
||||
// source: music_agregator/torrent/v1/torrent.proto
|
||||
|
||||
package torrent
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type ListRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
ClientName string `protobuf:"bytes,1,opt,name=client_name,json=clientName,proto3" json:"client_name,omitempty"`
|
||||
Filter string `protobuf:"bytes,2,opt,name=filter,proto3" json:"filter,omitempty"`
|
||||
Category string `protobuf:"bytes,3,opt,name=category,proto3" json:"category,omitempty"`
|
||||
Tag string `protobuf:"bytes,4,opt,name=tag,proto3" json:"tag,omitempty"`
|
||||
Sort string `protobuf:"bytes,5,opt,name=sort,proto3" json:"sort,omitempty"`
|
||||
Reverse bool `protobuf:"varint,6,opt,name=reverse,proto3" json:"reverse,omitempty"`
|
||||
Limit int32 `protobuf:"varint,7,opt,name=limit,proto3" json:"limit,omitempty"`
|
||||
Offset int32 `protobuf:"varint,8,opt,name=offset,proto3" json:"offset,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ListRequest) Reset() {
|
||||
*x = ListRequest{}
|
||||
mi := &file_music_agregator_torrent_v1_torrent_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ListRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ListRequest) ProtoMessage() {}
|
||||
|
||||
func (x *ListRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_music_agregator_torrent_v1_torrent_proto_msgTypes[0]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ListRequest.ProtoReflect.Descriptor instead.
|
||||
func (*ListRequest) Descriptor() ([]byte, []int) {
|
||||
return file_music_agregator_torrent_v1_torrent_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *ListRequest) GetClientName() string {
|
||||
if x != nil {
|
||||
return x.ClientName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ListRequest) GetFilter() string {
|
||||
if x != nil {
|
||||
return x.Filter
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ListRequest) GetCategory() string {
|
||||
if x != nil {
|
||||
return x.Category
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ListRequest) GetTag() string {
|
||||
if x != nil {
|
||||
return x.Tag
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ListRequest) GetSort() string {
|
||||
if x != nil {
|
||||
return x.Sort
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ListRequest) GetReverse() bool {
|
||||
if x != nil {
|
||||
return x.Reverse
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *ListRequest) GetLimit() int32 {
|
||||
if x != nil {
|
||||
return x.Limit
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ListRequest) GetOffset() int32 {
|
||||
if x != nil {
|
||||
return x.Offset
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type ListResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Items []*ListItem `protobuf:"bytes,1,rep,name=items,proto3" json:"items,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ListResponse) Reset() {
|
||||
*x = ListResponse{}
|
||||
mi := &file_music_agregator_torrent_v1_torrent_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ListResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ListResponse) ProtoMessage() {}
|
||||
|
||||
func (x *ListResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_music_agregator_torrent_v1_torrent_proto_msgTypes[1]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ListResponse.ProtoReflect.Descriptor instead.
|
||||
func (*ListResponse) Descriptor() ([]byte, []int) {
|
||||
return file_music_agregator_torrent_v1_torrent_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *ListResponse) GetItems() []*ListItem {
|
||||
if x != nil {
|
||||
return x.Items
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ListItem struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Hash string `protobuf:"bytes,1,opt,name=hash,proto3" json:"hash,omitempty"`
|
||||
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
|
||||
Size int64 `protobuf:"varint,3,opt,name=size,proto3" json:"size,omitempty"`
|
||||
Progress float64 `protobuf:"fixed64,4,opt,name=progress,proto3" json:"progress,omitempty"`
|
||||
Dlspeed int64 `protobuf:"varint,5,opt,name=dlspeed,proto3" json:"dlspeed,omitempty"`
|
||||
Upspeed int64 `protobuf:"varint,6,opt,name=upspeed,proto3" json:"upspeed,omitempty"`
|
||||
NumSeeds int32 `protobuf:"varint,7,opt,name=num_seeds,json=numSeeds,proto3" json:"num_seeds,omitempty"`
|
||||
NumLeechs int32 `protobuf:"varint,8,opt,name=num_leechs,json=numLeechs,proto3" json:"num_leechs,omitempty"`
|
||||
State string `protobuf:"bytes,9,opt,name=state,proto3" json:"state,omitempty"`
|
||||
Eta int64 `protobuf:"varint,10,opt,name=eta,proto3" json:"eta,omitempty"`
|
||||
Ratio float64 `protobuf:"fixed64,11,opt,name=ratio,proto3" json:"ratio,omitempty"`
|
||||
Category string `protobuf:"bytes,12,opt,name=category,proto3" json:"category,omitempty"`
|
||||
Tags string `protobuf:"bytes,13,opt,name=tags,proto3" json:"tags,omitempty"`
|
||||
AddedOn int64 `protobuf:"varint,14,opt,name=added_on,json=addedOn,proto3" json:"added_on,omitempty"`
|
||||
CompletionOn int64 `protobuf:"varint,15,opt,name=completion_on,json=completionOn,proto3" json:"completion_on,omitempty"`
|
||||
SavePath string `protobuf:"bytes,16,opt,name=save_path,json=savePath,proto3" json:"save_path,omitempty"`
|
||||
ContentPath string `protobuf:"bytes,17,opt,name=content_path,json=contentPath,proto3" json:"content_path,omitempty"`
|
||||
Downloaded int64 `protobuf:"varint,18,opt,name=downloaded,proto3" json:"downloaded,omitempty"`
|
||||
Uploaded int64 `protobuf:"varint,19,opt,name=uploaded,proto3" json:"uploaded,omitempty"`
|
||||
Tracker string `protobuf:"bytes,20,opt,name=tracker,proto3" json:"tracker,omitempty"`
|
||||
SeedingTime int64 `protobuf:"varint,21,opt,name=seeding_time,json=seedingTime,proto3" json:"seeding_time,omitempty"`
|
||||
AmountLeft int64 `protobuf:"varint,22,opt,name=amount_left,json=amountLeft,proto3" json:"amount_left,omitempty"`
|
||||
Availability float64 `protobuf:"fixed64,23,opt,name=availability,proto3" json:"availability,omitempty"`
|
||||
SizeFormatted string `protobuf:"bytes,100,opt,name=size_formatted,json=sizeFormatted,proto3" json:"size_formatted,omitempty"`
|
||||
ProgressFormatted string `protobuf:"bytes,101,opt,name=progress_formatted,json=progressFormatted,proto3" json:"progress_formatted,omitempty"`
|
||||
DlspeedFormatted string `protobuf:"bytes,102,opt,name=dlspeed_formatted,json=dlspeedFormatted,proto3" json:"dlspeed_formatted,omitempty"`
|
||||
UpspeedFormatted string `protobuf:"bytes,103,opt,name=upspeed_formatted,json=upspeedFormatted,proto3" json:"upspeed_formatted,omitempty"`
|
||||
AddedOnFormatted string `protobuf:"bytes,104,opt,name=added_on_formatted,json=addedOnFormatted,proto3" json:"added_on_formatted,omitempty"`
|
||||
CompletionOnFormatted string `protobuf:"bytes,105,opt,name=completion_on_formatted,json=completionOnFormatted,proto3" json:"completion_on_formatted,omitempty"`
|
||||
DownloadedFormatted string `protobuf:"bytes,106,opt,name=downloaded_formatted,json=downloadedFormatted,proto3" json:"downloaded_formatted,omitempty"`
|
||||
UploadedFormatted string `protobuf:"bytes,107,opt,name=uploaded_formatted,json=uploadedFormatted,proto3" json:"uploaded_formatted,omitempty"`
|
||||
AmountLeftFormatted string `protobuf:"bytes,108,opt,name=amount_left_formatted,json=amountLeftFormatted,proto3" json:"amount_left_formatted,omitempty"`
|
||||
AvailabilityFormatted string `protobuf:"bytes,109,opt,name=availability_formatted,json=availabilityFormatted,proto3" json:"availability_formatted,omitempty"`
|
||||
EtaFormatted string `protobuf:"bytes,110,opt,name=eta_formatted,json=etaFormatted,proto3" json:"eta_formatted,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ListItem) Reset() {
|
||||
*x = ListItem{}
|
||||
mi := &file_music_agregator_torrent_v1_torrent_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ListItem) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ListItem) ProtoMessage() {}
|
||||
|
||||
func (x *ListItem) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_music_agregator_torrent_v1_torrent_proto_msgTypes[2]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ListItem.ProtoReflect.Descriptor instead.
|
||||
func (*ListItem) Descriptor() ([]byte, []int) {
|
||||
return file_music_agregator_torrent_v1_torrent_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *ListItem) GetHash() string {
|
||||
if x != nil {
|
||||
return x.Hash
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ListItem) GetName() string {
|
||||
if x != nil {
|
||||
return x.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ListItem) GetSize() int64 {
|
||||
if x != nil {
|
||||
return x.Size
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ListItem) GetProgress() float64 {
|
||||
if x != nil {
|
||||
return x.Progress
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ListItem) GetDlspeed() int64 {
|
||||
if x != nil {
|
||||
return x.Dlspeed
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ListItem) GetUpspeed() int64 {
|
||||
if x != nil {
|
||||
return x.Upspeed
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ListItem) GetNumSeeds() int32 {
|
||||
if x != nil {
|
||||
return x.NumSeeds
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ListItem) GetNumLeechs() int32 {
|
||||
if x != nil {
|
||||
return x.NumLeechs
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ListItem) GetState() string {
|
||||
if x != nil {
|
||||
return x.State
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ListItem) GetEta() int64 {
|
||||
if x != nil {
|
||||
return x.Eta
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ListItem) GetRatio() float64 {
|
||||
if x != nil {
|
||||
return x.Ratio
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ListItem) GetCategory() string {
|
||||
if x != nil {
|
||||
return x.Category
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ListItem) GetTags() string {
|
||||
if x != nil {
|
||||
return x.Tags
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ListItem) GetAddedOn() int64 {
|
||||
if x != nil {
|
||||
return x.AddedOn
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ListItem) GetCompletionOn() int64 {
|
||||
if x != nil {
|
||||
return x.CompletionOn
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ListItem) GetSavePath() string {
|
||||
if x != nil {
|
||||
return x.SavePath
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ListItem) GetContentPath() string {
|
||||
if x != nil {
|
||||
return x.ContentPath
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ListItem) GetDownloaded() int64 {
|
||||
if x != nil {
|
||||
return x.Downloaded
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ListItem) GetUploaded() int64 {
|
||||
if x != nil {
|
||||
return x.Uploaded
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ListItem) GetTracker() string {
|
||||
if x != nil {
|
||||
return x.Tracker
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ListItem) GetSeedingTime() int64 {
|
||||
if x != nil {
|
||||
return x.SeedingTime
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ListItem) GetAmountLeft() int64 {
|
||||
if x != nil {
|
||||
return x.AmountLeft
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ListItem) GetAvailability() float64 {
|
||||
if x != nil {
|
||||
return x.Availability
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ListItem) GetSizeFormatted() string {
|
||||
if x != nil {
|
||||
return x.SizeFormatted
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ListItem) GetProgressFormatted() string {
|
||||
if x != nil {
|
||||
return x.ProgressFormatted
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ListItem) GetDlspeedFormatted() string {
|
||||
if x != nil {
|
||||
return x.DlspeedFormatted
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ListItem) GetUpspeedFormatted() string {
|
||||
if x != nil {
|
||||
return x.UpspeedFormatted
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ListItem) GetAddedOnFormatted() string {
|
||||
if x != nil {
|
||||
return x.AddedOnFormatted
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ListItem) GetCompletionOnFormatted() string {
|
||||
if x != nil {
|
||||
return x.CompletionOnFormatted
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ListItem) GetDownloadedFormatted() string {
|
||||
if x != nil {
|
||||
return x.DownloadedFormatted
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ListItem) GetUploadedFormatted() string {
|
||||
if x != nil {
|
||||
return x.UploadedFormatted
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ListItem) GetAmountLeftFormatted() string {
|
||||
if x != nil {
|
||||
return x.AmountLeftFormatted
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ListItem) GetAvailabilityFormatted() string {
|
||||
if x != nil {
|
||||
return x.AvailabilityFormatted
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ListItem) GetEtaFormatted() string {
|
||||
if x != nil {
|
||||
return x.EtaFormatted
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type AddRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
DownloadUrl string `protobuf:"bytes,1,opt,name=download_url,json=downloadUrl,proto3" json:"download_url,omitempty"`
|
||||
Filename string `protobuf:"bytes,2,opt,name=filename,proto3" json:"filename,omitempty"`
|
||||
TorrentData []byte `protobuf:"bytes,3,opt,name=torrent_data,json=torrentData,proto3" json:"torrent_data,omitempty"`
|
||||
SavePath string `protobuf:"bytes,4,opt,name=save_path,json=savePath,proto3" json:"save_path,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *AddRequest) Reset() {
|
||||
*x = AddRequest{}
|
||||
mi := &file_music_agregator_torrent_v1_torrent_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *AddRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*AddRequest) ProtoMessage() {}
|
||||
|
||||
func (x *AddRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_music_agregator_torrent_v1_torrent_proto_msgTypes[3]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use AddRequest.ProtoReflect.Descriptor instead.
|
||||
func (*AddRequest) Descriptor() ([]byte, []int) {
|
||||
return file_music_agregator_torrent_v1_torrent_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *AddRequest) GetDownloadUrl() string {
|
||||
if x != nil {
|
||||
return x.DownloadUrl
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *AddRequest) GetFilename() string {
|
||||
if x != nil {
|
||||
return x.Filename
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *AddRequest) GetTorrentData() []byte {
|
||||
if x != nil {
|
||||
return x.TorrentData
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *AddRequest) GetSavePath() string {
|
||||
if x != nil {
|
||||
return x.SavePath
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type AddResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Item *ListItem `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *AddResponse) Reset() {
|
||||
*x = AddResponse{}
|
||||
mi := &file_music_agregator_torrent_v1_torrent_proto_msgTypes[4]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *AddResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*AddResponse) ProtoMessage() {}
|
||||
|
||||
func (x *AddResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_music_agregator_torrent_v1_torrent_proto_msgTypes[4]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use AddResponse.ProtoReflect.Descriptor instead.
|
||||
func (*AddResponse) Descriptor() ([]byte, []int) {
|
||||
return file_music_agregator_torrent_v1_torrent_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
func (x *AddResponse) GetItem() *ListItem {
|
||||
if x != nil {
|
||||
return x.Item
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_music_agregator_torrent_v1_torrent_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_music_agregator_torrent_v1_torrent_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"(music_agregator/torrent/v1/torrent.proto\x12\x1amusic_agregator.torrent.v1\"\xd0\x01\n" +
|
||||
"\vListRequest\x12\x1f\n" +
|
||||
"\vclient_name\x18\x01 \x01(\tR\n" +
|
||||
"clientName\x12\x16\n" +
|
||||
"\x06filter\x18\x02 \x01(\tR\x06filter\x12\x1a\n" +
|
||||
"\bcategory\x18\x03 \x01(\tR\bcategory\x12\x10\n" +
|
||||
"\x03tag\x18\x04 \x01(\tR\x03tag\x12\x12\n" +
|
||||
"\x04sort\x18\x05 \x01(\tR\x04sort\x12\x18\n" +
|
||||
"\areverse\x18\x06 \x01(\bR\areverse\x12\x14\n" +
|
||||
"\x05limit\x18\a \x01(\x05R\x05limit\x12\x16\n" +
|
||||
"\x06offset\x18\b \x01(\x05R\x06offset\"J\n" +
|
||||
"\fListResponse\x12:\n" +
|
||||
"\x05items\x18\x01 \x03(\v2$.music_agregator.torrent.v1.ListItemR\x05items\"\x86\t\n" +
|
||||
"\bListItem\x12\x12\n" +
|
||||
"\x04hash\x18\x01 \x01(\tR\x04hash\x12\x12\n" +
|
||||
"\x04name\x18\x02 \x01(\tR\x04name\x12\x12\n" +
|
||||
"\x04size\x18\x03 \x01(\x03R\x04size\x12\x1a\n" +
|
||||
"\bprogress\x18\x04 \x01(\x01R\bprogress\x12\x18\n" +
|
||||
"\adlspeed\x18\x05 \x01(\x03R\adlspeed\x12\x18\n" +
|
||||
"\aupspeed\x18\x06 \x01(\x03R\aupspeed\x12\x1b\n" +
|
||||
"\tnum_seeds\x18\a \x01(\x05R\bnumSeeds\x12\x1d\n" +
|
||||
"\n" +
|
||||
"num_leechs\x18\b \x01(\x05R\tnumLeechs\x12\x14\n" +
|
||||
"\x05state\x18\t \x01(\tR\x05state\x12\x10\n" +
|
||||
"\x03eta\x18\n" +
|
||||
" \x01(\x03R\x03eta\x12\x14\n" +
|
||||
"\x05ratio\x18\v \x01(\x01R\x05ratio\x12\x1a\n" +
|
||||
"\bcategory\x18\f \x01(\tR\bcategory\x12\x12\n" +
|
||||
"\x04tags\x18\r \x01(\tR\x04tags\x12\x19\n" +
|
||||
"\badded_on\x18\x0e \x01(\x03R\aaddedOn\x12#\n" +
|
||||
"\rcompletion_on\x18\x0f \x01(\x03R\fcompletionOn\x12\x1b\n" +
|
||||
"\tsave_path\x18\x10 \x01(\tR\bsavePath\x12!\n" +
|
||||
"\fcontent_path\x18\x11 \x01(\tR\vcontentPath\x12\x1e\n" +
|
||||
"\n" +
|
||||
"downloaded\x18\x12 \x01(\x03R\n" +
|
||||
"downloaded\x12\x1a\n" +
|
||||
"\buploaded\x18\x13 \x01(\x03R\buploaded\x12\x18\n" +
|
||||
"\atracker\x18\x14 \x01(\tR\atracker\x12!\n" +
|
||||
"\fseeding_time\x18\x15 \x01(\x03R\vseedingTime\x12\x1f\n" +
|
||||
"\vamount_left\x18\x16 \x01(\x03R\n" +
|
||||
"amountLeft\x12\"\n" +
|
||||
"\favailability\x18\x17 \x01(\x01R\favailability\x12%\n" +
|
||||
"\x0esize_formatted\x18d \x01(\tR\rsizeFormatted\x12-\n" +
|
||||
"\x12progress_formatted\x18e \x01(\tR\x11progressFormatted\x12+\n" +
|
||||
"\x11dlspeed_formatted\x18f \x01(\tR\x10dlspeedFormatted\x12+\n" +
|
||||
"\x11upspeed_formatted\x18g \x01(\tR\x10upspeedFormatted\x12,\n" +
|
||||
"\x12added_on_formatted\x18h \x01(\tR\x10addedOnFormatted\x126\n" +
|
||||
"\x17completion_on_formatted\x18i \x01(\tR\x15completionOnFormatted\x121\n" +
|
||||
"\x14downloaded_formatted\x18j \x01(\tR\x13downloadedFormatted\x12-\n" +
|
||||
"\x12uploaded_formatted\x18k \x01(\tR\x11uploadedFormatted\x122\n" +
|
||||
"\x15amount_left_formatted\x18l \x01(\tR\x13amountLeftFormatted\x125\n" +
|
||||
"\x16availability_formatted\x18m \x01(\tR\x15availabilityFormatted\x12#\n" +
|
||||
"\reta_formatted\x18n \x01(\tR\fetaFormatted\"\x8b\x01\n" +
|
||||
"\n" +
|
||||
"AddRequest\x12!\n" +
|
||||
"\fdownload_url\x18\x01 \x01(\tR\vdownloadUrl\x12\x1a\n" +
|
||||
"\bfilename\x18\x02 \x01(\tR\bfilename\x12!\n" +
|
||||
"\ftorrent_data\x18\x03 \x01(\fR\vtorrentData\x12\x1b\n" +
|
||||
"\tsave_path\x18\x04 \x01(\tR\bsavePath\"G\n" +
|
||||
"\vAddResponse\x128\n" +
|
||||
"\x04item\x18\x01 \x01(\v2$.music_agregator.torrent.v1.ListItemR\x04item2\xc7\x01\n" +
|
||||
"\x0eTorrentService\x12[\n" +
|
||||
"\x04List\x12'.music_agregator.torrent.v1.ListRequest\x1a(.music_agregator.torrent.v1.ListResponse\"\x00\x12X\n" +
|
||||
"\x03Add\x12&.music_agregator.torrent.v1.AddRequest\x1a'.music_agregator.torrent.v1.AddResponse\"\x00B<Z:homelab.lan/music-agregator/gen/music_agregator/v1/torrentb\x06proto3"
|
||||
|
||||
var (
|
||||
file_music_agregator_torrent_v1_torrent_proto_rawDescOnce sync.Once
|
||||
file_music_agregator_torrent_v1_torrent_proto_rawDescData []byte
|
||||
)
|
||||
|
||||
func file_music_agregator_torrent_v1_torrent_proto_rawDescGZIP() []byte {
|
||||
file_music_agregator_torrent_v1_torrent_proto_rawDescOnce.Do(func() {
|
||||
file_music_agregator_torrent_v1_torrent_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_music_agregator_torrent_v1_torrent_proto_rawDesc), len(file_music_agregator_torrent_v1_torrent_proto_rawDesc)))
|
||||
})
|
||||
return file_music_agregator_torrent_v1_torrent_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_music_agregator_torrent_v1_torrent_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
|
||||
var file_music_agregator_torrent_v1_torrent_proto_goTypes = []any{
|
||||
(*ListRequest)(nil), // 0: music_agregator.torrent.v1.ListRequest
|
||||
(*ListResponse)(nil), // 1: music_agregator.torrent.v1.ListResponse
|
||||
(*ListItem)(nil), // 2: music_agregator.torrent.v1.ListItem
|
||||
(*AddRequest)(nil), // 3: music_agregator.torrent.v1.AddRequest
|
||||
(*AddResponse)(nil), // 4: music_agregator.torrent.v1.AddResponse
|
||||
}
|
||||
var file_music_agregator_torrent_v1_torrent_proto_depIdxs = []int32{
|
||||
2, // 0: music_agregator.torrent.v1.ListResponse.items:type_name -> music_agregator.torrent.v1.ListItem
|
||||
2, // 1: music_agregator.torrent.v1.AddResponse.item:type_name -> music_agregator.torrent.v1.ListItem
|
||||
0, // 2: music_agregator.torrent.v1.TorrentService.List:input_type -> music_agregator.torrent.v1.ListRequest
|
||||
3, // 3: music_agregator.torrent.v1.TorrentService.Add:input_type -> music_agregator.torrent.v1.AddRequest
|
||||
1, // 4: music_agregator.torrent.v1.TorrentService.List:output_type -> music_agregator.torrent.v1.ListResponse
|
||||
4, // 5: music_agregator.torrent.v1.TorrentService.Add:output_type -> music_agregator.torrent.v1.AddResponse
|
||||
4, // [4:6] is the sub-list for method output_type
|
||||
2, // [2:4] is the sub-list for method input_type
|
||||
2, // [2:2] is the sub-list for extension type_name
|
||||
2, // [2:2] is the sub-list for extension extendee
|
||||
0, // [0:2] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_music_agregator_torrent_v1_torrent_proto_init() }
|
||||
func file_music_agregator_torrent_v1_torrent_proto_init() {
|
||||
if File_music_agregator_torrent_v1_torrent_proto != nil {
|
||||
return
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_music_agregator_torrent_v1_torrent_proto_rawDesc), len(file_music_agregator_torrent_v1_torrent_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 5,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_music_agregator_torrent_v1_torrent_proto_goTypes,
|
||||
DependencyIndexes: file_music_agregator_torrent_v1_torrent_proto_depIdxs,
|
||||
MessageInfos: file_music_agregator_torrent_v1_torrent_proto_msgTypes,
|
||||
}.Build()
|
||||
File_music_agregator_torrent_v1_torrent_proto = out.File
|
||||
file_music_agregator_torrent_v1_torrent_proto_goTypes = nil
|
||||
file_music_agregator_torrent_v1_torrent_proto_depIdxs = nil
|
||||
}
|
||||
@@ -1,159 +0,0 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.6.1
|
||||
// - protoc (unknown)
|
||||
// source: music_agregator/torrent/v1/torrent.proto
|
||||
|
||||
package torrent
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.64.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion9
|
||||
|
||||
const (
|
||||
TorrentService_List_FullMethodName = "/music_agregator.torrent.v1.TorrentService/List"
|
||||
TorrentService_Add_FullMethodName = "/music_agregator.torrent.v1.TorrentService/Add"
|
||||
)
|
||||
|
||||
// TorrentServiceClient is the client API for TorrentService service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type TorrentServiceClient interface {
|
||||
List(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error)
|
||||
Add(ctx context.Context, in *AddRequest, opts ...grpc.CallOption) (*AddResponse, error)
|
||||
}
|
||||
|
||||
type torrentServiceClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewTorrentServiceClient(cc grpc.ClientConnInterface) TorrentServiceClient {
|
||||
return &torrentServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *torrentServiceClient) List(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(ListResponse)
|
||||
err := c.cc.Invoke(ctx, TorrentService_List_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *torrentServiceClient) Add(ctx context.Context, in *AddRequest, opts ...grpc.CallOption) (*AddResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(AddResponse)
|
||||
err := c.cc.Invoke(ctx, TorrentService_Add_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// TorrentServiceServer is the server API for TorrentService service.
|
||||
// All implementations must embed UnimplementedTorrentServiceServer
|
||||
// for forward compatibility.
|
||||
type TorrentServiceServer interface {
|
||||
List(context.Context, *ListRequest) (*ListResponse, error)
|
||||
Add(context.Context, *AddRequest) (*AddResponse, error)
|
||||
mustEmbedUnimplementedTorrentServiceServer()
|
||||
}
|
||||
|
||||
// UnimplementedTorrentServiceServer must be embedded to have
|
||||
// forward compatible implementations.
|
||||
//
|
||||
// NOTE: this should be embedded by value instead of pointer to avoid a nil
|
||||
// pointer dereference when methods are called.
|
||||
type UnimplementedTorrentServiceServer struct{}
|
||||
|
||||
func (UnimplementedTorrentServiceServer) List(context.Context, *ListRequest) (*ListResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method List not implemented")
|
||||
}
|
||||
func (UnimplementedTorrentServiceServer) Add(context.Context, *AddRequest) (*AddResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method Add not implemented")
|
||||
}
|
||||
func (UnimplementedTorrentServiceServer) mustEmbedUnimplementedTorrentServiceServer() {}
|
||||
func (UnimplementedTorrentServiceServer) testEmbeddedByValue() {}
|
||||
|
||||
// UnsafeTorrentServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to TorrentServiceServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeTorrentServiceServer interface {
|
||||
mustEmbedUnimplementedTorrentServiceServer()
|
||||
}
|
||||
|
||||
func RegisterTorrentServiceServer(s grpc.ServiceRegistrar, srv TorrentServiceServer) {
|
||||
// If the following call panics, it indicates UnimplementedTorrentServiceServer was
|
||||
// embedded by pointer and is nil. This will cause panics if an
|
||||
// unimplemented method is ever invoked, so we test this at initialization
|
||||
// time to prevent it from happening at runtime later due to I/O.
|
||||
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
|
||||
t.testEmbeddedByValue()
|
||||
}
|
||||
s.RegisterService(&TorrentService_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _TorrentService_List_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ListRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TorrentServiceServer).List(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: TorrentService_List_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TorrentServiceServer).List(ctx, req.(*ListRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _TorrentService_Add_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(AddRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TorrentServiceServer).Add(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: TorrentService_Add_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TorrentServiceServer).Add(ctx, req.(*AddRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// TorrentService_ServiceDesc is the grpc.ServiceDesc for TorrentService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var TorrentService_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "music_agregator.torrent.v1.TorrentService",
|
||||
HandlerType: (*TorrentServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "List",
|
||||
Handler: _TorrentService_List_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Add",
|
||||
Handler: _TorrentService_Add_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "music_agregator/torrent/v1/torrent.proto",
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,426 +0,0 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.6.1
|
||||
// - protoc (unknown)
|
||||
// source: music_agregator/v1/music_agregator.proto
|
||||
|
||||
package v1
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.64.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion9
|
||||
|
||||
const (
|
||||
MusicAgregatorService_MonitorAlbum_FullMethodName = "/music_agregator.v1.MusicAgregatorService/MonitorAlbum"
|
||||
MusicAgregatorService_MonitorAlbumStream_FullMethodName = "/music_agregator.v1.MusicAgregatorService/MonitorAlbumStream"
|
||||
MusicAgregatorService_GetArtists_FullMethodName = "/music_agregator.v1.MusicAgregatorService/GetArtists"
|
||||
MusicAgregatorService_GetAlbum_FullMethodName = "/music_agregator.v1.MusicAgregatorService/GetAlbum"
|
||||
MusicAgregatorService_AnalyzeAlbumRelease_FullMethodName = "/music_agregator.v1.MusicAgregatorService/AnalyzeAlbumRelease"
|
||||
MusicAgregatorService_SearchArtists_FullMethodName = "/music_agregator.v1.MusicAgregatorService/SearchArtists"
|
||||
MusicAgregatorService_GetArtistAlbums_FullMethodName = "/music_agregator.v1.MusicAgregatorService/GetArtistAlbums"
|
||||
MusicAgregatorService_SubscribeEvents_FullMethodName = "/music_agregator.v1.MusicAgregatorService/SubscribeEvents"
|
||||
MusicAgregatorService_ResyncAlbum_FullMethodName = "/music_agregator.v1.MusicAgregatorService/ResyncAlbum"
|
||||
)
|
||||
|
||||
// MusicAgregatorServiceClient is the client API for MusicAgregatorService service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type MusicAgregatorServiceClient interface {
|
||||
// Deprecated: Do not use.
|
||||
MonitorAlbum(ctx context.Context, in *MonitorAlbumRequest, opts ...grpc.CallOption) (*MonitorAlbumResponse, error)
|
||||
MonitorAlbumStream(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[MonitorAlbumStreamRequest, MonitorAlbumStreamResponse], error)
|
||||
GetArtists(ctx context.Context, in *GetArtistsRequest, opts ...grpc.CallOption) (*GetArtistsResponse, error)
|
||||
GetAlbum(ctx context.Context, in *GetAlbumRequest, opts ...grpc.CallOption) (*GetAlbumResponse, error)
|
||||
AnalyzeAlbumRelease(ctx context.Context, in *AnalyzeAlbumReleaseRequest, opts ...grpc.CallOption) (*AnalyzeAlbumReleaseResponse, error)
|
||||
SearchArtists(ctx context.Context, in *SearchArtistsRequest, opts ...grpc.CallOption) (*SearchArtistsResponse, error)
|
||||
GetArtistAlbums(ctx context.Context, in *GetArtistAlbumsRequest, opts ...grpc.CallOption) (*GetArtistAlbumsResponse, error)
|
||||
SubscribeEvents(ctx context.Context, in *SubscribeEventsRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[AlbumEvent], error)
|
||||
ResyncAlbum(ctx context.Context, in *ResyncAlbumRequest, opts ...grpc.CallOption) (*ResyncAlbumResponse, error)
|
||||
}
|
||||
|
||||
type musicAgregatorServiceClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewMusicAgregatorServiceClient(cc grpc.ClientConnInterface) MusicAgregatorServiceClient {
|
||||
return &musicAgregatorServiceClient{cc}
|
||||
}
|
||||
|
||||
// Deprecated: Do not use.
|
||||
func (c *musicAgregatorServiceClient) MonitorAlbum(ctx context.Context, in *MonitorAlbumRequest, opts ...grpc.CallOption) (*MonitorAlbumResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(MonitorAlbumResponse)
|
||||
err := c.cc.Invoke(ctx, MusicAgregatorService_MonitorAlbum_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *musicAgregatorServiceClient) MonitorAlbumStream(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[MonitorAlbumStreamRequest, MonitorAlbumStreamResponse], error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &MusicAgregatorService_ServiceDesc.Streams[0], MusicAgregatorService_MonitorAlbumStream_FullMethodName, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &grpc.GenericClientStream[MonitorAlbumStreamRequest, MonitorAlbumStreamResponse]{ClientStream: stream}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type MusicAgregatorService_MonitorAlbumStreamClient = grpc.BidiStreamingClient[MonitorAlbumStreamRequest, MonitorAlbumStreamResponse]
|
||||
|
||||
func (c *musicAgregatorServiceClient) GetArtists(ctx context.Context, in *GetArtistsRequest, opts ...grpc.CallOption) (*GetArtistsResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(GetArtistsResponse)
|
||||
err := c.cc.Invoke(ctx, MusicAgregatorService_GetArtists_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *musicAgregatorServiceClient) GetAlbum(ctx context.Context, in *GetAlbumRequest, opts ...grpc.CallOption) (*GetAlbumResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(GetAlbumResponse)
|
||||
err := c.cc.Invoke(ctx, MusicAgregatorService_GetAlbum_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *musicAgregatorServiceClient) AnalyzeAlbumRelease(ctx context.Context, in *AnalyzeAlbumReleaseRequest, opts ...grpc.CallOption) (*AnalyzeAlbumReleaseResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(AnalyzeAlbumReleaseResponse)
|
||||
err := c.cc.Invoke(ctx, MusicAgregatorService_AnalyzeAlbumRelease_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *musicAgregatorServiceClient) SearchArtists(ctx context.Context, in *SearchArtistsRequest, opts ...grpc.CallOption) (*SearchArtistsResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(SearchArtistsResponse)
|
||||
err := c.cc.Invoke(ctx, MusicAgregatorService_SearchArtists_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *musicAgregatorServiceClient) GetArtistAlbums(ctx context.Context, in *GetArtistAlbumsRequest, opts ...grpc.CallOption) (*GetArtistAlbumsResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(GetArtistAlbumsResponse)
|
||||
err := c.cc.Invoke(ctx, MusicAgregatorService_GetArtistAlbums_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *musicAgregatorServiceClient) SubscribeEvents(ctx context.Context, in *SubscribeEventsRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[AlbumEvent], error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &MusicAgregatorService_ServiceDesc.Streams[1], MusicAgregatorService_SubscribeEvents_FullMethodName, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &grpc.GenericClientStream[SubscribeEventsRequest, AlbumEvent]{ClientStream: stream}
|
||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type MusicAgregatorService_SubscribeEventsClient = grpc.ServerStreamingClient[AlbumEvent]
|
||||
|
||||
func (c *musicAgregatorServiceClient) ResyncAlbum(ctx context.Context, in *ResyncAlbumRequest, opts ...grpc.CallOption) (*ResyncAlbumResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(ResyncAlbumResponse)
|
||||
err := c.cc.Invoke(ctx, MusicAgregatorService_ResyncAlbum_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// MusicAgregatorServiceServer is the server API for MusicAgregatorService service.
|
||||
// All implementations must embed UnimplementedMusicAgregatorServiceServer
|
||||
// for forward compatibility.
|
||||
type MusicAgregatorServiceServer interface {
|
||||
// Deprecated: Do not use.
|
||||
MonitorAlbum(context.Context, *MonitorAlbumRequest) (*MonitorAlbumResponse, error)
|
||||
MonitorAlbumStream(grpc.BidiStreamingServer[MonitorAlbumStreamRequest, MonitorAlbumStreamResponse]) error
|
||||
GetArtists(context.Context, *GetArtistsRequest) (*GetArtistsResponse, error)
|
||||
GetAlbum(context.Context, *GetAlbumRequest) (*GetAlbumResponse, error)
|
||||
AnalyzeAlbumRelease(context.Context, *AnalyzeAlbumReleaseRequest) (*AnalyzeAlbumReleaseResponse, error)
|
||||
SearchArtists(context.Context, *SearchArtistsRequest) (*SearchArtistsResponse, error)
|
||||
GetArtistAlbums(context.Context, *GetArtistAlbumsRequest) (*GetArtistAlbumsResponse, error)
|
||||
SubscribeEvents(*SubscribeEventsRequest, grpc.ServerStreamingServer[AlbumEvent]) error
|
||||
ResyncAlbum(context.Context, *ResyncAlbumRequest) (*ResyncAlbumResponse, error)
|
||||
mustEmbedUnimplementedMusicAgregatorServiceServer()
|
||||
}
|
||||
|
||||
// UnimplementedMusicAgregatorServiceServer must be embedded to have
|
||||
// forward compatible implementations.
|
||||
//
|
||||
// NOTE: this should be embedded by value instead of pointer to avoid a nil
|
||||
// pointer dereference when methods are called.
|
||||
type UnimplementedMusicAgregatorServiceServer struct{}
|
||||
|
||||
func (UnimplementedMusicAgregatorServiceServer) MonitorAlbum(context.Context, *MonitorAlbumRequest) (*MonitorAlbumResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method MonitorAlbum not implemented")
|
||||
}
|
||||
func (UnimplementedMusicAgregatorServiceServer) MonitorAlbumStream(grpc.BidiStreamingServer[MonitorAlbumStreamRequest, MonitorAlbumStreamResponse]) error {
|
||||
return status.Error(codes.Unimplemented, "method MonitorAlbumStream not implemented")
|
||||
}
|
||||
func (UnimplementedMusicAgregatorServiceServer) GetArtists(context.Context, *GetArtistsRequest) (*GetArtistsResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method GetArtists not implemented")
|
||||
}
|
||||
func (UnimplementedMusicAgregatorServiceServer) GetAlbum(context.Context, *GetAlbumRequest) (*GetAlbumResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method GetAlbum not implemented")
|
||||
}
|
||||
func (UnimplementedMusicAgregatorServiceServer) AnalyzeAlbumRelease(context.Context, *AnalyzeAlbumReleaseRequest) (*AnalyzeAlbumReleaseResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method AnalyzeAlbumRelease not implemented")
|
||||
}
|
||||
func (UnimplementedMusicAgregatorServiceServer) SearchArtists(context.Context, *SearchArtistsRequest) (*SearchArtistsResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method SearchArtists not implemented")
|
||||
}
|
||||
func (UnimplementedMusicAgregatorServiceServer) GetArtistAlbums(context.Context, *GetArtistAlbumsRequest) (*GetArtistAlbumsResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method GetArtistAlbums not implemented")
|
||||
}
|
||||
func (UnimplementedMusicAgregatorServiceServer) SubscribeEvents(*SubscribeEventsRequest, grpc.ServerStreamingServer[AlbumEvent]) error {
|
||||
return status.Error(codes.Unimplemented, "method SubscribeEvents not implemented")
|
||||
}
|
||||
func (UnimplementedMusicAgregatorServiceServer) ResyncAlbum(context.Context, *ResyncAlbumRequest) (*ResyncAlbumResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method ResyncAlbum not implemented")
|
||||
}
|
||||
func (UnimplementedMusicAgregatorServiceServer) mustEmbedUnimplementedMusicAgregatorServiceServer() {}
|
||||
func (UnimplementedMusicAgregatorServiceServer) testEmbeddedByValue() {}
|
||||
|
||||
// UnsafeMusicAgregatorServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to MusicAgregatorServiceServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeMusicAgregatorServiceServer interface {
|
||||
mustEmbedUnimplementedMusicAgregatorServiceServer()
|
||||
}
|
||||
|
||||
func RegisterMusicAgregatorServiceServer(s grpc.ServiceRegistrar, srv MusicAgregatorServiceServer) {
|
||||
// If the following call panics, it indicates UnimplementedMusicAgregatorServiceServer was
|
||||
// embedded by pointer and is nil. This will cause panics if an
|
||||
// unimplemented method is ever invoked, so we test this at initialization
|
||||
// time to prevent it from happening at runtime later due to I/O.
|
||||
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
|
||||
t.testEmbeddedByValue()
|
||||
}
|
||||
s.RegisterService(&MusicAgregatorService_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _MusicAgregatorService_MonitorAlbum_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(MonitorAlbumRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(MusicAgregatorServiceServer).MonitorAlbum(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: MusicAgregatorService_MonitorAlbum_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(MusicAgregatorServiceServer).MonitorAlbum(ctx, req.(*MonitorAlbumRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _MusicAgregatorService_MonitorAlbumStream_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
return srv.(MusicAgregatorServiceServer).MonitorAlbumStream(&grpc.GenericServerStream[MonitorAlbumStreamRequest, MonitorAlbumStreamResponse]{ServerStream: stream})
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type MusicAgregatorService_MonitorAlbumStreamServer = grpc.BidiStreamingServer[MonitorAlbumStreamRequest, MonitorAlbumStreamResponse]
|
||||
|
||||
func _MusicAgregatorService_GetArtists_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetArtistsRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(MusicAgregatorServiceServer).GetArtists(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: MusicAgregatorService_GetArtists_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(MusicAgregatorServiceServer).GetArtists(ctx, req.(*GetArtistsRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _MusicAgregatorService_GetAlbum_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetAlbumRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(MusicAgregatorServiceServer).GetAlbum(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: MusicAgregatorService_GetAlbum_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(MusicAgregatorServiceServer).GetAlbum(ctx, req.(*GetAlbumRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _MusicAgregatorService_AnalyzeAlbumRelease_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(AnalyzeAlbumReleaseRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(MusicAgregatorServiceServer).AnalyzeAlbumRelease(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: MusicAgregatorService_AnalyzeAlbumRelease_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(MusicAgregatorServiceServer).AnalyzeAlbumRelease(ctx, req.(*AnalyzeAlbumReleaseRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _MusicAgregatorService_SearchArtists_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(SearchArtistsRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(MusicAgregatorServiceServer).SearchArtists(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: MusicAgregatorService_SearchArtists_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(MusicAgregatorServiceServer).SearchArtists(ctx, req.(*SearchArtistsRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _MusicAgregatorService_GetArtistAlbums_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetArtistAlbumsRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(MusicAgregatorServiceServer).GetArtistAlbums(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: MusicAgregatorService_GetArtistAlbums_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(MusicAgregatorServiceServer).GetArtistAlbums(ctx, req.(*GetArtistAlbumsRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _MusicAgregatorService_SubscribeEvents_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(SubscribeEventsRequest)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.(MusicAgregatorServiceServer).SubscribeEvents(m, &grpc.GenericServerStream[SubscribeEventsRequest, AlbumEvent]{ServerStream: stream})
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type MusicAgregatorService_SubscribeEventsServer = grpc.ServerStreamingServer[AlbumEvent]
|
||||
|
||||
func _MusicAgregatorService_ResyncAlbum_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ResyncAlbumRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(MusicAgregatorServiceServer).ResyncAlbum(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: MusicAgregatorService_ResyncAlbum_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(MusicAgregatorServiceServer).ResyncAlbum(ctx, req.(*ResyncAlbumRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// MusicAgregatorService_ServiceDesc is the grpc.ServiceDesc for MusicAgregatorService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var MusicAgregatorService_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "music_agregator.v1.MusicAgregatorService",
|
||||
HandlerType: (*MusicAgregatorServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "MonitorAlbum",
|
||||
Handler: _MusicAgregatorService_MonitorAlbum_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetArtists",
|
||||
Handler: _MusicAgregatorService_GetArtists_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetAlbum",
|
||||
Handler: _MusicAgregatorService_GetAlbum_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "AnalyzeAlbumRelease",
|
||||
Handler: _MusicAgregatorService_AnalyzeAlbumRelease_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "SearchArtists",
|
||||
Handler: _MusicAgregatorService_SearchArtists_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetArtistAlbums",
|
||||
Handler: _MusicAgregatorService_GetArtistAlbums_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ResyncAlbum",
|
||||
Handler: _MusicAgregatorService_ResyncAlbum_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
StreamName: "MonitorAlbumStream",
|
||||
Handler: _MusicAgregatorService_MonitorAlbumStream_Handler,
|
||||
ServerStreams: true,
|
||||
ClientStreams: true,
|
||||
},
|
||||
{
|
||||
StreamName: "SubscribeEvents",
|
||||
Handler: _MusicAgregatorService_SubscribeEvents_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: "music_agregator/v1/music_agregator.proto",
|
||||
}
|
||||
-3274
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,775 +0,0 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.6.1
|
||||
// - protoc (unknown)
|
||||
// source: musicfs.proto
|
||||
|
||||
package musicfsv1
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.64.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion9
|
||||
|
||||
const (
|
||||
MusicFS_Search_FullMethodName = "/musicfs.v1.MusicFS/Search"
|
||||
MusicFS_SearchStream_FullMethodName = "/musicfs.v1.MusicFS/SearchStream"
|
||||
MusicFS_GetStatus_FullMethodName = "/musicfs.v1.MusicFS/GetStatus"
|
||||
MusicFS_Shutdown_FullMethodName = "/musicfs.v1.MusicFS/Shutdown"
|
||||
MusicFS_GetCacheStats_FullMethodName = "/musicfs.v1.MusicFS/GetCacheStats"
|
||||
MusicFS_ClearCache_FullMethodName = "/musicfs.v1.MusicFS/ClearCache"
|
||||
MusicFS_Prefetch_FullMethodName = "/musicfs.v1.MusicFS/Prefetch"
|
||||
MusicFS_ListOrigins_FullMethodName = "/musicfs.v1.MusicFS/ListOrigins"
|
||||
MusicFS_GetOriginHealth_FullMethodName = "/musicfs.v1.MusicFS/GetOriginHealth"
|
||||
MusicFS_RescanOrigin_FullMethodName = "/musicfs.v1.MusicFS/RescanOrigin"
|
||||
MusicFS_SubscribeEvents_FullMethodName = "/musicfs.v1.MusicFS/SubscribeEvents"
|
||||
)
|
||||
|
||||
// MusicFSClient is the client API for MusicFS service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type MusicFSClient interface {
|
||||
Search(ctx context.Context, in *SearchRequest, opts ...grpc.CallOption) (*SearchResponse, error)
|
||||
SearchStream(ctx context.Context, in *SearchRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[SearchResult], error)
|
||||
GetStatus(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*StatusResponse, error)
|
||||
Shutdown(ctx context.Context, in *ShutdownRequest, opts ...grpc.CallOption) (*Empty, error)
|
||||
GetCacheStats(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*CacheStats, error)
|
||||
ClearCache(ctx context.Context, in *ClearCacheRequest, opts ...grpc.CallOption) (*ClearCacheResponse, error)
|
||||
Prefetch(ctx context.Context, in *PrefetchRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[PrefetchProgress], error)
|
||||
ListOrigins(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*OriginsResponse, error)
|
||||
GetOriginHealth(ctx context.Context, in *OriginRequest, opts ...grpc.CallOption) (*OriginHealthResponse, error)
|
||||
RescanOrigin(ctx context.Context, in *OriginRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[SyncProgress], error)
|
||||
SubscribeEvents(ctx context.Context, in *EventFilter, opts ...grpc.CallOption) (grpc.ServerStreamingClient[Event], error)
|
||||
}
|
||||
|
||||
type musicFSClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewMusicFSClient(cc grpc.ClientConnInterface) MusicFSClient {
|
||||
return &musicFSClient{cc}
|
||||
}
|
||||
|
||||
func (c *musicFSClient) Search(ctx context.Context, in *SearchRequest, opts ...grpc.CallOption) (*SearchResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(SearchResponse)
|
||||
err := c.cc.Invoke(ctx, MusicFS_Search_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *musicFSClient) SearchStream(ctx context.Context, in *SearchRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[SearchResult], error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &MusicFS_ServiceDesc.Streams[0], MusicFS_SearchStream_FullMethodName, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &grpc.GenericClientStream[SearchRequest, SearchResult]{ClientStream: stream}
|
||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type MusicFS_SearchStreamClient = grpc.ServerStreamingClient[SearchResult]
|
||||
|
||||
func (c *musicFSClient) GetStatus(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*StatusResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(StatusResponse)
|
||||
err := c.cc.Invoke(ctx, MusicFS_GetStatus_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *musicFSClient) Shutdown(ctx context.Context, in *ShutdownRequest, opts ...grpc.CallOption) (*Empty, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(Empty)
|
||||
err := c.cc.Invoke(ctx, MusicFS_Shutdown_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *musicFSClient) GetCacheStats(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*CacheStats, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(CacheStats)
|
||||
err := c.cc.Invoke(ctx, MusicFS_GetCacheStats_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *musicFSClient) ClearCache(ctx context.Context, in *ClearCacheRequest, opts ...grpc.CallOption) (*ClearCacheResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(ClearCacheResponse)
|
||||
err := c.cc.Invoke(ctx, MusicFS_ClearCache_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *musicFSClient) Prefetch(ctx context.Context, in *PrefetchRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[PrefetchProgress], error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &MusicFS_ServiceDesc.Streams[1], MusicFS_Prefetch_FullMethodName, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &grpc.GenericClientStream[PrefetchRequest, PrefetchProgress]{ClientStream: stream}
|
||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type MusicFS_PrefetchClient = grpc.ServerStreamingClient[PrefetchProgress]
|
||||
|
||||
func (c *musicFSClient) ListOrigins(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*OriginsResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(OriginsResponse)
|
||||
err := c.cc.Invoke(ctx, MusicFS_ListOrigins_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *musicFSClient) GetOriginHealth(ctx context.Context, in *OriginRequest, opts ...grpc.CallOption) (*OriginHealthResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(OriginHealthResponse)
|
||||
err := c.cc.Invoke(ctx, MusicFS_GetOriginHealth_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *musicFSClient) RescanOrigin(ctx context.Context, in *OriginRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[SyncProgress], error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &MusicFS_ServiceDesc.Streams[2], MusicFS_RescanOrigin_FullMethodName, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &grpc.GenericClientStream[OriginRequest, SyncProgress]{ClientStream: stream}
|
||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type MusicFS_RescanOriginClient = grpc.ServerStreamingClient[SyncProgress]
|
||||
|
||||
func (c *musicFSClient) SubscribeEvents(ctx context.Context, in *EventFilter, opts ...grpc.CallOption) (grpc.ServerStreamingClient[Event], error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &MusicFS_ServiceDesc.Streams[3], MusicFS_SubscribeEvents_FullMethodName, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &grpc.GenericClientStream[EventFilter, Event]{ClientStream: stream}
|
||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type MusicFS_SubscribeEventsClient = grpc.ServerStreamingClient[Event]
|
||||
|
||||
// MusicFSServer is the server API for MusicFS service.
|
||||
// All implementations must embed UnimplementedMusicFSServer
|
||||
// for forward compatibility.
|
||||
type MusicFSServer interface {
|
||||
Search(context.Context, *SearchRequest) (*SearchResponse, error)
|
||||
SearchStream(*SearchRequest, grpc.ServerStreamingServer[SearchResult]) error
|
||||
GetStatus(context.Context, *Empty) (*StatusResponse, error)
|
||||
Shutdown(context.Context, *ShutdownRequest) (*Empty, error)
|
||||
GetCacheStats(context.Context, *Empty) (*CacheStats, error)
|
||||
ClearCache(context.Context, *ClearCacheRequest) (*ClearCacheResponse, error)
|
||||
Prefetch(*PrefetchRequest, grpc.ServerStreamingServer[PrefetchProgress]) error
|
||||
ListOrigins(context.Context, *Empty) (*OriginsResponse, error)
|
||||
GetOriginHealth(context.Context, *OriginRequest) (*OriginHealthResponse, error)
|
||||
RescanOrigin(*OriginRequest, grpc.ServerStreamingServer[SyncProgress]) error
|
||||
SubscribeEvents(*EventFilter, grpc.ServerStreamingServer[Event]) error
|
||||
mustEmbedUnimplementedMusicFSServer()
|
||||
}
|
||||
|
||||
// UnimplementedMusicFSServer must be embedded to have
|
||||
// forward compatible implementations.
|
||||
//
|
||||
// NOTE: this should be embedded by value instead of pointer to avoid a nil
|
||||
// pointer dereference when methods are called.
|
||||
type UnimplementedMusicFSServer struct{}
|
||||
|
||||
func (UnimplementedMusicFSServer) Search(context.Context, *SearchRequest) (*SearchResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method Search not implemented")
|
||||
}
|
||||
func (UnimplementedMusicFSServer) SearchStream(*SearchRequest, grpc.ServerStreamingServer[SearchResult]) error {
|
||||
return status.Error(codes.Unimplemented, "method SearchStream not implemented")
|
||||
}
|
||||
func (UnimplementedMusicFSServer) GetStatus(context.Context, *Empty) (*StatusResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method GetStatus not implemented")
|
||||
}
|
||||
func (UnimplementedMusicFSServer) Shutdown(context.Context, *ShutdownRequest) (*Empty, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method Shutdown not implemented")
|
||||
}
|
||||
func (UnimplementedMusicFSServer) GetCacheStats(context.Context, *Empty) (*CacheStats, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method GetCacheStats not implemented")
|
||||
}
|
||||
func (UnimplementedMusicFSServer) ClearCache(context.Context, *ClearCacheRequest) (*ClearCacheResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method ClearCache not implemented")
|
||||
}
|
||||
func (UnimplementedMusicFSServer) Prefetch(*PrefetchRequest, grpc.ServerStreamingServer[PrefetchProgress]) error {
|
||||
return status.Error(codes.Unimplemented, "method Prefetch not implemented")
|
||||
}
|
||||
func (UnimplementedMusicFSServer) ListOrigins(context.Context, *Empty) (*OriginsResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method ListOrigins not implemented")
|
||||
}
|
||||
func (UnimplementedMusicFSServer) GetOriginHealth(context.Context, *OriginRequest) (*OriginHealthResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method GetOriginHealth not implemented")
|
||||
}
|
||||
func (UnimplementedMusicFSServer) RescanOrigin(*OriginRequest, grpc.ServerStreamingServer[SyncProgress]) error {
|
||||
return status.Error(codes.Unimplemented, "method RescanOrigin not implemented")
|
||||
}
|
||||
func (UnimplementedMusicFSServer) SubscribeEvents(*EventFilter, grpc.ServerStreamingServer[Event]) error {
|
||||
return status.Error(codes.Unimplemented, "method SubscribeEvents not implemented")
|
||||
}
|
||||
func (UnimplementedMusicFSServer) mustEmbedUnimplementedMusicFSServer() {}
|
||||
func (UnimplementedMusicFSServer) testEmbeddedByValue() {}
|
||||
|
||||
// UnsafeMusicFSServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to MusicFSServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeMusicFSServer interface {
|
||||
mustEmbedUnimplementedMusicFSServer()
|
||||
}
|
||||
|
||||
func RegisterMusicFSServer(s grpc.ServiceRegistrar, srv MusicFSServer) {
|
||||
// If the following call panics, it indicates UnimplementedMusicFSServer was
|
||||
// embedded by pointer and is nil. This will cause panics if an
|
||||
// unimplemented method is ever invoked, so we test this at initialization
|
||||
// time to prevent it from happening at runtime later due to I/O.
|
||||
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
|
||||
t.testEmbeddedByValue()
|
||||
}
|
||||
s.RegisterService(&MusicFS_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _MusicFS_Search_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(SearchRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(MusicFSServer).Search(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: MusicFS_Search_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(MusicFSServer).Search(ctx, req.(*SearchRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _MusicFS_SearchStream_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(SearchRequest)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.(MusicFSServer).SearchStream(m, &grpc.GenericServerStream[SearchRequest, SearchResult]{ServerStream: stream})
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type MusicFS_SearchStreamServer = grpc.ServerStreamingServer[SearchResult]
|
||||
|
||||
func _MusicFS_GetStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Empty)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(MusicFSServer).GetStatus(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: MusicFS_GetStatus_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(MusicFSServer).GetStatus(ctx, req.(*Empty))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _MusicFS_Shutdown_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ShutdownRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(MusicFSServer).Shutdown(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: MusicFS_Shutdown_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(MusicFSServer).Shutdown(ctx, req.(*ShutdownRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _MusicFS_GetCacheStats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Empty)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(MusicFSServer).GetCacheStats(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: MusicFS_GetCacheStats_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(MusicFSServer).GetCacheStats(ctx, req.(*Empty))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _MusicFS_ClearCache_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ClearCacheRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(MusicFSServer).ClearCache(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: MusicFS_ClearCache_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(MusicFSServer).ClearCache(ctx, req.(*ClearCacheRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _MusicFS_Prefetch_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(PrefetchRequest)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.(MusicFSServer).Prefetch(m, &grpc.GenericServerStream[PrefetchRequest, PrefetchProgress]{ServerStream: stream})
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type MusicFS_PrefetchServer = grpc.ServerStreamingServer[PrefetchProgress]
|
||||
|
||||
func _MusicFS_ListOrigins_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Empty)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(MusicFSServer).ListOrigins(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: MusicFS_ListOrigins_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(MusicFSServer).ListOrigins(ctx, req.(*Empty))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _MusicFS_GetOriginHealth_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(OriginRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(MusicFSServer).GetOriginHealth(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: MusicFS_GetOriginHealth_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(MusicFSServer).GetOriginHealth(ctx, req.(*OriginRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _MusicFS_RescanOrigin_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(OriginRequest)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.(MusicFSServer).RescanOrigin(m, &grpc.GenericServerStream[OriginRequest, SyncProgress]{ServerStream: stream})
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type MusicFS_RescanOriginServer = grpc.ServerStreamingServer[SyncProgress]
|
||||
|
||||
func _MusicFS_SubscribeEvents_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(EventFilter)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.(MusicFSServer).SubscribeEvents(m, &grpc.GenericServerStream[EventFilter, Event]{ServerStream: stream})
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type MusicFS_SubscribeEventsServer = grpc.ServerStreamingServer[Event]
|
||||
|
||||
// MusicFS_ServiceDesc is the grpc.ServiceDesc for MusicFS service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var MusicFS_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "musicfs.v1.MusicFS",
|
||||
HandlerType: (*MusicFSServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Search",
|
||||
Handler: _MusicFS_Search_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetStatus",
|
||||
Handler: _MusicFS_GetStatus_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Shutdown",
|
||||
Handler: _MusicFS_Shutdown_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetCacheStats",
|
||||
Handler: _MusicFS_GetCacheStats_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ClearCache",
|
||||
Handler: _MusicFS_ClearCache_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ListOrigins",
|
||||
Handler: _MusicFS_ListOrigins_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetOriginHealth",
|
||||
Handler: _MusicFS_GetOriginHealth_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
StreamName: "SearchStream",
|
||||
Handler: _MusicFS_SearchStream_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
{
|
||||
StreamName: "Prefetch",
|
||||
Handler: _MusicFS_Prefetch_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
{
|
||||
StreamName: "RescanOrigin",
|
||||
Handler: _MusicFS_RescanOrigin_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
{
|
||||
StreamName: "SubscribeEvents",
|
||||
Handler: _MusicFS_SubscribeEvents_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: "musicfs.proto",
|
||||
}
|
||||
|
||||
const (
|
||||
MetadataService_GetMetadata_FullMethodName = "/musicfs.v1.MetadataService/GetMetadata"
|
||||
MetadataService_UpdateMetadata_FullMethodName = "/musicfs.v1.MetadataService/UpdateMetadata"
|
||||
MetadataService_ClearOverlay_FullMethodName = "/musicfs.v1.MetadataService/ClearOverlay"
|
||||
MetadataService_BatchUpdateMetadata_FullMethodName = "/musicfs.v1.MetadataService/BatchUpdateMetadata"
|
||||
MetadataService_ImportMetadata_FullMethodName = "/musicfs.v1.MetadataService/ImportMetadata"
|
||||
)
|
||||
|
||||
// MetadataServiceClient is the client API for MetadataService service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type MetadataServiceClient interface {
|
||||
GetMetadata(ctx context.Context, in *GetMetadataRequest, opts ...grpc.CallOption) (*MetadataResponse, error)
|
||||
UpdateMetadata(ctx context.Context, in *UpdateMetadataRequest, opts ...grpc.CallOption) (*UpdateMetadataResponse, error)
|
||||
ClearOverlay(ctx context.Context, in *ClearOverlayRequest, opts ...grpc.CallOption) (*ClearOverlayResponse, error)
|
||||
BatchUpdateMetadata(ctx context.Context, in *BatchUpdateRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[BatchUpdateProgress], error)
|
||||
ImportMetadata(ctx context.Context, in *ImportMetadataRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[ImportProgress], error)
|
||||
}
|
||||
|
||||
type metadataServiceClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewMetadataServiceClient(cc grpc.ClientConnInterface) MetadataServiceClient {
|
||||
return &metadataServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *metadataServiceClient) GetMetadata(ctx context.Context, in *GetMetadataRequest, opts ...grpc.CallOption) (*MetadataResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(MetadataResponse)
|
||||
err := c.cc.Invoke(ctx, MetadataService_GetMetadata_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *metadataServiceClient) UpdateMetadata(ctx context.Context, in *UpdateMetadataRequest, opts ...grpc.CallOption) (*UpdateMetadataResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(UpdateMetadataResponse)
|
||||
err := c.cc.Invoke(ctx, MetadataService_UpdateMetadata_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *metadataServiceClient) ClearOverlay(ctx context.Context, in *ClearOverlayRequest, opts ...grpc.CallOption) (*ClearOverlayResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(ClearOverlayResponse)
|
||||
err := c.cc.Invoke(ctx, MetadataService_ClearOverlay_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *metadataServiceClient) BatchUpdateMetadata(ctx context.Context, in *BatchUpdateRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[BatchUpdateProgress], error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &MetadataService_ServiceDesc.Streams[0], MetadataService_BatchUpdateMetadata_FullMethodName, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &grpc.GenericClientStream[BatchUpdateRequest, BatchUpdateProgress]{ClientStream: stream}
|
||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type MetadataService_BatchUpdateMetadataClient = grpc.ServerStreamingClient[BatchUpdateProgress]
|
||||
|
||||
func (c *metadataServiceClient) ImportMetadata(ctx context.Context, in *ImportMetadataRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[ImportProgress], error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &MetadataService_ServiceDesc.Streams[1], MetadataService_ImportMetadata_FullMethodName, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &grpc.GenericClientStream[ImportMetadataRequest, ImportProgress]{ClientStream: stream}
|
||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type MetadataService_ImportMetadataClient = grpc.ServerStreamingClient[ImportProgress]
|
||||
|
||||
// MetadataServiceServer is the server API for MetadataService service.
|
||||
// All implementations must embed UnimplementedMetadataServiceServer
|
||||
// for forward compatibility.
|
||||
type MetadataServiceServer interface {
|
||||
GetMetadata(context.Context, *GetMetadataRequest) (*MetadataResponse, error)
|
||||
UpdateMetadata(context.Context, *UpdateMetadataRequest) (*UpdateMetadataResponse, error)
|
||||
ClearOverlay(context.Context, *ClearOverlayRequest) (*ClearOverlayResponse, error)
|
||||
BatchUpdateMetadata(*BatchUpdateRequest, grpc.ServerStreamingServer[BatchUpdateProgress]) error
|
||||
ImportMetadata(*ImportMetadataRequest, grpc.ServerStreamingServer[ImportProgress]) error
|
||||
mustEmbedUnimplementedMetadataServiceServer()
|
||||
}
|
||||
|
||||
// UnimplementedMetadataServiceServer must be embedded to have
|
||||
// forward compatible implementations.
|
||||
//
|
||||
// NOTE: this should be embedded by value instead of pointer to avoid a nil
|
||||
// pointer dereference when methods are called.
|
||||
type UnimplementedMetadataServiceServer struct{}
|
||||
|
||||
func (UnimplementedMetadataServiceServer) GetMetadata(context.Context, *GetMetadataRequest) (*MetadataResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method GetMetadata not implemented")
|
||||
}
|
||||
func (UnimplementedMetadataServiceServer) UpdateMetadata(context.Context, *UpdateMetadataRequest) (*UpdateMetadataResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method UpdateMetadata not implemented")
|
||||
}
|
||||
func (UnimplementedMetadataServiceServer) ClearOverlay(context.Context, *ClearOverlayRequest) (*ClearOverlayResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method ClearOverlay not implemented")
|
||||
}
|
||||
func (UnimplementedMetadataServiceServer) BatchUpdateMetadata(*BatchUpdateRequest, grpc.ServerStreamingServer[BatchUpdateProgress]) error {
|
||||
return status.Error(codes.Unimplemented, "method BatchUpdateMetadata not implemented")
|
||||
}
|
||||
func (UnimplementedMetadataServiceServer) ImportMetadata(*ImportMetadataRequest, grpc.ServerStreamingServer[ImportProgress]) error {
|
||||
return status.Error(codes.Unimplemented, "method ImportMetadata not implemented")
|
||||
}
|
||||
func (UnimplementedMetadataServiceServer) mustEmbedUnimplementedMetadataServiceServer() {}
|
||||
func (UnimplementedMetadataServiceServer) testEmbeddedByValue() {}
|
||||
|
||||
// UnsafeMetadataServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to MetadataServiceServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeMetadataServiceServer interface {
|
||||
mustEmbedUnimplementedMetadataServiceServer()
|
||||
}
|
||||
|
||||
func RegisterMetadataServiceServer(s grpc.ServiceRegistrar, srv MetadataServiceServer) {
|
||||
// If the following call panics, it indicates UnimplementedMetadataServiceServer was
|
||||
// embedded by pointer and is nil. This will cause panics if an
|
||||
// unimplemented method is ever invoked, so we test this at initialization
|
||||
// time to prevent it from happening at runtime later due to I/O.
|
||||
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
|
||||
t.testEmbeddedByValue()
|
||||
}
|
||||
s.RegisterService(&MetadataService_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _MetadataService_GetMetadata_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetMetadataRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(MetadataServiceServer).GetMetadata(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: MetadataService_GetMetadata_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(MetadataServiceServer).GetMetadata(ctx, req.(*GetMetadataRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _MetadataService_UpdateMetadata_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(UpdateMetadataRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(MetadataServiceServer).UpdateMetadata(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: MetadataService_UpdateMetadata_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(MetadataServiceServer).UpdateMetadata(ctx, req.(*UpdateMetadataRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _MetadataService_ClearOverlay_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ClearOverlayRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(MetadataServiceServer).ClearOverlay(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: MetadataService_ClearOverlay_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(MetadataServiceServer).ClearOverlay(ctx, req.(*ClearOverlayRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _MetadataService_BatchUpdateMetadata_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(BatchUpdateRequest)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.(MetadataServiceServer).BatchUpdateMetadata(m, &grpc.GenericServerStream[BatchUpdateRequest, BatchUpdateProgress]{ServerStream: stream})
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type MetadataService_BatchUpdateMetadataServer = grpc.ServerStreamingServer[BatchUpdateProgress]
|
||||
|
||||
func _MetadataService_ImportMetadata_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(ImportMetadataRequest)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.(MetadataServiceServer).ImportMetadata(m, &grpc.GenericServerStream[ImportMetadataRequest, ImportProgress]{ServerStream: stream})
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type MetadataService_ImportMetadataServer = grpc.ServerStreamingServer[ImportProgress]
|
||||
|
||||
// MetadataService_ServiceDesc is the grpc.ServiceDesc for MetadataService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var MetadataService_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "musicfs.v1.MetadataService",
|
||||
HandlerType: (*MetadataServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "GetMetadata",
|
||||
Handler: _MetadataService_GetMetadata_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "UpdateMetadata",
|
||||
Handler: _MetadataService_UpdateMetadata_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ClearOverlay",
|
||||
Handler: _MetadataService_ClearOverlay_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
StreamName: "BatchUpdateMetadata",
|
||||
Handler: _MetadataService_BatchUpdateMetadata_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
{
|
||||
StreamName: "ImportMetadata",
|
||||
Handler: _MetadataService_ImportMetadata_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: "musicfs.proto",
|
||||
}
|
||||
@@ -1,775 +0,0 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.6.1
|
||||
// - protoc (unknown)
|
||||
// source: musicfs.proto
|
||||
|
||||
package musicfsv1
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.64.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion9
|
||||
|
||||
const (
|
||||
MusicFS_Search_FullMethodName = "/musicfs.v1.MusicFS/Search"
|
||||
MusicFS_SearchStream_FullMethodName = "/musicfs.v1.MusicFS/SearchStream"
|
||||
MusicFS_GetStatus_FullMethodName = "/musicfs.v1.MusicFS/GetStatus"
|
||||
MusicFS_Shutdown_FullMethodName = "/musicfs.v1.MusicFS/Shutdown"
|
||||
MusicFS_GetCacheStats_FullMethodName = "/musicfs.v1.MusicFS/GetCacheStats"
|
||||
MusicFS_ClearCache_FullMethodName = "/musicfs.v1.MusicFS/ClearCache"
|
||||
MusicFS_Prefetch_FullMethodName = "/musicfs.v1.MusicFS/Prefetch"
|
||||
MusicFS_ListOrigins_FullMethodName = "/musicfs.v1.MusicFS/ListOrigins"
|
||||
MusicFS_GetOriginHealth_FullMethodName = "/musicfs.v1.MusicFS/GetOriginHealth"
|
||||
MusicFS_RescanOrigin_FullMethodName = "/musicfs.v1.MusicFS/RescanOrigin"
|
||||
MusicFS_SubscribeEvents_FullMethodName = "/musicfs.v1.MusicFS/SubscribeEvents"
|
||||
)
|
||||
|
||||
// MusicFSClient is the client API for MusicFS service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type MusicFSClient interface {
|
||||
Search(ctx context.Context, in *SearchRequest, opts ...grpc.CallOption) (*SearchResponse, error)
|
||||
SearchStream(ctx context.Context, in *SearchRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[SearchResult], error)
|
||||
GetStatus(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*StatusResponse, error)
|
||||
Shutdown(ctx context.Context, in *ShutdownRequest, opts ...grpc.CallOption) (*Empty, error)
|
||||
GetCacheStats(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*CacheStats, error)
|
||||
ClearCache(ctx context.Context, in *ClearCacheRequest, opts ...grpc.CallOption) (*ClearCacheResponse, error)
|
||||
Prefetch(ctx context.Context, in *PrefetchRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[PrefetchProgress], error)
|
||||
ListOrigins(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*OriginsResponse, error)
|
||||
GetOriginHealth(ctx context.Context, in *OriginRequest, opts ...grpc.CallOption) (*OriginHealthResponse, error)
|
||||
RescanOrigin(ctx context.Context, in *OriginRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[SyncProgress], error)
|
||||
SubscribeEvents(ctx context.Context, in *EventFilter, opts ...grpc.CallOption) (grpc.ServerStreamingClient[Event], error)
|
||||
}
|
||||
|
||||
type musicFSClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewMusicFSClient(cc grpc.ClientConnInterface) MusicFSClient {
|
||||
return &musicFSClient{cc}
|
||||
}
|
||||
|
||||
func (c *musicFSClient) Search(ctx context.Context, in *SearchRequest, opts ...grpc.CallOption) (*SearchResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(SearchResponse)
|
||||
err := c.cc.Invoke(ctx, MusicFS_Search_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *musicFSClient) SearchStream(ctx context.Context, in *SearchRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[SearchResult], error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &MusicFS_ServiceDesc.Streams[0], MusicFS_SearchStream_FullMethodName, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &grpc.GenericClientStream[SearchRequest, SearchResult]{ClientStream: stream}
|
||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type MusicFS_SearchStreamClient = grpc.ServerStreamingClient[SearchResult]
|
||||
|
||||
func (c *musicFSClient) GetStatus(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*StatusResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(StatusResponse)
|
||||
err := c.cc.Invoke(ctx, MusicFS_GetStatus_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *musicFSClient) Shutdown(ctx context.Context, in *ShutdownRequest, opts ...grpc.CallOption) (*Empty, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(Empty)
|
||||
err := c.cc.Invoke(ctx, MusicFS_Shutdown_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *musicFSClient) GetCacheStats(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*CacheStats, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(CacheStats)
|
||||
err := c.cc.Invoke(ctx, MusicFS_GetCacheStats_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *musicFSClient) ClearCache(ctx context.Context, in *ClearCacheRequest, opts ...grpc.CallOption) (*ClearCacheResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(ClearCacheResponse)
|
||||
err := c.cc.Invoke(ctx, MusicFS_ClearCache_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *musicFSClient) Prefetch(ctx context.Context, in *PrefetchRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[PrefetchProgress], error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &MusicFS_ServiceDesc.Streams[1], MusicFS_Prefetch_FullMethodName, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &grpc.GenericClientStream[PrefetchRequest, PrefetchProgress]{ClientStream: stream}
|
||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type MusicFS_PrefetchClient = grpc.ServerStreamingClient[PrefetchProgress]
|
||||
|
||||
func (c *musicFSClient) ListOrigins(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*OriginsResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(OriginsResponse)
|
||||
err := c.cc.Invoke(ctx, MusicFS_ListOrigins_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *musicFSClient) GetOriginHealth(ctx context.Context, in *OriginRequest, opts ...grpc.CallOption) (*OriginHealthResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(OriginHealthResponse)
|
||||
err := c.cc.Invoke(ctx, MusicFS_GetOriginHealth_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *musicFSClient) RescanOrigin(ctx context.Context, in *OriginRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[SyncProgress], error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &MusicFS_ServiceDesc.Streams[2], MusicFS_RescanOrigin_FullMethodName, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &grpc.GenericClientStream[OriginRequest, SyncProgress]{ClientStream: stream}
|
||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type MusicFS_RescanOriginClient = grpc.ServerStreamingClient[SyncProgress]
|
||||
|
||||
func (c *musicFSClient) SubscribeEvents(ctx context.Context, in *EventFilter, opts ...grpc.CallOption) (grpc.ServerStreamingClient[Event], error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &MusicFS_ServiceDesc.Streams[3], MusicFS_SubscribeEvents_FullMethodName, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &grpc.GenericClientStream[EventFilter, Event]{ClientStream: stream}
|
||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type MusicFS_SubscribeEventsClient = grpc.ServerStreamingClient[Event]
|
||||
|
||||
// MusicFSServer is the server API for MusicFS service.
|
||||
// All implementations must embed UnimplementedMusicFSServer
|
||||
// for forward compatibility.
|
||||
type MusicFSServer interface {
|
||||
Search(context.Context, *SearchRequest) (*SearchResponse, error)
|
||||
SearchStream(*SearchRequest, grpc.ServerStreamingServer[SearchResult]) error
|
||||
GetStatus(context.Context, *Empty) (*StatusResponse, error)
|
||||
Shutdown(context.Context, *ShutdownRequest) (*Empty, error)
|
||||
GetCacheStats(context.Context, *Empty) (*CacheStats, error)
|
||||
ClearCache(context.Context, *ClearCacheRequest) (*ClearCacheResponse, error)
|
||||
Prefetch(*PrefetchRequest, grpc.ServerStreamingServer[PrefetchProgress]) error
|
||||
ListOrigins(context.Context, *Empty) (*OriginsResponse, error)
|
||||
GetOriginHealth(context.Context, *OriginRequest) (*OriginHealthResponse, error)
|
||||
RescanOrigin(*OriginRequest, grpc.ServerStreamingServer[SyncProgress]) error
|
||||
SubscribeEvents(*EventFilter, grpc.ServerStreamingServer[Event]) error
|
||||
mustEmbedUnimplementedMusicFSServer()
|
||||
}
|
||||
|
||||
// UnimplementedMusicFSServer must be embedded to have
|
||||
// forward compatible implementations.
|
||||
//
|
||||
// NOTE: this should be embedded by value instead of pointer to avoid a nil
|
||||
// pointer dereference when methods are called.
|
||||
type UnimplementedMusicFSServer struct{}
|
||||
|
||||
func (UnimplementedMusicFSServer) Search(context.Context, *SearchRequest) (*SearchResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method Search not implemented")
|
||||
}
|
||||
func (UnimplementedMusicFSServer) SearchStream(*SearchRequest, grpc.ServerStreamingServer[SearchResult]) error {
|
||||
return status.Error(codes.Unimplemented, "method SearchStream not implemented")
|
||||
}
|
||||
func (UnimplementedMusicFSServer) GetStatus(context.Context, *Empty) (*StatusResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method GetStatus not implemented")
|
||||
}
|
||||
func (UnimplementedMusicFSServer) Shutdown(context.Context, *ShutdownRequest) (*Empty, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method Shutdown not implemented")
|
||||
}
|
||||
func (UnimplementedMusicFSServer) GetCacheStats(context.Context, *Empty) (*CacheStats, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method GetCacheStats not implemented")
|
||||
}
|
||||
func (UnimplementedMusicFSServer) ClearCache(context.Context, *ClearCacheRequest) (*ClearCacheResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method ClearCache not implemented")
|
||||
}
|
||||
func (UnimplementedMusicFSServer) Prefetch(*PrefetchRequest, grpc.ServerStreamingServer[PrefetchProgress]) error {
|
||||
return status.Error(codes.Unimplemented, "method Prefetch not implemented")
|
||||
}
|
||||
func (UnimplementedMusicFSServer) ListOrigins(context.Context, *Empty) (*OriginsResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method ListOrigins not implemented")
|
||||
}
|
||||
func (UnimplementedMusicFSServer) GetOriginHealth(context.Context, *OriginRequest) (*OriginHealthResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method GetOriginHealth not implemented")
|
||||
}
|
||||
func (UnimplementedMusicFSServer) RescanOrigin(*OriginRequest, grpc.ServerStreamingServer[SyncProgress]) error {
|
||||
return status.Error(codes.Unimplemented, "method RescanOrigin not implemented")
|
||||
}
|
||||
func (UnimplementedMusicFSServer) SubscribeEvents(*EventFilter, grpc.ServerStreamingServer[Event]) error {
|
||||
return status.Error(codes.Unimplemented, "method SubscribeEvents not implemented")
|
||||
}
|
||||
func (UnimplementedMusicFSServer) mustEmbedUnimplementedMusicFSServer() {}
|
||||
func (UnimplementedMusicFSServer) testEmbeddedByValue() {}
|
||||
|
||||
// UnsafeMusicFSServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to MusicFSServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeMusicFSServer interface {
|
||||
mustEmbedUnimplementedMusicFSServer()
|
||||
}
|
||||
|
||||
func RegisterMusicFSServer(s grpc.ServiceRegistrar, srv MusicFSServer) {
|
||||
// If the following call panics, it indicates UnimplementedMusicFSServer was
|
||||
// embedded by pointer and is nil. This will cause panics if an
|
||||
// unimplemented method is ever invoked, so we test this at initialization
|
||||
// time to prevent it from happening at runtime later due to I/O.
|
||||
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
|
||||
t.testEmbeddedByValue()
|
||||
}
|
||||
s.RegisterService(&MusicFS_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _MusicFS_Search_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(SearchRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(MusicFSServer).Search(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: MusicFS_Search_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(MusicFSServer).Search(ctx, req.(*SearchRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _MusicFS_SearchStream_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(SearchRequest)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.(MusicFSServer).SearchStream(m, &grpc.GenericServerStream[SearchRequest, SearchResult]{ServerStream: stream})
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type MusicFS_SearchStreamServer = grpc.ServerStreamingServer[SearchResult]
|
||||
|
||||
func _MusicFS_GetStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Empty)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(MusicFSServer).GetStatus(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: MusicFS_GetStatus_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(MusicFSServer).GetStatus(ctx, req.(*Empty))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _MusicFS_Shutdown_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ShutdownRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(MusicFSServer).Shutdown(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: MusicFS_Shutdown_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(MusicFSServer).Shutdown(ctx, req.(*ShutdownRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _MusicFS_GetCacheStats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Empty)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(MusicFSServer).GetCacheStats(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: MusicFS_GetCacheStats_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(MusicFSServer).GetCacheStats(ctx, req.(*Empty))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _MusicFS_ClearCache_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ClearCacheRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(MusicFSServer).ClearCache(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: MusicFS_ClearCache_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(MusicFSServer).ClearCache(ctx, req.(*ClearCacheRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _MusicFS_Prefetch_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(PrefetchRequest)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.(MusicFSServer).Prefetch(m, &grpc.GenericServerStream[PrefetchRequest, PrefetchProgress]{ServerStream: stream})
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type MusicFS_PrefetchServer = grpc.ServerStreamingServer[PrefetchProgress]
|
||||
|
||||
func _MusicFS_ListOrigins_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Empty)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(MusicFSServer).ListOrigins(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: MusicFS_ListOrigins_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(MusicFSServer).ListOrigins(ctx, req.(*Empty))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _MusicFS_GetOriginHealth_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(OriginRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(MusicFSServer).GetOriginHealth(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: MusicFS_GetOriginHealth_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(MusicFSServer).GetOriginHealth(ctx, req.(*OriginRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _MusicFS_RescanOrigin_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(OriginRequest)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.(MusicFSServer).RescanOrigin(m, &grpc.GenericServerStream[OriginRequest, SyncProgress]{ServerStream: stream})
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type MusicFS_RescanOriginServer = grpc.ServerStreamingServer[SyncProgress]
|
||||
|
||||
func _MusicFS_SubscribeEvents_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(EventFilter)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.(MusicFSServer).SubscribeEvents(m, &grpc.GenericServerStream[EventFilter, Event]{ServerStream: stream})
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type MusicFS_SubscribeEventsServer = grpc.ServerStreamingServer[Event]
|
||||
|
||||
// MusicFS_ServiceDesc is the grpc.ServiceDesc for MusicFS service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var MusicFS_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "musicfs.v1.MusicFS",
|
||||
HandlerType: (*MusicFSServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Search",
|
||||
Handler: _MusicFS_Search_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetStatus",
|
||||
Handler: _MusicFS_GetStatus_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Shutdown",
|
||||
Handler: _MusicFS_Shutdown_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetCacheStats",
|
||||
Handler: _MusicFS_GetCacheStats_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ClearCache",
|
||||
Handler: _MusicFS_ClearCache_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ListOrigins",
|
||||
Handler: _MusicFS_ListOrigins_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetOriginHealth",
|
||||
Handler: _MusicFS_GetOriginHealth_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
StreamName: "SearchStream",
|
||||
Handler: _MusicFS_SearchStream_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
{
|
||||
StreamName: "Prefetch",
|
||||
Handler: _MusicFS_Prefetch_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
{
|
||||
StreamName: "RescanOrigin",
|
||||
Handler: _MusicFS_RescanOrigin_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
{
|
||||
StreamName: "SubscribeEvents",
|
||||
Handler: _MusicFS_SubscribeEvents_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: "musicfs.proto",
|
||||
}
|
||||
|
||||
const (
|
||||
MetadataService_GetMetadata_FullMethodName = "/musicfs.v1.MetadataService/GetMetadata"
|
||||
MetadataService_UpdateMetadata_FullMethodName = "/musicfs.v1.MetadataService/UpdateMetadata"
|
||||
MetadataService_ClearOverlay_FullMethodName = "/musicfs.v1.MetadataService/ClearOverlay"
|
||||
MetadataService_BatchUpdateMetadata_FullMethodName = "/musicfs.v1.MetadataService/BatchUpdateMetadata"
|
||||
MetadataService_ImportMetadata_FullMethodName = "/musicfs.v1.MetadataService/ImportMetadata"
|
||||
)
|
||||
|
||||
// MetadataServiceClient is the client API for MetadataService service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type MetadataServiceClient interface {
|
||||
GetMetadata(ctx context.Context, in *GetMetadataRequest, opts ...grpc.CallOption) (*MetadataResponse, error)
|
||||
UpdateMetadata(ctx context.Context, in *UpdateMetadataRequest, opts ...grpc.CallOption) (*UpdateMetadataResponse, error)
|
||||
ClearOverlay(ctx context.Context, in *ClearOverlayRequest, opts ...grpc.CallOption) (*ClearOverlayResponse, error)
|
||||
BatchUpdateMetadata(ctx context.Context, in *BatchUpdateRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[BatchUpdateProgress], error)
|
||||
ImportMetadata(ctx context.Context, in *ImportMetadataRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[ImportProgress], error)
|
||||
}
|
||||
|
||||
type metadataServiceClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewMetadataServiceClient(cc grpc.ClientConnInterface) MetadataServiceClient {
|
||||
return &metadataServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *metadataServiceClient) GetMetadata(ctx context.Context, in *GetMetadataRequest, opts ...grpc.CallOption) (*MetadataResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(MetadataResponse)
|
||||
err := c.cc.Invoke(ctx, MetadataService_GetMetadata_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *metadataServiceClient) UpdateMetadata(ctx context.Context, in *UpdateMetadataRequest, opts ...grpc.CallOption) (*UpdateMetadataResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(UpdateMetadataResponse)
|
||||
err := c.cc.Invoke(ctx, MetadataService_UpdateMetadata_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *metadataServiceClient) ClearOverlay(ctx context.Context, in *ClearOverlayRequest, opts ...grpc.CallOption) (*ClearOverlayResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(ClearOverlayResponse)
|
||||
err := c.cc.Invoke(ctx, MetadataService_ClearOverlay_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *metadataServiceClient) BatchUpdateMetadata(ctx context.Context, in *BatchUpdateRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[BatchUpdateProgress], error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &MetadataService_ServiceDesc.Streams[0], MetadataService_BatchUpdateMetadata_FullMethodName, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &grpc.GenericClientStream[BatchUpdateRequest, BatchUpdateProgress]{ClientStream: stream}
|
||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type MetadataService_BatchUpdateMetadataClient = grpc.ServerStreamingClient[BatchUpdateProgress]
|
||||
|
||||
func (c *metadataServiceClient) ImportMetadata(ctx context.Context, in *ImportMetadataRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[ImportProgress], error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &MetadataService_ServiceDesc.Streams[1], MetadataService_ImportMetadata_FullMethodName, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &grpc.GenericClientStream[ImportMetadataRequest, ImportProgress]{ClientStream: stream}
|
||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type MetadataService_ImportMetadataClient = grpc.ServerStreamingClient[ImportProgress]
|
||||
|
||||
// MetadataServiceServer is the server API for MetadataService service.
|
||||
// All implementations must embed UnimplementedMetadataServiceServer
|
||||
// for forward compatibility.
|
||||
type MetadataServiceServer interface {
|
||||
GetMetadata(context.Context, *GetMetadataRequest) (*MetadataResponse, error)
|
||||
UpdateMetadata(context.Context, *UpdateMetadataRequest) (*UpdateMetadataResponse, error)
|
||||
ClearOverlay(context.Context, *ClearOverlayRequest) (*ClearOverlayResponse, error)
|
||||
BatchUpdateMetadata(*BatchUpdateRequest, grpc.ServerStreamingServer[BatchUpdateProgress]) error
|
||||
ImportMetadata(*ImportMetadataRequest, grpc.ServerStreamingServer[ImportProgress]) error
|
||||
mustEmbedUnimplementedMetadataServiceServer()
|
||||
}
|
||||
|
||||
// UnimplementedMetadataServiceServer must be embedded to have
|
||||
// forward compatible implementations.
|
||||
//
|
||||
// NOTE: this should be embedded by value instead of pointer to avoid a nil
|
||||
// pointer dereference when methods are called.
|
||||
type UnimplementedMetadataServiceServer struct{}
|
||||
|
||||
func (UnimplementedMetadataServiceServer) GetMetadata(context.Context, *GetMetadataRequest) (*MetadataResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method GetMetadata not implemented")
|
||||
}
|
||||
func (UnimplementedMetadataServiceServer) UpdateMetadata(context.Context, *UpdateMetadataRequest) (*UpdateMetadataResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method UpdateMetadata not implemented")
|
||||
}
|
||||
func (UnimplementedMetadataServiceServer) ClearOverlay(context.Context, *ClearOverlayRequest) (*ClearOverlayResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method ClearOverlay not implemented")
|
||||
}
|
||||
func (UnimplementedMetadataServiceServer) BatchUpdateMetadata(*BatchUpdateRequest, grpc.ServerStreamingServer[BatchUpdateProgress]) error {
|
||||
return status.Error(codes.Unimplemented, "method BatchUpdateMetadata not implemented")
|
||||
}
|
||||
func (UnimplementedMetadataServiceServer) ImportMetadata(*ImportMetadataRequest, grpc.ServerStreamingServer[ImportProgress]) error {
|
||||
return status.Error(codes.Unimplemented, "method ImportMetadata not implemented")
|
||||
}
|
||||
func (UnimplementedMetadataServiceServer) mustEmbedUnimplementedMetadataServiceServer() {}
|
||||
func (UnimplementedMetadataServiceServer) testEmbeddedByValue() {}
|
||||
|
||||
// UnsafeMetadataServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to MetadataServiceServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeMetadataServiceServer interface {
|
||||
mustEmbedUnimplementedMetadataServiceServer()
|
||||
}
|
||||
|
||||
func RegisterMetadataServiceServer(s grpc.ServiceRegistrar, srv MetadataServiceServer) {
|
||||
// If the following call panics, it indicates UnimplementedMetadataServiceServer was
|
||||
// embedded by pointer and is nil. This will cause panics if an
|
||||
// unimplemented method is ever invoked, so we test this at initialization
|
||||
// time to prevent it from happening at runtime later due to I/O.
|
||||
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
|
||||
t.testEmbeddedByValue()
|
||||
}
|
||||
s.RegisterService(&MetadataService_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _MetadataService_GetMetadata_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetMetadataRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(MetadataServiceServer).GetMetadata(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: MetadataService_GetMetadata_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(MetadataServiceServer).GetMetadata(ctx, req.(*GetMetadataRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _MetadataService_UpdateMetadata_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(UpdateMetadataRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(MetadataServiceServer).UpdateMetadata(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: MetadataService_UpdateMetadata_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(MetadataServiceServer).UpdateMetadata(ctx, req.(*UpdateMetadataRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _MetadataService_ClearOverlay_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ClearOverlayRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(MetadataServiceServer).ClearOverlay(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: MetadataService_ClearOverlay_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(MetadataServiceServer).ClearOverlay(ctx, req.(*ClearOverlayRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _MetadataService_BatchUpdateMetadata_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(BatchUpdateRequest)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.(MetadataServiceServer).BatchUpdateMetadata(m, &grpc.GenericServerStream[BatchUpdateRequest, BatchUpdateProgress]{ServerStream: stream})
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type MetadataService_BatchUpdateMetadataServer = grpc.ServerStreamingServer[BatchUpdateProgress]
|
||||
|
||||
func _MetadataService_ImportMetadata_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(ImportMetadataRequest)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.(MetadataServiceServer).ImportMetadata(m, &grpc.GenericServerStream[ImportMetadataRequest, ImportProgress]{ServerStream: stream})
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type MetadataService_ImportMetadataServer = grpc.ServerStreamingServer[ImportProgress]
|
||||
|
||||
// MetadataService_ServiceDesc is the grpc.ServiceDesc for MetadataService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var MetadataService_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "musicfs.v1.MetadataService",
|
||||
HandlerType: (*MetadataServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "GetMetadata",
|
||||
Handler: _MetadataService_GetMetadata_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "UpdateMetadata",
|
||||
Handler: _MetadataService_UpdateMetadata_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ClearOverlay",
|
||||
Handler: _MetadataService_ClearOverlay_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
StreamName: "BatchUpdateMetadata",
|
||||
Handler: _MetadataService_BatchUpdateMetadata_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
{
|
||||
StreamName: "ImportMetadata",
|
||||
Handler: _MetadataService_ImportMetadata_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: "musicfs.proto",
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
module homelab.lan/music-agregator
|
||||
|
||||
go 1.26.2
|
||||
|
||||
require (
|
||||
github.com/anacrolix/torrent v1.61.0
|
||||
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0
|
||||
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3
|
||||
github.com/jackc/pgx/v5 v5.9.2
|
||||
github.com/mewkiz/flac v1.0.13
|
||||
github.com/prometheus/client_golang v1.23.2
|
||||
github.com/riverqueue/river v0.35.1
|
||||
github.com/riverqueue/river/riverdriver/riverpgxv5 v0.35.1
|
||||
github.com/rs/zerolog v1.35.1
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/tcolgate/mp3 v0.0.0-20170426193717-e79c5a46d300
|
||||
github.com/testcontainers/testcontainers-go v0.42.0
|
||||
github.com/testcontainers/testcontainers-go/modules/postgres v0.42.0
|
||||
)
|
||||
|
||||
require (
|
||||
dario.cat/mergo v1.0.2 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/RoaringBitmap/roaring v1.2.3 // indirect
|
||||
github.com/alecthomas/atomic v0.1.0-alpha2 // indirect
|
||||
github.com/anacrolix/btree v0.0.0-20251201064447-d86c3fa41bd8 // indirect
|
||||
github.com/anacrolix/chansync v0.7.0 // indirect
|
||||
github.com/anacrolix/dht/v2 v2.23.0 // indirect
|
||||
github.com/anacrolix/envpprof v1.4.0 // indirect
|
||||
github.com/anacrolix/generics v0.1.1-0.20251125230353-15d98d46693b // indirect
|
||||
github.com/anacrolix/go-libutp v1.3.2 // indirect
|
||||
github.com/anacrolix/log v0.17.1-0.20251118025802-918f1157b7bb // indirect
|
||||
github.com/anacrolix/missinggo v1.3.0 // indirect
|
||||
github.com/anacrolix/missinggo/perf v1.0.0 // indirect
|
||||
github.com/anacrolix/missinggo/v2 v2.10.0 // indirect
|
||||
github.com/anacrolix/mmsg v1.0.1 // indirect
|
||||
github.com/anacrolix/multiless v0.4.0 // indirect
|
||||
github.com/anacrolix/stm v0.5.0 // indirect
|
||||
github.com/anacrolix/sync v0.5.5-0.20251119100342-d78dd1f686f1 // indirect
|
||||
github.com/anacrolix/upnp v0.1.4 // indirect
|
||||
github.com/anacrolix/utp v0.1.0 // indirect
|
||||
github.com/bahlo/generic-list-go v0.2.0 // indirect
|
||||
github.com/benbjohnson/immutable v0.4.1-0.20221220213129-8932b999621d // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.2.2 // indirect
|
||||
github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/cespare/xxhash v1.1.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/containerd/errdefs v1.0.0 // indirect
|
||||
github.com/containerd/errdefs/pkg v0.3.0 // indirect
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/containerd/platforms v0.2.1 // indirect
|
||||
github.com/cpuguy83/dockercfg v0.3.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/distribution/reference v0.6.0 // indirect
|
||||
github.com/docker/docker v28.3.2+incompatible // indirect
|
||||
github.com/docker/go-connections v0.6.0 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/dustin/go-humanize v1.0.0 // indirect
|
||||
github.com/ebitengine/purego v0.10.0 // indirect
|
||||
github.com/edsrzf/mmap-go v1.1.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/go-llsqlite/adapter v0.0.0-20230927005056-7f5ce7f0c916 // indirect
|
||||
github.com/go-llsqlite/crawshaw v0.5.6-0.20250312230104-194977a03421 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/google/btree v1.1.2 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.0 // indirect
|
||||
github.com/huandu/xstrings v1.3.2 // indirect
|
||||
github.com/icza/bitio v1.1.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||
github.com/klauspost/compress v1.18.5 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.3 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 // indirect
|
||||
github.com/magiconair/properties v1.8.10 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mewkiz/pkg v0.0.0-20250417130911-3f050ff8c56d // indirect
|
||||
github.com/mewpkg/term v0.0.0-20241026122259-37a80af23985 // indirect
|
||||
github.com/minio/sha256-simd v1.0.0 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/moby/go-archive v0.2.0 // indirect
|
||||
github.com/moby/moby/api v1.54.1 // indirect
|
||||
github.com/moby/moby/client v0.4.0 // indirect
|
||||
github.com/moby/patternmatcher v0.6.1 // indirect
|
||||
github.com/moby/sys/sequential v0.6.0 // indirect
|
||||
github.com/moby/sys/user v0.4.0 // indirect
|
||||
github.com/moby/sys/userns v0.1.0 // indirect
|
||||
github.com/moby/term v0.5.2 // indirect
|
||||
github.com/mr-tron/base58 v1.2.0 // indirect
|
||||
github.com/mschoch/smat v0.2.0 // indirect
|
||||
github.com/multiformats/go-multihash v0.2.3 // indirect
|
||||
github.com/multiformats/go-varint v0.0.6 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.1 // indirect
|
||||
github.com/pion/datachannel v1.5.9 // indirect
|
||||
github.com/pion/dtls/v3 v3.0.3 // indirect
|
||||
github.com/pion/ice/v4 v4.0.2 // indirect
|
||||
github.com/pion/interceptor v0.1.40 // indirect
|
||||
github.com/pion/logging v0.2.3 // indirect
|
||||
github.com/pion/mdns/v2 v2.0.7 // indirect
|
||||
github.com/pion/randutil v0.1.0 // indirect
|
||||
github.com/pion/rtcp v1.2.15 // indirect
|
||||
github.com/pion/rtp v1.8.18 // indirect
|
||||
github.com/pion/sctp v1.8.33 // indirect
|
||||
github.com/pion/sdp/v3 v3.0.9 // indirect
|
||||
github.com/pion/srtp/v3 v3.0.4 // indirect
|
||||
github.com/pion/stun/v3 v3.0.0 // indirect
|
||||
github.com/pion/transport/v3 v3.0.7 // indirect
|
||||
github.com/pion/turn/v4 v4.0.0 // indirect
|
||||
github.com/pion/webrtc/v4 v4.0.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.66.1 // indirect
|
||||
github.com/prometheus/procfs v0.16.1 // indirect
|
||||
github.com/protolambda/ctxlock v0.1.0 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/riverqueue/river/riverdriver v0.35.1 // indirect
|
||||
github.com/riverqueue/river/rivershared v0.35.1 // indirect
|
||||
github.com/riverqueue/river/rivertype v0.35.1 // indirect
|
||||
github.com/rs/dnscache v0.0.0-20211102005908-e0241e321417 // indirect
|
||||
github.com/shirou/gopsutil/v4 v4.26.3 // indirect
|
||||
github.com/sirupsen/logrus v1.9.4 // indirect
|
||||
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||
github.com/tidwall/btree v1.8.1 // indirect
|
||||
github.com/tidwall/gjson v1.18.0 // indirect
|
||||
github.com/tidwall/match v1.2.0 // indirect
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
github.com/tidwall/sjson v1.2.5 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.16 // indirect
|
||||
github.com/tklauser/numcpus v0.11.0 // indirect
|
||||
github.com/wlynxg/anet v0.0.3 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
go.etcd.io/bbolt v1.3.6 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
|
||||
go.opentelemetry.io/otel v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.43.0 // indirect
|
||||
go.uber.org/goleak v1.3.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
||||
golang.org/x/crypto v0.48.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6 // indirect
|
||||
golang.org/x/sync v0.20.0 // indirect
|
||||
golang.org/x/time v0.14.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
lukechampine.com/blake3 v1.1.6 // indirect
|
||||
modernc.org/libc v1.22.3 // indirect
|
||||
modernc.org/mathutil v1.5.0 // indirect
|
||||
modernc.org/memory v1.5.0 // indirect
|
||||
modernc.org/sqlite v1.21.1 // indirect
|
||||
zombiezen.com/go/sqlite v0.13.1 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
golang.org/x/net v0.51.0 // indirect
|
||||
golang.org/x/sys v0.42.0 // indirect
|
||||
golang.org/x/text v0.36.0 // indirect
|
||||
google.golang.org/grpc v1.81.0
|
||||
google.golang.org/protobuf v1.36.11
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
@@ -1,710 +0,0 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
crawshaw.io/iox v0.0.0-20181124134642-c51c3df30797/go.mod h1:sXBiorCo8c46JlQV3oXPKINnZ8mcqnye1EkVkqsectk=
|
||||
crawshaw.io/sqlite v0.3.2/go.mod h1:igAO5JulrQ1DbdZdtVq48mnZUBAPOeFzer7VhDWNtW4=
|
||||
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
|
||||
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
|
||||
filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmGfU=
|
||||
filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/RoaringBitmap/roaring v0.4.7/go.mod h1:8khRDP4HmeXns4xIj9oGrKSz7XTQiJx2zgh7AcNke4w=
|
||||
github.com/RoaringBitmap/roaring v0.4.17/go.mod h1:D3qVegWTmfCaX4Bl5CrBE9hfrSrrXIr8KVNvRsDi1NI=
|
||||
github.com/RoaringBitmap/roaring v0.4.23/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06Mq5mKs52e1TwOo=
|
||||
github.com/RoaringBitmap/roaring v1.2.3 h1:yqreLINqIrX22ErkKI0vY47/ivtJr6n+kMhVOVmhWBY=
|
||||
github.com/RoaringBitmap/roaring v1.2.3/go.mod h1:plvDsJQpxOC5bw8LRteu/MLWHsHez/3y6cubLI4/1yE=
|
||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
||||
github.com/alecthomas/assert/v2 v2.0.0-alpha3 h1:pcHeMvQ3OMstAWgaeaXIAL8uzB9xMm2zlxt+/4ml8lk=
|
||||
github.com/alecthomas/assert/v2 v2.0.0-alpha3/go.mod h1:+zD0lmDXTeQj7TgDgCt0ePWxb0hMC1G+PGTsTCv1B9o=
|
||||
github.com/alecthomas/atomic v0.1.0-alpha2 h1:dqwXmax66gXvHhsOS4pGPZKqYOlTkapELkLb3MNdlH8=
|
||||
github.com/alecthomas/atomic v0.1.0-alpha2/go.mod h1:zD6QGEyw49HIq19caJDc2NMXAy8rNi9ROrxtMXATfyI=
|
||||
github.com/alecthomas/repr v0.0.0-20210801044451-80ca428c5142 h1:8Uy0oSf5co/NZXje7U1z8Mpep++QJOldL2hs/sBQf48=
|
||||
github.com/alecthomas/repr v0.0.0-20210801044451-80ca428c5142/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/anacrolix/btree v0.0.0-20251201064447-d86c3fa41bd8 h1:c02PsmoaChabVqAFm7pqPI1UIkDdDAjUaWa6ZmfxybQ=
|
||||
github.com/anacrolix/btree v0.0.0-20251201064447-d86c3fa41bd8/go.mod h1:7stWJ39LeusmMI8mjJuhFNRqep//vx0AsaySRoK9or0=
|
||||
github.com/anacrolix/chansync v0.7.0 h1:wgwxbsJRmOqNjil4INpxHrDp4rlqQhECxR8/WBP4Et0=
|
||||
github.com/anacrolix/chansync v0.7.0/go.mod h1:DZsatdsdXxD0WiwcGl0nJVwyjCKMDv+knl1q2iBjA2k=
|
||||
github.com/anacrolix/dht/v2 v2.23.0 h1:EuD17ykTTEkAMPLjBsS5QjGOwuBgLTdQhds6zPAjeVY=
|
||||
github.com/anacrolix/dht/v2 v2.23.0/go.mod h1:seXRz6HLw8zEnxlysf9ye2eQbrKUmch6PyOHpe/Nb/U=
|
||||
github.com/anacrolix/envpprof v0.0.0-20180404065416-323002cec2fa/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c=
|
||||
github.com/anacrolix/envpprof v1.0.0/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c=
|
||||
github.com/anacrolix/envpprof v1.1.0/go.mod h1:My7T5oSqVfEn4MD4Meczkw/f5lSIndGAKu/0SM/rkf4=
|
||||
github.com/anacrolix/envpprof v1.4.0 h1:QHeIcrgHcRChhnxR8l6rlaLlRQx9zd7Q2NII6Zbt83w=
|
||||
github.com/anacrolix/envpprof v1.4.0/go.mod h1:7QIG4CaX1uexQ3tqd5+BRa/9e2D02Wcertl6Yh0jCB0=
|
||||
github.com/anacrolix/generics v0.0.0-20230113004304-d6428d516633/go.mod h1:ff2rHB/joTV03aMSSn/AZNnaIpUw0h3njetGsaXcMy8=
|
||||
github.com/anacrolix/generics v0.1.1-0.20251125230353-15d98d46693b h1:Kuvx/A/TTJuT9x8mn7DeGx2KW9tWn1LI8bira67xdT0=
|
||||
github.com/anacrolix/generics v0.1.1-0.20251125230353-15d98d46693b/go.mod h1:NGehhfeXJPBujPx0s6cstSj8B+TERsTY32Xckfx5ftc=
|
||||
github.com/anacrolix/go-libutp v1.3.2 h1:WswiaxTIogchbkzNgGHuHRfbrYLpv4o290mlvcx+++M=
|
||||
github.com/anacrolix/go-libutp v1.3.2/go.mod h1:fCUiEnXJSe3jsPG554A200Qv+45ZzIIyGEvE56SHmyA=
|
||||
github.com/anacrolix/log v0.3.0/go.mod h1:lWvLTqzAnCWPJA08T2HCstZi0L1y2Wyvm3FJgwU9jwU=
|
||||
github.com/anacrolix/log v0.6.0/go.mod h1:lWvLTqzAnCWPJA08T2HCstZi0L1y2Wyvm3FJgwU9jwU=
|
||||
github.com/anacrolix/log v0.13.1/go.mod h1:D4+CvN8SnruK6zIFS/xPoRJmtvtnxs+CSfDQ+BFxZ68=
|
||||
github.com/anacrolix/log v0.14.2/go.mod h1:1OmJESOtxQGNMlUO5rcv96Vpp9mfMqXXbe2RdinFLdY=
|
||||
github.com/anacrolix/log v0.17.1-0.20251118025802-918f1157b7bb h1:nGNLCQbxFQZz7/9PXLGQ9GmavI/W+eX66pSwVeUwugU=
|
||||
github.com/anacrolix/log v0.17.1-0.20251118025802-918f1157b7bb/go.mod h1:YjBZbwe2v3RsU7WdoBlVSPVpfKuOAno9SRQ/8tIl+hk=
|
||||
github.com/anacrolix/lsan v0.0.0-20211126052245-807000409a62/go.mod h1:66cFKPCO7Sl4vbFnAaSq7e4OXtdMhRSBagJGWgmpJbM=
|
||||
github.com/anacrolix/lsan v0.1.0 h1:TbgB8fdVXgBwrNsJGHtht9+9FepNFu5H7dU8ek6XYAY=
|
||||
github.com/anacrolix/lsan v0.1.0/go.mod h1:66cFKPCO7Sl4vbFnAaSq7e4OXtdMhRSBagJGWgmpJbM=
|
||||
github.com/anacrolix/missinggo v0.0.0-20180725070939-60ef2fbf63df/go.mod h1:kwGiTUTZ0+p4vAz3VbAI5a30t2YbvemcmspjKwrAz5s=
|
||||
github.com/anacrolix/missinggo v1.1.0/go.mod h1:MBJu3Sk/k3ZfGYcS7z18gwfu72Ey/xopPFJJbTi5yIo=
|
||||
github.com/anacrolix/missinggo v1.1.2-0.20190815015349-b888af804467/go.mod h1:MBJu3Sk/k3ZfGYcS7z18gwfu72Ey/xopPFJJbTi5yIo=
|
||||
github.com/anacrolix/missinggo v1.2.1/go.mod h1:J5cMhif8jPmFoC3+Uvob3OXXNIhOUikzMt+uUjeM21Y=
|
||||
github.com/anacrolix/missinggo v1.3.0 h1:06HlMsudotL7BAELRZs0yDZ4yVXsHXGi323QBjAVASw=
|
||||
github.com/anacrolix/missinggo v1.3.0/go.mod h1:bqHm8cE8xr+15uVfMG3BFui/TxyB6//H5fwlq/TeqMc=
|
||||
github.com/anacrolix/missinggo/perf v1.0.0 h1:7ZOGYziGEBytW49+KmYGTaNfnwUqP1HBsy6BqESAJVw=
|
||||
github.com/anacrolix/missinggo/perf v1.0.0/go.mod h1:ljAFWkBuzkO12MQclXzZrosP5urunoLS0Cbvb4V0uMQ=
|
||||
github.com/anacrolix/missinggo/v2 v2.2.0/go.mod h1:o0jgJoYOyaoYQ4E2ZMISVa9c88BbUBVQQW4QeRkNCGY=
|
||||
github.com/anacrolix/missinggo/v2 v2.5.1/go.mod h1:WEjqh2rmKECd0t1VhQkLGTdIWXO6f6NLjp5GlMZ+6FA=
|
||||
github.com/anacrolix/missinggo/v2 v2.10.0 h1:pg0iO4Z/UhP2MAnmGcaMtp5ZP9kyWsusENWN9aolrkY=
|
||||
github.com/anacrolix/missinggo/v2 v2.10.0/go.mod h1:nCRMW6bRCMOVcw5z9BnSYKF+kDbtenx+hQuphf4bK8Y=
|
||||
github.com/anacrolix/mmsg v1.0.1 h1:TxfpV7kX70m3f/O7ielL/2I3OFkMPjrRCPo7+4X5AWw=
|
||||
github.com/anacrolix/mmsg v1.0.1/go.mod h1:x8kRaJY/dCrY9Al0PEcj1mb/uFHwP6GCJ9fLl4thEPc=
|
||||
github.com/anacrolix/multiless v0.4.0 h1:lqSszHkliMsZd2hsyrDvHOw4AbYWa+ijQ66LzbjqWjM=
|
||||
github.com/anacrolix/multiless v0.4.0/go.mod h1:zJv1JF9AqdZiHwxqPgjuOZDGWER6nyE48WBCi/OOrMM=
|
||||
github.com/anacrolix/stm v0.2.0/go.mod h1:zoVQRvSiGjGoTmbM0vSLIiaKjWtNPeTvXUSdJQA4hsg=
|
||||
github.com/anacrolix/stm v0.5.0 h1:9df1KBpttF0TzLgDq51Z+TEabZKMythqgx89f1FQJt8=
|
||||
github.com/anacrolix/stm v0.5.0/go.mod h1:MOwrSy+jCm8Y7HYfMAwPj7qWVu7XoVvjOiYwJmpeB/M=
|
||||
github.com/anacrolix/sync v0.0.0-20180808010631-44578de4e778/go.mod h1:s735Etp3joe/voe2sdaXLcqDdJSay1O0OPnM0ystjqk=
|
||||
github.com/anacrolix/sync v0.3.0/go.mod h1:BbecHL6jDSExojhNtgTFSBcdGerzNc64tz3DCOj/I0g=
|
||||
github.com/anacrolix/sync v0.5.5-0.20251119100342-d78dd1f686f1 h1:oLCfNgEOR3/Z98mSwmwTM1pcqCDb/1zIjxCNn7dzVaE=
|
||||
github.com/anacrolix/sync v0.5.5-0.20251119100342-d78dd1f686f1/go.mod h1:21cUWerw9eiu/3T3kyoChu37AVO+YFue1/H15qqubS0=
|
||||
github.com/anacrolix/tagflag v0.0.0-20180109131632-2146c8d41bf0/go.mod h1:1m2U/K6ZT+JZG0+bdMK6qauP49QT4wE5pmhJXOKKCHw=
|
||||
github.com/anacrolix/tagflag v1.0.0/go.mod h1:1m2U/K6ZT+JZG0+bdMK6qauP49QT4wE5pmhJXOKKCHw=
|
||||
github.com/anacrolix/tagflag v1.1.0/go.mod h1:Scxs9CV10NQatSmbyjqmqmeQNwGzlNe0CMUMIxqHIG8=
|
||||
github.com/anacrolix/torrent v1.61.0 h1:vxo+B4SwnoP5AQWbhvnTYIaTgPSX+llYUVuQVsN4Jg8=
|
||||
github.com/anacrolix/torrent v1.61.0/go.mod h1:yKUKuZSSDdyOsCbuH+rDOpswl/g546gICapdrU7aUmQ=
|
||||
github.com/anacrolix/upnp v0.1.4 h1:+2t2KA6QOhm/49zeNyeVwDu1ZYS9dB9wfxyVvh/wk7U=
|
||||
github.com/anacrolix/upnp v0.1.4/go.mod h1:Qyhbqo69gwNWvEk1xNTXsS5j7hMHef9hdr984+9fIic=
|
||||
github.com/anacrolix/utp v0.1.0 h1:FOpQOmIwYsnENnz7tAGohA+r6iXpRjrq8ssKSre2Cp4=
|
||||
github.com/anacrolix/utp v0.1.0/go.mod h1:MDwc+vsGEq7RMw6lr2GKOEqjWny5hO5OZXRVNaBJ2Dk=
|
||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
|
||||
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
|
||||
github.com/benbjohnson/immutable v0.2.0/go.mod h1:uc6OHo6PN2++n98KHLxW8ef4W42ylHiQSENghE1ezxI=
|
||||
github.com/benbjohnson/immutable v0.4.1-0.20221220213129-8932b999621d h1:2qVb9bsAMtmAfnxXltm+6eBzrrS7SZ52c3SedsulaMI=
|
||||
github.com/benbjohnson/immutable v0.4.1-0.20221220213129-8932b999621d/go.mod h1:iAr8OjJGLnLmVUr9MZ/rz4PWUy6Ouc2JLYuMArmvAJM=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
|
||||
github.com/bits-and-blooms/bitset v1.2.2 h1:J5gbX05GpMdBjCvQ9MteIg2KKDExr7DrgK+Yc15FvIk=
|
||||
github.com/bits-and-blooms/bitset v1.2.2/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
|
||||
github.com/bradfitz/iter v0.0.0-20140124041915-454541ec3da2/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo=
|
||||
github.com/bradfitz/iter v0.0.0-20190303215204-33e6a9893b0c/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo=
|
||||
github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8 h1:GKTyiRCL6zVf5wWaqKnf+7Qs6GbEPfd4iMOitWzXJx8=
|
||||
github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8/go.mod h1:spo1JLcs67NmW1aVLEgtA8Yy1elc+X8y5SRW1sFW4Og=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
|
||||
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
|
||||
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
|
||||
github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
|
||||
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||
github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A=
|
||||
github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=
|
||||
github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA=
|
||||
github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
|
||||
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/docker/docker v28.3.2+incompatible h1:wn66NJ6pWB1vBZIilP8G3qQPqHy5XymfYn5vsqeA5oA=
|
||||
github.com/docker/docker v28.3.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94=
|
||||
github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE=
|
||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
||||
github.com/dustin/go-humanize v0.0.0-20180421182945-02af3965c54e/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||
github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU=
|
||||
github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
github.com/edsrzf/mmap-go v1.1.0 h1:6EUwBLQ/Mcr1EYLE4Tn1VdW1A4ckqCQWZBw8Hr0kjpQ=
|
||||
github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/frankban/quicktest v1.9.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y=
|
||||
github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
|
||||
github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
|
||||
github.com/glycerine/go-unsnap-stream v0.0.0-20190901134440-81cf024a9e0a/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
|
||||
github.com/glycerine/goconvey v0.0.0-20180728074245-46e3a41ad493/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
|
||||
github.com/glycerine/goconvey v0.0.0-20190315024820-982ee783a72e/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
|
||||
github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-llsqlite/adapter v0.0.0-20230927005056-7f5ce7f0c916 h1:OyQmpAN302wAopDgwVjgs2HkFawP9ahIEqkUYz7V7CA=
|
||||
github.com/go-llsqlite/adapter v0.0.0-20230927005056-7f5ce7f0c916/go.mod h1:DADrR88ONKPPeSGjFp5iEN55Arx3fi2qXZeKCYDpbmU=
|
||||
github.com/go-llsqlite/crawshaw v0.5.6-0.20250312230104-194977a03421 h1:GClwZI0at7xwV0TpgUMTYr/DoTE7TJZ/tc29LcPcs7o=
|
||||
github.com/go-llsqlite/crawshaw v0.5.6-0.20250312230104-194977a03421/go.mod h1:/YJdV7uBQaYDE0fwe4z3wwJIZBJxdYzd38ICggWqtaE=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||
github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
|
||||
github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
|
||||
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20190309154008-847fc94819f9/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 h1:QGLs/O40yoNK9vmy4rhUGBVyMf1lISBGtXRpsu/Qu/o=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0/go.mod h1:hM2alZsMUni80N33RBe6J0e423LB+odMj7d3EMP9l20=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3 h1:B+8ClL/kCQkRiU82d9xajRPKYMrB7E0MbtzWVi1K4ns=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3/go.mod h1:NbCUVmiS4foBGBHOYlCT25+YmGpJ32dZPi75pGEUpj4=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
|
||||
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo=
|
||||
github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4=
|
||||
github.com/huandu/xstrings v1.3.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw=
|
||||
github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/icza/bitio v1.1.0 h1:ysX4vtldjdi3Ygai5m1cWy4oLkhWTAi+SyO6HC8L9T0=
|
||||
github.com/icza/bitio v1.1.0/go.mod h1:0jGnlLAx8MKMr9VGnn/4YrvZiprkvBelsVIbA9Jjr9A=
|
||||
github.com/icza/mighty v0.0.0-20180919140131-cfd07d671de6 h1:8UsGZ2rr2ksmEru6lToqnXgA8Mz1DP11X4zSJ159C3k=
|
||||
github.com/icza/mighty v0.0.0-20180919140131-cfd07d671de6/go.mod h1:xQig96I1VNBDIWGCdTt54nHt6EeI639SmHycLYL7FkA=
|
||||
github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438 h1:Dj0L5fhJ9F82ZJyVOmBx6msDp/kfd1t9GRfny/mfJA0=
|
||||
github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.9.2 h1:3ZhOzMWnR4yJ+RW1XImIPsD1aNSz4T4fyP7zlQb56hw=
|
||||
github.com/jackc/pgx/v5 v5.9.2/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4=
|
||||
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
||||
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE=
|
||||
github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
|
||||
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU=
|
||||
github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 h1:PwQumkgq4/acIiZhtifTV5OUqqiP82UAl0h87xj/l9k=
|
||||
github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg=
|
||||
github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE=
|
||||
github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mdelapenya/tlscert v0.2.0 h1:7H81W6Z/4weDvZBNOfQte5GpIMo0lGYEeWbkGp5LJHI=
|
||||
github.com/mdelapenya/tlscert v0.2.0/go.mod h1:O4njj3ELLnJjGdkN7M/vIVCpZ+Cf0L6muqOG4tLSl8o=
|
||||
github.com/mewkiz/flac v1.0.13 h1:6wF8rRQKBFW159Daqx6Ro7K5ZnlVhHUKfS5aTsC4oXs=
|
||||
github.com/mewkiz/flac v1.0.13/go.mod h1:HfPYDA+oxjyuqMu2V+cyKcxF51KM6incpw5eZXmfA6k=
|
||||
github.com/mewkiz/pkg v0.0.0-20250417130911-3f050ff8c56d h1:IL2tii4jXLdhCeQN69HNzYYW1kl0meSG0wt5+sLwszU=
|
||||
github.com/mewkiz/pkg v0.0.0-20250417130911-3f050ff8c56d/go.mod h1:SIpumAnUWSy0q9RzKD3pyH3g1t5vdawUAPcW5tQrUtI=
|
||||
github.com/mewpkg/term v0.0.0-20241026122259-37a80af23985 h1:h8O1byDZ1uk6RUXMhj1QJU3VXFKXHDZxr4TXRPGeBa8=
|
||||
github.com/mewpkg/term v0.0.0-20241026122259-37a80af23985/go.mod h1:uiPmbdUbdt1NkGApKl7htQjZ8S7XaGUAVulJUJ9v6q4=
|
||||
github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g=
|
||||
github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM=
|
||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||
github.com/moby/go-archive v0.2.0 h1:zg5QDUM2mi0JIM9fdQZWC7U8+2ZfixfTYoHL7rWUcP8=
|
||||
github.com/moby/go-archive v0.2.0/go.mod h1:mNeivT14o8xU+5q1YnNrkQVpK+dnNe/K6fHqnTg4qPU=
|
||||
github.com/moby/moby/api v1.54.1 h1:TqVzuJkOLsgLDDwNLmYqACUuTehOHRGKiPhvH8V3Nn4=
|
||||
github.com/moby/moby/api v1.54.1/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs=
|
||||
github.com/moby/moby/client v0.4.0 h1:S+2XegzHQrrvTCvF6s5HFzcrywWQmuVnhOXe2kiWjIw=
|
||||
github.com/moby/moby/client v0.4.0/go.mod h1:QWPbvWchQbxBNdaLSpoKpCdf5E+WxFAgNHogCWDoa7g=
|
||||
github.com/moby/patternmatcher v0.6.1 h1:qlhtafmr6kgMIJjKJMDmMWq7WLkKIo23hsrpR3x084U=
|
||||
github.com/moby/patternmatcher v0.6.1/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
|
||||
github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU=
|
||||
github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko=
|
||||
github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs=
|
||||
github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
|
||||
github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=
|
||||
github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
|
||||
github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ=
|
||||
github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
|
||||
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
|
||||
github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg=
|
||||
github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM=
|
||||
github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw=
|
||||
github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U=
|
||||
github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM=
|
||||
github.com/multiformats/go-varint v0.0.6 h1:gk85QWKxh3TazbLxED/NlDVv8+q+ReFJk7Y2W/KhfNY=
|
||||
github.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
|
||||
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
|
||||
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
||||
github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pion/datachannel v1.5.9 h1:LpIWAOYPyDrXtU+BW7X0Yt/vGtYxtXQ8ql7dFfYUVZA=
|
||||
github.com/pion/datachannel v1.5.9/go.mod h1:kDUuk4CU4Uxp82NH4LQZbISULkX/HtzKa4P7ldf9izE=
|
||||
github.com/pion/dtls/v3 v3.0.3 h1:j5ajZbQwff7Z8k3pE3S+rQ4STvKvXUdKsi/07ka+OWM=
|
||||
github.com/pion/dtls/v3 v3.0.3/go.mod h1:weOTUyIV4z0bQaVzKe8kpaP17+us3yAuiQsEAG1STMU=
|
||||
github.com/pion/ice/v4 v4.0.2 h1:1JhBRX8iQLi0+TfcavTjPjI6GO41MFn4CeTBX+Y9h5s=
|
||||
github.com/pion/ice/v4 v4.0.2/go.mod h1:DCdqyzgtsDNYN6/3U8044j3U7qsJ9KFJC92VnOWHvXg=
|
||||
github.com/pion/interceptor v0.1.40 h1:e0BjnPcGpr2CFQgKhrQisBU7V3GXK6wrfYrGYaU6Jq4=
|
||||
github.com/pion/interceptor v0.1.40/go.mod h1:Z6kqH7M/FYirg3frjGJ21VLSRJGBXB/KqaTIrdqnOic=
|
||||
github.com/pion/logging v0.2.3 h1:gHuf0zpoh1GW67Nr6Gj4cv5Z9ZscU7g/EaoC/Ke/igI=
|
||||
github.com/pion/logging v0.2.3/go.mod h1:z8YfknkquMe1csOrxK5kc+5/ZPAzMxbKLX5aXpbpC90=
|
||||
github.com/pion/mdns/v2 v2.0.7 h1:c9kM8ewCgjslaAmicYMFQIde2H9/lrZpjBkN8VwoVtM=
|
||||
github.com/pion/mdns/v2 v2.0.7/go.mod h1:vAdSYNAT0Jy3Ru0zl2YiW3Rm/fJCwIeM0nToenfOJKA=
|
||||
github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
|
||||
github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
|
||||
github.com/pion/rtcp v1.2.15 h1:LZQi2JbdipLOj4eBjK4wlVoQWfrZbh3Q6eHtWtJBZBo=
|
||||
github.com/pion/rtcp v1.2.15/go.mod h1:jlGuAjHMEXwMUHK78RgX0UmEJFV4zUKOFHR7OP+D3D0=
|
||||
github.com/pion/rtp v1.8.18 h1:yEAb4+4a8nkPCecWzQB6V/uEU18X1lQCGAQCjP+pyvU=
|
||||
github.com/pion/rtp v1.8.18/go.mod h1:bAu2UFKScgzyFqvUKmbvzSdPr+NGbZtv6UB2hesqXBk=
|
||||
github.com/pion/sctp v1.8.33 h1:dSE4wX6uTJBcNm8+YlMg7lw1wqyKHggsP5uKbdj+NZw=
|
||||
github.com/pion/sctp v1.8.33/go.mod h1:beTnqSzewI53KWoG3nqB282oDMGrhNxBdb+JZnkCwRM=
|
||||
github.com/pion/sdp/v3 v3.0.9 h1:pX++dCHoHUwq43kuwf3PyJfHlwIj4hXA7Vrifiq0IJY=
|
||||
github.com/pion/sdp/v3 v3.0.9/go.mod h1:B5xmvENq5IXJimIO4zfp6LAe1fD9N+kFv+V/1lOdz8M=
|
||||
github.com/pion/srtp/v3 v3.0.4 h1:2Z6vDVxzrX3UHEgrUyIGM4rRouoC7v+NiF1IHtp9B5M=
|
||||
github.com/pion/srtp/v3 v3.0.4/go.mod h1:1Jx3FwDoxpRaTh1oRV8A/6G1BnFL+QI82eK4ms8EEJQ=
|
||||
github.com/pion/stun/v3 v3.0.0 h1:4h1gwhWLWuZWOJIJR9s2ferRO+W3zA/b6ijOI6mKzUw=
|
||||
github.com/pion/stun/v3 v3.0.0/go.mod h1:HvCN8txt8mwi4FBvS3EmDghW6aQJ24T+y+1TKjB5jyU=
|
||||
github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0=
|
||||
github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo=
|
||||
github.com/pion/turn/v4 v4.0.0 h1:qxplo3Rxa9Yg1xXDxxH8xaqcyGUtbHYw4QSCvmFWvhM=
|
||||
github.com/pion/turn/v4 v4.0.0/go.mod h1:MuPDkm15nYSklKpN8vWJ9W2M0PlyQZqYt1McGuxG7mA=
|
||||
github.com/pion/webrtc/v4 v4.0.0 h1:x8ec7uJQPP3D1iI8ojPAiTOylPI7Fa7QgqZrhpLyqZ8=
|
||||
github.com/pion/webrtc/v4 v4.0.0/go.mod h1:SfNn8CcFxR6OUVjLXVslAQ3a3994JhyE3Hw1jAuqEto=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
|
||||
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
|
||||
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
|
||||
github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=
|
||||
github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
||||
github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
|
||||
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
|
||||
github.com/protolambda/ctxlock v0.1.0 h1:rCUY3+vRdcdZXqT07iXgyr744J2DU2LCBIXowYAjBCE=
|
||||
github.com/protolambda/ctxlock v0.1.0/go.mod h1:vefhX6rIZH8rsg5ZpOJfEDYQOppZi19SfPiGOFrNnwM=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/riverqueue/river v0.35.1 h1:TK1LLGRdTWL7ARPbIUB+TqMnTYJ0GiCoy5Q/yEf5yBE=
|
||||
github.com/riverqueue/river v0.35.1/go.mod h1:jDt0LimObI+5e6FVy7LyuIWfHftmV0wARmiK7W+9D64=
|
||||
github.com/riverqueue/river/riverdriver v0.35.1 h1:zJx8SaQdMP7zVEfd8SDoe8KjVHCXoXoFfzt6v+SJtQg=
|
||||
github.com/riverqueue/river/riverdriver v0.35.1/go.mod h1:Y+rQzz0uvh+pQI+mzJh3qgAGGNxestOWgjKa7mob87w=
|
||||
github.com/riverqueue/river/riverdriver/riverpgxv5 v0.35.1 h1:GL+ztwpXgIqBin/3wNzq8h1/H8befxl61/DlLvVCAAY=
|
||||
github.com/riverqueue/river/riverdriver/riverpgxv5 v0.35.1/go.mod h1:5Llh5ONCFsW67dLm5+OelSWTKhliQ989JLbVMwyuN2U=
|
||||
github.com/riverqueue/river/rivershared v0.35.1 h1:XEHf7yj35p5Os5r6K08q9BVaAKsvWhP9hfxEr+MwXqg=
|
||||
github.com/riverqueue/river/rivershared v0.35.1/go.mod h1:YqVk7bZoojLsx58kyQ6ZU2FHP91HP4whVj6MTCtih/c=
|
||||
github.com/riverqueue/river/rivertype v0.35.1 h1:7SfjZ3Hkr7gRjItMHAUzJBAHIqx41yS/4yjVPQVtNfM=
|
||||
github.com/riverqueue/river/rivertype v0.35.1/go.mod h1:D1Ad+EaZiaXbQbJcJcfeicXJMBKno0n6UcfKI5Q7DIQ=
|
||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/rs/dnscache v0.0.0-20211102005908-e0241e321417 h1:Lt9DzQALzHoDwMBGJ6v8ObDPR0dzr2a6sXTB1Fq7IHs=
|
||||
github.com/rs/dnscache v0.0.0-20211102005908-e0241e321417/go.mod h1:qe5TWALJ8/a1Lqznoc5BDHpYX/8HU60Hm2AwRmqzxqA=
|
||||
github.com/rs/zerolog v1.35.1 h1:m7xQeoiLIiV0BCEY4Hs+j2NG4Gp2o2KPKmhnnLiazKI=
|
||||
github.com/rs/zerolog v1.35.1/go.mod h1:EjML9kdfa/RMA7h/6z6pYmq1ykOuA8/mjWaEvGI+jcw=
|
||||
github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 h1:GHRpF1pTW19a8tTFrMLUcfWwyC0pnifVo2ClaLq+hP8=
|
||||
github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8=
|
||||
github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc=
|
||||
github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
|
||||
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/assertions v0.0.0-20190215210624-980c5ac6f3ac/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190306220146-200a235640ff/go.mod h1:KSQcGKpxUMHk3nbYzs/tIBAM2iDooCn0BmttHOJEbLs=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4=
|
||||
github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0=
|
||||
github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/tcolgate/mp3 v0.0.0-20170426193717-e79c5a46d300 h1:XQdibLKagjdevRB6vAjVY4qbSr8rQ610YzTkWcxzxSI=
|
||||
github.com/tcolgate/mp3 v0.0.0-20170426193717-e79c5a46d300/go.mod h1:FNa/dfN95vAYCNFrIKRrlRo+MBLbwmR9Asa5f2ljmBI=
|
||||
github.com/testcontainers/testcontainers-go v0.42.0 h1:He3IhTzTZOygSXLJPMX7n44XtK+qhjat1nI9cneBbUY=
|
||||
github.com/testcontainers/testcontainers-go v0.42.0/go.mod h1:vZjdY1YmUA1qEForxOIOazfsrdyORJAbhi0bp8plN30=
|
||||
github.com/testcontainers/testcontainers-go/modules/postgres v0.42.0 h1:GCbb1ndrF7OTDiIvxXyItaDab4qkzTFJ48LKFdM7EIo=
|
||||
github.com/testcontainers/testcontainers-go/modules/postgres v0.42.0/go.mod h1:IRPBaI8jXdrNfD0e4Zm7Fbcgaz5shKxOQv4axiL09xs=
|
||||
github.com/tidwall/btree v1.8.1 h1:27ehoXvm5AG/g+1VxLS1SD3vRhp/H7LuEfwNvddEdmA=
|
||||
github.com/tidwall/btree v1.8.1/go.mod h1:jBbTdUWhSZClZWoDg54VnvV7/54modSOzDN7VXftj1A=
|
||||
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
|
||||
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/match v1.2.0 h1:0pt8FlkOwjN2fPt4bIl4BoNxb98gGHN2ObFEDkrfZnM=
|
||||
github.com/tidwall/match v1.2.0/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
|
||||
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
|
||||
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
|
||||
github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
|
||||
github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
|
||||
github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
|
||||
github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA=
|
||||
github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI=
|
||||
github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw=
|
||||
github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ=
|
||||
github.com/willf/bitset v1.1.9/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
|
||||
github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
|
||||
github.com/wlynxg/anet v0.0.3 h1:PvR53psxFXstc12jelG6f1Lv4MWqE0tI76/hHGjh9rg=
|
||||
github.com/wlynxg/anet v0.0.3/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
|
||||
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
|
||||
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
|
||||
go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
|
||||
go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
|
||||
go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM=
|
||||
go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY=
|
||||
go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg=
|
||||
go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A=
|
||||
go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
|
||||
go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
|
||||
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
|
||||
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20220428152302-39d4317da171/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
|
||||
golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6 h1:zfMcR1Cs4KNuomFFgGefv5N0czO2XZpUbxGUy8i8ug0=
|
||||
golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6/go.mod h1:46edojNIoXTNOhySWIWdix628clX9ODXwPsQuG6hsK0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
|
||||
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
||||
golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg=
|
||||
golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
|
||||
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
|
||||
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
|
||||
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
|
||||
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
|
||||
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 h1:ggcbiqK8WWh6l1dnltU4BgWGIGo+EVYxCaAPih/zQXQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.81.0 h1:W3G9N3KQf3BU+YuCtGKJk0CmxQNbAISICD/9AORxLIw=
|
||||
google.golang.org/grpc v1.81.0/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
|
||||
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
lukechampine.com/blake3 v1.1.6 h1:H3cROdztr7RCfoaTpGZFQsrqvweFLrqS73j7L7cmR5c=
|
||||
lukechampine.com/blake3 v1.1.6/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA=
|
||||
modernc.org/libc v1.22.3 h1:D/g6O5ftAfavceqlLOFwaZuA5KYafKwmr30A6iSqoyY=
|
||||
modernc.org/libc v1.22.3/go.mod h1:MQrloYP209xa2zHome2a8HLiLm6k0UT8CoHpV74tOFw=
|
||||
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
|
||||
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds=
|
||||
modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
|
||||
modernc.org/sqlite v1.21.1 h1:GyDFqNnESLOhwwDRaHGdp2jKLDzpyT/rNLglX3ZkMSU=
|
||||
modernc.org/sqlite v1.21.1/go.mod h1:XwQ0wZPIh1iKb5mkvCJ3szzbhk+tykC8ZWqTRTgYRwI=
|
||||
pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk=
|
||||
pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=
|
||||
zombiezen.com/go/sqlite v0.13.1 h1:qDzxyWWmMtSSEH5qxamqBFmqA2BLSSbtODi3ojaE02o=
|
||||
zombiezen.com/go/sqlite v0.13.1/go.mod h1:Ht/5Rg3Ae2hoyh1I7gbWtWAl89CNocfqeb/aAMTkJr4=
|
||||
@@ -1,285 +0,0 @@
|
||||
package analysis
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
"homelab.lan/music-agregator/internal/audio"
|
||||
"homelab.lan/music-agregator/internal/database"
|
||||
)
|
||||
|
||||
var audioExtensions = map[string]bool{
|
||||
".flac": true, ".mp3": true, ".aac": true, ".m4a": true,
|
||||
".ape": true, ".wv": true, ".ogg": true, ".wav": true, ".alac": true,
|
||||
}
|
||||
|
||||
var trackNumberRegex = regexp.MustCompile(`^(\d+)[\s\-._]+`)
|
||||
|
||||
type ReleaseAnalyzer struct {
|
||||
downloads *database.DownloadRepository
|
||||
downloadFiles *database.DownloadFileRepository
|
||||
albumReleases *database.AlbumReleaseRepository
|
||||
trackReleases *database.TrackReleaseRepository
|
||||
}
|
||||
|
||||
func NewReleaseAnalyzer(db *database.DB) *ReleaseAnalyzer {
|
||||
return &ReleaseAnalyzer{
|
||||
downloads: database.NewDownloadRepository(db.Pool),
|
||||
downloadFiles: database.NewDownloadFileRepository(db.Pool),
|
||||
albumReleases: database.NewAlbumReleaseRepository(db.Pool),
|
||||
trackReleases: database.NewTrackReleaseRepository(db.Pool),
|
||||
}
|
||||
}
|
||||
|
||||
func (a *ReleaseAnalyzer) Analyze(ctx context.Context, downloadID string, contentPath string) (*database.AlbumRelease, []*database.TrackRelease, error) {
|
||||
download, err := a.downloads.GetByID(ctx, downloadID)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("getting download: %w", err)
|
||||
}
|
||||
|
||||
files, err := a.downloadFiles.GetByDownloadID(ctx, downloadID)
|
||||
if err != nil || len(files) == 0 {
|
||||
log.Info().Str("download_id", downloadID).Msg("no download files in DB, scanning filesystem")
|
||||
scanned, scanErr := ScanAndHashFiles(contentPath)
|
||||
if scanErr != nil {
|
||||
return nil, nil, fmt.Errorf("scanning files: %w", scanErr)
|
||||
}
|
||||
for _, f := range scanned {
|
||||
f.DownloadID = downloadID
|
||||
}
|
||||
if err := a.downloadFiles.CreateBatch(ctx, scanned); err != nil {
|
||||
return nil, nil, fmt.Errorf("persisting scanned files: %w", err)
|
||||
}
|
||||
files = scanned
|
||||
}
|
||||
|
||||
var audioFiles []*database.DownloadFile
|
||||
var hasCoverArt, hasCueSheet, hasRipLog bool
|
||||
for _, f := range files {
|
||||
if audioExtensions["."+f.FileType] {
|
||||
audioFiles = append(audioFiles, f)
|
||||
}
|
||||
switch f.FileType {
|
||||
case "jpg", "jpeg", "png", "gif", "webp":
|
||||
hasCoverArt = true
|
||||
case "cue":
|
||||
hasCueSheet = true
|
||||
case "log":
|
||||
hasRipLog = true
|
||||
}
|
||||
}
|
||||
|
||||
if len(audioFiles) == 0 {
|
||||
return nil, nil, fmt.Errorf("no audio files found for download %s", downloadID)
|
||||
}
|
||||
|
||||
var trackReleases []*database.TrackRelease
|
||||
var totalSize int64
|
||||
var totalDuration int
|
||||
formatCounts := make(map[string]int)
|
||||
var firstBitDepth, firstSampleRate, firstChannels int
|
||||
var firstIsLossless bool
|
||||
|
||||
for i, f := range audioFiles {
|
||||
fullPath := filepath.Join(contentPath, f.FilePath)
|
||||
info, err := audio.Analyze(fullPath)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Str("path", f.FilePath).Msg("failed to analyze audio file")
|
||||
info = &audio.TrackInfo{
|
||||
Format: strings.ToUpper(f.FileType),
|
||||
}
|
||||
}
|
||||
|
||||
if i == 0 {
|
||||
firstBitDepth = info.BitDepth
|
||||
firstSampleRate = info.SampleRate
|
||||
firstChannels = info.Channels
|
||||
firstIsLossless = info.IsLossless
|
||||
}
|
||||
|
||||
formatCounts[info.Format]++
|
||||
totalSize += f.FileSize
|
||||
totalDuration += info.DurationMs
|
||||
|
||||
trackNum := extractTrackNumber(f.FilePath)
|
||||
title := extractTitle(f.FilePath)
|
||||
|
||||
tr := &database.TrackRelease{
|
||||
Title: title,
|
||||
TrackNumber: trackNum,
|
||||
DiscNumber: 1,
|
||||
Format: info.Format,
|
||||
Channels: info.Channels,
|
||||
FileSize: f.FileSize,
|
||||
FilePath: f.FilePath,
|
||||
}
|
||||
if info.DurationMs > 0 {
|
||||
dur := info.DurationMs
|
||||
tr.DurationMs = &dur
|
||||
}
|
||||
if info.BitDepth > 0 {
|
||||
bd := info.BitDepth
|
||||
tr.BitDepth = &bd
|
||||
}
|
||||
if info.SampleRate > 0 {
|
||||
sr := info.SampleRate
|
||||
tr.SampleRate = &sr
|
||||
}
|
||||
if info.BitrateKbps > 0 {
|
||||
br := info.BitrateKbps
|
||||
tr.BitrateKbps = &br
|
||||
}
|
||||
trackReleases = append(trackReleases, tr)
|
||||
}
|
||||
|
||||
dominantFormat := ""
|
||||
maxCount := 0
|
||||
for format, count := range formatCounts {
|
||||
if count > maxCount {
|
||||
dominantFormat = format
|
||||
maxCount = count
|
||||
}
|
||||
}
|
||||
|
||||
var source *string
|
||||
if hasRipLog {
|
||||
s := "CD"
|
||||
source = &s
|
||||
}
|
||||
|
||||
release := &database.AlbumRelease{
|
||||
AlbumID: download.AlbumID,
|
||||
DownloadID: downloadID,
|
||||
Format: dominantFormat,
|
||||
Channels: firstChannels,
|
||||
IsLossless: firstIsLossless,
|
||||
Source: source,
|
||||
TotalSize: totalSize,
|
||||
TotalDurationMs: totalDuration,
|
||||
TrackCount: len(audioFiles),
|
||||
HasCoverArt: hasCoverArt,
|
||||
HasCueSheet: hasCueSheet,
|
||||
HasRipLog: hasRipLog,
|
||||
Path: contentPath,
|
||||
}
|
||||
if firstBitDepth > 0 {
|
||||
release.BitDepth = &firstBitDepth
|
||||
}
|
||||
if firstSampleRate > 0 {
|
||||
release.SampleRate = &firstSampleRate
|
||||
}
|
||||
|
||||
return release, trackReleases, nil
|
||||
}
|
||||
|
||||
func (a *ReleaseAnalyzer) AnalyzeAndPersist(ctx context.Context, downloadID string, contentPath string) (*database.AlbumRelease, []*database.TrackRelease, error) {
|
||||
release, trackReleases, err := a.Analyze(ctx, downloadID, contentPath)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if err := a.albumReleases.DeleteByDownloadID(ctx, downloadID); err != nil {
|
||||
log.Warn().Err(err).Str("download_id", downloadID).Msg("failed to delete existing album release")
|
||||
}
|
||||
|
||||
if err := a.albumReleases.Create(ctx, release); err != nil {
|
||||
return nil, nil, fmt.Errorf("creating album release: %w", err)
|
||||
}
|
||||
|
||||
for _, tr := range trackReleases {
|
||||
tr.AlbumReleaseID = release.ID
|
||||
}
|
||||
if err := a.trackReleases.CreateBatch(ctx, trackReleases); err != nil {
|
||||
return nil, nil, fmt.Errorf("creating track releases: %w", err)
|
||||
}
|
||||
|
||||
return release, trackReleases, nil
|
||||
}
|
||||
|
||||
func extractTrackNumber(filePath string) int {
|
||||
base := filepath.Base(filePath)
|
||||
matches := trackNumberRegex.FindStringSubmatch(base)
|
||||
if len(matches) >= 2 {
|
||||
var num int
|
||||
fmt.Sscanf(matches[1], "%d", &num)
|
||||
return num
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func extractTitle(filePath string) string {
|
||||
base := filepath.Base(filePath)
|
||||
ext := filepath.Ext(base)
|
||||
name := strings.TrimSuffix(base, ext)
|
||||
name = trackNumberRegex.ReplaceAllString(name, "")
|
||||
return strings.TrimSpace(name)
|
||||
}
|
||||
|
||||
func IsAudioExtension(ext string) bool {
|
||||
return audioExtensions[ext]
|
||||
}
|
||||
|
||||
func ScanAndHashFiles(rootPath string) ([]*database.DownloadFile, error) {
|
||||
var files []*database.DownloadFile
|
||||
|
||||
err := filepath.Walk(rootPath, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil || info.IsDir() {
|
||||
return err
|
||||
}
|
||||
|
||||
ext := strings.ToLower(filepath.Ext(path))
|
||||
relPath, _ := filepath.Rel(rootPath, path)
|
||||
|
||||
fileType := strings.TrimPrefix(ext, ".")
|
||||
if fileType == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
df := &database.DownloadFile{
|
||||
FilePath: relPath,
|
||||
FileSize: info.Size(),
|
||||
FileType: fileType,
|
||||
}
|
||||
|
||||
if IsAudioExtension(ext) || ext == ".cue" || ext == ".log" {
|
||||
hash, err := hashFile(path)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Str("path", path).Msg("failed to hash file")
|
||||
} else {
|
||||
df.SHA256Hash = hash
|
||||
now := time.Now()
|
||||
df.VerifiedAt = &now
|
||||
}
|
||||
}
|
||||
|
||||
files = append(files, df)
|
||||
return nil
|
||||
})
|
||||
|
||||
return files, err
|
||||
}
|
||||
|
||||
func hashFile(path string) (string, error) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("opening file: %w", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
h := sha256.New()
|
||||
if _, err := io.Copy(h, f); err != nil {
|
||||
return "", fmt.Errorf("hashing file: %w", err)
|
||||
}
|
||||
|
||||
return hex.EncodeToString(h.Sum(nil)), nil
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package audio
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type TrackInfo struct {
|
||||
Format string
|
||||
BitDepth int
|
||||
SampleRate int
|
||||
Channels int
|
||||
DurationMs int
|
||||
BitrateKbps int
|
||||
IsLossless bool
|
||||
}
|
||||
|
||||
func Analyze(filePath string) (*TrackInfo, error) {
|
||||
ext := strings.ToLower(filepath.Ext(filePath))
|
||||
|
||||
switch ext {
|
||||
case ".flac":
|
||||
return analyzeFLAC(filePath)
|
||||
case ".mp3":
|
||||
return analyzeMP3(filePath)
|
||||
case ".aac", ".m4a", ".ogg", ".wav", ".ape", ".wv", ".alac":
|
||||
return &TrackInfo{
|
||||
Format: strings.ToUpper(strings.TrimPrefix(ext, ".")),
|
||||
IsLossless: ext == ".wav" || ext == ".ape" || ext == ".wv" || ext == ".alac",
|
||||
}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported audio format: %s", ext)
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package audio
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/mewkiz/flac"
|
||||
)
|
||||
|
||||
func analyzeFLAC(filePath string) (*TrackInfo, error) {
|
||||
stream, err := flac.ParseFile(filePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing FLAC: %w", err)
|
||||
}
|
||||
defer stream.Close()
|
||||
|
||||
info := stream.Info
|
||||
durationMs := int(info.NSamples * 1000 / uint64(info.SampleRate))
|
||||
|
||||
return &TrackInfo{
|
||||
Format: "FLAC",
|
||||
BitDepth: int(info.BitsPerSample),
|
||||
SampleRate: int(info.SampleRate),
|
||||
Channels: int(info.NChannels),
|
||||
DurationMs: durationMs,
|
||||
IsLossless: true,
|
||||
}, nil
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
package audio
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/tcolgate/mp3"
|
||||
)
|
||||
|
||||
func analyzeMP3(filePath string) (*TrackInfo, error) {
|
||||
f, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("opening MP3: %w", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
decoder := mp3.NewDecoder(f)
|
||||
var frame mp3.Frame
|
||||
var skipped int
|
||||
var totalDuration time.Duration
|
||||
var sampleRate, channels, bitrate int
|
||||
var frameCount int
|
||||
|
||||
for {
|
||||
err := decoder.Decode(&frame, &skipped)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
if frameCount == 0 {
|
||||
sampleRate = int(frame.Header().SampleRate())
|
||||
channels = channelCount(frame.Header().ChannelMode())
|
||||
bitrate = int(frame.Header().BitRate()) / 1000
|
||||
}
|
||||
totalDuration += frame.Duration()
|
||||
frameCount++
|
||||
}
|
||||
|
||||
return &TrackInfo{
|
||||
Format: "MP3",
|
||||
SampleRate: sampleRate,
|
||||
Channels: channels,
|
||||
DurationMs: int(totalDuration.Milliseconds()),
|
||||
BitrateKbps: bitrate,
|
||||
IsLossless: false,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func channelCount(mode mp3.FrameChannelMode) int {
|
||||
if mode == mp3.SingleChannel {
|
||||
return 1
|
||||
}
|
||||
return 2
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
IndexerTypeJackett IndexerType = "jackett"
|
||||
)
|
||||
|
||||
const (
|
||||
TorrentClientQbittorrent TorrentClientType = "qbittorrent"
|
||||
)
|
||||
|
||||
type IndexerType string
|
||||
type TorrentClientType string
|
||||
|
||||
type Config struct {
|
||||
App struct {
|
||||
Host string `yaml:"host"`
|
||||
Port string `yaml:"port"`
|
||||
} `yaml:"app"`
|
||||
|
||||
Database struct {
|
||||
URL string `yaml:"url"`
|
||||
} `yaml:"database"`
|
||||
|
||||
Indexer struct {
|
||||
Url string `yaml:"url"`
|
||||
Port string `yaml:"port"`
|
||||
Type IndexerType `yaml:"type"`
|
||||
ApiKey string `yaml:"api_key"`
|
||||
Cache CacheConfig `yaml:"cache"`
|
||||
} `yaml:"indexer"`
|
||||
|
||||
Torrent struct {
|
||||
ClientType TorrentClientType `yaml:"client_type"`
|
||||
Url string `yaml:"url"`
|
||||
Username string `yaml:"username"`
|
||||
Password string `yaml:"password"`
|
||||
ContainerName string `yaml:"container_name"`
|
||||
} `yaml:"torrent"`
|
||||
|
||||
Metadata struct {
|
||||
Endpoint string `yaml:"endpoint"`
|
||||
} `yaml:"metadata"`
|
||||
|
||||
MusicFS struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
Endpoint string `yaml:"endpoint"`
|
||||
OriginID string `yaml:"origin_id"`
|
||||
OriginRoot string `yaml:"origin_root"`
|
||||
TimeoutSeconds int `yaml:"timeout_seconds"`
|
||||
} `yaml:"musicfs"`
|
||||
}
|
||||
|
||||
type CacheConfig struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
RefreshInterval time.Duration `yaml:"refresh_interval"`
|
||||
TTL time.Duration `yaml:"ttl"`
|
||||
}
|
||||
|
||||
func (t *IndexerType) UnmarshalYAML(unmarshal func(any) error) error {
|
||||
var value string
|
||||
if err := unmarshal(&value); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch IndexerType(value) {
|
||||
case IndexerTypeJackett:
|
||||
*t = IndexerType(value)
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("unknown indexer type: %s", value)
|
||||
}
|
||||
}
|
||||
|
||||
func NewConfig() *Config {
|
||||
config := Config{}
|
||||
config.App.Host = "localhost"
|
||||
config.App.Port = "8080"
|
||||
|
||||
return &config
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
type AlbumEvent struct {
|
||||
ID string
|
||||
Seq int64
|
||||
WorkflowRunID string
|
||||
AlbumID string
|
||||
EventType string
|
||||
Step string
|
||||
Message string
|
||||
DataJSON []byte
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
type AlbumEventRepository struct {
|
||||
pool *pgxpool.Pool
|
||||
}
|
||||
|
||||
func NewAlbumEventRepository(pool *pgxpool.Pool) *AlbumEventRepository {
|
||||
return &AlbumEventRepository{pool: pool}
|
||||
}
|
||||
|
||||
func (r *AlbumEventRepository) Create(ctx context.Context, event *AlbumEvent) error {
|
||||
err := r.pool.QueryRow(ctx,
|
||||
`INSERT INTO album_events (workflow_run_id, album_id, event_type, step, message, data_json)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)
|
||||
RETURNING id, seq, created_at`,
|
||||
event.WorkflowRunID, event.AlbumID, event.EventType, event.Step, event.Message, event.DataJSON,
|
||||
).Scan(&event.ID, &event.Seq, &event.CreatedAt)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating album event: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *AlbumEventRepository) GetByWorkflowRun(ctx context.Context, workflowRunID string) ([]*AlbumEvent, error) {
|
||||
rows, err := r.pool.Query(ctx,
|
||||
`SELECT id, seq, workflow_run_id, album_id, event_type, step, message, data_json, created_at
|
||||
FROM album_events WHERE workflow_run_id = $1 ORDER BY seq`, workflowRunID,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("listing album events by workflow run: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var events []*AlbumEvent
|
||||
for rows.Next() {
|
||||
event := &AlbumEvent{}
|
||||
if err := rows.Scan(&event.ID, &event.Seq, &event.WorkflowRunID, &event.AlbumID, &event.EventType, &event.Step, &event.Message, &event.DataJSON, &event.CreatedAt); err != nil {
|
||||
return nil, fmt.Errorf("scanning album event: %w", err)
|
||||
}
|
||||
events = append(events, event)
|
||||
}
|
||||
return events, nil
|
||||
}
|
||||
|
||||
func (r *AlbumEventRepository) GetByAlbum(ctx context.Context, albumID string, afterSeq int64, limit int) ([]*AlbumEvent, error) {
|
||||
rows, err := r.pool.Query(ctx,
|
||||
`SELECT id, seq, workflow_run_id, album_id, event_type, step, message, data_json, created_at
|
||||
FROM album_events WHERE album_id = $1 AND seq > $2 ORDER BY seq LIMIT $3`, albumID, afterSeq, limit,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("listing album events by album: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var events []*AlbumEvent
|
||||
for rows.Next() {
|
||||
event := &AlbumEvent{}
|
||||
if err := rows.Scan(&event.ID, &event.Seq, &event.WorkflowRunID, &event.AlbumID, &event.EventType, &event.Step, &event.Message, &event.DataJSON, &event.CreatedAt); err != nil {
|
||||
return nil, fmt.Errorf("scanning album event: %w", err)
|
||||
}
|
||||
events = append(events, event)
|
||||
}
|
||||
return events, nil
|
||||
}
|
||||
|
||||
func (r *AlbumEventRepository) GetLatestSeq(ctx context.Context) (int64, error) {
|
||||
var seq int64
|
||||
err := r.pool.QueryRow(ctx,
|
||||
`SELECT COALESCE(MAX(seq), 0) FROM album_events`,
|
||||
).Scan(&seq)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("getting latest album event seq: %w", err)
|
||||
}
|
||||
return seq, nil
|
||||
}
|
||||
|
||||
func (r *AlbumEventRepository) GetAfterSeq(ctx context.Context, afterSeq int64) ([]*AlbumEvent, error) {
|
||||
rows, err := r.pool.Query(ctx,
|
||||
`SELECT id, seq, workflow_run_id, album_id, event_type, step, message, data_json, created_at
|
||||
FROM album_events WHERE seq > $1 ORDER BY seq LIMIT 1000`, afterSeq,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("listing album events after seq: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var events []*AlbumEvent
|
||||
for rows.Next() {
|
||||
event := &AlbumEvent{}
|
||||
if err := rows.Scan(&event.ID, &event.Seq, &event.WorkflowRunID, &event.AlbumID, &event.EventType, &event.Step, &event.Message, &event.DataJSON, &event.CreatedAt); err != nil {
|
||||
return nil, fmt.Errorf("scanning album event: %w", err)
|
||||
}
|
||||
events = append(events, event)
|
||||
}
|
||||
return events, nil
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
type AlbumRelease struct {
|
||||
ID string
|
||||
AlbumID string
|
||||
DownloadID string
|
||||
Format string
|
||||
BitDepth *int
|
||||
SampleRate *int
|
||||
Channels int
|
||||
IsLossless bool
|
||||
Source *string
|
||||
TotalSize int64
|
||||
TotalDurationMs int
|
||||
TrackCount int
|
||||
HasCoverArt bool
|
||||
HasCueSheet bool
|
||||
HasRipLog bool
|
||||
Path string
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
type AlbumReleaseRepository struct {
|
||||
pool *pgxpool.Pool
|
||||
}
|
||||
|
||||
func NewAlbumReleaseRepository(pool *pgxpool.Pool) *AlbumReleaseRepository {
|
||||
return &AlbumReleaseRepository{pool: pool}
|
||||
}
|
||||
|
||||
func (r *AlbumReleaseRepository) Create(ctx context.Context, ar *AlbumRelease) error {
|
||||
err := r.pool.QueryRow(ctx,
|
||||
`INSERT INTO album_releases (album_id, download_id, format, bit_depth, sample_rate, channels, is_lossless, source, total_size, total_duration_ms, track_count, has_cover_art, has_cue_sheet, has_rip_log, path)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)
|
||||
RETURNING id, created_at`,
|
||||
ar.AlbumID, ar.DownloadID, ar.Format, ar.BitDepth, ar.SampleRate, ar.Channels, ar.IsLossless, ar.Source, ar.TotalSize, ar.TotalDurationMs, ar.TrackCount, ar.HasCoverArt, ar.HasCueSheet, ar.HasRipLog, ar.Path,
|
||||
).Scan(&ar.ID, &ar.CreatedAt)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating album release: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *AlbumReleaseRepository) GetByAlbumID(ctx context.Context, albumID string) ([]*AlbumRelease, error) {
|
||||
rows, err := r.pool.Query(ctx,
|
||||
`SELECT id, album_id, download_id, format, bit_depth, sample_rate, channels, is_lossless, source, total_size, total_duration_ms, track_count, has_cover_art, has_cue_sheet, has_rip_log, path, created_at
|
||||
FROM album_releases WHERE album_id = $1 ORDER BY created_at DESC`, albumID,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("listing album releases: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var releases []*AlbumRelease
|
||||
for rows.Next() {
|
||||
ar := &AlbumRelease{}
|
||||
if err := rows.Scan(&ar.ID, &ar.AlbumID, &ar.DownloadID, &ar.Format, &ar.BitDepth, &ar.SampleRate, &ar.Channels, &ar.IsLossless, &ar.Source, &ar.TotalSize, &ar.TotalDurationMs, &ar.TrackCount, &ar.HasCoverArt, &ar.HasCueSheet, &ar.HasRipLog, &ar.Path, &ar.CreatedAt); err != nil {
|
||||
return nil, fmt.Errorf("scanning album release: %w", err)
|
||||
}
|
||||
releases = append(releases, ar)
|
||||
}
|
||||
return releases, nil
|
||||
}
|
||||
|
||||
func (r *AlbumReleaseRepository) GetByDownloadID(ctx context.Context, downloadID string) (*AlbumRelease, error) {
|
||||
ar := &AlbumRelease{}
|
||||
err := r.pool.QueryRow(ctx,
|
||||
`SELECT id, album_id, download_id, format, bit_depth, sample_rate, channels, is_lossless, source, total_size, total_duration_ms, track_count, has_cover_art, has_cue_sheet, has_rip_log, path, created_at
|
||||
FROM album_releases WHERE download_id = $1`, downloadID,
|
||||
).Scan(&ar.ID, &ar.AlbumID, &ar.DownloadID, &ar.Format, &ar.BitDepth, &ar.SampleRate, &ar.Channels, &ar.IsLossless, &ar.Source, &ar.TotalSize, &ar.TotalDurationMs, &ar.TrackCount, &ar.HasCoverArt, &ar.HasCueSheet, &ar.HasRipLog, &ar.Path, &ar.CreatedAt)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting album release by download: %w", err)
|
||||
}
|
||||
return ar, nil
|
||||
}
|
||||
|
||||
func (r *AlbumReleaseRepository) DeleteByDownloadID(ctx context.Context, downloadID string) error {
|
||||
_, err := r.pool.Exec(ctx, `DELETE FROM album_releases WHERE download_id = $1`, downloadID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("deleting album release by download: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,178 +0,0 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
type Album struct {
|
||||
ID string
|
||||
ExternalID string
|
||||
ArtistID string
|
||||
Title string
|
||||
AlbumType string
|
||||
ReleaseDate *time.Time
|
||||
TotalTracks int
|
||||
TotalDiscs int
|
||||
Label string
|
||||
Genres []string
|
||||
CoverURL string
|
||||
MonitorState MonitorState
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
type AlbumRepository struct {
|
||||
pool *pgxpool.Pool
|
||||
}
|
||||
|
||||
func NewAlbumRepository(pool *pgxpool.Pool) *AlbumRepository {
|
||||
return &AlbumRepository{pool: pool}
|
||||
}
|
||||
|
||||
func (r *AlbumRepository) Create(ctx context.Context, a *Album) error {
|
||||
_, err := r.pool.Exec(ctx,
|
||||
`INSERT INTO albums (external_id, artist_id, title, album_type, release_date, total_tracks, total_discs, label, genres, cover_url, monitor_state)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
|
||||
ON CONFLICT (external_id) DO UPDATE SET
|
||||
title = EXCLUDED.title,
|
||||
album_type = EXCLUDED.album_type,
|
||||
release_date = EXCLUDED.release_date,
|
||||
total_tracks = EXCLUDED.total_tracks,
|
||||
total_discs = EXCLUDED.total_discs,
|
||||
label = EXCLUDED.label,
|
||||
genres = EXCLUDED.genres,
|
||||
cover_url = EXCLUDED.cover_url,
|
||||
monitor_state = CASE
|
||||
WHEN albums.monitor_state = 'excluded' THEN albums.monitor_state
|
||||
WHEN albums.monitor_state = 'monitored' THEN albums.monitor_state
|
||||
ELSE EXCLUDED.monitor_state
|
||||
END,
|
||||
updated_at = NOW()`,
|
||||
a.ExternalID, a.ArtistID, a.Title, a.AlbumType, a.ReleaseDate, a.TotalTracks, a.TotalDiscs, a.Label, a.Genres, a.CoverURL, a.MonitorState,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating album: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *AlbumRepository) CreateBatch(ctx context.Context, albums []*Album) error {
|
||||
if len(albums) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
batch := &pgx.Batch{}
|
||||
for _, a := range albums {
|
||||
batch.Queue(
|
||||
`INSERT INTO albums (external_id, artist_id, title, album_type, release_date, total_tracks, total_discs, label, genres, cover_url, monitor_state)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
|
||||
ON CONFLICT (external_id) DO UPDATE SET
|
||||
title = EXCLUDED.title,
|
||||
album_type = EXCLUDED.album_type,
|
||||
release_date = EXCLUDED.release_date,
|
||||
total_tracks = EXCLUDED.total_tracks,
|
||||
total_discs = EXCLUDED.total_discs,
|
||||
label = EXCLUDED.label,
|
||||
genres = EXCLUDED.genres,
|
||||
cover_url = EXCLUDED.cover_url,
|
||||
monitor_state = CASE
|
||||
WHEN albums.monitor_state = 'excluded' THEN albums.monitor_state
|
||||
WHEN albums.monitor_state = 'monitored' THEN albums.monitor_state
|
||||
ELSE EXCLUDED.monitor_state
|
||||
END,
|
||||
updated_at = NOW()`,
|
||||
a.ExternalID, a.ArtistID, a.Title, a.AlbumType, a.ReleaseDate, a.TotalTracks, a.TotalDiscs, a.Label, a.Genres, a.CoverURL, a.MonitorState,
|
||||
)
|
||||
}
|
||||
|
||||
results := r.pool.SendBatch(ctx, batch)
|
||||
defer results.Close()
|
||||
|
||||
for range albums {
|
||||
if _, err := results.Exec(); err != nil {
|
||||
return fmt.Errorf("batch creating album: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *AlbumRepository) GetByExternalID(ctx context.Context, externalID string) (*Album, error) {
|
||||
a := &Album{}
|
||||
err := r.pool.QueryRow(ctx,
|
||||
`SELECT id, external_id, artist_id, title, album_type, release_date, total_tracks, total_discs, label, genres, cover_url, monitor_state, created_at, updated_at
|
||||
FROM albums WHERE external_id = $1`, externalID,
|
||||
).Scan(&a.ID, &a.ExternalID, &a.ArtistID, &a.Title, &a.AlbumType, &a.ReleaseDate, &a.TotalTracks, &a.TotalDiscs, &a.Label, &a.Genres, &a.CoverURL, &a.MonitorState, &a.CreatedAt, &a.UpdatedAt)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting album: %w", err)
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
func (r *AlbumRepository) GetByID(ctx context.Context, id string) (*Album, error) {
|
||||
a := &Album{}
|
||||
err := r.pool.QueryRow(ctx,
|
||||
`SELECT id, external_id, artist_id, title, album_type, release_date, total_tracks, total_discs, label, genres, cover_url, monitor_state, created_at, updated_at
|
||||
FROM albums WHERE id = $1`, id,
|
||||
).Scan(&a.ID, &a.ExternalID, &a.ArtistID, &a.Title, &a.AlbumType, &a.ReleaseDate, &a.TotalTracks, &a.TotalDiscs, &a.Label, &a.Genres, &a.CoverURL, &a.MonitorState, &a.CreatedAt, &a.UpdatedAt)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting album: %w", err)
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
func (r *AlbumRepository) GetByArtistID(ctx context.Context, artistID string) ([]*Album, error) {
|
||||
rows, err := r.pool.Query(ctx,
|
||||
`SELECT id, external_id, artist_id, title, album_type, release_date, total_tracks, total_discs, label, genres, cover_url, monitor_state, created_at, updated_at
|
||||
FROM albums WHERE artist_id = $1 ORDER BY release_date DESC`, artistID,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("listing albums: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var albums []*Album
|
||||
for rows.Next() {
|
||||
a := &Album{}
|
||||
if err := rows.Scan(&a.ID, &a.ExternalID, &a.ArtistID, &a.Title, &a.AlbumType, &a.ReleaseDate, &a.TotalTracks, &a.TotalDiscs, &a.Label, &a.Genres, &a.CoverURL, &a.MonitorState, &a.CreatedAt, &a.UpdatedAt); err != nil {
|
||||
return nil, fmt.Errorf("scanning album: %w", err)
|
||||
}
|
||||
albums = append(albums, a)
|
||||
}
|
||||
return albums, nil
|
||||
}
|
||||
|
||||
func (r *AlbumRepository) GetMonitored(ctx context.Context) ([]*Album, error) {
|
||||
rows, err := r.pool.Query(ctx,
|
||||
`SELECT id, external_id, artist_id, title, album_type, release_date, total_tracks, total_discs, label, genres, cover_url, monitor_state, created_at, updated_at
|
||||
FROM albums WHERE monitor_state = 'monitored' ORDER BY release_date DESC`,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("listing monitored albums: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var albums []*Album
|
||||
for rows.Next() {
|
||||
a := &Album{}
|
||||
if err := rows.Scan(&a.ID, &a.ExternalID, &a.ArtistID, &a.Title, &a.AlbumType, &a.ReleaseDate, &a.TotalTracks, &a.TotalDiscs, &a.Label, &a.Genres, &a.CoverURL, &a.MonitorState, &a.CreatedAt, &a.UpdatedAt); err != nil {
|
||||
return nil, fmt.Errorf("scanning album: %w", err)
|
||||
}
|
||||
albums = append(albums, a)
|
||||
}
|
||||
return albums, nil
|
||||
}
|
||||
|
||||
func (r *AlbumRepository) SetMonitorState(ctx context.Context, id string, state MonitorState) error {
|
||||
_, err := r.pool.Exec(ctx,
|
||||
`UPDATE albums SET monitor_state = $1, updated_at = NOW() WHERE id = $2`, state, id,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("updating monitor state: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
type Artist struct {
|
||||
ID string
|
||||
ExternalID string
|
||||
Name string
|
||||
ArtistType string
|
||||
Country string
|
||||
Genres []string
|
||||
ImageURL string
|
||||
MonitorState MonitorState
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
type ArtistRepository struct {
|
||||
pool *pgxpool.Pool
|
||||
}
|
||||
|
||||
func NewArtistRepository(pool *pgxpool.Pool) *ArtistRepository {
|
||||
return &ArtistRepository{pool: pool}
|
||||
}
|
||||
|
||||
func (r *ArtistRepository) Create(ctx context.Context, a *Artist) error {
|
||||
_, err := r.pool.Exec(ctx,
|
||||
`INSERT INTO artists (external_id, name, artist_type, country, genres, image_url, monitor_state)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||
ON CONFLICT (external_id) DO UPDATE SET
|
||||
name = EXCLUDED.name,
|
||||
artist_type = EXCLUDED.artist_type,
|
||||
country = EXCLUDED.country,
|
||||
genres = EXCLUDED.genres,
|
||||
image_url = EXCLUDED.image_url,
|
||||
monitor_state = CASE
|
||||
WHEN artists.monitor_state = 'excluded' THEN artists.monitor_state
|
||||
WHEN artists.monitor_state = 'monitored' THEN artists.monitor_state
|
||||
ELSE EXCLUDED.monitor_state
|
||||
END,
|
||||
updated_at = NOW()
|
||||
RETURNING id, created_at, updated_at`,
|
||||
a.ExternalID, a.Name, a.ArtistType, a.Country, a.Genres, a.ImageURL, a.MonitorState,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating artist: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *ArtistRepository) GetByExternalID(ctx context.Context, externalID string) (*Artist, error) {
|
||||
a := &Artist{}
|
||||
err := r.pool.QueryRow(ctx,
|
||||
`SELECT id, external_id, name, artist_type, country, genres, image_url, monitor_state, created_at, updated_at
|
||||
FROM artists WHERE external_id = $1`, externalID,
|
||||
).Scan(&a.ID, &a.ExternalID, &a.Name, &a.ArtistType, &a.Country, &a.Genres, &a.ImageURL, &a.MonitorState, &a.CreatedAt, &a.UpdatedAt)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting artist: %w", err)
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
func (r *ArtistRepository) GetAll(ctx context.Context) ([]*Artist, error) {
|
||||
rows, err := r.pool.Query(ctx,
|
||||
`SELECT id, external_id, name, artist_type, country, genres, image_url, monitor_state, created_at, updated_at
|
||||
FROM artists ORDER BY name ASC`,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("listing artists: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var artists []*Artist
|
||||
for rows.Next() {
|
||||
a := &Artist{}
|
||||
if err := rows.Scan(&a.ID, &a.ExternalID, &a.Name, &a.ArtistType, &a.Country, &a.Genres, &a.ImageURL, &a.MonitorState, &a.CreatedAt, &a.UpdatedAt); err != nil {
|
||||
return nil, fmt.Errorf("scanning artist: %w", err)
|
||||
}
|
||||
artists = append(artists, a)
|
||||
}
|
||||
return artists, nil
|
||||
}
|
||||
|
||||
func (r *ArtistRepository) GetByID(ctx context.Context, id string) (*Artist, error) {
|
||||
a := &Artist{}
|
||||
err := r.pool.QueryRow(ctx,
|
||||
`SELECT id, external_id, name, artist_type, country, genres, image_url, monitor_state, created_at, updated_at
|
||||
FROM artists WHERE id = $1`, id,
|
||||
).Scan(&a.ID, &a.ExternalID, &a.Name, &a.ArtistType, &a.Country, &a.Genres, &a.ImageURL, &a.MonitorState, &a.CreatedAt, &a.UpdatedAt)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting artist: %w", err)
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
type MonitorState string
|
||||
|
||||
const (
|
||||
Monitored MonitorState = "monitored"
|
||||
Unmonitored MonitorState = "unmonitored"
|
||||
Excluded MonitorState = "excluded"
|
||||
)
|
||||
|
||||
type DB struct {
|
||||
Pool *pgxpool.Pool
|
||||
}
|
||||
|
||||
func New(ctx context.Context, databaseURL string) (*DB, error) {
|
||||
pool, err := pgxpool.New(ctx, databaseURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("connecting to database: %w", err)
|
||||
}
|
||||
|
||||
if err := pool.Ping(ctx); err != nil {
|
||||
return nil, fmt.Errorf("pinging database: %w", err)
|
||||
}
|
||||
|
||||
log.Info().Str("url", databaseURL).Msg("database connected")
|
||||
|
||||
return &DB{Pool: pool}, nil
|
||||
}
|
||||
|
||||
func (db *DB) Close() {
|
||||
db.Pool.Close()
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
type DownloadFile struct {
|
||||
ID string
|
||||
DownloadID string
|
||||
TrackID *string
|
||||
FilePath string
|
||||
FileSize int64
|
||||
FileType string
|
||||
SHA256Hash string
|
||||
VerifiedAt *time.Time
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
type DownloadFileRepository struct {
|
||||
pool *pgxpool.Pool
|
||||
}
|
||||
|
||||
func NewDownloadFileRepository(pool *pgxpool.Pool) *DownloadFileRepository {
|
||||
return &DownloadFileRepository{pool: pool}
|
||||
}
|
||||
|
||||
func (r *DownloadFileRepository) Create(ctx context.Context, f *DownloadFile) error {
|
||||
err := r.pool.QueryRow(ctx,
|
||||
`INSERT INTO download_files (download_id, track_id, file_path, file_size, file_type, sha256_hash)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)
|
||||
RETURNING id, created_at`,
|
||||
f.DownloadID, f.TrackID, f.FilePath, f.FileSize, f.FileType, f.SHA256Hash,
|
||||
).Scan(&f.ID, &f.CreatedAt)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating download file: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *DownloadFileRepository) CreateBatch(ctx context.Context, files []*DownloadFile) error {
|
||||
for _, f := range files {
|
||||
if err := r.Create(ctx, f); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *DownloadFileRepository) GetByDownloadID(ctx context.Context, downloadID string) ([]*DownloadFile, error) {
|
||||
rows, err := r.pool.Query(ctx,
|
||||
`SELECT id, download_id, track_id, file_path, file_size, file_type, sha256_hash, verified_at, created_at
|
||||
FROM download_files WHERE download_id = $1 ORDER BY file_path`, downloadID,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("listing download files: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var files []*DownloadFile
|
||||
for rows.Next() {
|
||||
f := &DownloadFile{}
|
||||
if err := rows.Scan(&f.ID, &f.DownloadID, &f.TrackID, &f.FilePath, &f.FileSize, &f.FileType, &f.SHA256Hash, &f.VerifiedAt, &f.CreatedAt); err != nil {
|
||||
return nil, fmt.Errorf("scanning download file: %w", err)
|
||||
}
|
||||
files = append(files, f)
|
||||
}
|
||||
return files, nil
|
||||
}
|
||||
|
||||
func (r *DownloadFileRepository) SetHash(ctx context.Context, id string, hash string) error {
|
||||
_, err := r.pool.Exec(ctx,
|
||||
`UPDATE download_files SET sha256_hash = $1, verified_at = NOW() WHERE id = $2`, hash, id,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("setting file hash: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *DownloadFileRepository) GetUnverified(ctx context.Context) ([]*DownloadFile, error) {
|
||||
rows, err := r.pool.Query(ctx,
|
||||
`SELECT id, download_id, track_id, file_path, file_size, file_type, sha256_hash, verified_at, created_at
|
||||
FROM download_files WHERE sha256_hash IS NULL OR verified_at < NOW() - INTERVAL '30 days'
|
||||
ORDER BY created_at`,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("listing unverified files: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var files []*DownloadFile
|
||||
for rows.Next() {
|
||||
f := &DownloadFile{}
|
||||
if err := rows.Scan(&f.ID, &f.DownloadID, &f.TrackID, &f.FilePath, &f.FileSize, &f.FileType, &f.SHA256Hash, &f.VerifiedAt, &f.CreatedAt); err != nil {
|
||||
return nil, fmt.Errorf("scanning download file: %w", err)
|
||||
}
|
||||
files = append(files, f)
|
||||
}
|
||||
return files, nil
|
||||
}
|
||||
@@ -1,227 +0,0 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
type Download struct {
|
||||
ID string
|
||||
TorrentID string
|
||||
AlbumID string
|
||||
Format string
|
||||
Quality string
|
||||
State string
|
||||
QbitHash string
|
||||
SavePath *string
|
||||
ErrorMessage *string
|
||||
QueuedAt time.Time
|
||||
StartedAt *time.Time
|
||||
CompletedAt *time.Time
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
type DownloadRepository struct {
|
||||
pool *pgxpool.Pool
|
||||
}
|
||||
|
||||
func NewDownloadRepository(pool *pgxpool.Pool) *DownloadRepository {
|
||||
return &DownloadRepository{pool: pool}
|
||||
}
|
||||
|
||||
func (r *DownloadRepository) Create(ctx context.Context, d *Download) error {
|
||||
err := r.pool.QueryRow(ctx,
|
||||
`INSERT INTO downloads (torrent_id, album_id, format, quality, state, qbit_hash, save_path)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||
RETURNING id, queued_at, created_at, updated_at`,
|
||||
d.TorrentID, d.AlbumID, d.Format, d.Quality, d.State, d.QbitHash, d.SavePath,
|
||||
).Scan(&d.ID, &d.QueuedAt, &d.CreatedAt, &d.UpdatedAt)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating download: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *DownloadRepository) UpdateState(ctx context.Context, id string, state string) error {
|
||||
_, err := r.pool.Exec(ctx,
|
||||
`UPDATE downloads SET state = $1, updated_at = NOW() WHERE id = $2`, state, id,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("updating download state: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *DownloadRepository) SetStarted(ctx context.Context, id string) error {
|
||||
_, err := r.pool.Exec(ctx,
|
||||
`UPDATE downloads SET state = 'downloading', started_at = NOW(), updated_at = NOW() WHERE id = $1`, id,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("setting download started: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *DownloadRepository) SetCompleted(ctx context.Context, id string, savePath string) error {
|
||||
_, err := r.pool.Exec(ctx,
|
||||
`UPDATE downloads SET state = 'completed', save_path = $1, completed_at = NOW(), updated_at = NOW() WHERE id = $2`, savePath, id,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("setting download completed: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *DownloadRepository) SetFailed(ctx context.Context, id string, errorMsg string) error {
|
||||
_, err := r.pool.Exec(ctx,
|
||||
`UPDATE downloads SET state = 'failed', error_message = $1, updated_at = NOW() WHERE id = $2`, errorMsg, id,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("setting download failed: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *DownloadRepository) SetCancelled(ctx context.Context, id string) error {
|
||||
_, err := r.pool.Exec(ctx,
|
||||
`UPDATE downloads SET state = 'cancelled', updated_at = NOW() WHERE id = $1`, id,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("setting download cancelled: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *DownloadRepository) SetCancelledByQbitHash(ctx context.Context, hash string) error {
|
||||
_, err := r.pool.Exec(ctx,
|
||||
`UPDATE downloads SET state = 'cancelled', updated_at = NOW() WHERE qbit_hash = $1 AND state NOT IN ('completed', 'failed', 'cancelled')`, hash,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("setting download cancelled by hash: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *DownloadRepository) GetByAlbumID(ctx context.Context, albumID string) ([]*Download, error) {
|
||||
rows, err := r.pool.Query(ctx,
|
||||
`SELECT id, torrent_id, album_id, format, quality, state, qbit_hash, save_path, error_message, queued_at, started_at, completed_at, created_at, updated_at
|
||||
FROM downloads WHERE album_id = $1 ORDER BY created_at DESC`, albumID,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("listing downloads: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var downloads []*Download
|
||||
for rows.Next() {
|
||||
d := &Download{}
|
||||
if err := rows.Scan(&d.ID, &d.TorrentID, &d.AlbumID, &d.Format, &d.Quality, &d.State, &d.QbitHash, &d.SavePath, &d.ErrorMessage, &d.QueuedAt, &d.StartedAt, &d.CompletedAt, &d.CreatedAt, &d.UpdatedAt); err != nil {
|
||||
return nil, fmt.Errorf("scanning download: %w", err)
|
||||
}
|
||||
downloads = append(downloads, d)
|
||||
}
|
||||
return downloads, nil
|
||||
}
|
||||
|
||||
func (r *DownloadRepository) GetActiveByTorrentID(ctx context.Context, torrentID string) (*Download, error) {
|
||||
d := &Download{}
|
||||
err := r.pool.QueryRow(ctx,
|
||||
`SELECT id, torrent_id, album_id, format, quality, state, qbit_hash, save_path, error_message, queued_at, started_at, completed_at, created_at, updated_at
|
||||
FROM downloads WHERE torrent_id = $1 AND state NOT IN ('failed')
|
||||
ORDER BY created_at DESC LIMIT 1`, torrentID,
|
||||
).Scan(&d.ID, &d.TorrentID, &d.AlbumID, &d.Format, &d.Quality, &d.State, &d.QbitHash, &d.SavePath, &d.ErrorMessage, &d.QueuedAt, &d.StartedAt, &d.CompletedAt, &d.CreatedAt, &d.UpdatedAt)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting active download by torrent: %w", err)
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
func (r *DownloadRepository) GetActive(ctx context.Context) ([]*Download, error) {
|
||||
rows, err := r.pool.Query(ctx,
|
||||
`SELECT id, torrent_id, album_id, format, quality, state, qbit_hash, save_path, error_message, queued_at, started_at, completed_at, created_at, updated_at
|
||||
FROM downloads WHERE state IN ('pending', 'downloading') ORDER BY created_at`,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("listing active downloads: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var downloads []*Download
|
||||
for rows.Next() {
|
||||
d := &Download{}
|
||||
if err := rows.Scan(&d.ID, &d.TorrentID, &d.AlbumID, &d.Format, &d.Quality, &d.State, &d.QbitHash, &d.SavePath, &d.ErrorMessage, &d.QueuedAt, &d.StartedAt, &d.CompletedAt, &d.CreatedAt, &d.UpdatedAt); err != nil {
|
||||
return nil, fmt.Errorf("scanning download: %w", err)
|
||||
}
|
||||
downloads = append(downloads, d)
|
||||
}
|
||||
return downloads, nil
|
||||
}
|
||||
|
||||
func (r *DownloadRepository) GetByID(ctx context.Context, id string) (*Download, error) {
|
||||
d := &Download{}
|
||||
err := r.pool.QueryRow(ctx,
|
||||
`SELECT id, torrent_id, album_id, format, quality, state, qbit_hash, save_path, error_message, queued_at, started_at, completed_at, created_at, updated_at
|
||||
FROM downloads WHERE id = $1`, id,
|
||||
).Scan(&d.ID, &d.TorrentID, &d.AlbumID, &d.Format, &d.Quality, &d.State, &d.QbitHash, &d.SavePath, &d.ErrorMessage, &d.QueuedAt, &d.StartedAt, &d.CompletedAt, &d.CreatedAt, &d.UpdatedAt)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting download by id: %w", err)
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
func (r *DownloadRepository) GetLatestCompleted(ctx context.Context, albumID string) (*Download, error) {
|
||||
d := &Download{}
|
||||
err := r.pool.QueryRow(ctx,
|
||||
`SELECT id, torrent_id, album_id, format, quality, state, qbit_hash, save_path, error_message, queued_at, started_at, completed_at, created_at, updated_at
|
||||
FROM downloads WHERE album_id = $1 AND state = 'completed'
|
||||
ORDER BY completed_at DESC LIMIT 1`, albumID,
|
||||
).Scan(&d.ID, &d.TorrentID, &d.AlbumID, &d.Format, &d.Quality, &d.State, &d.QbitHash, &d.SavePath, &d.ErrorMessage, &d.QueuedAt, &d.StartedAt, &d.CompletedAt, &d.CreatedAt, &d.UpdatedAt)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting latest completed download: %w", err)
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
func (r *DownloadRepository) GetByAlbumIDs(ctx context.Context, albumIDs []string) (map[string][]*Download, error) {
|
||||
if len(albumIDs) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
rows, err := r.pool.Query(ctx,
|
||||
`SELECT id, torrent_id, album_id, format, quality, state, qbit_hash, save_path, error_message, queued_at, started_at, completed_at, created_at, updated_at
|
||||
FROM downloads WHERE album_id = ANY($1) AND state NOT IN ('failed', 'cancelled')
|
||||
ORDER BY album_id, created_at DESC`, albumIDs,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("batch listing downloads: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
result := make(map[string][]*Download, len(albumIDs))
|
||||
for rows.Next() {
|
||||
d := &Download{}
|
||||
if err := rows.Scan(&d.ID, &d.TorrentID, &d.AlbumID, &d.Format, &d.Quality, &d.State, &d.QbitHash, &d.SavePath, &d.ErrorMessage, &d.QueuedAt, &d.StartedAt, &d.CompletedAt, &d.CreatedAt, &d.UpdatedAt); err != nil {
|
||||
return nil, fmt.Errorf("scanning download: %w", err)
|
||||
}
|
||||
result[d.AlbumID] = append(result[d.AlbumID], d)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (r *DownloadRepository) HasAlbumInQuality(ctx context.Context, albumID string, format string, quality string) (bool, error) {
|
||||
var exists bool
|
||||
err := r.pool.QueryRow(ctx,
|
||||
`SELECT EXISTS(
|
||||
SELECT 1 FROM downloads
|
||||
WHERE album_id = $1 AND format = $2 AND quality = $3 AND state IN ('completed', 'seeding')
|
||||
)`, albumID, format, quality,
|
||||
).Scan(&exists)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("checking album quality: %w", err)
|
||||
}
|
||||
return exists, nil
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
type Torrent struct {
|
||||
ID string
|
||||
AlbumID string
|
||||
InfoHash string
|
||||
Tracker string
|
||||
Title string
|
||||
Format string
|
||||
Quality string
|
||||
Source string
|
||||
BitDepth int
|
||||
SampleRate int
|
||||
Seeders int
|
||||
Peers int
|
||||
Size int64
|
||||
TrackCount int
|
||||
HasCoverArt bool
|
||||
HasCueSheet bool
|
||||
HasRipLog bool
|
||||
DownloadLink string
|
||||
TorrentFile []byte
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
type TorrentRepository struct {
|
||||
pool *pgxpool.Pool
|
||||
}
|
||||
|
||||
func NewTorrentRepository(pool *pgxpool.Pool) *TorrentRepository {
|
||||
return &TorrentRepository{pool: pool}
|
||||
}
|
||||
|
||||
func (r *TorrentRepository) Create(ctx context.Context, t *Torrent) error {
|
||||
_, err := r.pool.Exec(ctx,
|
||||
`INSERT INTO torrents (album_id, info_hash, tracker, title, format, quality, source, bit_depth, sample_rate, seeders, peers, size, track_count, has_cover_art, has_cue_sheet, has_rip_log, download_link, torrent_file)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18)
|
||||
ON CONFLICT (info_hash) DO UPDATE SET
|
||||
seeders = EXCLUDED.seeders,
|
||||
peers = EXCLUDED.peers,
|
||||
updated_at = NOW()`,
|
||||
t.AlbumID, t.InfoHash, t.Tracker, t.Title, t.Format, t.Quality, t.Source, t.BitDepth, t.SampleRate, t.Seeders, t.Peers, t.Size, t.TrackCount, t.HasCoverArt, t.HasCueSheet, t.HasRipLog, t.DownloadLink, t.TorrentFile,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating torrent: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *TorrentRepository) GetByInfoHash(ctx context.Context, infoHash string) (*Torrent, error) {
|
||||
t := &Torrent{}
|
||||
err := r.pool.QueryRow(ctx,
|
||||
`SELECT id, album_id, info_hash, tracker, title, format, quality, source, bit_depth, sample_rate, seeders, peers, size, track_count, has_cover_art, has_cue_sheet, has_rip_log, download_link, torrent_file, created_at, updated_at
|
||||
FROM torrents WHERE info_hash = $1`, infoHash,
|
||||
).Scan(&t.ID, &t.AlbumID, &t.InfoHash, &t.Tracker, &t.Title, &t.Format, &t.Quality, &t.Source, &t.BitDepth, &t.SampleRate, &t.Seeders, &t.Peers, &t.Size, &t.TrackCount, &t.HasCoverArt, &t.HasCueSheet, &t.HasRipLog, &t.DownloadLink, &t.TorrentFile, &t.CreatedAt, &t.UpdatedAt)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting torrent: %w", err)
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func (r *TorrentRepository) GetByAlbumID(ctx context.Context, albumID string) ([]*Torrent, error) {
|
||||
rows, err := r.pool.Query(ctx,
|
||||
`SELECT id, album_id, info_hash, tracker, title, format, quality, source, bit_depth, sample_rate, seeders, peers, size, track_count, has_cover_art, has_cue_sheet, has_rip_log, download_link, torrent_file, created_at, updated_at
|
||||
FROM torrents WHERE album_id = $1 ORDER BY seeders DESC`, albumID,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("listing torrents: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var torrents []*Torrent
|
||||
for rows.Next() {
|
||||
t := &Torrent{}
|
||||
if err := rows.Scan(&t.ID, &t.AlbumID, &t.InfoHash, &t.Tracker, &t.Title, &t.Format, &t.Quality, &t.Source, &t.BitDepth, &t.SampleRate, &t.Seeders, &t.Peers, &t.Size, &t.TrackCount, &t.HasCoverArt, &t.HasCueSheet, &t.HasRipLog, &t.DownloadLink, &t.TorrentFile, &t.CreatedAt, &t.UpdatedAt); err != nil {
|
||||
return nil, fmt.Errorf("scanning torrent: %w", err)
|
||||
}
|
||||
torrents = append(torrents, t)
|
||||
}
|
||||
return torrents, nil
|
||||
}
|
||||
|
||||
func (r *TorrentRepository) HasAlbumInFormat(ctx context.Context, albumID string, format string) (bool, error) {
|
||||
var exists bool
|
||||
err := r.pool.QueryRow(ctx,
|
||||
`SELECT EXISTS(
|
||||
SELECT 1 FROM downloads
|
||||
WHERE album_id = $1 AND format = $2 AND state IN ('completed', 'seeding')
|
||||
)`, albumID, format,
|
||||
).Scan(&exists)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("checking album format: %w", err)
|
||||
}
|
||||
return exists, nil
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
type TrackRelease struct {
|
||||
ID string
|
||||
AlbumReleaseID string
|
||||
TrackID *string
|
||||
DownloadFileID *string
|
||||
Title string
|
||||
TrackNumber int
|
||||
DiscNumber int
|
||||
DurationMs *int
|
||||
Format string
|
||||
BitDepth *int
|
||||
SampleRate *int
|
||||
Channels int
|
||||
BitrateKbps *int
|
||||
FileSize int64
|
||||
FilePath string
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
type TrackReleaseRepository struct {
|
||||
pool *pgxpool.Pool
|
||||
}
|
||||
|
||||
func NewTrackReleaseRepository(pool *pgxpool.Pool) *TrackReleaseRepository {
|
||||
return &TrackReleaseRepository{pool: pool}
|
||||
}
|
||||
|
||||
func (r *TrackReleaseRepository) Create(ctx context.Context, tr *TrackRelease) error {
|
||||
err := r.pool.QueryRow(ctx,
|
||||
`INSERT INTO track_releases (album_release_id, track_id, download_file_id, title, track_number, disc_number, duration_ms, format, bit_depth, sample_rate, channels, bitrate_kbps, file_size, file_path)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)
|
||||
RETURNING id, created_at`,
|
||||
tr.AlbumReleaseID, tr.TrackID, tr.DownloadFileID, tr.Title, tr.TrackNumber, tr.DiscNumber, tr.DurationMs, tr.Format, tr.BitDepth, tr.SampleRate, tr.Channels, tr.BitrateKbps, tr.FileSize, tr.FilePath,
|
||||
).Scan(&tr.ID, &tr.CreatedAt)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating track release: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *TrackReleaseRepository) CreateBatch(ctx context.Context, tracks []*TrackRelease) error {
|
||||
for _, tr := range tracks {
|
||||
if err := r.Create(ctx, tr); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *TrackReleaseRepository) GetByAlbumReleaseID(ctx context.Context, albumReleaseID string) ([]*TrackRelease, error) {
|
||||
rows, err := r.pool.Query(ctx,
|
||||
`SELECT id, album_release_id, track_id, download_file_id, title, track_number, disc_number, duration_ms, format, bit_depth, sample_rate, channels, bitrate_kbps, file_size, file_path, created_at
|
||||
FROM track_releases WHERE album_release_id = $1 ORDER BY disc_number, track_number`, albumReleaseID,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("listing track releases: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var tracks []*TrackRelease
|
||||
for rows.Next() {
|
||||
tr := &TrackRelease{}
|
||||
if err := rows.Scan(&tr.ID, &tr.AlbumReleaseID, &tr.TrackID, &tr.DownloadFileID, &tr.Title, &tr.TrackNumber, &tr.DiscNumber, &tr.DurationMs, &tr.Format, &tr.BitDepth, &tr.SampleRate, &tr.Channels, &tr.BitrateKbps, &tr.FileSize, &tr.FilePath, &tr.CreatedAt); err != nil {
|
||||
return nil, fmt.Errorf("scanning track release: %w", err)
|
||||
}
|
||||
tracks = append(tracks, tr)
|
||||
}
|
||||
return tracks, nil
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
type Track struct {
|
||||
ID string
|
||||
ExternalID string
|
||||
AlbumID string
|
||||
Title string
|
||||
DurationMS int
|
||||
ISRC string
|
||||
DiscNumber int
|
||||
TrackNumber int
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
type TrackRepository struct {
|
||||
pool *pgxpool.Pool
|
||||
}
|
||||
|
||||
func NewTrackRepository(pool *pgxpool.Pool) *TrackRepository {
|
||||
return &TrackRepository{pool: pool}
|
||||
}
|
||||
|
||||
func (r *TrackRepository) Create(ctx context.Context, t *Track) error {
|
||||
_, err := r.pool.Exec(ctx,
|
||||
`INSERT INTO tracks (external_id, album_id, title, duration_ms, isrc, disc_number, track_number)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||
ON CONFLICT (external_id) DO UPDATE SET
|
||||
title = EXCLUDED.title,
|
||||
duration_ms = EXCLUDED.duration_ms,
|
||||
isrc = EXCLUDED.isrc,
|
||||
disc_number = EXCLUDED.disc_number,
|
||||
track_number = EXCLUDED.track_number`,
|
||||
t.ExternalID, t.AlbumID, t.Title, t.DurationMS, t.ISRC, t.DiscNumber, t.TrackNumber,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating track: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *TrackRepository) GetByAlbumID(ctx context.Context, albumID string) ([]*Track, error) {
|
||||
rows, err := r.pool.Query(ctx,
|
||||
`SELECT id, external_id, album_id, title, duration_ms, isrc, disc_number, track_number, created_at
|
||||
FROM tracks WHERE album_id = $1 ORDER BY disc_number, track_number`, albumID,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("listing tracks: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var tracks []*Track
|
||||
for rows.Next() {
|
||||
t := &Track{}
|
||||
if err := rows.Scan(&t.ID, &t.ExternalID, &t.AlbumID, &t.Title, &t.DurationMS, &t.ISRC, &t.DiscNumber, &t.TrackNumber, &t.CreatedAt); err != nil {
|
||||
return nil, fmt.Errorf("scanning track: %w", err)
|
||||
}
|
||||
tracks = append(tracks, t)
|
||||
}
|
||||
return tracks, nil
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
var ErrWorkflowAlreadyRunning = fmt.Errorf("workflow already running for this album and quality")
|
||||
|
||||
type WorkflowRun struct {
|
||||
ID string
|
||||
AlbumID string
|
||||
Quality string
|
||||
Status string
|
||||
ErrorMessage *string
|
||||
StartedAt time.Time
|
||||
CompletedAt *time.Time
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
type WorkflowRunRepository struct {
|
||||
pool *pgxpool.Pool
|
||||
}
|
||||
|
||||
func NewWorkflowRunRepository(pool *pgxpool.Pool) *WorkflowRunRepository {
|
||||
return &WorkflowRunRepository{pool: pool}
|
||||
}
|
||||
|
||||
func (r *WorkflowRunRepository) Create(ctx context.Context, run *WorkflowRun) error {
|
||||
err := r.pool.QueryRow(ctx,
|
||||
`INSERT INTO workflow_runs (album_id, quality, status) VALUES ($1, $2, 'running')
|
||||
ON CONFLICT ON CONSTRAINT idx_workflow_runs_active DO NOTHING
|
||||
RETURNING id, started_at, created_at, updated_at`,
|
||||
run.AlbumID, run.Quality,
|
||||
).Scan(&run.ID, &run.StartedAt, &run.CreatedAt, &run.UpdatedAt)
|
||||
if err != nil {
|
||||
if err == pgx.ErrNoRows {
|
||||
return ErrWorkflowAlreadyRunning
|
||||
}
|
||||
return fmt.Errorf("creating workflow run: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *WorkflowRunRepository) SetCompleted(ctx context.Context, id string) error {
|
||||
_, err := r.pool.Exec(ctx,
|
||||
`UPDATE workflow_runs SET status = 'completed', completed_at = NOW(), updated_at = NOW() WHERE id = $1`, id,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("setting workflow run completed: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *WorkflowRunRepository) SetFailed(ctx context.Context, id string, errorMsg string) error {
|
||||
_, err := r.pool.Exec(ctx,
|
||||
`UPDATE workflow_runs SET status = 'failed', error_message = $1, completed_at = NOW(), updated_at = NOW() WHERE id = $2`, errorMsg, id,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("setting workflow run failed: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *WorkflowRunRepository) SetCancelled(ctx context.Context, id string) error {
|
||||
_, err := r.pool.Exec(ctx,
|
||||
`UPDATE workflow_runs SET status = 'cancelled', completed_at = NOW(), updated_at = NOW() WHERE id = $1`, id,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("setting workflow run cancelled: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *WorkflowRunRepository) GetByAlbumAndQuality(ctx context.Context, albumID string, quality string) (*WorkflowRun, error) {
|
||||
run := &WorkflowRun{}
|
||||
err := r.pool.QueryRow(ctx,
|
||||
`SELECT id, album_id, quality, status, error_message, started_at, completed_at, created_at, updated_at
|
||||
FROM workflow_runs WHERE album_id = $1 AND quality = $2 AND status = 'running' LIMIT 1`, albumID, quality,
|
||||
).Scan(&run.ID, &run.AlbumID, &run.Quality, &run.Status, &run.ErrorMessage, &run.StartedAt, &run.CompletedAt, &run.CreatedAt, &run.UpdatedAt)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting workflow run by album and quality: %w", err)
|
||||
}
|
||||
return run, nil
|
||||
}
|
||||
|
||||
func (r *WorkflowRunRepository) GetRunning(ctx context.Context) ([]*WorkflowRun, error) {
|
||||
rows, err := r.pool.Query(ctx,
|
||||
`SELECT id, album_id, quality, status, error_message, started_at, completed_at, created_at, updated_at
|
||||
FROM workflow_runs WHERE status = 'running' ORDER BY started_at`,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("listing running workflow runs: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var runs []*WorkflowRun
|
||||
for rows.Next() {
|
||||
run := &WorkflowRun{}
|
||||
if err := rows.Scan(&run.ID, &run.AlbumID, &run.Quality, &run.Status, &run.ErrorMessage, &run.StartedAt, &run.CompletedAt, &run.CreatedAt, &run.UpdatedAt); err != nil {
|
||||
return nil, fmt.Errorf("scanning workflow run: %w", err)
|
||||
}
|
||||
runs = append(runs, run)
|
||||
}
|
||||
return runs, nil
|
||||
}
|
||||
|
||||
func (r *WorkflowRunRepository) GetByID(ctx context.Context, id string) (*WorkflowRun, error) {
|
||||
run := &WorkflowRun{}
|
||||
err := r.pool.QueryRow(ctx,
|
||||
`SELECT id, album_id, quality, status, error_message, started_at, completed_at, created_at, updated_at
|
||||
FROM workflow_runs WHERE id = $1`, id,
|
||||
).Scan(&run.ID, &run.AlbumID, &run.Quality, &run.Status, &run.ErrorMessage, &run.StartedAt, &run.CompletedAt, &run.CreatedAt, &run.UpdatedAt)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting workflow run by id: %w", err)
|
||||
}
|
||||
return run, nil
|
||||
}
|
||||
@@ -1,260 +0,0 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"sync"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
pb "homelab.lan/music-agregator/gen/music_agregator/v1"
|
||||
"homelab.lan/music-agregator/internal/database"
|
||||
"homelab.lan/music-agregator/internal/eventbus"
|
||||
)
|
||||
|
||||
type EventPublisher interface {
|
||||
PublishStatus(ctx context.Context, step pb.MonitorStep, msg string, data interface{}) error
|
||||
PublishError(ctx context.Context, step pb.MonitorStep, err error, recoverable bool) error
|
||||
PublishResult(ctx context.Context, result *pb.MonitorAlbumResponse) error
|
||||
SetAlbumID(albumID string)
|
||||
SetWorkflowRunID(id string)
|
||||
}
|
||||
|
||||
type dbEventPublisher struct {
|
||||
mu sync.Mutex
|
||||
workflowRunID string
|
||||
albumID string
|
||||
quality string
|
||||
events *database.AlbumEventRepository
|
||||
bus *eventbus.EventBus
|
||||
topic string
|
||||
}
|
||||
|
||||
func newDBEventPublisher(albumID, quality string, events *database.AlbumEventRepository, bus *eventbus.EventBus, topic string) *dbEventPublisher {
|
||||
return &dbEventPublisher{
|
||||
albumID: albumID,
|
||||
quality: quality,
|
||||
events: events,
|
||||
bus: bus,
|
||||
topic: topic,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *dbEventPublisher) SetAlbumID(albumID string) {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
p.albumID = albumID
|
||||
}
|
||||
|
||||
func (p *dbEventPublisher) getAlbumID() string {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
return p.albumID
|
||||
}
|
||||
|
||||
func (p *dbEventPublisher) SetWorkflowRunID(id string) {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
p.workflowRunID = id
|
||||
}
|
||||
|
||||
func (p *dbEventPublisher) getWorkflowRunID() string {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
return p.workflowRunID
|
||||
}
|
||||
|
||||
func (p *dbEventPublisher) PublishStatus(ctx context.Context, step pb.MonitorStep, msg string, data interface{}) error {
|
||||
var dataJSON []byte
|
||||
if data != nil {
|
||||
var err error
|
||||
dataJSON, err = json.Marshal(data)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msg("failed to marshal status data to JSON")
|
||||
dataJSON = nil
|
||||
}
|
||||
}
|
||||
|
||||
albumID := p.getAlbumID()
|
||||
workflowRunID := p.getWorkflowRunID()
|
||||
|
||||
var seq int64
|
||||
if albumID != "" {
|
||||
event := &database.AlbumEvent{
|
||||
WorkflowRunID: workflowRunID,
|
||||
AlbumID: albumID,
|
||||
EventType: "status",
|
||||
Step: step.String(),
|
||||
Message: msg,
|
||||
DataJSON: dataJSON,
|
||||
}
|
||||
|
||||
if err := p.events.Create(ctx, event); err != nil {
|
||||
log.Error().Err(err).Msg("failed to persist status event")
|
||||
} else {
|
||||
seq = event.Seq
|
||||
}
|
||||
}
|
||||
|
||||
p.bus.Publish(p.topic, &eventbus.Event{
|
||||
Seq: seq,
|
||||
WorkflowRunID: workflowRunID,
|
||||
AlbumID: albumID,
|
||||
Quality: p.quality,
|
||||
EventType: "status",
|
||||
Step: step.String(),
|
||||
Message: msg,
|
||||
Data: data,
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *dbEventPublisher) PublishError(ctx context.Context, step pb.MonitorStep, err error, recoverable bool) error {
|
||||
albumID := p.getAlbumID()
|
||||
workflowRunID := p.getWorkflowRunID()
|
||||
|
||||
var seq int64
|
||||
if albumID != "" {
|
||||
event := &database.AlbumEvent{
|
||||
WorkflowRunID: workflowRunID,
|
||||
AlbumID: albumID,
|
||||
EventType: "error",
|
||||
Step: step.String(),
|
||||
Message: err.Error(),
|
||||
}
|
||||
|
||||
if dbErr := p.events.Create(ctx, event); dbErr != nil {
|
||||
log.Error().Err(dbErr).Msg("failed to persist error event")
|
||||
} else {
|
||||
seq = event.Seq
|
||||
}
|
||||
}
|
||||
|
||||
p.bus.Publish(p.topic, &eventbus.Event{
|
||||
Seq: seq,
|
||||
WorkflowRunID: workflowRunID,
|
||||
AlbumID: albumID,
|
||||
Quality: p.quality,
|
||||
EventType: "error",
|
||||
Step: step.String(),
|
||||
Message: err.Error(),
|
||||
Data: map[string]bool{"recoverable": recoverable},
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *dbEventPublisher) PublishResult(ctx context.Context, result *pb.MonitorAlbumResponse) error {
|
||||
var dataJSON []byte
|
||||
if result != nil {
|
||||
var err error
|
||||
dataJSON, err = json.Marshal(result)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msg("failed to marshal result to JSON")
|
||||
dataJSON = nil
|
||||
}
|
||||
}
|
||||
|
||||
albumID := p.getAlbumID()
|
||||
workflowRunID := p.getWorkflowRunID()
|
||||
|
||||
var seq int64
|
||||
if albumID != "" {
|
||||
event := &database.AlbumEvent{
|
||||
WorkflowRunID: workflowRunID,
|
||||
AlbumID: albumID,
|
||||
EventType: "result",
|
||||
Step: pb.MonitorStep_MONITOR_STEP_COMPLETE.String(),
|
||||
Message: "workflow completed",
|
||||
DataJSON: dataJSON,
|
||||
}
|
||||
|
||||
if err := p.events.Create(ctx, event); err != nil {
|
||||
log.Error().Err(err).Msg("failed to persist result event")
|
||||
} else {
|
||||
seq = event.Seq
|
||||
}
|
||||
}
|
||||
|
||||
p.bus.Publish(p.topic, &eventbus.Event{
|
||||
Seq: seq,
|
||||
WorkflowRunID: workflowRunID,
|
||||
AlbumID: albumID,
|
||||
Quality: p.quality,
|
||||
EventType: "result",
|
||||
Step: pb.MonitorStep_MONITOR_STEP_COMPLETE.String(),
|
||||
Message: "workflow completed",
|
||||
Data: result,
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type streamEventPublisher struct {
|
||||
*dbEventPublisher
|
||||
stream pb.MusicAgregatorService_MonitorAlbumStreamServer
|
||||
}
|
||||
|
||||
func newStreamEventPublisher(db *dbEventPublisher, stream pb.MusicAgregatorService_MonitorAlbumStreamServer) *streamEventPublisher {
|
||||
return &streamEventPublisher{
|
||||
dbEventPublisher: db,
|
||||
stream: stream,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *streamEventPublisher) PublishStatus(ctx context.Context, step pb.MonitorStep, msg string, data interface{}) error {
|
||||
if err := p.dbEventPublisher.PublishStatus(ctx, step, msg, data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
status := &pb.StatusUpdate{
|
||||
Step: step,
|
||||
Message: msg,
|
||||
}
|
||||
|
||||
switch v := data.(type) {
|
||||
case *pb.StreamAlbumInfo:
|
||||
status.Data = &pb.StatusUpdate_AlbumInfo{AlbumInfo: v}
|
||||
case *pb.TorrentList:
|
||||
status.Data = &pb.StatusUpdate_Torrents{Torrents: v}
|
||||
case *pb.ReleaseInfo:
|
||||
status.Data = &pb.StatusUpdate_ReleaseInfo{ReleaseInfo: v}
|
||||
}
|
||||
|
||||
return p.stream.Send(&pb.MonitorAlbumStreamResponse{
|
||||
Message: &pb.MonitorAlbumStreamResponse_Status{Status: status},
|
||||
})
|
||||
}
|
||||
|
||||
func (p *streamEventPublisher) PublishError(ctx context.Context, step pb.MonitorStep, err error, recoverable bool) error {
|
||||
if dbErr := p.dbEventPublisher.PublishError(ctx, step, err, recoverable); dbErr != nil {
|
||||
return dbErr
|
||||
}
|
||||
|
||||
return p.stream.Send(&pb.MonitorAlbumStreamResponse{
|
||||
Message: &pb.MonitorAlbumStreamResponse_Error{
|
||||
Error: &pb.ErrorUpdate{
|
||||
FailedStep: step,
|
||||
Message: err.Error(),
|
||||
Recoverable: recoverable,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (p *streamEventPublisher) PublishResult(ctx context.Context, result *pb.MonitorAlbumResponse) error {
|
||||
if err := p.dbEventPublisher.PublishResult(ctx, result); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return p.stream.Send(&pb.MonitorAlbumStreamResponse{
|
||||
Message: &pb.MonitorAlbumStreamResponse_Result{Result: result},
|
||||
})
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
package eventbus
|
||||
|
||||
import "sync"
|
||||
|
||||
type Event struct {
|
||||
Seq int64
|
||||
WorkflowRunID string
|
||||
AlbumID string
|
||||
Quality string
|
||||
EventType string
|
||||
Step string
|
||||
Message string
|
||||
Data interface{}
|
||||
}
|
||||
|
||||
type Subscription struct {
|
||||
Ring *RingBuffer[*Event]
|
||||
C chan struct{}
|
||||
done chan struct{}
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
type EventBus struct {
|
||||
mu sync.RWMutex
|
||||
topics map[string]map[*Subscription]struct{}
|
||||
global map[*Subscription]struct{}
|
||||
}
|
||||
|
||||
func New() *EventBus {
|
||||
return &EventBus{
|
||||
topics: make(map[string]map[*Subscription]struct{}),
|
||||
global: make(map[*Subscription]struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (b *EventBus) Publish(topic string, event *Event) {
|
||||
b.mu.RLock()
|
||||
defer b.mu.RUnlock()
|
||||
|
||||
if subs, ok := b.topics[topic]; ok {
|
||||
for sub := range subs {
|
||||
sub.Ring.Push(event)
|
||||
select {
|
||||
case sub.C <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for sub := range b.global {
|
||||
sub.Ring.Push(event)
|
||||
select {
|
||||
case sub.C <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *EventBus) Subscribe(topic string) (*Subscription, func()) {
|
||||
sub := &Subscription{
|
||||
Ring: NewRingBuffer[*Event](256),
|
||||
C: make(chan struct{}, 1),
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
|
||||
b.mu.Lock()
|
||||
if b.topics[topic] == nil {
|
||||
b.topics[topic] = make(map[*Subscription]struct{})
|
||||
}
|
||||
b.topics[topic][sub] = struct{}{}
|
||||
b.mu.Unlock()
|
||||
|
||||
cleanup := func() {
|
||||
sub.once.Do(func() {
|
||||
b.mu.Lock()
|
||||
delete(b.topics[topic], sub)
|
||||
if len(b.topics[topic]) == 0 {
|
||||
delete(b.topics, topic)
|
||||
}
|
||||
b.mu.Unlock()
|
||||
close(sub.done)
|
||||
})
|
||||
}
|
||||
|
||||
return sub, cleanup
|
||||
}
|
||||
|
||||
func (b *EventBus) SubscribeGlobal() (*Subscription, func()) {
|
||||
sub := &Subscription{
|
||||
Ring: NewRingBuffer[*Event](256),
|
||||
C: make(chan struct{}, 1),
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
|
||||
b.mu.Lock()
|
||||
b.global[sub] = struct{}{}
|
||||
b.mu.Unlock()
|
||||
|
||||
cleanup := func() {
|
||||
sub.once.Do(func() {
|
||||
b.mu.Lock()
|
||||
delete(b.global, sub)
|
||||
b.mu.Unlock()
|
||||
close(sub.done)
|
||||
})
|
||||
}
|
||||
|
||||
return sub, cleanup
|
||||
}
|
||||
|
||||
func (b *EventBus) HasTopic(topic string) bool {
|
||||
b.mu.RLock()
|
||||
defer b.mu.RUnlock()
|
||||
_, ok := b.topics[topic]
|
||||
return ok
|
||||
}
|
||||
@@ -1,168 +0,0 @@
|
||||
package eventbus
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestEventBus_PublishSubscribe(t *testing.T) {
|
||||
bus := New()
|
||||
sub, cleanup := bus.Subscribe("test-topic")
|
||||
defer cleanup()
|
||||
|
||||
event := &Event{Seq: 1, EventType: "status", Message: "hello"}
|
||||
bus.Publish("test-topic", event)
|
||||
|
||||
got, ok := sub.Ring.Pop()
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, event, got)
|
||||
}
|
||||
|
||||
func TestEventBus_MultipleSubscribers(t *testing.T) {
|
||||
bus := New()
|
||||
sub1, cleanup1 := bus.Subscribe("topic")
|
||||
defer cleanup1()
|
||||
sub2, cleanup2 := bus.Subscribe("topic")
|
||||
defer cleanup2()
|
||||
sub3, cleanup3 := bus.Subscribe("topic")
|
||||
defer cleanup3()
|
||||
|
||||
event := &Event{Seq: 1, EventType: "status"}
|
||||
bus.Publish("topic", event)
|
||||
|
||||
got1, ok := sub1.Ring.Pop()
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, event, got1)
|
||||
|
||||
got2, ok := sub2.Ring.Pop()
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, event, got2)
|
||||
|
||||
got3, ok := sub3.Ring.Pop()
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, event, got3)
|
||||
}
|
||||
|
||||
func TestEventBus_GlobalSubscriber(t *testing.T) {
|
||||
bus := New()
|
||||
sub, cleanup := bus.SubscribeGlobal()
|
||||
defer cleanup()
|
||||
|
||||
bus.Publish("topic-a", &Event{Seq: 1})
|
||||
bus.Publish("topic-b", &Event{Seq: 2})
|
||||
bus.Publish("topic-c", &Event{Seq: 3})
|
||||
|
||||
got, ok := sub.Ring.Pop()
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, int64(1), got.Seq)
|
||||
|
||||
got, ok = sub.Ring.Pop()
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, int64(2), got.Seq)
|
||||
|
||||
got, ok = sub.Ring.Pop()
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, int64(3), got.Seq)
|
||||
}
|
||||
|
||||
func TestEventBus_TopicIsolation(t *testing.T) {
|
||||
bus := New()
|
||||
subA, cleanupA := bus.Subscribe("topic-a")
|
||||
defer cleanupA()
|
||||
|
||||
bus.Publish("topic-b", &Event{Seq: 1})
|
||||
|
||||
_, ok := subA.Ring.Pop()
|
||||
assert.False(t, ok)
|
||||
}
|
||||
|
||||
func TestEventBus_Notification(t *testing.T) {
|
||||
bus := New()
|
||||
sub, cleanup := bus.Subscribe("topic")
|
||||
defer cleanup()
|
||||
|
||||
bus.Publish("topic", &Event{Seq: 1})
|
||||
|
||||
select {
|
||||
case <-sub.C:
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
t.Fatal("expected notification on channel")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEventBus_Unsubscribe(t *testing.T) {
|
||||
bus := New()
|
||||
sub, cleanup := bus.Subscribe("topic")
|
||||
|
||||
bus.Publish("topic", &Event{Seq: 1})
|
||||
_, ok := sub.Ring.Pop()
|
||||
require.True(t, ok)
|
||||
|
||||
cleanup()
|
||||
|
||||
bus.Publish("topic", &Event{Seq: 2})
|
||||
_, ok = sub.Ring.Pop()
|
||||
assert.False(t, ok)
|
||||
}
|
||||
|
||||
func TestEventBus_SlowSubscriber(t *testing.T) {
|
||||
bus := New()
|
||||
sub, cleanup := bus.Subscribe("topic")
|
||||
defer cleanup()
|
||||
|
||||
for i := 0; i < 500; i++ {
|
||||
bus.Publish("topic", &Event{Seq: int64(i)})
|
||||
}
|
||||
|
||||
assert.Equal(t, 256, sub.Ring.Len())
|
||||
|
||||
first, ok := sub.Ring.Pop()
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, int64(244), first.Seq)
|
||||
}
|
||||
|
||||
func TestEventBus_HasTopic(t *testing.T) {
|
||||
bus := New()
|
||||
|
||||
assert.False(t, bus.HasTopic("topic"))
|
||||
|
||||
sub, cleanup := bus.Subscribe("topic")
|
||||
_ = sub
|
||||
assert.True(t, bus.HasTopic("topic"))
|
||||
|
||||
cleanup()
|
||||
assert.False(t, bus.HasTopic("topic"))
|
||||
}
|
||||
|
||||
func TestEventBus_ConcurrentPublishSubscribe(t *testing.T) {
|
||||
bus := New()
|
||||
var wg sync.WaitGroup
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
wg.Add(1)
|
||||
go func(id int) {
|
||||
defer wg.Done()
|
||||
sub, cleanup := bus.Subscribe("topic")
|
||||
defer cleanup()
|
||||
for j := 0; j < 100; j++ {
|
||||
sub.Ring.Pop()
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
wg.Add(1)
|
||||
go func(id int) {
|
||||
defer wg.Done()
|
||||
for j := 0; j < 100; j++ {
|
||||
bus.Publish("topic", &Event{Seq: int64(id*100 + j)})
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
package eventbus
|
||||
|
||||
import "sync"
|
||||
|
||||
type RingBuffer[T any] struct {
|
||||
mu sync.Mutex
|
||||
buf []T
|
||||
head int
|
||||
tail int
|
||||
count int
|
||||
cap int
|
||||
}
|
||||
|
||||
func NewRingBuffer[T any](capacity int) *RingBuffer[T] {
|
||||
return &RingBuffer[T]{
|
||||
buf: make([]T, capacity),
|
||||
cap: capacity,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RingBuffer[T]) Push(item T) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
r.buf[r.head] = item
|
||||
r.head = (r.head + 1) % r.cap
|
||||
|
||||
if r.count == r.cap {
|
||||
r.tail = (r.tail + 1) % r.cap
|
||||
} else {
|
||||
r.count++
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RingBuffer[T]) Pop() (T, bool) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
var zero T
|
||||
if r.count == 0 {
|
||||
return zero, false
|
||||
}
|
||||
|
||||
item := r.buf[r.tail]
|
||||
r.buf[r.tail] = zero
|
||||
r.tail = (r.tail + 1) % r.cap
|
||||
r.count--
|
||||
|
||||
return item, true
|
||||
}
|
||||
|
||||
func (r *RingBuffer[T]) Len() int {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
return r.count
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
package eventbus
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestRingBuffer_PushPop(t *testing.T) {
|
||||
ring := NewRingBuffer[int](5)
|
||||
|
||||
ring.Push(1)
|
||||
ring.Push(2)
|
||||
ring.Push(3)
|
||||
|
||||
v, ok := ring.Pop()
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, 1, v)
|
||||
|
||||
v, ok = ring.Pop()
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, 2, v)
|
||||
|
||||
v, ok = ring.Pop()
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, 3, v)
|
||||
}
|
||||
|
||||
func TestRingBuffer_Empty(t *testing.T) {
|
||||
ring := NewRingBuffer[int](5)
|
||||
|
||||
v, ok := ring.Pop()
|
||||
assert.False(t, ok)
|
||||
assert.Equal(t, 0, v)
|
||||
}
|
||||
|
||||
func TestRingBuffer_OverwriteOldest(t *testing.T) {
|
||||
ring := NewRingBuffer[int](4)
|
||||
|
||||
ring.Push(1)
|
||||
ring.Push(2)
|
||||
ring.Push(3)
|
||||
ring.Push(4)
|
||||
ring.Push(5)
|
||||
ring.Push(6)
|
||||
|
||||
var values []int
|
||||
for {
|
||||
v, ok := ring.Pop()
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
values = append(values, v)
|
||||
}
|
||||
|
||||
assert.Equal(t, []int{3, 4, 5, 6}, values)
|
||||
}
|
||||
|
||||
func TestRingBuffer_Len(t *testing.T) {
|
||||
ring := NewRingBuffer[int](5)
|
||||
|
||||
assert.Equal(t, 0, ring.Len())
|
||||
|
||||
ring.Push(1)
|
||||
assert.Equal(t, 1, ring.Len())
|
||||
|
||||
ring.Push(2)
|
||||
ring.Push(3)
|
||||
assert.Equal(t, 3, ring.Len())
|
||||
|
||||
ring.Pop()
|
||||
assert.Equal(t, 2, ring.Len())
|
||||
|
||||
ring.Push(4)
|
||||
ring.Push(5)
|
||||
ring.Push(6)
|
||||
ring.Push(7)
|
||||
assert.Equal(t, 5, ring.Len())
|
||||
|
||||
ring.Push(8)
|
||||
assert.Equal(t, 5, ring.Len())
|
||||
}
|
||||
|
||||
func TestRingBuffer_Concurrent(t *testing.T) {
|
||||
ring := NewRingBuffer[int](100)
|
||||
var wg sync.WaitGroup
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
wg.Add(1)
|
||||
go func(id int) {
|
||||
defer wg.Done()
|
||||
for j := 0; j < 100; j++ {
|
||||
ring.Push(id*100 + j)
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for i := 0; i < 500; i++ {
|
||||
ring.Pop()
|
||||
}
|
||||
}()
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package hello
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
pb "homelab.lan/music-agregator/gen/music_agregator/hello/v1"
|
||||
)
|
||||
|
||||
type HelloServer struct {
|
||||
pb.UnimplementedHelloServiceServer
|
||||
}
|
||||
|
||||
func NewHelloServer() *HelloServer {
|
||||
return &HelloServer{}
|
||||
}
|
||||
|
||||
func (server *HelloServer) Ping(ctx context.Context, req *pb.PingRequest) (*pb.PongResponse, error) {
|
||||
return &pb.PongResponse{}, nil
|
||||
}
|
||||
|
||||
func (server *HelloServer) Echo(ctx context.Context, req *pb.EchoRequest) (*pb.EchoResponse, error) {
|
||||
response := &pb.EchoResponse{
|
||||
Response: req.GetMsg(),
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (s *HelloServer) Register(server *grpc.Server) {
|
||||
pb.RegisterHelloServiceServer(server, s)
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
package indexer
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
type CacheEntry struct {
|
||||
Key string
|
||||
URL string
|
||||
Result SearchResult
|
||||
CreatedAt time.Time
|
||||
TTL time.Duration
|
||||
RefreshInterval time.Duration
|
||||
}
|
||||
|
||||
func (e *CacheEntry) IsExpired() bool {
|
||||
return time.Now().After(e.CreatedAt.Add(e.TTL))
|
||||
}
|
||||
|
||||
type IndexerCache struct {
|
||||
entries map[string]*CacheEntry
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
func NewIndexerCache() *IndexerCache {
|
||||
return &IndexerCache{
|
||||
entries: make(map[string]*CacheEntry),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *IndexerCache) Get(key string) (*CacheEntry, bool) {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
entry, ok := c.entries[key]
|
||||
if !ok {
|
||||
log.Trace().Str("key", key).Msg("cache miss")
|
||||
return nil, false
|
||||
}
|
||||
|
||||
if entry.IsExpired() {
|
||||
log.Trace().Str("key", key).Msg("cache expired")
|
||||
return nil, false
|
||||
}
|
||||
|
||||
log.Trace().Str("key", key).Int("items", len(entry.Result.Items)).Msg("cache hit")
|
||||
return entry, true
|
||||
}
|
||||
|
||||
func (c *IndexerCache) Add(entry CacheEntry) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
c.entries[entry.Key] = &entry
|
||||
log.Debug().Str("key", entry.Key).Int("items", len(entry.Result.Items)).Dur("ttl", entry.TTL).Dur("refresh", entry.RefreshInterval).Msg("cache entry added")
|
||||
}
|
||||
|
||||
func (c *IndexerCache) Update(key string, result SearchResult) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
if entry, ok := c.entries[key]; ok {
|
||||
entry.Result = result
|
||||
log.Debug().Str("key", key).Int("items", len(result.Items)).Msg("cache entry updated")
|
||||
} else {
|
||||
log.Warn().Str("key", key).Msg("cache update for missing entry")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *IndexerCache) Remove(key string) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
delete(c.entries, key)
|
||||
log.Debug().Str("key", key).Msg("cache entry removed")
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
package indexer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/riverqueue/river"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
type CacheRefreshArgs struct {
|
||||
Key string `json:"key"`
|
||||
URL string `json:"url"`
|
||||
TTLExpires time.Time `json:"ttl_expires"`
|
||||
RefreshInterval time.Duration `json:"refresh_interval"`
|
||||
}
|
||||
|
||||
func (CacheRefreshArgs) Kind() string { return "indexer_cache_refresh" }
|
||||
|
||||
type CacheRefreshWorker struct {
|
||||
river.WorkerDefaults[CacheRefreshArgs]
|
||||
Cache *IndexerCache
|
||||
Indexer Indexer
|
||||
RiverClient *river.Client[pgx.Tx]
|
||||
}
|
||||
|
||||
func (w *CacheRefreshWorker) Work(ctx context.Context, job *river.Job[CacheRefreshArgs]) error {
|
||||
args := job.Args
|
||||
|
||||
if w.Cache == nil || w.Indexer == nil {
|
||||
log.Trace().Str("key", args.Key).Msg("cache disabled, discarding refresh job")
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Trace().Str("key", args.Key).Int64("job_id", job.ID).Time("ttl_expires", args.TTLExpires).Msg("cache refresh worker started")
|
||||
|
||||
if time.Now().After(args.TTLExpires) {
|
||||
w.Cache.Remove(args.Key)
|
||||
log.Debug().Str("key", args.Key).Msg("cache entry TTL expired, removed")
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Trace().Str("key", args.Key).Str("url", args.URL).Msg("fetching fresh data from indexer")
|
||||
start := time.Now()
|
||||
result, err := w.Indexer.FetchURL(args.URL)
|
||||
if err != nil {
|
||||
retryAt := time.Now().Add(5 * time.Minute)
|
||||
log.Error().Err(err).Str("key", args.Key).Time("retry_at", retryAt).Msg("cache refresh failed, scheduling retry")
|
||||
w.RiverClient.Insert(ctx, args, &river.InsertOpts{
|
||||
ScheduledAt: retryAt,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
log.Trace().Str("key", args.Key).Int("items", len(result.Items)).Dur("duration", time.Since(start)).Msg("fresh data fetched")
|
||||
|
||||
w.Cache.Update(args.Key, result)
|
||||
|
||||
nextRefresh := time.Now().Add(args.RefreshInterval)
|
||||
_, err = w.RiverClient.Insert(ctx, args, &river.InsertOpts{
|
||||
ScheduledAt: nextRefresh,
|
||||
})
|
||||
if err != nil {
|
||||
log.Error().Err(err).Str("key", args.Key).Msg("failed to schedule next cache refresh")
|
||||
} else {
|
||||
log.Trace().Str("key", args.Key).Time("next_refresh", nextRefresh).Msg("next refresh scheduled")
|
||||
}
|
||||
|
||||
log.Debug().Str("key", args.Key).Int("items", len(result.Items)).Msg("cache refreshed")
|
||||
return nil
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
package indexer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/riverqueue/river"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
"homelab.lan/music-agregator/internal/config"
|
||||
)
|
||||
|
||||
type CachedIndexer struct {
|
||||
inner Indexer
|
||||
cache *IndexerCache
|
||||
riverClient *river.Client[pgx.Tx]
|
||||
cfg config.CacheConfig
|
||||
}
|
||||
|
||||
func NewCachedIndexer(inner Indexer, cache *IndexerCache, riverClient *river.Client[pgx.Tx], cfg config.CacheConfig) *CachedIndexer {
|
||||
return &CachedIndexer{
|
||||
inner: inner,
|
||||
cache: cache,
|
||||
riverClient: riverClient,
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CachedIndexer) Search(query string, limit int32, tracker string) (SearchResult, error) {
|
||||
key := query + "|" + tracker
|
||||
log.Trace().Str("key", key).Str("query", query).Str("tracker", tracker).Msg("cached indexer search")
|
||||
|
||||
if entry, ok := c.cache.Get(key); ok {
|
||||
log.Debug().Str("key", key).Int("items", len(entry.Result.Items)).Msg("returning cached result")
|
||||
return entry.Result, nil
|
||||
}
|
||||
|
||||
log.Trace().Str("key", key).Msg("cache miss, fetching from indexer")
|
||||
result, err := c.inner.Search(query, limit, tracker)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Str("key", key).Msg("cached indexer fetch failed")
|
||||
return SearchResult{}, err
|
||||
}
|
||||
|
||||
url := c.inner.BuildSearchURL(query, limit, tracker)
|
||||
log.Trace().Str("key", key).Str("url", url).Int("items", len(result.Items)).Msg("caching result")
|
||||
|
||||
c.cache.Add(CacheEntry{
|
||||
Key: key,
|
||||
URL: url,
|
||||
Result: result,
|
||||
CreatedAt: time.Now(),
|
||||
TTL: c.cfg.TTL,
|
||||
RefreshInterval: c.cfg.RefreshInterval,
|
||||
})
|
||||
|
||||
scheduleAt := time.Now().Add(c.cfg.RefreshInterval)
|
||||
_, err = c.riverClient.Insert(context.Background(), CacheRefreshArgs{
|
||||
Key: key,
|
||||
URL: url,
|
||||
TTLExpires: time.Now().Add(c.cfg.TTL),
|
||||
RefreshInterval: c.cfg.RefreshInterval,
|
||||
}, &river.InsertOpts{
|
||||
ScheduledAt: scheduleAt,
|
||||
})
|
||||
if err != nil {
|
||||
log.Error().Err(err).Str("key", key).Msg("failed to schedule cache refresh job")
|
||||
} else {
|
||||
log.Debug().Str("key", key).Time("scheduled_at", scheduleAt).Msg("cache refresh job scheduled")
|
||||
}
|
||||
|
||||
log.Debug().Str("key", key).Dur("ttl", c.cfg.TTL).Dur("refresh", c.cfg.RefreshInterval).Int("items", len(result.Items)).Msg("cached indexer search complete")
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (c *CachedIndexer) FetchURL(url string) (SearchResult, error) {
|
||||
log.Trace().Str("url", url).Msg("cached indexer fetch URL passthrough")
|
||||
return c.inner.FetchURL(url)
|
||||
}
|
||||
|
||||
func (c *CachedIndexer) BuildSearchURL(query string, limit int32, tracker string) string {
|
||||
return c.inner.BuildSearchURL(query, limit, tracker)
|
||||
}
|
||||
|
||||
func (c *CachedIndexer) Capabilities(indexerName string) (IndexerCapabilities, error) {
|
||||
log.Trace().Str("indexer", indexerName).Msg("cached indexer capabilities passthrough")
|
||||
return c.inner.Capabilities(indexerName)
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
package indexer
|
||||
|
||||
type Filter interface {
|
||||
IsKnownCategory(categories []string) bool
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
package indexer
|
||||
|
||||
type Indexer interface {
|
||||
Search(query string, limit int32, indexer string) (SearchResult, error)
|
||||
FetchURL(url string) (SearchResult, error)
|
||||
BuildSearchURL(query string, limit int32, tracker string) string
|
||||
Capabilities(indexerName string) (IndexerCapabilities, error)
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
package indexer
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"strings"
|
||||
|
||||
pb "homelab.lan/music-agregator/gen/music_agregator/indexer/v1"
|
||||
)
|
||||
|
||||
type IndexerCapabilities struct {
|
||||
XMLName xml.Name `xml:"caps"`
|
||||
Server Server `xml:"server"`
|
||||
Limits Limits `xml:"limits"`
|
||||
Searching Searching `xml:"searching"`
|
||||
Categories []Category `xml:"categories>category"`
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
Title string `xml:"title,attr"`
|
||||
}
|
||||
|
||||
type Limits struct {
|
||||
Default int `xml:"default,attr"`
|
||||
Max int `xml:"max,attr"`
|
||||
}
|
||||
|
||||
type Searching struct {
|
||||
Search SearchCapability `xml:"search"`
|
||||
TvSearch SearchCapability `xml:"tv-search"`
|
||||
MovieSearch SearchCapability `xml:"movie-search"`
|
||||
MusicSearch SearchCapability `xml:"music-search"`
|
||||
AudioSearch SearchCapability `xml:"audio-search"`
|
||||
BookSearch SearchCapability `xml:"book-search"`
|
||||
}
|
||||
|
||||
type SearchCapability struct {
|
||||
Available string `xml:"available,attr"`
|
||||
SupportedParams string `xml:"supportedParams,attr"`
|
||||
SearchEngine string `xml:"searchEngine,attr"`
|
||||
}
|
||||
|
||||
type Category struct {
|
||||
ID int `xml:"id,attr"`
|
||||
Name string `xml:"name,attr"`
|
||||
Subcats []Subcat `xml:"subcat"`
|
||||
}
|
||||
|
||||
type Subcat struct {
|
||||
ID int `xml:"id,attr"`
|
||||
Name string `xml:"name,attr"`
|
||||
}
|
||||
|
||||
func (c *IndexerCapabilities) ToProto() *pb.CapabilitiesResponse {
|
||||
return &pb.CapabilitiesResponse{
|
||||
Server: &pb.Server{
|
||||
Title: c.Server.Title,
|
||||
},
|
||||
Limits: &pb.Limits{
|
||||
Default: int32(c.Limits.Default),
|
||||
Max: int32(c.Limits.Max),
|
||||
},
|
||||
Searching: &pb.Searching{
|
||||
Search: c.Searching.Search.toProto(),
|
||||
TvSearch: c.Searching.TvSearch.toProto(),
|
||||
MovieSearch: c.Searching.MovieSearch.toProto(),
|
||||
MusicSearch: c.Searching.MusicSearch.toProto(),
|
||||
AudioSearch: c.Searching.AudioSearch.toProto(),
|
||||
BookSearch: c.Searching.BookSearch.toProto(),
|
||||
},
|
||||
Categories: c.categoriesToProto(),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SearchCapability) toProto() *pb.SearchCapability {
|
||||
var params []string
|
||||
if s.SupportedParams != "" {
|
||||
params = strings.Split(s.SupportedParams, ",")
|
||||
}
|
||||
return &pb.SearchCapability{
|
||||
Available: s.Available == "yes",
|
||||
SupportedParams: params,
|
||||
SearchEngine: s.SearchEngine,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *IndexerCapabilities) categoriesToProto() []*pb.Category {
|
||||
categories := make([]*pb.Category, len(c.Categories))
|
||||
for i, cat := range c.Categories {
|
||||
subcats := make([]*pb.Subcat, len(cat.Subcats))
|
||||
for j, sub := range cat.Subcats {
|
||||
subcats[j] = &pb.Subcat{
|
||||
Id: int32(sub.ID),
|
||||
Name: sub.Name,
|
||||
}
|
||||
}
|
||||
categories[i] = &pb.Category{
|
||||
Id: int32(cat.ID),
|
||||
Name: cat.Name,
|
||||
Subcats: subcats,
|
||||
}
|
||||
}
|
||||
return categories
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
package indexer
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"homelab.lan/music-agregator/internal/config"
|
||||
)
|
||||
|
||||
type JacketIndexer struct {
|
||||
cfg config.Config
|
||||
client *http.Client
|
||||
}
|
||||
|
||||
func NewIndexer(cfg config.Config) Indexer {
|
||||
return &JacketIndexer{
|
||||
cfg: cfg,
|
||||
client: &http.Client{
|
||||
Timeout: 60 * time.Second,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (indexer *JacketIndexer) BuildSearchURL(query string, limit int32, tracker string) string {
|
||||
searchTracker := "all"
|
||||
if len(tracker) != 0 {
|
||||
searchTracker = tracker
|
||||
}
|
||||
|
||||
uri := fmt.Sprintf("%v/api/v2.0/indexers/%v/results/torznab?apikey=%v&cat=3010,3040&q=%v&t=search",
|
||||
indexer.cfg.Indexer.Url, searchTracker, indexer.cfg.Indexer.ApiKey, url.QueryEscape(query))
|
||||
if limit > 0 {
|
||||
uri += fmt.Sprintf("&limit=%d", limit)
|
||||
}
|
||||
|
||||
return uri
|
||||
}
|
||||
|
||||
func (indexer *JacketIndexer) Search(query string, limit int32, tracker string) (SearchResult, error) {
|
||||
uri := indexer.BuildSearchURL(query, limit, tracker)
|
||||
return indexer.FetchURL(uri)
|
||||
}
|
||||
|
||||
type JackettError struct {
|
||||
Code string `xml:"code,attr"`
|
||||
Description string `xml:"description,attr"`
|
||||
}
|
||||
|
||||
func (e *JackettError) Error() string {
|
||||
return fmt.Sprintf("jackett error %s: %s", e.Code, e.Description)
|
||||
}
|
||||
|
||||
func (indexer *JacketIndexer) FetchURL(uri string) (SearchResult, error) {
|
||||
log.Trace().Str("uri", uri).Msg("jackett request")
|
||||
|
||||
req, err := http.NewRequest("GET", uri, nil)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("error creating request")
|
||||
return SearchResult{}, err
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
resp, err := indexer.client.Do(req)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("error making search request")
|
||||
return SearchResult{}, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("error reading search response body")
|
||||
return SearchResult{}, err
|
||||
}
|
||||
|
||||
log.Trace().
|
||||
Int("status", resp.StatusCode).
|
||||
Int("body_bytes", len(body)).
|
||||
Dur("duration", time.Since(start)).
|
||||
Msg("jackett response")
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
var jackettErr JackettError
|
||||
if xmlErr := xml.Unmarshal(body, &jackettErr); xmlErr == nil && jackettErr.Code != "" {
|
||||
log.Error().Str("code", jackettErr.Code).Str("description", jackettErr.Description).Msg("jackett returned error")
|
||||
return SearchResult{}, &jackettErr
|
||||
}
|
||||
return SearchResult{}, fmt.Errorf("jackett returned HTTP %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
var searchResult SearchResult
|
||||
if err := xml.Unmarshal(body, &searchResult); err != nil {
|
||||
log.Error().Err(err).Msg("error parsing search XML")
|
||||
return SearchResult{}, err
|
||||
}
|
||||
|
||||
log.Trace().Int("items", len(searchResult.Items)).Msg("jackett XML parsed")
|
||||
|
||||
return searchResult, nil
|
||||
}
|
||||
|
||||
func (indexer *JacketIndexer) Capabilities(indexerName string) (IndexerCapabilities, error) {
|
||||
url := indexer.cfg.Indexer.Url
|
||||
uri := fmt.Sprintf("%v/api/v2.0/indexers/%v/results/torznab/api?apikey=%v&t=caps", url, indexerName, indexer.cfg.Indexer.ApiKey)
|
||||
|
||||
req, err := http.NewRequest("GET", uri, nil)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Error creating request")
|
||||
return IndexerCapabilities{}, err
|
||||
}
|
||||
|
||||
resp, err := indexer.client.Do(req)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Error making capabilities request")
|
||||
return IndexerCapabilities{}, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Error reading response body")
|
||||
return IndexerCapabilities{}, err
|
||||
}
|
||||
|
||||
var capabilities IndexerCapabilities
|
||||
if err := xml.Unmarshal(body, &capabilities); err != nil {
|
||||
log.Error().Err(err).Msg("Error parsing capabilities XML")
|
||||
return IndexerCapabilities{}, err
|
||||
}
|
||||
|
||||
log.Debug().Str("server", capabilities.Server.Title).Msg("Parsed capabilities")
|
||||
|
||||
return capabilities, nil
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
package indexer
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"strconv"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
pb "homelab.lan/music-agregator/gen/music_agregator/indexer/v1"
|
||||
"homelab.lan/music-agregator/internal/release"
|
||||
"homelab.lan/music-agregator/internal/tracker"
|
||||
)
|
||||
|
||||
type SearchResult struct {
|
||||
XMLName xml.Name `xml:"rss"`
|
||||
Items []Item `xml:"channel>item"`
|
||||
}
|
||||
|
||||
type JackettIndexer struct {
|
||||
ID string `xml:"id,attr"`
|
||||
}
|
||||
|
||||
type Item struct {
|
||||
Title string `xml:"title"`
|
||||
Link string `xml:"link"`
|
||||
Guid string `xml:"guid"`
|
||||
PubDate string `xml:"pubDate"`
|
||||
Size int64 `xml:"size"`
|
||||
Description string `xml:"description"`
|
||||
Categories []string `xml:"category"`
|
||||
Enclosure Enclosure `xml:"enclosure"`
|
||||
TorznabAttrs []TorznabAttr `xml:"attr"`
|
||||
JackettIndexer JackettIndexer `xml:"jackettindexer"`
|
||||
}
|
||||
|
||||
type Enclosure struct {
|
||||
URL string `xml:"url,attr"`
|
||||
Length int64 `xml:"length,attr"`
|
||||
Type string `xml:"type,attr"`
|
||||
}
|
||||
|
||||
type TorznabAttr struct {
|
||||
Name string `xml:"name,attr"`
|
||||
Value string `xml:"value,attr"`
|
||||
}
|
||||
|
||||
type SearchItemResult struct {
|
||||
Title string
|
||||
DownloadLink string
|
||||
TorrentPageUrl string
|
||||
PubDate string
|
||||
Size int64
|
||||
Description string
|
||||
Categories []string
|
||||
Tracker string
|
||||
Seeders int
|
||||
Peers int
|
||||
Attrs map[string]string
|
||||
Release *release.Release
|
||||
}
|
||||
|
||||
func (si *SearchItemResult) ToProto() *pb.SearchItem {
|
||||
var pbAttrs []*pb.TorznabAttr
|
||||
for k, v := range si.Attrs {
|
||||
pbAttrs = append(pbAttrs, &pb.TorznabAttr{Name: k, Value: v})
|
||||
}
|
||||
|
||||
return &pb.SearchItem{
|
||||
Title: si.Title,
|
||||
DownloadLink: si.DownloadLink,
|
||||
TorrentPageUrl: si.TorrentPageUrl,
|
||||
PubDate: si.PubDate,
|
||||
Size: si.Size,
|
||||
Description: si.Description,
|
||||
Categories: si.Categories,
|
||||
TorznabAttrs: pbAttrs,
|
||||
Release: si.Release.ToProto(),
|
||||
}
|
||||
}
|
||||
|
||||
type SearchResponse struct {
|
||||
Items []*SearchItemResult
|
||||
}
|
||||
|
||||
func (sr *SearchResponse) ToProto() *pb.SearchResponse {
|
||||
pbItems := make([]*pb.SearchItem, len(sr.Items))
|
||||
for i, item := range sr.Items {
|
||||
pbItems[i] = item.ToProto()
|
||||
}
|
||||
return &pb.SearchResponse{Result: pbItems}
|
||||
}
|
||||
|
||||
var genericParser = tracker.NewGenericParser()
|
||||
|
||||
func (sr *SearchResult) ToSearchResponse() *SearchResponse {
|
||||
var items []*SearchItemResult
|
||||
|
||||
for _, item := range sr.Items {
|
||||
rel := genericParser.Parse(item.Title)
|
||||
|
||||
log.Trace().
|
||||
Str("tracker", item.JackettIndexer.ID).
|
||||
Str("title", item.Title).
|
||||
Str("artist", rel.Artist).
|
||||
Str("album", rel.Album).
|
||||
Int("year", rel.Year).
|
||||
Bool("parsed", rel.ParsedSuccessfully).
|
||||
Msg("parsed item")
|
||||
|
||||
attrs := make(map[string]string, len(item.TorznabAttrs))
|
||||
for _, attr := range item.TorznabAttrs {
|
||||
attrs[attr.Name] = attr.Value
|
||||
}
|
||||
|
||||
seeders, _ := strconv.Atoi(attrs["seeders"])
|
||||
peers, _ := strconv.Atoi(attrs["peers"])
|
||||
|
||||
items = append(items, &SearchItemResult{
|
||||
Title: item.Title,
|
||||
DownloadLink: item.Link,
|
||||
TorrentPageUrl: item.Guid,
|
||||
PubDate: item.PubDate,
|
||||
Size: item.Size,
|
||||
Description: item.Description,
|
||||
Categories: item.Categories,
|
||||
Tracker: item.JackettIndexer.ID,
|
||||
Seeders: seeders,
|
||||
Peers: peers,
|
||||
Attrs: attrs,
|
||||
Release: rel,
|
||||
})
|
||||
}
|
||||
|
||||
log.Trace().
|
||||
Int("total", len(sr.Items)).
|
||||
Int("items", len(items)).
|
||||
Msg("conversion complete")
|
||||
|
||||
return &SearchResponse{Items: items}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
package indexer
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/riverqueue/river"
|
||||
"github.com/rs/zerolog/log"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
pb "homelab.lan/music-agregator/gen/music_agregator/indexer/v1"
|
||||
"homelab.lan/music-agregator/internal/config"
|
||||
)
|
||||
|
||||
type IndexerServer struct {
|
||||
service *IndexerService
|
||||
pb.UnimplementedIndexerServiceServer
|
||||
}
|
||||
|
||||
func NewIndexerServer(cfg config.Config, riverClient *river.Client[pgx.Tx], cacheWorker *CacheRefreshWorker) (*IndexerServer, error) {
|
||||
service, err := NewIndexerService(cfg, riverClient, cacheWorker)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("failed to initialize IndexerService")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &IndexerServer{service: service}, nil
|
||||
}
|
||||
|
||||
func (server *IndexerServer) Search(ctx context.Context, req *pb.SearchRequest) (*pb.SearchResponse, error) {
|
||||
log.Debug().
|
||||
Str("query", req.GetQuery()).
|
||||
Int32("limit", req.GetLimit()).
|
||||
Str("tracker", req.GetTracker()).
|
||||
Msg("search started")
|
||||
|
||||
log.Trace().Str("query", req.GetQuery()).Msg("fetching results from indexer")
|
||||
|
||||
resp, err := server.service.Search(req.GetQuery(), req.GetLimit(), req.GetTracker())
|
||||
if err != nil {
|
||||
log.Error().Err(err).Str("query", req.GetQuery()).Msg("search failed")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Debug().
|
||||
Str("query", req.GetQuery()).
|
||||
Int("results", len(resp.Items)).
|
||||
Msg("search completed")
|
||||
|
||||
return resp.ToProto(), nil
|
||||
}
|
||||
|
||||
func (server *IndexerServer) Capabilities(ctx context.Context, req *pb.CapabilitiesRequest) (*pb.CapabilitiesResponse, error) {
|
||||
log.Debug().Str("indexer", req.GetIndexer()).Msg("capabilities requested")
|
||||
|
||||
resp, err := server.service.Capabilities(req)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Str("indexer", req.GetIndexer()).Msg("capabilities failed")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (s *IndexerServer) Register(server *grpc.Server) {
|
||||
pb.RegisterIndexerServiceServer(server, s)
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
package indexer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/riverqueue/river"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
pb "homelab.lan/music-agregator/gen/music_agregator/indexer/v1"
|
||||
"homelab.lan/music-agregator/internal/config"
|
||||
)
|
||||
|
||||
type Searcher interface {
|
||||
Search(query string, limit int32, indexer string) (*SearchResponse, error)
|
||||
}
|
||||
|
||||
type IndexerService struct {
|
||||
indexer Indexer
|
||||
}
|
||||
|
||||
func NewIndexerService(cfg config.Config, riverClient *river.Client[pgx.Tx], cacheWorker *CacheRefreshWorker) (*IndexerService, error) {
|
||||
var idx Indexer
|
||||
|
||||
switch cfg.Indexer.Type {
|
||||
case config.IndexerTypeJackett:
|
||||
idx = NewIndexer(cfg)
|
||||
default:
|
||||
return nil, fmt.Errorf("unable to create the indexer for type: %v", cfg.Indexer.Type)
|
||||
}
|
||||
|
||||
if cfg.Indexer.Cache.Enabled && riverClient != nil {
|
||||
cache := NewIndexerCache()
|
||||
idx = NewCachedIndexer(idx, cache, riverClient, cfg.Indexer.Cache)
|
||||
|
||||
if cacheWorker != nil {
|
||||
cacheWorker.Cache = cache
|
||||
cacheWorker.Indexer = idx
|
||||
}
|
||||
|
||||
log.Info().Dur("ttl", cfg.Indexer.Cache.TTL).Dur("refresh", cfg.Indexer.Cache.RefreshInterval).Msg("indexer cache enabled")
|
||||
}
|
||||
|
||||
return &IndexerService{indexer: idx}, nil
|
||||
}
|
||||
|
||||
func (service *IndexerService) Search(query string, limit int32, indexer string) (*SearchResponse, error) {
|
||||
searchResult, err := service.indexer.Search(query, limit, indexer)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed to search in indexer")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Trace().Int("raw_items", len(searchResult.Items)).Msg("indexer returned results")
|
||||
|
||||
return searchResult.ToSearchResponse(), nil
|
||||
}
|
||||
|
||||
func (service *IndexerService) Capabilities(req *pb.CapabilitiesRequest) (*pb.CapabilitiesResponse, error) {
|
||||
capabilities, err := service.indexer.Capabilities(req.GetIndexer())
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to get capabilities from indexer")
|
||||
return nil, err
|
||||
}
|
||||
return capabilities.ToProto(), nil
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
package matcher
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const MatchThreshold = 0.7
|
||||
|
||||
var (
|
||||
punctuationRe = regexp.MustCompile(`[^\w\s]`)
|
||||
yearRe = regexp.MustCompile(`\b(19|20)\d{2}\b`)
|
||||
whitespaceRe = regexp.MustCompile(`\s+`)
|
||||
noiseWords = map[string]struct{}{
|
||||
"remastered": {}, "remaster": {}, "deluxe": {}, "edition": {},
|
||||
"expanded": {}, "anniversary": {}, "special": {}, "limited": {},
|
||||
"collectors": {}, "collector": {}, "bonus": {}, "tracks": {},
|
||||
"standard": {}, "super": {},
|
||||
"flac": {}, "mp3": {}, "aac": {}, "ogg": {}, "wav": {},
|
||||
"24bit": {}, "16bit": {}, "48khz": {}, "44khz": {}, "96khz": {}, "192khz": {},
|
||||
"cd": {}, "vinyl": {}, "lp": {}, "web": {}, "sacd": {},
|
||||
"box": {}, "set": {}, "japan": {}, "shm": {},
|
||||
"singles": {}, "single": {},
|
||||
"pmedia": {},
|
||||
}
|
||||
)
|
||||
|
||||
func normalize(s string) string {
|
||||
s = strings.ToLower(s)
|
||||
s = punctuationRe.ReplaceAllString(s, " ")
|
||||
s = yearRe.ReplaceAllString(s, " ")
|
||||
s = whitespaceRe.ReplaceAllString(s, " ")
|
||||
return strings.TrimSpace(s)
|
||||
}
|
||||
|
||||
func tokenize(s string) []string {
|
||||
var tokens []string
|
||||
for _, word := range strings.Fields(s) {
|
||||
if _, noise := noiseWords[word]; !noise && len(word) > 1 {
|
||||
tokens = append(tokens, word)
|
||||
}
|
||||
}
|
||||
return tokens
|
||||
}
|
||||
|
||||
func AlbumMatchScore(albumTitle, torrentTitle, artistName string) float64 {
|
||||
normAlbum := normalize(albumTitle)
|
||||
normTorrent := normalize(torrentTitle)
|
||||
|
||||
if artistName != "" {
|
||||
normArtist := normalize(artistName)
|
||||
normTorrent = strings.Replace(normTorrent, normArtist, "", 1)
|
||||
normTorrent = strings.TrimSpace(whitespaceRe.ReplaceAllString(normTorrent, " "))
|
||||
}
|
||||
|
||||
if normAlbum == "" {
|
||||
return 0
|
||||
}
|
||||
|
||||
if strings.Contains(normTorrent, normAlbum) {
|
||||
return 1.0
|
||||
}
|
||||
|
||||
albumTokens := tokenize(normAlbum)
|
||||
if len(albumTokens) == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
torrentTokens := make(map[string]struct{}, len(albumTokens))
|
||||
for _, t := range tokenize(normTorrent) {
|
||||
torrentTokens[t] = struct{}{}
|
||||
}
|
||||
|
||||
var matches int
|
||||
for _, token := range albumTokens {
|
||||
if _, found := torrentTokens[token]; found {
|
||||
matches++
|
||||
continue
|
||||
}
|
||||
for tt := range torrentTokens {
|
||||
if levenshteinClose(token, tt) {
|
||||
matches++
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return float64(matches) / float64(len(albumTokens))
|
||||
}
|
||||
|
||||
func levenshteinClose(a, b string) bool {
|
||||
if len(a) < 3 || len(b) < 3 {
|
||||
return false
|
||||
}
|
||||
maxDist := 1
|
||||
if len(a) > 6 {
|
||||
maxDist = 2
|
||||
}
|
||||
return levenshtein(a, b) <= maxDist
|
||||
}
|
||||
|
||||
func levenshtein(a, b string) int {
|
||||
la, lb := len(a), len(b)
|
||||
if la == 0 {
|
||||
return lb
|
||||
}
|
||||
if lb == 0 {
|
||||
return la
|
||||
}
|
||||
|
||||
prev := make([]int, lb+1)
|
||||
curr := make([]int, lb+1)
|
||||
|
||||
for j := 0; j <= lb; j++ {
|
||||
prev[j] = j
|
||||
}
|
||||
|
||||
for i := 1; i <= la; i++ {
|
||||
curr[0] = i
|
||||
for j := 1; j <= lb; j++ {
|
||||
cost := 1
|
||||
if a[i-1] == b[j-1] {
|
||||
cost = 0
|
||||
}
|
||||
curr[j] = min(curr[j-1]+1, min(prev[j]+1, prev[j-1]+cost))
|
||||
}
|
||||
prev, curr = curr, prev
|
||||
}
|
||||
|
||||
return prev[lb]
|
||||
}
|
||||
|
||||
func IsAlbumMatch(albumTitle, torrentTitle, artistName string) bool {
|
||||
return AlbumMatchScore(albumTitle, torrentTitle, artistName) >= MatchThreshold
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
package matcher
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestAlbumMatchScore(t *testing.T) {
|
||||
tests := []struct {
|
||||
album string
|
||||
torrent string
|
||||
artist string
|
||||
wantMin float64
|
||||
wantMax float64
|
||||
match bool
|
||||
}{
|
||||
{
|
||||
album: "Ain't My Bitch",
|
||||
torrent: "Metallica - Ain't My Bitch (2025) [FLAC]",
|
||||
artist: "Metallica",
|
||||
wantMin: 0.9, wantMax: 1.0, match: true,
|
||||
},
|
||||
{
|
||||
album: "Ain't My Bitch",
|
||||
torrent: "Metallica - Aint My Bitch Remastered 2025 FLAC",
|
||||
artist: "Metallica",
|
||||
wantMin: 0.9, wantMax: 1.0, match: true,
|
||||
},
|
||||
{
|
||||
album: "72 Seasons",
|
||||
torrent: "Metallica - 72 Seasons (2023) [24Bit-48kHz] FLAC [PMEDIA] ⭐️",
|
||||
artist: "Metallica",
|
||||
wantMin: 0.9, wantMax: 1.0, match: true,
|
||||
},
|
||||
{
|
||||
album: "Kill 'Em All",
|
||||
torrent: "Metallica - Kill Em All [1983] FLAC",
|
||||
artist: "Metallica",
|
||||
wantMin: 0.9, wantMax: 1.0, match: true,
|
||||
},
|
||||
{
|
||||
album: "...And Justice for All",
|
||||
torrent: "Metallica - And Justice For All (Remastered) FLAC",
|
||||
artist: "Metallica",
|
||||
wantMin: 0.7, wantMax: 1.0, match: true,
|
||||
},
|
||||
{
|
||||
album: "Master of Puppets",
|
||||
torrent: "Metallica - Master of Puppets (Remastered Deluxe Box Set) FLAC",
|
||||
artist: "Metallica",
|
||||
wantMin: 0.9, wantMax: 1.0, match: true,
|
||||
},
|
||||
{
|
||||
album: "Ain't My Bitch",
|
||||
torrent: "Metallica - Load 1996 (Japan SHM-CD)",
|
||||
artist: "Metallica",
|
||||
wantMin: 0.0, wantMax: 0.3, match: false,
|
||||
},
|
||||
{
|
||||
album: "Ain't My Bitch",
|
||||
torrent: "Metallica - The Memory Remains (Singles, 2026 remastered)",
|
||||
artist: "Metallica",
|
||||
wantMin: 0.0, wantMax: 0.3, match: false,
|
||||
},
|
||||
{
|
||||
album: "Ain't My Bitch",
|
||||
torrent: "Metallica - 72 Seasons (2023) [24Bit-48kHz] FLAC",
|
||||
artist: "Metallica",
|
||||
wantMin: 0.0, wantMax: 0.3, match: false,
|
||||
},
|
||||
{
|
||||
album: "Hardwired...to Self-Destruct",
|
||||
torrent: "Metallica - Hardwired To Self-Destruct (Deluxe 2016) [FLAC]",
|
||||
artist: "Metallica",
|
||||
wantMin: 0.7, wantMax: 1.0, match: true,
|
||||
},
|
||||
{
|
||||
album: "S&M",
|
||||
torrent: "Metallica - S&M (1999) [FLAC]",
|
||||
artist: "Metallica",
|
||||
wantMin: 0.9, wantMax: 1.0, match: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.album+" vs "+tt.torrent, func(t *testing.T) {
|
||||
score := AlbumMatchScore(tt.album, tt.torrent, tt.artist)
|
||||
assert.GreaterOrEqual(t, score, tt.wantMin, "score too low: %.2f", score)
|
||||
assert.LessOrEqual(t, score, tt.wantMax, "score too high: %.2f", score)
|
||||
assert.Equal(t, tt.match, IsAlbumMatch(tt.album, tt.torrent, tt.artist),
|
||||
"match mismatch for score %.2f", score)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package metadata
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
|
||||
pb "homelab.lan/music-agregator/gen/metadata/v1"
|
||||
)
|
||||
|
||||
func NewMetadataClient(endpoint string) (pb.MetadataServiceClient, *grpc.ClientConn, error) {
|
||||
log.Trace().Str("endpoint", endpoint).Msg("connecting to metadata service")
|
||||
|
||||
conn, err := grpc.NewClient(endpoint, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("connecting to metadata service: %w", err)
|
||||
}
|
||||
|
||||
log.Info().Str("endpoint", endpoint).Msg("metadata service connected")
|
||||
|
||||
return pb.NewMetadataServiceClient(conn), conn, nil
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
package metadata
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
pb "homelab.lan/music-agregator/gen/metadata/v1"
|
||||
"homelab.lan/music-agregator/internal/config"
|
||||
)
|
||||
|
||||
type MetadataServer struct {
|
||||
client pb.MetadataServiceClient
|
||||
conn *grpc.ClientConn
|
||||
pb.UnimplementedMetadataServiceServer
|
||||
}
|
||||
|
||||
func NewMetadataServer(cfg config.Config) (*MetadataServer, error) {
|
||||
client, conn, err := NewMetadataClient(cfg.Metadata.Endpoint)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("failed to initialize MetadataServer")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &MetadataServer{client: client, conn: conn}, nil
|
||||
}
|
||||
|
||||
func (s *MetadataServer) Close() {
|
||||
if s.conn != nil {
|
||||
s.conn.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *MetadataServer) Register(server *grpc.Server) {
|
||||
pb.RegisterMetadataServiceServer(server, s)
|
||||
}
|
||||
|
||||
func (s *MetadataServer) GetArtist(ctx context.Context, req *pb.GetArtistRequest) (*pb.GetArtistResponse, error) {
|
||||
log.Debug().Msg("metadata GetArtist")
|
||||
return s.client.GetArtist(ctx, req)
|
||||
}
|
||||
|
||||
func (s *MetadataServer) SearchArtists(ctx context.Context, req *pb.SearchArtistsRequest) (*pb.SearchArtistsResponse, error) {
|
||||
log.Debug().Str("query", req.GetQuery()).Int32("limit", req.GetLimit()).Msg("metadata SearchArtists")
|
||||
return s.client.SearchArtists(ctx, req)
|
||||
}
|
||||
|
||||
func (s *MetadataServer) GetAlbum(ctx context.Context, req *pb.GetAlbumRequest) (*pb.GetAlbumResponse, error) {
|
||||
log.Debug().Msg("metadata GetAlbum")
|
||||
return s.client.GetAlbum(ctx, req)
|
||||
}
|
||||
|
||||
func (s *MetadataServer) GetArtistAlbums(ctx context.Context, req *pb.GetArtistAlbumsRequest) (*pb.GetArtistAlbumsResponse, error) {
|
||||
log.Debug().Str("artist_id", req.GetArtistId()).Msg("metadata GetArtistAlbums")
|
||||
return s.client.GetArtistAlbums(ctx, req)
|
||||
}
|
||||
|
||||
func (s *MetadataServer) GetTrack(ctx context.Context, req *pb.GetTrackRequest) (*pb.GetTrackResponse, error) {
|
||||
log.Debug().Msg("metadata GetTrack")
|
||||
return s.client.GetTrack(ctx, req)
|
||||
}
|
||||
|
||||
func (s *MetadataServer) GetAlbumTracks(ctx context.Context, req *pb.GetAlbumTracksRequest) (*pb.GetAlbumTracksResponse, error) {
|
||||
log.Debug().Str("album_id", req.GetAlbumId()).Msg("metadata GetAlbumTracks")
|
||||
return s.client.GetAlbumTracks(ctx, req)
|
||||
}
|
||||
|
||||
func (s *MetadataServer) SearchAlbums(ctx context.Context, req *pb.SearchAlbumsRequest) (*pb.SearchAlbumsResponse, error) {
|
||||
log.Debug().Str("query", req.GetQuery()).Str("artist", req.GetArtist()).Msg("metadata SearchAlbums")
|
||||
return s.client.SearchAlbums(ctx, req)
|
||||
}
|
||||
|
||||
func (s *MetadataServer) SyncArtist(ctx context.Context, req *pb.SyncArtistRequest) (*pb.SyncArtistResponse, error) {
|
||||
log.Debug().Msg("metadata SyncArtist")
|
||||
return s.client.SyncArtist(ctx, req)
|
||||
}
|
||||
@@ -1,235 +0,0 @@
|
||||
package metadata
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
metadataPb "homelab.lan/music-agregator/gen/metadata/v1"
|
||||
"homelab.lan/music-agregator/internal/database"
|
||||
)
|
||||
|
||||
type MetadataService struct {
|
||||
client metadataPb.MetadataServiceClient
|
||||
artists *database.ArtistRepository
|
||||
albums *database.AlbumRepository
|
||||
tracks *database.TrackRepository
|
||||
}
|
||||
|
||||
func NewMetadataService(client metadataPb.MetadataServiceClient, db *database.DB) *MetadataService {
|
||||
return &MetadataService{
|
||||
client: client,
|
||||
artists: database.NewArtistRepository(db.Pool),
|
||||
albums: database.NewAlbumRepository(db.Pool),
|
||||
tracks: database.NewTrackRepository(db.Pool),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *MetadataService) GetAlbum(ctx context.Context, albumID string) (*metadataPb.Album, error) {
|
||||
resp, err := s.client.GetAlbum(ctx, &metadataPb.GetAlbumRequest{
|
||||
Identifier: &metadataPb.GetAlbumRequest_Id{Id: albumID},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fetching album: %w", err)
|
||||
}
|
||||
|
||||
album := resp.GetAlbum()
|
||||
|
||||
if _, err := s.albums.GetByExternalID(ctx, album.GetId()); err != nil {
|
||||
s.PersistArtist(ctx, album, database.Monitored)
|
||||
s.PersistAlbum(ctx, album, database.Monitored)
|
||||
}
|
||||
|
||||
return album, nil
|
||||
}
|
||||
|
||||
func (s *MetadataService) GetArtistAlbums(ctx context.Context, artistExternalID string) ([]*metadataPb.Album, error) {
|
||||
resp, err := s.client.GetArtistAlbums(ctx, &metadataPb.GetArtistAlbumsRequest{
|
||||
ArtistId: artistExternalID,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fetching artist albums: %w", err)
|
||||
}
|
||||
return resp.GetAlbums(), nil
|
||||
}
|
||||
|
||||
func (s *MetadataService) SearchArtists(ctx context.Context, query string, limit, offset int32) (*metadataPb.SearchArtistsResponse, error) {
|
||||
resp, err := s.client.SearchArtists(ctx, &metadataPb.SearchArtistsRequest{
|
||||
Query: query,
|
||||
Limit: limit,
|
||||
Offset: offset,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("searching artists: %w", err)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (s *MetadataService) GetArtistAlbumsWithPagination(ctx context.Context, artistID string, limit, offset int32) (*metadataPb.GetArtistAlbumsResponse, error) {
|
||||
resp, err := s.client.GetArtistAlbums(ctx, &metadataPb.GetArtistAlbumsRequest{
|
||||
ArtistId: artistID,
|
||||
Limit: limit,
|
||||
Offset: offset,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fetching artist albums: %w", err)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (s *MetadataService) GetAlbumTracks(ctx context.Context, albumExternalID string) ([]*metadataPb.Track, error) {
|
||||
resp, err := s.client.GetAlbumTracks(ctx, &metadataPb.GetAlbumTracksRequest{
|
||||
AlbumId: albumExternalID,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fetching album tracks: %w", err)
|
||||
}
|
||||
return resp.GetTracks(), nil
|
||||
}
|
||||
|
||||
func (s *MetadataService) GetArtistByExternalID(ctx context.Context, externalID string) (*database.Artist, error) {
|
||||
return s.artists.GetByExternalID(ctx, externalID)
|
||||
}
|
||||
|
||||
func (s *MetadataService) GetAlbumByID(ctx context.Context, id string) (*database.Album, error) {
|
||||
return s.albums.GetByID(ctx, id)
|
||||
}
|
||||
|
||||
func (s *MetadataService) GetAlbumByExternalID(ctx context.Context, externalID string) (*database.Album, error) {
|
||||
return s.albums.GetByExternalID(ctx, externalID)
|
||||
}
|
||||
|
||||
func (s *MetadataService) GetAlbumsByArtistID(ctx context.Context, artistID string) ([]*database.Album, error) {
|
||||
return s.albums.GetByArtistID(ctx, artistID)
|
||||
}
|
||||
|
||||
func (s *MetadataService) GetTracksByAlbumID(ctx context.Context, albumID string) ([]*database.Track, error) {
|
||||
return s.tracks.GetByAlbumID(ctx, albumID)
|
||||
}
|
||||
|
||||
func (s *MetadataService) SetAlbumMonitorState(ctx context.Context, id string, state database.MonitorState) error {
|
||||
return s.albums.SetMonitorState(ctx, id, state)
|
||||
}
|
||||
|
||||
func (s *MetadataService) PersistArtist(ctx context.Context, album *metadataPb.Album, state database.MonitorState) {
|
||||
if len(album.GetArtists()) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
artist := album.GetArtists()[0].GetArtist()
|
||||
var genres []string
|
||||
for _, g := range artist.GetGenres() {
|
||||
genres = append(genres, g.GetName())
|
||||
}
|
||||
|
||||
err := s.artists.Create(ctx, &database.Artist{
|
||||
ExternalID: artist.GetId(),
|
||||
Name: artist.GetName(),
|
||||
ArtistType: artist.GetArtistType(),
|
||||
Country: artist.GetCountry(),
|
||||
Genres: genres,
|
||||
ImageURL: artist.GetImageUrl(),
|
||||
MonitorState: state,
|
||||
})
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Str("name", artist.GetName()).Msg("failed to persist artist")
|
||||
}
|
||||
}
|
||||
|
||||
func (s *MetadataService) PersistAlbum(ctx context.Context, album *metadataPb.Album, state database.MonitorState) {
|
||||
s.PersistAlbumForArtist(ctx, album, "", state)
|
||||
}
|
||||
|
||||
func (s *MetadataService) PersistAlbumsForArtist(ctx context.Context, metadataAlbums []*metadataPb.Album, artistDBID string, state database.MonitorState) {
|
||||
if len(metadataAlbums) == 0 || artistDBID == "" {
|
||||
return
|
||||
}
|
||||
|
||||
dbAlbums := make([]*database.Album, 0, len(metadataAlbums))
|
||||
for _, album := range metadataAlbums {
|
||||
var genres []string
|
||||
for _, g := range album.GetGenres() {
|
||||
genres = append(genres, g.GetName())
|
||||
}
|
||||
labelName := ""
|
||||
if album.GetLabel() != nil {
|
||||
labelName = album.GetLabel().GetName()
|
||||
}
|
||||
dbAlbums = append(dbAlbums, &database.Album{
|
||||
ExternalID: album.GetId(),
|
||||
ArtistID: artistDBID,
|
||||
Title: album.GetTitle(),
|
||||
AlbumType: album.GetAlbumType(),
|
||||
TotalTracks: int(album.GetTotalTracks()),
|
||||
TotalDiscs: int(album.GetTotalDiscs()),
|
||||
Label: labelName,
|
||||
Genres: genres,
|
||||
CoverURL: album.GetCoverUrl(),
|
||||
MonitorState: state,
|
||||
})
|
||||
}
|
||||
|
||||
if err := s.albums.CreateBatch(ctx, dbAlbums); err != nil {
|
||||
log.Warn().Err(err).Int("count", len(dbAlbums)).Msg("failed to batch persist albums")
|
||||
}
|
||||
}
|
||||
|
||||
func (s *MetadataService) PersistAlbumForArtist(ctx context.Context, album *metadataPb.Album, artistDBID string, state database.MonitorState) {
|
||||
if artistDBID == "" {
|
||||
if len(album.GetArtists()) > 0 {
|
||||
a, err := s.artists.GetByExternalID(ctx, album.GetArtists()[0].GetArtist().GetId())
|
||||
if err == nil {
|
||||
artistDBID = a.ID
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if artistDBID == "" {
|
||||
log.Trace().Str("album", album.GetTitle()).Msg("skipping album persist, no artist in DB")
|
||||
return
|
||||
}
|
||||
|
||||
var genres []string
|
||||
for _, g := range album.GetGenres() {
|
||||
genres = append(genres, g.GetName())
|
||||
}
|
||||
|
||||
labelName := ""
|
||||
if album.GetLabel() != nil {
|
||||
labelName = album.GetLabel().GetName()
|
||||
}
|
||||
|
||||
err := s.albums.Create(ctx, &database.Album{
|
||||
ExternalID: album.GetId(),
|
||||
ArtistID: artistDBID,
|
||||
Title: album.GetTitle(),
|
||||
AlbumType: album.GetAlbumType(),
|
||||
TotalTracks: int(album.GetTotalTracks()),
|
||||
TotalDiscs: int(album.GetTotalDiscs()),
|
||||
Label: labelName,
|
||||
Genres: genres,
|
||||
CoverURL: album.GetCoverUrl(),
|
||||
MonitorState: state,
|
||||
})
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Str("title", album.GetTitle()).Msg("failed to persist album")
|
||||
}
|
||||
}
|
||||
|
||||
func (s *MetadataService) PersistTracks(ctx context.Context, albumDBID string, tracks []*metadataPb.Track) {
|
||||
for _, t := range tracks {
|
||||
err := s.tracks.Create(ctx, &database.Track{
|
||||
ExternalID: t.GetId(),
|
||||
AlbumID: albumDBID,
|
||||
Title: t.GetTitle(),
|
||||
DurationMS: int(t.GetDurationMs()),
|
||||
ISRC: t.GetIsrc(),
|
||||
DiscNumber: int(t.GetDiscNumber()),
|
||||
TrackNumber: int(t.GetTrackNumber()),
|
||||
})
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Str("title", t.GetTitle()).Msg("failed to persist track")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,456 +0,0 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
pb "homelab.lan/music-agregator/gen/music_agregator/v1"
|
||||
"homelab.lan/music-agregator/internal/database"
|
||||
)
|
||||
|
||||
var MaxPromptTimeout = 300 * time.Second
|
||||
|
||||
type monitorWorkflow struct {
|
||||
mode pb.InteractionMode
|
||||
req *pb.StartMonitorRequest
|
||||
service *MusicAgregatorService
|
||||
publisher EventPublisher
|
||||
|
||||
stream pb.MusicAgregatorService_MonitorAlbumStreamServer
|
||||
decisions chan *pb.UserDecision
|
||||
cancel context.CancelFunc
|
||||
|
||||
addedHash string
|
||||
workflowRunID string
|
||||
|
||||
mu sync.Mutex
|
||||
promptID int
|
||||
}
|
||||
|
||||
func (w *monitorWorkflow) nextPromptID() string {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
w.promptID++
|
||||
return fmt.Sprintf("prompt-%d", w.promptID)
|
||||
}
|
||||
|
||||
func (w *monitorWorkflow) promptAndWait(ctx context.Context, prompt *pb.PromptForDecision) (*pb.UserDecision, error) {
|
||||
if w.mode == pb.InteractionMode_INTERACTION_MODE_AUTOMATIC {
|
||||
return w.defaultDecision(prompt), nil
|
||||
}
|
||||
|
||||
if w.stream == nil {
|
||||
return w.defaultDecision(prompt), nil
|
||||
}
|
||||
|
||||
if err := w.stream.Send(&pb.MonitorAlbumStreamResponse{
|
||||
Message: &pb.MonitorAlbumStreamResponse_Prompt{Prompt: prompt},
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
timeout := time.Duration(prompt.TimeoutSeconds) * time.Second
|
||||
if timeout == 0 || timeout > MaxPromptTimeout {
|
||||
timeout = MaxPromptTimeout
|
||||
}
|
||||
|
||||
timeoutCtx, cancel := context.WithTimeout(ctx, timeout)
|
||||
defer cancel()
|
||||
|
||||
select {
|
||||
case decision := <-w.decisions:
|
||||
if decision.PromptId != prompt.PromptId {
|
||||
return nil, status.Error(codes.InvalidArgument, "prompt_id mismatch")
|
||||
}
|
||||
return decision, nil
|
||||
case <-timeoutCtx.Done():
|
||||
return w.defaultDecision(prompt), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (w *monitorWorkflow) defaultDecision(prompt *pb.PromptForDecision) *pb.UserDecision {
|
||||
decision := &pb.UserDecision{PromptId: prompt.PromptId}
|
||||
|
||||
switch prompt.Type {
|
||||
case pb.PromptType_PROMPT_TYPE_CONFIRM:
|
||||
decision.Decision = &pb.UserDecision_Confirm{
|
||||
Confirm: prompt.GetConfirm().GetDefaultValue(),
|
||||
}
|
||||
case pb.PromptType_PROMPT_TYPE_SELECT_ONE:
|
||||
decision.Decision = &pb.UserDecision_SelectedId{
|
||||
SelectedId: prompt.GetSelectOne().GetDefaultId(),
|
||||
}
|
||||
case pb.PromptType_PROMPT_TYPE_SELECT_MANY:
|
||||
decision.Decision = &pb.UserDecision_SelectedIds{
|
||||
SelectedIds: &pb.SelectedIds{Ids: prompt.GetSelectMany().GetDefaultIds()},
|
||||
}
|
||||
}
|
||||
|
||||
return decision
|
||||
}
|
||||
|
||||
func (w *monitorWorkflow) receiveDecisions(ctx context.Context) {
|
||||
if w.stream == nil {
|
||||
return
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
msg, err := w.stream.Recv()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if msg.GetCancel() != nil {
|
||||
if w.cancel != nil {
|
||||
w.cancel()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if decision := msg.GetDecision(); decision != nil {
|
||||
select {
|
||||
case w.decisions <- decision:
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (w *monitorWorkflow) run(ctx context.Context) error {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
w.publisher.PublishStatus(ctx, pb.MonitorStep_MONITOR_STEP_FETCHING_METADATA, "Fetching album metadata...", nil)
|
||||
|
||||
album, err := w.service.metadata.GetAlbum(ctx, w.req.AlbumId)
|
||||
if err != nil {
|
||||
if ctx.Err() != nil {
|
||||
return ctx.Err()
|
||||
}
|
||||
log.Error().Err(err).Str("album_id", w.req.AlbumId).Msg("failed to get album")
|
||||
w.publisher.PublishError(ctx, pb.MonitorStep_MONITOR_STEP_FETCHING_METADATA, err, false)
|
||||
return err
|
||||
}
|
||||
|
||||
artistName := ""
|
||||
if len(album.GetArtists()) > 0 {
|
||||
artistName = album.GetArtists()[0].GetArtist().GetName()
|
||||
}
|
||||
|
||||
w.publisher.PublishStatus(ctx, pb.MonitorStep_MONITOR_STEP_FETCHING_METADATA,
|
||||
fmt.Sprintf("Got metadata: %s - %s", artistName, album.GetTitle()),
|
||||
&pb.StreamAlbumInfo{
|
||||
Artist: artistName,
|
||||
Title: album.GetTitle(),
|
||||
ReleaseDate: album.GetReleaseDate(),
|
||||
})
|
||||
|
||||
w.publisher.PublishStatus(ctx, pb.MonitorStep_MONITOR_STEP_CHECKING_OWNED, "Checking if already owned...", nil)
|
||||
|
||||
dbAlbum, _ := w.service.metadata.GetAlbumByExternalID(ctx, album.GetId())
|
||||
if dbAlbum != nil {
|
||||
w.publisher.SetAlbumID(dbAlbum.ID)
|
||||
w.service.metadata.SetAlbumMonitorState(ctx, dbAlbum.ID, database.Monitored)
|
||||
dbAlbum.MonitorState = database.Monitored
|
||||
|
||||
if w.workflowRunID == "" {
|
||||
run := &database.WorkflowRun{AlbumID: dbAlbum.ID, Quality: w.req.Quality.String()}
|
||||
if err := w.service.workflowRuns.Create(ctx, run); err != nil && err != database.ErrWorkflowAlreadyRunning {
|
||||
log.Warn().Err(err).Msg("failed to create workflow run")
|
||||
} else if err == nil {
|
||||
w.workflowRunID = run.ID
|
||||
w.publisher.SetWorkflowRunID(run.ID)
|
||||
}
|
||||
}
|
||||
|
||||
qualityStr := normalizeQuality(w.req.Quality, 0, 0)
|
||||
owned, err := w.service.downloads.HasAlbumInQuality(ctx, dbAlbum.ID, w.req.Quality.String(), qualityStr)
|
||||
if err == nil && owned {
|
||||
w.publisher.PublishStatus(ctx, pb.MonitorStep_MONITOR_STEP_CHECKING_OWNED,
|
||||
fmt.Sprintf("Already owned in %s quality", qualityStr), nil)
|
||||
|
||||
if w.mode == pb.InteractionMode_INTERACTION_MODE_MANUAL {
|
||||
decision, err := w.promptAndWait(ctx, &pb.PromptForDecision{
|
||||
PromptId: w.nextPromptID(),
|
||||
Type: pb.PromptType_PROMPT_TYPE_CONFIRM,
|
||||
Message: "Album already owned. Download anyway?",
|
||||
|
||||
Options: &pb.PromptForDecision_Confirm{
|
||||
Confirm: &pb.ConfirmPrompt{
|
||||
ConfirmLabel: "Download anyway",
|
||||
CancelLabel: "Skip",
|
||||
DefaultValue: false,
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
w.publisher.PublishError(ctx, pb.MonitorStep_MONITOR_STEP_CHECKING_OWNED, err, false)
|
||||
return err
|
||||
}
|
||||
if !decision.GetConfirm() {
|
||||
w.publisher.PublishStatus(ctx, pb.MonitorStep_MONITOR_STEP_COMPLETE, "Skipped - already owned", nil)
|
||||
return w.publisher.PublishResult(ctx, w.service.buildMonitorAlbumResponse(ctx, album, dbAlbum, nil))
|
||||
}
|
||||
} else {
|
||||
w.publisher.PublishStatus(ctx, pb.MonitorStep_MONITOR_STEP_COMPLETE, "Already owned", nil)
|
||||
return w.publisher.PublishResult(ctx, w.service.buildMonitorAlbumResponse(ctx, album, dbAlbum, nil))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
w.publisher.PublishStatus(ctx, pb.MonitorStep_MONITOR_STEP_SEARCHING_INDEXER,
|
||||
fmt.Sprintf("Searching indexers for %s - %s...", artistName, album.GetTitle()), nil)
|
||||
|
||||
searchResult, err := w.service.searchIndexer(album, w.req.IndexerOptions.GetTracker())
|
||||
if err != nil {
|
||||
w.publisher.PublishError(ctx, pb.MonitorStep_MONITOR_STEP_SEARCHING_INDEXER, err, true)
|
||||
return err
|
||||
}
|
||||
|
||||
parsed := w.service.parseSearchResults(ctx, searchResult, album, func(phase string, current, total int) {
|
||||
w.publisher.PublishStatus(ctx, pb.MonitorStep_MONITOR_STEP_PARSING_RESULTS,
|
||||
fmt.Sprintf("%s: %d/%d", phase, current, total), nil)
|
||||
})
|
||||
|
||||
if len(parsed) > 0 {
|
||||
summaries := make([]*pb.TorrentSummary, len(parsed))
|
||||
for i, p := range parsed {
|
||||
summaries[i] = &pb.TorrentSummary{
|
||||
Id: fmt.Sprintf("torrent-%d", i),
|
||||
Title: p.item.Title,
|
||||
Tracker: p.item.Tracker,
|
||||
Seeders: int32(p.item.Seeders),
|
||||
Format: p.rel.Format.String(),
|
||||
Lossless: p.rel.Format.IsLossless(),
|
||||
}
|
||||
}
|
||||
w.publisher.PublishStatus(ctx, pb.MonitorStep_MONITOR_STEP_PARSING_RESULTS,
|
||||
fmt.Sprintf("Parsed %d from %d torrents", len(parsed), len(searchResult.Items)),
|
||||
&pb.TorrentList{Torrents: summaries})
|
||||
} else {
|
||||
w.publisher.PublishStatus(ctx, pb.MonitorStep_MONITOR_STEP_PARSING_RESULTS,
|
||||
fmt.Sprintf("Found %d torrents, none parseable", len(searchResult.Items)), nil)
|
||||
}
|
||||
|
||||
if len(parsed) == 0 {
|
||||
err := fmt.Errorf("no torrents found for %s", album.GetTitle())
|
||||
w.publisher.PublishError(ctx, pb.MonitorStep_MONITOR_STEP_PARSING_RESULTS, err, false)
|
||||
return err
|
||||
}
|
||||
|
||||
parsed = filterByAlbumMatch(parsed, album.GetTitle(), artistName)
|
||||
if len(parsed) == 0 {
|
||||
err := fmt.Errorf("no torrents match album title %q", album.GetTitle())
|
||||
w.publisher.PublishError(ctx, pb.MonitorStep_MONITOR_STEP_PARSING_RESULTS, err, false)
|
||||
return err
|
||||
}
|
||||
|
||||
if w.mode == pb.InteractionMode_INTERACTION_MODE_MANUAL && len(parsed) > 1 {
|
||||
options := make([]*pb.SelectOption, len(parsed))
|
||||
defaultIDs := make([]string, len(parsed))
|
||||
for i, p := range parsed {
|
||||
id := fmt.Sprintf("torrent-%d", i)
|
||||
options[i] = &pb.SelectOption{
|
||||
Id: id,
|
||||
Label: p.item.Title,
|
||||
Description: fmt.Sprintf("%s - %d seeders", p.item.Tracker, p.item.Seeders),
|
||||
}
|
||||
defaultIDs[i] = id
|
||||
}
|
||||
|
||||
decision, err := w.promptAndWait(ctx, &pb.PromptForDecision{
|
||||
PromptId: w.nextPromptID(),
|
||||
Type: pb.PromptType_PROMPT_TYPE_SELECT_MANY,
|
||||
Message: "Select torrents to consider",
|
||||
|
||||
Options: &pb.PromptForDecision_SelectMany{
|
||||
SelectMany: &pb.SelectManyPrompt{
|
||||
Options: options,
|
||||
DefaultIds: defaultIDs,
|
||||
MinSelections: 1,
|
||||
MaxSelections: int32(len(parsed)),
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
w.publisher.PublishError(ctx, pb.MonitorStep_MONITOR_STEP_PARSING_RESULTS, err, false)
|
||||
return err
|
||||
}
|
||||
|
||||
selectedIDs := make(map[string]bool)
|
||||
if ids := decision.GetSelectedIds(); ids != nil {
|
||||
for _, id := range ids.GetIds() {
|
||||
selectedIDs[id] = true
|
||||
}
|
||||
}
|
||||
|
||||
var selected []parsedItem
|
||||
for i, p := range parsed {
|
||||
id := fmt.Sprintf("torrent-%d", i)
|
||||
if selectedIDs[id] {
|
||||
selected = append(selected, p)
|
||||
}
|
||||
}
|
||||
if len(selected) > 0 {
|
||||
parsed = selected
|
||||
}
|
||||
}
|
||||
|
||||
w.publisher.PublishStatus(ctx, pb.MonitorStep_MONITOR_STEP_FILTERING_QUALITY,
|
||||
fmt.Sprintf("Filtering %d results by quality...", len(parsed)), nil)
|
||||
|
||||
filtered := filterByQuality(parsed, w.req.Quality)
|
||||
if len(filtered) == 0 {
|
||||
err := fmt.Errorf("no releases match quality filter for %s", album.GetTitle())
|
||||
log.Warn().Str("album", album.GetTitle()).Str("quality", w.req.Quality.String()).Msg("no releases match quality filter")
|
||||
w.publisher.PublishError(ctx, pb.MonitorStep_MONITOR_STEP_FILTERING_QUALITY, err, false)
|
||||
return err
|
||||
}
|
||||
|
||||
filtered = filterByActiveSeeders(filtered)
|
||||
if len(filtered) == 0 {
|
||||
err := fmt.Errorf("no releases with active seeders for %s", album.GetTitle())
|
||||
log.Warn().Str("album", album.GetTitle()).Msg("no releases with active seeders")
|
||||
w.publisher.PublishError(ctx, pb.MonitorStep_MONITOR_STEP_FILTERING_QUALITY, err, false)
|
||||
return err
|
||||
}
|
||||
|
||||
var best parsedItem
|
||||
if w.mode == pb.InteractionMode_INTERACTION_MODE_MANUAL && len(filtered) > 1 {
|
||||
options := make([]*pb.SelectOption, len(filtered))
|
||||
for i, p := range filtered {
|
||||
options[i] = &pb.SelectOption{
|
||||
Id: fmt.Sprintf("release-%d", i),
|
||||
Label: p.item.Title,
|
||||
Description: fmt.Sprintf("%s - %d seeders - %s", p.item.Tracker, p.item.Seeders, p.rel.Format.String()),
|
||||
}
|
||||
}
|
||||
|
||||
bestIdx := 0
|
||||
for i, p := range filtered {
|
||||
if p.item.Seeders > filtered[bestIdx].item.Seeders {
|
||||
bestIdx = i
|
||||
}
|
||||
}
|
||||
|
||||
decision, err := w.promptAndWait(ctx, &pb.PromptForDecision{
|
||||
PromptId: w.nextPromptID(),
|
||||
Type: pb.PromptType_PROMPT_TYPE_SELECT_ONE,
|
||||
Message: "Select release",
|
||||
|
||||
Options: &pb.PromptForDecision_SelectOne{
|
||||
SelectOne: &pb.SelectOnePrompt{
|
||||
Options: options,
|
||||
DefaultId: fmt.Sprintf("release-%d", bestIdx),
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
w.publisher.PublishError(ctx, pb.MonitorStep_MONITOR_STEP_SELECTING_RELEASE, err, false)
|
||||
return err
|
||||
}
|
||||
|
||||
selectedIdx := 0
|
||||
if id := decision.GetSelectedId(); id != "" {
|
||||
for i := range filtered {
|
||||
if fmt.Sprintf("release-%d", i) == id {
|
||||
selectedIdx = i
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
best = filtered[selectedIdx]
|
||||
} else {
|
||||
best = selectBestRelease(filtered, album.GetTitle())
|
||||
}
|
||||
|
||||
w.publisher.PublishStatus(ctx, pb.MonitorStep_MONITOR_STEP_SELECTING_RELEASE,
|
||||
fmt.Sprintf("Selected: %s (%d seeders)", best.item.Title, best.item.Seeders),
|
||||
&pb.ReleaseInfo{
|
||||
InfoHash: best.rel.InfoHash,
|
||||
Format: best.rel.Format.String(),
|
||||
BitDepth: int32(best.rel.BitDepth),
|
||||
SampleRate: int32(best.rel.SampleRate),
|
||||
Seeders: int32(best.item.Seeders),
|
||||
Tracker: best.item.Tracker,
|
||||
})
|
||||
|
||||
w.publisher.PublishStatus(ctx, pb.MonitorStep_MONITOR_STEP_ADDING_TORRENT,
|
||||
fmt.Sprintf("Adding torrent: %s...", best.item.Title), nil)
|
||||
|
||||
if w.mode == pb.InteractionMode_INTERACTION_MODE_MANUAL {
|
||||
decision, err := w.promptAndWait(ctx, &pb.PromptForDecision{
|
||||
PromptId: w.nextPromptID(),
|
||||
Type: pb.PromptType_PROMPT_TYPE_CONFIRM,
|
||||
Message: fmt.Sprintf("Add torrent '%s' to client?", best.item.Title),
|
||||
|
||||
Options: &pb.PromptForDecision_Confirm{
|
||||
Confirm: &pb.ConfirmPrompt{
|
||||
ConfirmLabel: "Add",
|
||||
CancelLabel: "Skip",
|
||||
DefaultValue: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
w.publisher.PublishError(ctx, pb.MonitorStep_MONITOR_STEP_ADDING_TORRENT, err, false)
|
||||
return err
|
||||
}
|
||||
if !decision.GetConfirm() {
|
||||
w.publisher.PublishStatus(ctx, pb.MonitorStep_MONITOR_STEP_COMPLETE, "Skipped by user", nil)
|
||||
return w.publisher.PublishResult(ctx, w.service.buildMonitorAlbumResponse(ctx, album, dbAlbum, nil))
|
||||
}
|
||||
}
|
||||
|
||||
w.publisher.PublishStatus(ctx, pb.MonitorStep_MONITOR_STEP_SAVING, "Saving to database...", nil)
|
||||
|
||||
dbAlbum, _ = w.service.metadata.GetAlbumByExternalID(ctx, album.GetId())
|
||||
if dbAlbum == nil {
|
||||
w.service.metadata.PersistArtist(ctx, album, database.Monitored)
|
||||
w.service.metadata.PersistAlbum(ctx, album, database.Monitored)
|
||||
dbAlbum, _ = w.service.metadata.GetAlbumByExternalID(ctx, album.GetId())
|
||||
}
|
||||
if dbAlbum != nil {
|
||||
w.publisher.SetAlbumID(dbAlbum.ID)
|
||||
w.service.saveTorrentAndDownload(ctx, dbAlbum.ID, w.workflowRunID, best)
|
||||
} else {
|
||||
log.Warn().Str("album_id", w.req.AlbumId).Msg("album not in DB after persist attempt, skipping torrent/download persistence")
|
||||
}
|
||||
|
||||
w.addedHash = best.rel.InfoHash
|
||||
|
||||
if err := w.service.addToTorrentClient(best); err != nil {
|
||||
w.publisher.PublishError(ctx, pb.MonitorStep_MONITOR_STEP_ADDING_TORRENT, err, true)
|
||||
return err
|
||||
}
|
||||
|
||||
w.publisher.PublishStatus(ctx, pb.MonitorStep_MONITOR_STEP_COMPLETE, "Done!", nil)
|
||||
|
||||
return w.publisher.PublishResult(ctx, w.service.buildMonitorAlbumResponse(ctx, album, dbAlbum, &best))
|
||||
}
|
||||
|
||||
func (w *monitorWorkflow) cleanup(ctx context.Context) {
|
||||
if w.addedHash != "" {
|
||||
if err := w.service.torrentClient.DeleteTorrent(w.addedHash); err != nil {
|
||||
log.Warn().Err(err).Str("hash", w.addedHash).Msg("failed to delete torrent during cancel cleanup")
|
||||
}
|
||||
if err := w.service.downloads.SetCancelledByQbitHash(ctx, w.addedHash); err != nil {
|
||||
log.Warn().Err(err).Str("hash", w.addedHash).Msg("failed to cancel download during cleanup")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package musicfs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
|
||||
pb "homelab.lan/music-agregator/gen/musicfs/v1"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
MusicFS pb.MusicFSClient
|
||||
Metadata pb.MetadataServiceClient
|
||||
}
|
||||
|
||||
func NewClient(endpoint string) (*Client, *grpc.ClientConn, error) {
|
||||
log.Trace().Str("endpoint", endpoint).Msg("connecting to musicfs")
|
||||
|
||||
conn, err := grpc.NewClient(endpoint, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("connecting to musicfs: %w", err)
|
||||
}
|
||||
|
||||
log.Info().Str("endpoint", endpoint).Msg("musicfs connected")
|
||||
|
||||
return &Client{
|
||||
MusicFS: pb.NewMusicFSClient(conn),
|
||||
Metadata: pb.NewMetadataServiceClient(conn),
|
||||
}, conn, nil
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
package musicfs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
metadataPb "homelab.lan/music-agregator/gen/metadata/v1"
|
||||
pb "homelab.lan/music-agregator/gen/musicfs/v1"
|
||||
)
|
||||
|
||||
func (c *Client) EnrichFiles(ctx context.Context, files []SyncedFile, album *metadataPb.Album) error {
|
||||
if len(files) == 0 || album == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
genre := firstGenreName(album.GetGenres())
|
||||
label := labelName(album.GetLabel())
|
||||
albumType := album.GetAlbumType()
|
||||
coverURL := album.GetCoverUrl()
|
||||
|
||||
var items []*pb.BatchUpdateItem
|
||||
for _, f := range files {
|
||||
meta := &pb.UpdateMetadataRequest{
|
||||
FileId: f.FileID,
|
||||
}
|
||||
if genre != "" {
|
||||
meta.Genre = &genre
|
||||
}
|
||||
if label != "" {
|
||||
meta.Label = &label
|
||||
}
|
||||
if albumType != "" {
|
||||
meta.AlbumType = &albumType
|
||||
}
|
||||
if coverURL != "" {
|
||||
meta.CoverUrl = &coverURL
|
||||
}
|
||||
items = append(items, &pb.BatchUpdateItem{
|
||||
FileId: f.FileID,
|
||||
Metadata: meta,
|
||||
})
|
||||
}
|
||||
|
||||
stream, err := c.Metadata.BatchUpdateMetadata(ctx, &pb.BatchUpdateRequest{Items: items})
|
||||
if err != nil {
|
||||
return fmt.Errorf("batch update: %w", err)
|
||||
}
|
||||
|
||||
var updated, failed int
|
||||
for {
|
||||
progress, err := stream.Recv()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("batch update stream: %w", err)
|
||||
}
|
||||
if progress.ErrorMessage != nil {
|
||||
log.Warn().
|
||||
Int64("file_id", progress.GetCurrentFileId()).
|
||||
Str("error", progress.GetErrorMessage()).
|
||||
Msg("batch update item failed")
|
||||
failed++
|
||||
} else {
|
||||
updated++
|
||||
}
|
||||
}
|
||||
|
||||
log.Info().
|
||||
Int("updated", updated).
|
||||
Int("failed", failed).
|
||||
Msg("enrichment batch complete")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func firstGenreName(genres []*metadataPb.Genre) string {
|
||||
if len(genres) == 0 {
|
||||
return ""
|
||||
}
|
||||
return genres[0].GetName()
|
||||
}
|
||||
|
||||
func labelName(label *metadataPb.Label) string {
|
||||
if label == nil {
|
||||
return ""
|
||||
}
|
||||
return label.GetName()
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package musicfs
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func DeriveSubdir(originRoot, contentPath string) string {
|
||||
rel, err := filepath.Rel(originRoot, contentPath)
|
||||
if err != nil || strings.HasPrefix(rel, "..") {
|
||||
log.Warn().
|
||||
Str("origin_root", originRoot).
|
||||
Str("content_path", contentPath).
|
||||
Msg("content path outside origin root, falling back to full rescan")
|
||||
return ""
|
||||
}
|
||||
return rel
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user