feature/bakery-integration #1
95 changed files with 453 additions and 100420 deletions
24
.forgejo/workflows/check.yml
Normal file
24
.forgejo/workflows/check.yml
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
name: check
|
||||||
|
|
||||||
|
# Fast-fail lint/test on short-lived work branches, before it ever reaches
|
||||||
|
# main and triggers a dev-track release build.
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: ['feature/**', 'fix/**']
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check:
|
||||||
|
runs-on: [self-hosted, hestia]
|
||||||
|
steps:
|
||||||
|
- name: checkout
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
rm -rf src && mkdir src
|
||||||
|
git clone --branch "${GITHUB_REF_NAME}" --depth 1 \
|
||||||
|
"https://git.breadway.dev/${GITHUB_REPOSITORY}.git" src
|
||||||
|
|
||||||
|
- name: clippy
|
||||||
|
run: cd src && bash ci/build.sh cargo clippy --workspace --all-targets --locked -- -D warnings
|
||||||
|
|
||||||
|
- name: test
|
||||||
|
run: cd src && bash ci/build.sh cargo test --workspace --locked
|
||||||
99
.forgejo/workflows/dev-release.yml
Normal file
99
.forgejo/workflows/dev-release.yml
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
name: dev release
|
||||||
|
|
||||||
|
# Publishes a dev-track build on every push to `main` (the trunk branch —
|
||||||
|
# there is no separate `dev` branch). See bread-ecosystem's
|
||||||
|
# docs/release-channels.md for the release-track policy this is part of.
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: ['main']
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: [self-hosted, hestia]
|
||||||
|
steps:
|
||||||
|
- name: checkout
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
rm -rf src && mkdir src
|
||||||
|
git clone --branch main --depth 1 \
|
||||||
|
"https://git.breadway.dev/${GITHUB_REPOSITORY}.git" src
|
||||||
|
|
||||||
|
- name: build
|
||||||
|
run: cd src && bash ci/build.sh cargo build --release --locked
|
||||||
|
|
||||||
|
- name: compute dev version
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
cd src
|
||||||
|
# Base the dev version off the latest published stable tag, not
|
||||||
|
# Cargo.toml — Cargo.toml can go stale relative to the last real
|
||||||
|
# release, which would make a dev build sort as OLDER than what's
|
||||||
|
# already installed and bakery would correctly refuse it.
|
||||||
|
LATEST_TAG="$(git ls-remote --tags --refs \
|
||||||
|
"https://git.breadway.dev/${GITHUB_REPOSITORY}.git" 'v*' \
|
||||||
|
| awk -F/ '{print $NF}' | sed 's/^v//' | (grep -v -- '-' || true) | sort -V | tail -1)"
|
||||||
|
if [ -n "${LATEST_TAG}" ]; then
|
||||||
|
CUR="${LATEST_TAG}"
|
||||||
|
else
|
||||||
|
# breadarr's own Cargo.toml is a virtual workspace manifest with
|
||||||
|
# no [workspace.package] version — breadarrd/Cargo.toml is the
|
||||||
|
# daemon crate's own version, used as the fallback instead.
|
||||||
|
CUR="$(grep -m1 '^version' breadarrd/Cargo.toml | sed -E 's/.*"(.*)".*/\1/')"
|
||||||
|
fi
|
||||||
|
IFS='.' read -r MA MI PA <<< "${CUR}"
|
||||||
|
SHA="$(git rev-parse --short HEAD)"
|
||||||
|
TS="$(date -u +%Y%m%d%H%M%S)"
|
||||||
|
echo "VERSION=${MA}.${MI}.$((PA + 1))-dev.${TS}+${SHA}" >> "$GITHUB_ENV"
|
||||||
|
|
||||||
|
- name: prepare artifacts
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
PKG_DIR="/srv/breadway-dl/dev/breadarr/${VERSION}"
|
||||||
|
mkdir -p "${PKG_DIR}"
|
||||||
|
for bin in breadarrd breadarr-tui; do
|
||||||
|
cp "src/target/release/${bin}" "${PKG_DIR}/${bin}-x86_64"
|
||||||
|
strip "${PKG_DIR}/${bin}-x86_64"
|
||||||
|
sha256sum "${PKG_DIR}/${bin}-x86_64" | awk '{print $1}' \
|
||||||
|
> "${PKG_DIR}/${bin}-x86_64.sha256"
|
||||||
|
done
|
||||||
|
cp src/packaging/systemd/breadarrd.service "${PKG_DIR}/"
|
||||||
|
cp src/bakery.toml "${PKG_DIR}/bakery.toml"
|
||||||
|
ln -sfn "${VERSION}" "/srv/breadway-dl/dev/breadarr/latest"
|
||||||
|
|
||||||
|
- name: sign dev binaries
|
||||||
|
env:
|
||||||
|
MINISIGN_SEC_KEY: ${{ secrets.BAKERY_MINISIGN_SEC_KEY_PATH }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
PKG_DIR="/srv/breadway-dl/dev/breadarr/${VERSION}"
|
||||||
|
if [ -n "${MINISIGN_SEC_KEY:-}" ]; then
|
||||||
|
for bin in breadarrd breadarr-tui; do
|
||||||
|
minisign -W -S -s "${MINISIGN_SEC_KEY}" -m "${PKG_DIR}/${bin}-x86_64" \
|
||||||
|
-x "${PKG_DIR}/${bin}-x86_64.minisig" </dev/null
|
||||||
|
echo "signed ${bin}-x86_64"
|
||||||
|
done
|
||||||
|
else
|
||||||
|
echo "::warning::BAKERY_MINISIGN_SEC_KEY_PATH not set — shipping breadarrd-x86_64/breadarr-tui-x86_64 UNSIGNED"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# No GitHub Release upload step here — breadarr has no GitHub mirror,
|
||||||
|
# and dev builds happen on every push and would spam a release per
|
||||||
|
# commit anyway, so dl.breadway.dev/dev/ is the only distribution
|
||||||
|
# point for this track.
|
||||||
|
- name: regenerate dev index.json
|
||||||
|
env:
|
||||||
|
MINISIGN_SEC_KEY: ${{ secrets.BAKERY_MINISIGN_SEC_KEY_PATH }}
|
||||||
|
TRACK: dev
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
if [ -z "${MINISIGN_SEC_KEY:-}" ]; then
|
||||||
|
echo "::error::BAKERY_MINISIGN_SEC_KEY_PATH secret not set — refusing to regenerate dev index.json unsigned (would leave a stale signature mismatched against fresh content and break bakery for everyone on the dev track)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
rm -rf /tmp/bread-ecosystem-ci-* 2>/dev/null || true
|
||||||
|
# mktemp: a fixed clone path races when multiple repos' dev/rc
|
||||||
|
# workflows run close together on the same self-hosted runner.
|
||||||
|
ECOSYSTEM_CI_DIR="$(mktemp -d /tmp/bread-ecosystem-ci-XXXXXX)"
|
||||||
|
git clone --branch main https://git.breadway.dev/Breadway/bread-ecosystem.git "${ECOSYSTEM_CI_DIR}"
|
||||||
|
TRACK=dev bash "${ECOSYSTEM_CI_DIR}/scripts/gen-index.sh"
|
||||||
|
rm -rf "${ECOSYSTEM_CI_DIR}"
|
||||||
79
.forgejo/workflows/rc-release.yml
Normal file
79
.forgejo/workflows/rc-release.yml
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
name: beta (rc) release
|
||||||
|
|
||||||
|
# Publishes a beta-track build for any `vX.Y.Z-rc.N` prerelease tag pushed
|
||||||
|
# to `main` — there is no separate `beta` branch; "freezing" is just
|
||||||
|
# pausing pushes to main while an RC gets tested. See bread-ecosystem's
|
||||||
|
# docs/release-channels.md for the release-track policy.
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags: ['v*']
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
if: ${{ contains(github.ref_name, '-rc.') }}
|
||||||
|
runs-on: [self-hosted, hestia]
|
||||||
|
steps:
|
||||||
|
- name: checkout
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
rm -rf src && mkdir src
|
||||||
|
git clone --branch "${GITHUB_REF_NAME}" --depth 1 \
|
||||||
|
"https://git.breadway.dev/${GITHUB_REPOSITORY}.git" src
|
||||||
|
|
||||||
|
- name: build
|
||||||
|
run: cd src && bash ci/build.sh cargo build --release --locked
|
||||||
|
|
||||||
|
- name: prepare artifacts
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
VERSION="${GITHUB_REF_NAME#v}"
|
||||||
|
PKG_DIR="/srv/breadway-dl/beta/breadarr/${VERSION}"
|
||||||
|
mkdir -p "${PKG_DIR}"
|
||||||
|
for bin in breadarrd breadarr-tui; do
|
||||||
|
cp "src/target/release/${bin}" "${PKG_DIR}/${bin}-x86_64"
|
||||||
|
strip "${PKG_DIR}/${bin}-x86_64"
|
||||||
|
sha256sum "${PKG_DIR}/${bin}-x86_64" | awk '{print $1}' \
|
||||||
|
> "${PKG_DIR}/${bin}-x86_64.sha256"
|
||||||
|
done
|
||||||
|
cp src/packaging/systemd/breadarrd.service "${PKG_DIR}/"
|
||||||
|
cp src/bakery.toml "${PKG_DIR}/bakery.toml"
|
||||||
|
ln -sfn "${VERSION}" "/srv/breadway-dl/beta/breadarr/latest"
|
||||||
|
|
||||||
|
- name: sign beta binaries
|
||||||
|
env:
|
||||||
|
MINISIGN_SEC_KEY: ${{ secrets.BAKERY_MINISIGN_SEC_KEY_PATH }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
VERSION="${GITHUB_REF_NAME#v}"
|
||||||
|
PKG_DIR="/srv/breadway-dl/beta/breadarr/${VERSION}"
|
||||||
|
if [ -n "${MINISIGN_SEC_KEY:-}" ]; then
|
||||||
|
for bin in breadarrd breadarr-tui; do
|
||||||
|
minisign -W -S -s "${MINISIGN_SEC_KEY}" -m "${PKG_DIR}/${bin}-x86_64" \
|
||||||
|
-x "${PKG_DIR}/${bin}-x86_64.minisig" </dev/null
|
||||||
|
echo "signed ${bin}-x86_64"
|
||||||
|
done
|
||||||
|
else
|
||||||
|
echo "::warning::BAKERY_MINISIGN_SEC_KEY_PATH not set — shipping breadarrd-x86_64/breadarr-tui-x86_64 UNSIGNED"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# No GitHub Release upload step here — breadarr has no GitHub mirror,
|
||||||
|
# and beta builds happen on every RC tag while the branch is frozen
|
||||||
|
# for testing, so dl.breadway.dev/beta/ is the only distribution
|
||||||
|
# point for this track.
|
||||||
|
- name: regenerate beta index.json
|
||||||
|
env:
|
||||||
|
MINISIGN_SEC_KEY: ${{ secrets.BAKERY_MINISIGN_SEC_KEY_PATH }}
|
||||||
|
TRACK: beta
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
if [ -z "${MINISIGN_SEC_KEY:-}" ]; then
|
||||||
|
echo "::error::BAKERY_MINISIGN_SEC_KEY_PATH secret not set — refusing to regenerate beta index.json unsigned (would leave a stale signature mismatched against fresh content and break bakery for everyone on the beta track)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
rm -rf /tmp/bread-ecosystem-ci-* 2>/dev/null || true
|
||||||
|
# mktemp: a fixed clone path races when multiple repos' dev/rc
|
||||||
|
# workflows run close together on the same self-hosted runner.
|
||||||
|
ECOSYSTEM_CI_DIR="$(mktemp -d /tmp/bread-ecosystem-ci-XXXXXX)"
|
||||||
|
git clone https://git.breadway.dev/Breadway/bread-ecosystem.git "${ECOSYSTEM_CI_DIR}"
|
||||||
|
TRACK=beta bash "${ECOSYSTEM_CI_DIR}/scripts/gen-index.sh"
|
||||||
|
rm -rf "${ECOSYSTEM_CI_DIR}"
|
||||||
79
.forgejo/workflows/release.yml
Normal file
79
.forgejo/workflows/release.yml
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
name: release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags: ['v*']
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
if: ${{ !contains(github.ref_name, '-rc.') }}
|
||||||
|
runs-on: [self-hosted, hestia]
|
||||||
|
steps:
|
||||||
|
- name: checkout
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
rm -rf src && mkdir src
|
||||||
|
git clone --branch "${GITHUB_REF_NAME}" --depth 1 \
|
||||||
|
"https://git.breadway.dev/${GITHUB_REPOSITORY}.git" src
|
||||||
|
|
||||||
|
- name: build
|
||||||
|
run: cd src && bash ci/build.sh cargo build --release --locked
|
||||||
|
|
||||||
|
- name: prepare artifacts
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
VERSION="${GITHUB_REF_NAME#v}"
|
||||||
|
PKG_DIR="/srv/breadway-dl/breadarr/${VERSION}"
|
||||||
|
mkdir -p "${PKG_DIR}"
|
||||||
|
for bin in breadarrd breadarr-tui; do
|
||||||
|
cp "src/target/release/${bin}" "${PKG_DIR}/${bin}-x86_64"
|
||||||
|
strip "${PKG_DIR}/${bin}-x86_64"
|
||||||
|
sha256sum "${PKG_DIR}/${bin}-x86_64" | awk '{print $1}' \
|
||||||
|
> "${PKG_DIR}/${bin}-x86_64.sha256"
|
||||||
|
done
|
||||||
|
cp src/packaging/systemd/breadarrd.service "${PKG_DIR}/"
|
||||||
|
cp src/bakery.toml "${PKG_DIR}/bakery.toml"
|
||||||
|
ln -sfn "${VERSION}" "/srv/breadway-dl/breadarr/latest"
|
||||||
|
|
||||||
|
# Signs with the shared bakery ecosystem signing key (same key that
|
||||||
|
# signs index.json). BAKERY_MINISIGN_SEC_KEY_PATH is a *path on this
|
||||||
|
# runner's disk* (hestia has persistent storage), not the key
|
||||||
|
# contents. Dormant (binaries ship unsigned) until that secret is
|
||||||
|
# provisioned for this repo.
|
||||||
|
- name: sign release binaries
|
||||||
|
env:
|
||||||
|
MINISIGN_SEC_KEY: ${{ secrets.BAKERY_MINISIGN_SEC_KEY_PATH }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
VERSION="${GITHUB_REF_NAME#v}"
|
||||||
|
PKG_DIR="/srv/breadway-dl/breadarr/${VERSION}"
|
||||||
|
if [ -n "${MINISIGN_SEC_KEY:-}" ]; then
|
||||||
|
for bin in breadarrd breadarr-tui; do
|
||||||
|
minisign -W -S -s "${MINISIGN_SEC_KEY}" -m "${PKG_DIR}/${bin}-x86_64" \
|
||||||
|
-x "${PKG_DIR}/${bin}-x86_64.minisig" </dev/null
|
||||||
|
echo "signed ${bin}-x86_64"
|
||||||
|
done
|
||||||
|
else
|
||||||
|
echo "::warning::BAKERY_MINISIGN_SEC_KEY_PATH not set — shipping breadarrd-x86_64/breadarr-tui-x86_64 UNSIGNED"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: regenerate index.json
|
||||||
|
env:
|
||||||
|
MINISIGN_SEC_KEY: ${{ secrets.BAKERY_MINISIGN_SEC_KEY_PATH }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
if [ -z "${MINISIGN_SEC_KEY:-}" ]; then
|
||||||
|
echo "::error::BAKERY_MINISIGN_SEC_KEY_PATH secret not set — refusing to regenerate stable index.json unsigned (would leave a stale signature mismatched against fresh content and break bakery for everyone on the stable track)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
rm -rf /tmp/bread-ecosystem-ci-* 2>/dev/null || true
|
||||||
|
# mktemp: a fixed clone path races when multiple repos' release
|
||||||
|
# workflows run close together on the same self-hosted runner.
|
||||||
|
ECOSYSTEM_CI_DIR="$(mktemp -d /tmp/bread-ecosystem-ci-XXXXXX)"
|
||||||
|
git clone https://git.breadway.dev/Breadway/bread-ecosystem.git "${ECOSYSTEM_CI_DIR}"
|
||||||
|
bash "${ECOSYSTEM_CI_DIR}/scripts/gen-index.sh"
|
||||||
|
rm -rf "${ECOSYSTEM_CI_DIR}"
|
||||||
|
|
||||||
|
# No GitHub Release upload step — breadarr has no GitHub mirror,
|
||||||
|
# unlike most sibling bread-ecosystem repos. dl.breadway.dev is the
|
||||||
|
# only distribution point for this repo.
|
||||||
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -6,3 +6,6 @@ config.toml
|
||||||
|
|
||||||
# Local hygiene notes (not for commit)
|
# Local hygiene notes (not for commit)
|
||||||
CLAUDE.md
|
CLAUDE.md
|
||||||
|
|
||||||
|
# graphify knowledge-graph output (local tool cache, not for commit)
|
||||||
|
graphify-out/
|
||||||
|
|
|
||||||
44
bakery.toml
Normal file
44
bakery.toml
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
name = "breadarr"
|
||||||
|
description = "Single-daemon Sonarr+Radarr+Prowlarr replacement — release watching, matching, grabbing, importing, and a terminal UI, no web UI"
|
||||||
|
binaries = ["breadarrd", "breadarr-tui"]
|
||||||
|
# mkvtoolnix-cli / ffmpeg: `mkvmerge` and `ffprobe`/`ffmpeg` are shelled out
|
||||||
|
# to directly via Command::new() (breadarrd/src/importer/mkv.rs for the
|
||||||
|
# audio-track remux fix; breadarrd/src/importer/ffprobe.rs and
|
||||||
|
# breadarrd/src/transcode/mod.rs for media probing/corruption verification/
|
||||||
|
# transcode-backlog work) rather than linked, so neither shows up in
|
||||||
|
# `ldd target/release/breadarrd` -- confirmed by grepping breadarrd/src for
|
||||||
|
# Command::new("mkvmerge"|"ffprobe"|"ffmpeg") and cross-checking against the
|
||||||
|
# README's own host-requirements instructions. The ONNX embedding model (the
|
||||||
|
# `ort` crate, used for fuzzy title matching) needs no system_deps entry:
|
||||||
|
# `ldd target/release/breadarrd` lists no libonnxruntime.so, and `strings`
|
||||||
|
# on the built binary is full of onnxruntime's own C++ symbol names --
|
||||||
|
# confirming the `ort` crate's default strategy statically bundled its own
|
||||||
|
# onnxruntime build directly into breadarrd rather than dynamically linking
|
||||||
|
# the system onnxruntime-cpu package. openssl: libssl.so.3/libcrypto.so.3
|
||||||
|
# (reqwest's TLS backend) show up directly in `ldd` for both breadarrd and
|
||||||
|
# breadarr-tui. libstdc++/libgcc/glibc/zlib/zstd/brotli also appear in
|
||||||
|
# `ldd` but are omitted here the same way breadcast's bakery.toml omits
|
||||||
|
# them: glibc/gcc-libs are unavoidable base-system dependencies, and
|
||||||
|
# zlib/zstd/brotli are themselves transitive deps of openssl/curl/pacman
|
||||||
|
# already guaranteed present on any real Arch install.
|
||||||
|
system_deps = [
|
||||||
|
"mkvtoolnix-cli",
|
||||||
|
"ffmpeg",
|
||||||
|
"openssl",
|
||||||
|
]
|
||||||
|
optional_system_deps = []
|
||||||
|
bread_deps = []
|
||||||
|
license_file = "LICENSE"
|
||||||
|
|
||||||
|
[[service]]
|
||||||
|
unit = "breadarrd.service"
|
||||||
|
enable = true
|
||||||
|
|
||||||
|
[config]
|
||||||
|
dir = "~/.config/breadarr"
|
||||||
|
example = "config.example.toml"
|
||||||
|
|
||||||
|
[install]
|
||||||
|
post_install = [
|
||||||
|
"systemctl --user is-active --quiet breadarrd || systemctl --user start breadarrd",
|
||||||
|
]
|
||||||
|
|
@ -27,19 +27,31 @@ pub struct Config {
|
||||||
pub transcode: TranscodeConfig,
|
pub transcode: TranscodeConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Where the TUI's "add show" flow places new series by default. Sonarr/
|
/// Where the TUI's "add" flow places new series/movies by default. Sonarr/
|
||||||
/// Radarr let you pick a root folder per add; a single configured default
|
/// Radarr let you pick a root folder per add; a single configured default per
|
||||||
/// is a reasonable v1 simplification — per-add picking can follow later.
|
/// kind is a reasonable v1 simplification — per-add picking can follow later.
|
||||||
|
/// Series and movies need *separate* defaults (not one shared value) because
|
||||||
|
/// they live under different category roots on disk (e.g. `TV Shows/` vs
|
||||||
|
/// `Movies/`) — a real production bug had both kinds falling back to one
|
||||||
|
/// bare library root with no per-item subfolder, landing new grabs directly
|
||||||
|
/// in the library root instead of inside their own show/movie folder,
|
||||||
|
/// invisible to Jellyfin's per-category libraries.
|
||||||
#[derive(Debug, Clone, Default, Deserialize)]
|
#[derive(Debug, Clone, Default, Deserialize)]
|
||||||
pub struct LibraryConfig {
|
pub struct LibraryConfig {
|
||||||
#[serde(default = "default_root_folder")]
|
#[serde(default = "default_root_folder")]
|
||||||
pub default_root_folder: String,
|
pub default_root_folder: String,
|
||||||
|
#[serde(default = "default_movies_root_folder")]
|
||||||
|
pub movies_root_folder: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_root_folder() -> String {
|
fn default_root_folder() -> String {
|
||||||
"~/breadarr-library".to_string()
|
"~/breadarr-library".to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn default_movies_root_folder() -> String {
|
||||||
|
"~/breadarr-library/Movies".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
pub struct SourcesConfig {
|
pub struct SourcesConfig {
|
||||||
/// 1337x's main domain bans IPs at the Cloudflare WAF level after
|
/// 1337x's main domain bans IPs at the Cloudflare WAF level after
|
||||||
|
|
@ -625,6 +637,10 @@ impl Config {
|
||||||
pub fn default_root_folder(&self) -> PathBuf {
|
pub fn default_root_folder(&self) -> PathBuf {
|
||||||
expand_home(&self.library.default_root_folder)
|
expand_home(&self.library.default_root_folder)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn movies_root_folder(&self) -> PathBuf {
|
||||||
|
expand_home(&self.library.movies_root_folder)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn config_path() -> PathBuf {
|
fn config_path() -> PathBuf {
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,39 @@ use breadarr_shared::dto::{
|
||||||
};
|
};
|
||||||
use breadarr_shared::DaemonClient;
|
use breadarr_shared::DaemonClient;
|
||||||
use ratatui::widgets::ListState;
|
use ratatui::widgets::ListState;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
/// Category roots the TUI's "Add" flow can place new items under — kept as
|
||||||
|
/// two separate paths (not one shared default) since a series and a movie
|
||||||
|
/// added with the same title must not collide on disk, and Jellyfin's
|
||||||
|
/// per-category libraries only see files under their own category root.
|
||||||
|
pub struct LibraryRoots {
|
||||||
|
pub series: String,
|
||||||
|
pub movies: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Builds the per-item folder a freshly added series/movie lands in —
|
||||||
|
/// `{root}/{Title} ({Year})`, matching the convention the importer already
|
||||||
|
/// assumes (`import_one` places a movie's file directly inside its
|
||||||
|
/// `media_item.root_folder`, with no further subfolder of its own, and
|
||||||
|
/// `season_dir` does the same for a show's `Season NN` folders). Passing the
|
||||||
|
/// bare category root straight through as `root_folder` — the bug this
|
||||||
|
/// replaces — landed every new grab directly in that shared root instead of
|
||||||
|
/// its own show/movie folder.
|
||||||
|
fn item_root_folder(root: &str, title: &str, year: Option<i64>) -> String {
|
||||||
|
let sanitized: String = title
|
||||||
|
.chars()
|
||||||
|
.map(|c| if "/\\:*?\"<>|".contains(c) { '_' } else { c })
|
||||||
|
.collect();
|
||||||
|
let folder_name = match year {
|
||||||
|
Some(y) => format!("{sanitized} ({y})"),
|
||||||
|
None => sanitized,
|
||||||
|
};
|
||||||
|
Path::new(root)
|
||||||
|
.join(folder_name)
|
||||||
|
.to_string_lossy()
|
||||||
|
.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum Tab {
|
pub enum Tab {
|
||||||
|
|
@ -565,7 +598,7 @@ impl App {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let (media_item_id, season_number, monitored) =
|
let (media_item_id, season_number, monitored) =
|
||||||
(detail.id, episode.season_number as i64, episode.monitored);
|
(detail.id, episode.season_number, episode.monitored);
|
||||||
let result = if monitored {
|
let result = if monitored {
|
||||||
self.client
|
self.client
|
||||||
.unmonitor_season(media_item_id, season_number)
|
.unmonitor_season(media_item_id, season_number)
|
||||||
|
|
@ -659,7 +692,7 @@ impl App {
|
||||||
self.focus = Focus::AddResults;
|
self.focus = Focus::AddResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn add_selected_search_result(&mut self, root_folder: &str) {
|
pub async fn add_selected_search_result(&mut self, roots: &LibraryRoots) {
|
||||||
let Some(idx) = self.add_results_state.selected() else {
|
let Some(idx) = self.add_results_state.selected() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
@ -673,7 +706,7 @@ impl App {
|
||||||
title: result.title.clone(),
|
title: result.title.clone(),
|
||||||
year: result.year,
|
year: result.year,
|
||||||
aliases: Vec::new(),
|
aliases: Vec::new(),
|
||||||
root_folder: root_folder.to_string(),
|
root_folder: item_root_folder(&roots.series, &result.title, result.year),
|
||||||
};
|
};
|
||||||
self.client.add_series(&req).await.map(|r| r.media_item_id)
|
self.client.add_series(&req).await.map(|r| r.media_item_id)
|
||||||
}
|
}
|
||||||
|
|
@ -682,7 +715,7 @@ impl App {
|
||||||
tmdb_id: result.external_id.clone(),
|
tmdb_id: result.external_id.clone(),
|
||||||
title: result.title.clone(),
|
title: result.title.clone(),
|
||||||
year: result.year,
|
year: result.year,
|
||||||
root_folder: root_folder.to_string(),
|
root_folder: item_root_folder(&roots.movies, &result.title, result.year),
|
||||||
};
|
};
|
||||||
self.client.add_movie(&req).await.map(|r| r.media_item_id)
|
self.client.add_movie(&req).await.map(|r| r.media_item_id)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,14 +14,17 @@ use crossterm::terminal::{
|
||||||
use ratatui::backend::CrosstermBackend;
|
use ratatui::backend::CrosstermBackend;
|
||||||
use ratatui::Terminal;
|
use ratatui::Terminal;
|
||||||
|
|
||||||
use app::{App, Focus, StuckSection, Tab};
|
use app::{App, Focus, LibraryRoots, StuckSection, Tab};
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
let config = Config::load()?;
|
let config = Config::load()?;
|
||||||
let base_url = format!("http://{}", config.daemon.listen_addr);
|
let base_url = format!("http://{}", config.daemon.listen_addr);
|
||||||
let client = DaemonClient::new(base_url, &config.daemon.api_token);
|
let client = DaemonClient::new(base_url, &config.daemon.api_token);
|
||||||
let root_folder = config.default_root_folder().to_string_lossy().to_string();
|
let roots = LibraryRoots {
|
||||||
|
series: config.default_root_folder().to_string_lossy().to_string(),
|
||||||
|
movies: config.movies_root_folder().to_string_lossy().to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
enable_raw_mode()?;
|
enable_raw_mode()?;
|
||||||
let mut stdout = io::stdout();
|
let mut stdout = io::stdout();
|
||||||
|
|
@ -30,7 +33,7 @@ async fn main() -> Result<()> {
|
||||||
let mut terminal = Terminal::new(backend)?;
|
let mut terminal = Terminal::new(backend)?;
|
||||||
|
|
||||||
let mut app = App::new(client);
|
let mut app = App::new(client);
|
||||||
let result = run(&mut terminal, &mut app, &root_folder).await;
|
let result = run(&mut terminal, &mut app, &roots).await;
|
||||||
|
|
||||||
disable_raw_mode()?;
|
disable_raw_mode()?;
|
||||||
execute!(terminal.backend_mut(), LeaveAlternateScreen)?;
|
execute!(terminal.backend_mut(), LeaveAlternateScreen)?;
|
||||||
|
|
@ -42,7 +45,7 @@ async fn main() -> Result<()> {
|
||||||
async fn run(
|
async fn run(
|
||||||
terminal: &mut Terminal<CrosstermBackend<io::Stdout>>,
|
terminal: &mut Terminal<CrosstermBackend<io::Stdout>>,
|
||||||
app: &mut App,
|
app: &mut App,
|
||||||
root_folder: &str,
|
roots: &LibraryRoots,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let mut last_refresh = tokio::time::Instant::now() - Duration::from_secs(10);
|
let mut last_refresh = tokio::time::Instant::now() - Duration::from_secs(10);
|
||||||
|
|
||||||
|
|
@ -60,7 +63,7 @@ async fn run(
|
||||||
if key.kind != KeyEventKind::Press {
|
if key.kind != KeyEventKind::Press {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
handle_key(app, key.code, root_folder).await;
|
handle_key(app, key.code, roots).await;
|
||||||
if app.should_quit {
|
if app.should_quit {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
@ -69,7 +72,7 @@ async fn run(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_key(app: &mut App, code: KeyCode, root_folder: &str) {
|
async fn handle_key(app: &mut App, code: KeyCode, roots: &LibraryRoots) {
|
||||||
// Typing into the add-show search box takes priority over global keys.
|
// Typing into the add-show search box takes priority over global keys.
|
||||||
if matches!(app.tab, Tab::Add) && matches!(app.focus, Focus::AddSearchInput) {
|
if matches!(app.tab, Tab::Add) && matches!(app.focus, Focus::AddSearchInput) {
|
||||||
match code {
|
match code {
|
||||||
|
|
@ -140,7 +143,7 @@ async fn handle_key(app: &mut App, code: KeyCode, root_folder: &str) {
|
||||||
}
|
}
|
||||||
Tab::Library => app.open_detail().await,
|
Tab::Library => app.open_detail().await,
|
||||||
Tab::Add => match app.focus {
|
Tab::Add => match app.focus {
|
||||||
Focus::AddResults => app.add_selected_search_result(root_folder).await,
|
Focus::AddResults => app.add_selected_search_result(roots).await,
|
||||||
_ => app.focus = Focus::AddSearchInput,
|
_ => app.focus = Focus::AddSearchInput,
|
||||||
},
|
},
|
||||||
Tab::Profiles if app.profile_detail.is_some() => {
|
Tab::Profiles if app.profile_detail.is_some() => {
|
||||||
|
|
|
||||||
|
|
@ -99,14 +99,6 @@ impl PendingGrab {
|
||||||
PendingGrab::Movie { .. } | PendingGrab::SeasonPack { .. } => None,
|
PendingGrab::Movie { .. } | PendingGrab::SeasonPack { .. } => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn root_folder(&self) -> &str {
|
|
||||||
match self {
|
|
||||||
PendingGrab::Episode { root_folder, .. }
|
|
||||||
| PendingGrab::Movie { root_folder, .. }
|
|
||||||
| PendingGrab::SeasonPack { root_folder, .. } => root_folder,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Three separate queries (rather than one `LEFT JOIN episode`) because a
|
/// Three separate queries (rather than one `LEFT JOIN episode`) because a
|
||||||
|
|
@ -1615,7 +1607,7 @@ impl StaleFile {
|
||||||
/// later step (free-space check, the actual move) fails, so a failed
|
/// later step (free-space check, the actual move) fails, so a failed
|
||||||
/// import never leaves neither the old file nor the new one behind.
|
/// import never leaves neither the old file nor the new one behind.
|
||||||
fn restore(&self) {
|
fn restore(&self) {
|
||||||
if self.parked_at.as_os_str().len() > 0 {
|
if !self.parked_at.as_os_str().is_empty() {
|
||||||
std::fs::rename(&self.parked_at, &self.restore_to).ok();
|
std::fs::rename(&self.parked_at, &self.restore_to).ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1909,7 +1901,7 @@ fn import_one(
|
||||||
if let Some(row_id) = stale.row_id {
|
if let Some(row_id) = stale.row_id {
|
||||||
conn.execute("DELETE FROM episode_file WHERE id = ?1", params![row_id])?;
|
conn.execute("DELETE FROM episode_file WHERE id = ?1", params![row_id])?;
|
||||||
}
|
}
|
||||||
if stale.parked_at.as_os_str().len() > 0 {
|
if !stale.parked_at.as_os_str().is_empty() {
|
||||||
std::fs::remove_file(&stale.parked_at).ok();
|
std::fs::remove_file(&stale.parked_at).ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2309,7 +2301,7 @@ fn import_season_pack_file(
|
||||||
if let Some(row_id) = stale.row_id {
|
if let Some(row_id) = stale.row_id {
|
||||||
conn.execute("DELETE FROM episode_file WHERE id = ?1", params![row_id])?;
|
conn.execute("DELETE FROM episode_file WHERE id = ?1", params![row_id])?;
|
||||||
}
|
}
|
||||||
if stale.parked_at.as_os_str().len() > 0 {
|
if !stale.parked_at.as_os_str().is_empty() {
|
||||||
std::fs::remove_file(&stale.parked_at).ok();
|
std::fs::remove_file(&stale.parked_at).ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -633,10 +633,11 @@ async fn background_loop(
|
||||||
// repaired (a rename or an in-place transcode) gets re-probed
|
// repaired (a rename or an in-place transcode) gets re-probed
|
||||||
// immediately rather than waiting for its own turn a full
|
// immediately rather than waiting for its own turn a full
|
||||||
// interval later.
|
// interval later.
|
||||||
match {
|
let probe_result = {
|
||||||
let conn = conn.lock().await;
|
let conn = conn.lock().await;
|
||||||
importer::probe_library(&conn)
|
importer::probe_library(&conn)
|
||||||
} {
|
};
|
||||||
|
match probe_result {
|
||||||
Ok(report) if report.probed > 0 || report.failed > 0 => {
|
Ok(report) if report.probed > 0 || report.failed > 0 => {
|
||||||
info!(?report, "media probe sweep complete");
|
info!(?report, "media probe sweep complete");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ pub struct EpisodeInfo {
|
||||||
/// `MutexGuard` across an `.await` point — this convenience wrapper is only
|
/// `MutexGuard` across an `.await` point — this convenience wrapper is only
|
||||||
/// safe for callers with an owned, unshared `Connection` (e.g. the debug
|
/// safe for callers with an owned, unshared `Connection` (e.g. the debug
|
||||||
/// CLI commands).
|
/// CLI commands).
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub async fn add_series(
|
pub async fn add_series(
|
||||||
conn: &Connection,
|
conn: &Connection,
|
||||||
tvdb: &tvdb::TvdbClient,
|
tvdb: &tvdb::TvdbClient,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use super::{EpisodeInfo, MovieSearchResult, SeriesSearchResult};
|
use super::MovieSearchResult;
|
||||||
|
|
||||||
pub struct TmdbClient {
|
pub struct TmdbClient {
|
||||||
bearer_token: String,
|
bearer_token: String,
|
||||||
|
|
@ -22,44 +22,6 @@ impl TmdbClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn search_tv(&self, query: &str) -> Result<Vec<SeriesSearchResult>> {
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct SearchResponse {
|
|
||||||
results: Vec<TvItem>,
|
|
||||||
}
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct TvItem {
|
|
||||||
id: u64,
|
|
||||||
name: String,
|
|
||||||
first_air_date: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
let resp: SearchResponse = self
|
|
||||||
.client
|
|
||||||
.get("https://api.themoviedb.org/3/search/tv")
|
|
||||||
.bearer_auth(&self.bearer_token)
|
|
||||||
.query(&[("query", query)])
|
|
||||||
.send()
|
|
||||||
.await
|
|
||||||
.context("tmdb tv search request failed")?
|
|
||||||
.error_for_status()
|
|
||||||
.context("tmdb tv search returned an error status")?
|
|
||||||
.json()
|
|
||||||
.await
|
|
||||||
.context("tmdb tv search response was not valid JSON")?;
|
|
||||||
|
|
||||||
Ok(resp
|
|
||||||
.results
|
|
||||||
.into_iter()
|
|
||||||
.map(|item| SeriesSearchResult {
|
|
||||||
external_id: item.id.to_string(),
|
|
||||||
name: item.name,
|
|
||||||
year: year_from_date(item.first_air_date.as_deref()),
|
|
||||||
aliases: Vec::new(),
|
|
||||||
})
|
|
||||||
.collect())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn search_movie(&self, query: &str) -> Result<Vec<MovieSearchResult>> {
|
pub async fn search_movie(&self, query: &str) -> Result<Vec<MovieSearchResult>> {
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct SearchResponse {
|
struct SearchResponse {
|
||||||
|
|
@ -96,48 +58,6 @@ impl TmdbClient {
|
||||||
})
|
})
|
||||||
.collect())
|
.collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn tv_season_episodes(&self, tv_id: u64, season: u32) -> Result<Vec<EpisodeInfo>> {
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct SeasonResponse {
|
|
||||||
#[serde(default)]
|
|
||||||
episodes: Vec<EpisodeItem>,
|
|
||||||
}
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct EpisodeItem {
|
|
||||||
season_number: u32,
|
|
||||||
episode_number: u32,
|
|
||||||
name: Option<String>,
|
|
||||||
air_date: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
let resp: SeasonResponse = self
|
|
||||||
.client
|
|
||||||
.get(format!(
|
|
||||||
"https://api.themoviedb.org/3/tv/{tv_id}/season/{season}"
|
|
||||||
))
|
|
||||||
.bearer_auth(&self.bearer_token)
|
|
||||||
.send()
|
|
||||||
.await
|
|
||||||
.context("tmdb season request failed")?
|
|
||||||
.error_for_status()
|
|
||||||
.context("tmdb season returned an error status")?
|
|
||||||
.json()
|
|
||||||
.await
|
|
||||||
.context("tmdb season response was not valid JSON")?;
|
|
||||||
|
|
||||||
Ok(resp
|
|
||||||
.episodes
|
|
||||||
.into_iter()
|
|
||||||
.map(|e| EpisodeInfo {
|
|
||||||
season_number: e.season_number,
|
|
||||||
episode_number: e.episode_number,
|
|
||||||
absolute_number: None,
|
|
||||||
title: e.name,
|
|
||||||
air_date: e.air_date,
|
|
||||||
})
|
|
||||||
.collect())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn year_from_date(date: Option<&str>) -> Option<u32> {
|
fn year_from_date(date: Option<&str>) -> Option<u32> {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
use anyhow::Result;
|
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,9 @@ pub struct TorrentInfo {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub state: String,
|
pub state: String,
|
||||||
pub progress: f64,
|
pub progress: f64,
|
||||||
|
// Not read internally today, but kept to mirror qBittorrent's actual API
|
||||||
|
// shape 1:1 — useful in `{:?}` debug logging and cheap to keep in sync.
|
||||||
|
#[allow(dead_code)]
|
||||||
pub save_path: String,
|
pub save_path: String,
|
||||||
/// Full path to the torrent's content (file or directory root) —
|
/// Full path to the torrent's content (file or directory root) —
|
||||||
/// qBittorrent resolves this for us, so importers don't need to guess
|
/// qBittorrent resolves this for us, so importers don't need to guess
|
||||||
|
|
|
||||||
|
|
@ -752,6 +752,10 @@ async fn grab_and_capture_hash(
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct GrabCycleStats {
|
pub struct GrabCycleStats {
|
||||||
|
// Only read via the derived `Debug` (the debug-grab-cycle CLI command
|
||||||
|
// prints the whole struct), which clippy's dead-code analysis doesn't
|
||||||
|
// credit as a read.
|
||||||
|
#[allow(dead_code)]
|
||||||
pub items_seen: usize,
|
pub items_seen: usize,
|
||||||
pub new_items: usize,
|
pub new_items: usize,
|
||||||
pub grabbed: usize,
|
pub grabbed: usize,
|
||||||
|
|
@ -851,6 +855,7 @@ enum SearchRoute {
|
||||||
/// (not just "no relevant results") — the mirror-rotation/cooldown
|
/// (not just "no relevant results") — the mirror-rotation/cooldown
|
||||||
/// machinery already built for it is real resilience worth keeping,
|
/// machinery already built for it is real resilience worth keeping,
|
||||||
/// just no longer the first choice given TPB's better precision.
|
/// just no longer the first choice given TPB's better precision.
|
||||||
|
#[allow(dead_code)]
|
||||||
X1337,
|
X1337,
|
||||||
NyaaSearch,
|
NyaaSearch,
|
||||||
}
|
}
|
||||||
|
|
@ -1658,16 +1663,16 @@ pub async fn execute_search_targets(
|
||||||
// with, since dedup (`is_seen`/`mark_seen`) and grab records
|
// with, since dedup (`is_seen`/`mark_seen`) and grab records
|
||||||
// are keyed by source id — attributing a 1337x-sourced guid to
|
// are keyed by source id — attributing a 1337x-sourced guid to
|
||||||
// TPB's source id would silently break dedup between the two.
|
// TPB's source id would silently break dedup between the two.
|
||||||
let (fetch_result, result_source_id) =
|
let (fetch_result, result_source_id) = match primary_result {
|
||||||
if primary_result.is_err() && matches!(target.route, SearchRoute::Tpb) {
|
Err(e) if matches!(target.route, SearchRoute::Tpb) => {
|
||||||
tracing::warn!(
|
tracing::warn!(
|
||||||
query = %target.query,
|
query = %target.query,
|
||||||
error = %primary_result.as_ref().unwrap_err(),
|
error = %e,
|
||||||
"TPB search failed, falling back to 1337x"
|
"TPB search failed, falling back to 1337x"
|
||||||
);
|
);
|
||||||
(scrape.fetch(Some(&target.query)).await, scrape_source_id)
|
(scrape.fetch(Some(&target.query)).await, scrape_source_id)
|
||||||
} else {
|
}
|
||||||
(primary_result, primary_id)
|
other => (other, primary_id),
|
||||||
};
|
};
|
||||||
|
|
||||||
match fetch_result {
|
match fetch_result {
|
||||||
|
|
@ -2821,7 +2826,7 @@ mod tests {
|
||||||
seeders: Some(500),
|
seeders: Some(500),
|
||||||
leechers: None,
|
leechers: None,
|
||||||
};
|
};
|
||||||
let mut items = vec![episode.clone(), pack.clone()];
|
let mut items = [episode.clone(), pack.clone()];
|
||||||
items.sort_by_key(release_sort_key);
|
items.sort_by_key(release_sort_key);
|
||||||
assert_eq!(items[0].guid, "pack");
|
assert_eq!(items[0].guid, "pack");
|
||||||
assert_eq!(items[1].guid, "episode");
|
assert_eq!(items[1].guid, "episode");
|
||||||
|
|
@ -2845,7 +2850,7 @@ mod tests {
|
||||||
seeders: Some(50),
|
seeders: Some(50),
|
||||||
leechers: None,
|
leechers: None,
|
||||||
};
|
};
|
||||||
let mut items = vec![low.clone(), high.clone()];
|
let mut items = [low.clone(), high.clone()];
|
||||||
items.sort_by_key(release_sort_key);
|
items.sort_by_key(release_sort_key);
|
||||||
assert_eq!(items[0].guid, "high");
|
assert_eq!(items[0].guid, "high");
|
||||||
assert_eq!(items[1].guid, "low");
|
assert_eq!(items[1].guid, "low");
|
||||||
|
|
|
||||||
|
|
@ -337,6 +337,7 @@ enum EncodeOutcome {
|
||||||
/// original on most of a real backfill — regardless of how well-tuned the
|
/// original on most of a real backfill — regardless of how well-tuned the
|
||||||
/// rate control is, this is what makes "never keep a non-improvement" true
|
/// rate control is, this is what makes "never keep a non-improvement" true
|
||||||
/// unconditionally.
|
/// unconditionally.
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn encode_and_verify(
|
fn encode_and_verify(
|
||||||
input: PathBuf,
|
input: PathBuf,
|
||||||
job_id: i64,
|
job_id: i64,
|
||||||
|
|
@ -726,6 +727,10 @@ pub fn find_oversized_av1_candidates(conn: &Connection, cfg: &TranscodeConfig) -
|
||||||
|
|
||||||
pub struct BacklogCandidate {
|
pub struct BacklogCandidate {
|
||||||
pub episode_file_id: i64,
|
pub episode_file_id: i64,
|
||||||
|
// Not read by current callers (they only need episode_file_id to look up
|
||||||
|
// the row again), but cheap to carry along for future logging/debugging
|
||||||
|
// of which file a candidate refers to.
|
||||||
|
#[allow(dead_code)]
|
||||||
pub path: String,
|
pub path: String,
|
||||||
pub size_bytes: i64,
|
pub size_bytes: i64,
|
||||||
pub video_codec: Option<String>,
|
pub video_codec: Option<String>,
|
||||||
|
|
|
||||||
1
ci/bread-ecosystem.rev
Normal file
1
ci/bread-ecosystem.rev
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
147cfbbf96ae4b171027defa1130d2caddb934b1
|
||||||
21
ci/build.sh
Executable file
21
ci/build.sh
Executable file
|
|
@ -0,0 +1,21 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Delegates to bread-ecosystem's shared CI build image/script, pinned to
|
||||||
|
# the commit in ci/bread-ecosystem.rev — not `main`. bread-ecosystem's CI
|
||||||
|
# files now affect every product's release pipeline, so bumping the pin
|
||||||
|
# is a deliberate act instead of silent drift (see the bread-theme test
|
||||||
|
# that broke here for exactly that reason, before it was pinned by rev).
|
||||||
|
#
|
||||||
|
# Usage: ci/build.sh cargo build --release --locked
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
REV="$(cat "${ROOT}/ci/bread-ecosystem.rev")"
|
||||||
|
|
||||||
|
CACHE_DIR="/tmp/bread-ecosystem-ci-${REV}"
|
||||||
|
if [ ! -d "$CACHE_DIR" ]; then
|
||||||
|
rm -rf /tmp/bread-ecosystem-ci-*
|
||||||
|
git clone https://git.breadway.dev/Breadway/bread-ecosystem.git "$CACHE_DIR"
|
||||||
|
git -C "$CACHE_DIR" checkout --quiet "$REV"
|
||||||
|
fi
|
||||||
|
|
||||||
|
bash "${CACHE_DIR}/ci/build.sh" breadarr "$ROOT" "$@"
|
||||||
|
|
@ -50,8 +50,13 @@ api_key = ""
|
||||||
bearer_token = ""
|
bearer_token = ""
|
||||||
|
|
||||||
[library]
|
[library]
|
||||||
# Default root folder for shows added via the TUI's "Add Show" flow.
|
# Default root folder for shows added via the TUI's "Add" flow. Each show
|
||||||
|
# lands in its own "{root}/{Title} ({Year})" subfolder.
|
||||||
default_root_folder = "~/breadarr-library"
|
default_root_folder = "~/breadarr-library"
|
||||||
|
# Same, but for movies added via the TUI's "Add" flow — kept separate from
|
||||||
|
# default_root_folder since movies and shows live under different category
|
||||||
|
# roots on disk.
|
||||||
|
movies_root_folder = "~/breadarr-library/Movies"
|
||||||
|
|
||||||
[sources]
|
[sources]
|
||||||
# nyaa's English-translated anime category — the daemon polls this
|
# nyaa's English-translated anime category — the daemon polls this
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,134 +0,0 @@
|
||||||
{
|
|
||||||
"nodes": [
|
|
||||||
{"id": "readme", "label": "breadarr README", "file_type": "document", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_breadarrd", "label": "breadarrd (daemon)", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_breadarr_tui", "label": "breadarr-tui (terminal client)", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_nyaa_si_rss", "label": "nyaa.si (RSS source, anime TV)", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_apibay_org", "label": "apibay.org (TPB mirror JSON API source)", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_1337x", "label": "1337x (scraped HTML source, mirrored)", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_nyaa_si_search_mode", "label": "nyaa.si search mode (anime movies)", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_request_budget", "label": "Shared per-cycle search request budget", "file_type": "rationale", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_wrong_default_audio_track", "label": "Wrong default audio track fix", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_anime_numbering", "label": "Anime absolute-episode numbering resolution", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_fuzzy_title_matching", "label": "Fuzzy title matching (ONNX embedding)", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_library_normalization", "label": "Library normalization (folder rename to Title (Year))", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_season_packs", "label": "Season pack per-episode splitting", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_media_intelligence", "label": "Media intelligence (ffprobe ground truth)", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_quality_upgrades", "label": "Post-import quality upgrade background cycle", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_manual_release_picker", "label": "Manual release picker (TUI 'c' key)", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_mkvmerge", "label": "mkvmerge (mkvtoolnix-cli)", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_ffprobe", "label": "ffprobe", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_ffmpeg", "label": "ffmpeg (incl. -xerror decode verify)", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_jellyfin", "label": "Jellyfin", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_qbittorrent", "label": "qBittorrent (WebUI)", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_tvdb", "label": "TVDB API", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_tmdb", "label": "TMDB API (v4 Read Access Token)", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_anidb", "label": "AniDB (mapping source)", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_all_minilm_l6_v2", "label": "all-MiniLM-L6-v2 (ONNX embedding model, CPU-only)", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_whisper", "label": "Whisper (external tool being reimplemented)", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_token_overlap_gate", "label": "Token-overlap sanity gate", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_review_queue", "label": "Review queue (low-confidence title matches)", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_quality_profile_weights", "label": "Hardcoded quality-scoring weights (known limitation)", "file_type": "rationale", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "breadarrd_src_scoring_profile_default_tv", "label": "QualityProfile::default_tv", "file_type": "code", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "breadarrd_src_scoring_profile_default_movie", "label": "QualityProfile::default_movie", "file_type": "code", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "breadarrd_src_matcher_mod_min_token_overlap", "label": "MIN_TOKEN_OVERLAP constant", "file_type": "code", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "breadarrd_src_matcher_mod_auto_match_confidence", "label": "AUTO_MATCH_CONFIDENCE constant", "file_type": "code", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "breadarrd_src_scoring_score_module", "label": "scoring/score.rs module", "file_type": "code", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_remux_backlog", "label": "breadarrd remux-backlog command", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_probe_library", "label": "breadarrd probe-library command", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_verify_library", "label": "breadarrd verify-library command", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_disk_reconciliation", "label": "Hourly disk-reconciliation pass", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_database_backup", "label": "Startup database backup (WAL/SHM sidecars)", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_health_endpoint", "label": "/health endpoint", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_library_health_endpoint", "label": "/library/health endpoint", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_media_file_probe_table", "label": "media_file_probe table", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_episode_file_table", "label": "episode_file table", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_torrent_fetch_table", "label": "torrent_fetch table", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_quality_profile_table", "label": "quality_profile table (weights JSON column)", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_release_table", "label": "release table (status field)", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_tdarr_hand_off", "label": "Roadmap: Tdarr hand-off", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_smart_library_auto_upgrade_daemon", "label": "Roadmap: Smart-library auto-upgrade daemon", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_tracker_release_group_reliability_scoring", "label": "Roadmap: Tracker/release-group reliability scoring", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_mining_raw_ffprobe_json", "label": "Roadmap: Mining the raw ffprobe JSON", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_learning_from_review_queue_decisions", "label": "Roadmap: Learning from review-queue decisions", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_seerr_overseerr_compat_shim", "label": "Roadmap: Seerr/Overseerr compatibility shim + request fulfillment", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_whisper_subtitle_generation", "label": "Roadmap: Whisper subtitle generation", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_compat_module", "label": "compat/ module (reserved, Sonarr/Radarr v3 API shim)", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_no_indexer_plugin_system_no_web_ui", "label": "Design rejection: no indexer-plugin system, no web UI", "file_type": "rationale", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_gotify_webhook", "label": "Gotify-shaped notifications.webhook_url", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null},
|
|
||||||
{"id": "readme_systemd_user_service", "label": "systemd --user service deployment", "file_type": "concept", "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "source_url": null, "captured_at": null, "author": null, "contributor": null}
|
|
||||||
],
|
|
||||||
"edges": [
|
|
||||||
{"source": "readme_breadarrd", "target": "readme_wrong_default_audio_track", "relation": "implements", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_breadarrd", "target": "readme_anime_numbering", "relation": "implements", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_breadarrd", "target": "readme_fuzzy_title_matching", "relation": "implements", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_breadarrd", "target": "readme_library_normalization", "relation": "implements", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_breadarrd", "target": "readme_season_packs", "relation": "implements", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_breadarrd", "target": "readme_media_intelligence", "relation": "implements", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_breadarrd", "target": "readme_quality_upgrades", "relation": "implements", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_breadarrd", "target": "readme_manual_release_picker", "relation": "implements", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_breadarrd", "target": "readme_disk_reconciliation", "relation": "implements", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_breadarrd", "target": "readme_database_backup", "relation": "implements", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_breadarrd", "target": "readme_health_endpoint", "relation": "implements", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_breadarrd", "target": "readme_library_health_endpoint", "relation": "implements", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_breadarrd", "target": "readme_jellyfin", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_breadarrd", "target": "readme_qbittorrent", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_breadarrd", "target": "readme_tvdb", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_breadarrd", "target": "readme_tmdb", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_breadarrd", "target": "readme_systemd_user_service", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_breadarrd", "target": "readme_gotify_webhook", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_breadarr_tui", "target": "readme_breadarrd", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_breadarr_tui", "target": "readme_manual_release_picker", "relation": "implements", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_breadarr_tui", "target": "readme_review_queue", "relation": "implements", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_breadarr_tui", "target": "readme_library_health_endpoint", "relation": "implements", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_wrong_default_audio_track", "target": "readme_mkvmerge", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_remux_backlog", "target": "readme_wrong_default_audio_track", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_anime_numbering", "target": "readme_anidb", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_anime_numbering", "target": "readme_tvdb", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_anime_numbering", "target": "readme_tmdb", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_fuzzy_title_matching", "target": "readme_all_minilm_l6_v2", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_fuzzy_title_matching", "target": "readme_token_overlap_gate", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_token_overlap_gate", "target": "breadarrd_src_matcher_mod_min_token_overlap", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_fuzzy_title_matching", "target": "readme_review_queue", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_library_normalization", "target": "readme_tvdb", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_library_normalization", "target": "readme_tmdb", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_media_intelligence", "target": "readme_ffprobe", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_quality_upgrades", "target": "readme_media_file_probe_table", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_quality_upgrades", "target": "readme_request_budget", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_apibay_org", "target": "readme_request_budget", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_1337x", "target": "readme_request_budget", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_nyaa_si_search_mode", "target": "readme_request_budget", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_quality_profile_weights", "target": "breadarrd_src_scoring_profile_default_tv", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_quality_profile_weights", "target": "breadarrd_src_scoring_profile_default_movie", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_quality_profile_weights", "target": "readme_quality_profile_table", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_learning_from_review_queue_decisions", "target": "breadarrd_src_matcher_mod_auto_match_confidence", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_learning_from_review_queue_decisions", "target": "breadarrd_src_matcher_mod_min_token_overlap", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_learning_from_review_queue_decisions", "target": "readme_review_queue", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_tdarr_hand_off", "target": "readme_media_file_probe_table", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_tracker_release_group_reliability_scoring", "target": "readme_torrent_fetch_table", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_tracker_release_group_reliability_scoring", "target": "readme_release_table", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_tracker_release_group_reliability_scoring", "target": "breadarrd_src_scoring_score_module", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_mining_raw_ffprobe_json", "target": "readme_media_file_probe_table", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_seerr_overseerr_compat_shim", "target": "readme_compat_module", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_whisper_subtitle_generation", "target": "readme_episode_file_table", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_whisper_subtitle_generation", "target": "readme_mkvmerge", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_whisper_subtitle_generation", "target": "readme_whisper", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_no_indexer_plugin_system_no_web_ui", "target": "readme_breadarrd", "relation": "rationale_for", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_no_indexer_plugin_system_no_web_ui", "target": "readme_breadarr_tui", "relation": "rationale_for", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_smart_library_auto_upgrade_daemon", "target": "readme_quality_upgrades", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_smart_library_auto_upgrade_daemon", "target": "readme_library_health_endpoint", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_smart_library_auto_upgrade_daemon", "target": "readme_gotify_webhook", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_verify_library", "target": "readme_media_intelligence", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_probe_library", "target": "readme_media_intelligence", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_disk_reconciliation", "target": "readme_episode_file_table", "relation": "references", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_disk_reconciliation", "target": "readme_database_backup", "relation": "semantically_similar_to", "confidence": "INFERRED", "confidence_score": 0.65, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0},
|
|
||||||
{"source": "readme_quality_upgrades", "target": "readme_remux_backlog", "relation": "semantically_similar_to", "confidence": "INFERRED", "confidence_score": 0.75, "source_file": "/home/breadway/Projects/breadarr/README.md", "source_location": null, "weight": 1.0}
|
|
||||||
],
|
|
||||||
"hyperedges": [
|
|
||||||
{"id": "search_budget_sources", "label": "Search-driven sources sharing per-cycle request budget", "nodes": ["readme_apibay_org", "readme_1337x", "readme_nyaa_si_search_mode", "readme_request_budget"], "relation": "participate_in", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md"},
|
|
||||||
{"id": "medium_term_roadmap_group", "label": "Medium-term roadmap items closing the loop on already-collected data", "nodes": ["readme_tdarr_hand_off", "readme_smart_library_auto_upgrade_daemon", "readme_tracker_release_group_reliability_scoring", "readme_mining_raw_ffprobe_json"], "relation": "participate_in", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md"},
|
|
||||||
{"id": "core_problems_solved_directly", "label": "Problems breadarr solves directly instead of configuring around", "nodes": ["readme_wrong_default_audio_track", "readme_anime_numbering", "readme_fuzzy_title_matching", "readme_library_normalization", "readme_season_packs", "readme_media_intelligence", "readme_quality_upgrades", "readme_manual_release_picker"], "relation": "implement", "confidence": "EXTRACTED", "confidence_score": 1.0, "source_file": "/home/breadway/Projects/breadarr/README.md"}
|
|
||||||
],
|
|
||||||
"input_tokens": 0,
|
|
||||||
"output_tokens": 0
|
|
||||||
}
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
{"files": {"code": ["/home/breadway/Projects/breadarr/breadarr-shared/src/client.rs", "/home/breadway/Projects/breadarr/breadarr-shared/src/config.rs", "/home/breadway/Projects/breadarr/breadarr-shared/src/dto.rs", "/home/breadway/Projects/breadarr/breadarr-shared/src/lib.rs", "/home/breadway/Projects/breadarr/breadarr-tui/src/app.rs", "/home/breadway/Projects/breadarr/breadarr-tui/src/main.rs", "/home/breadway/Projects/breadarr/breadarr-tui/src/ui.rs", "/home/breadway/Projects/breadarr/breadarrd/src/api/mod.rs", "/home/breadway/Projects/breadarr/breadarrd/src/api/routes/calendar.rs", "/home/breadway/Projects/breadarr/breadarrd/src/api/routes/health.rs", "/home/breadway/Projects/breadarr/breadarrd/src/api/routes/library_health.rs", "/home/breadway/Projects/breadarr/breadarrd/src/api/routes/media.rs", "/home/breadway/Projects/breadarr/breadarrd/src/api/routes/mod.rs", "/home/breadway/Projects/breadarr/breadarrd/src/api/routes/quality_profiles.rs", "/home/breadway/Projects/breadarr/breadarrd/src/api/routes/releases.rs", "/home/breadway/Projects/breadarr/breadarrd/src/api/routes/review.rs", "/home/breadway/Projects/breadarr/breadarrd/src/api/routes/search.rs", "/home/breadway/Projects/breadarr/breadarrd/src/api/routes/stuck.rs", "/home/breadway/Projects/breadarr/breadarrd/src/db.rs", "/home/breadway/Projects/breadarr/breadarrd/src/importer/ffprobe.rs", "/home/breadway/Projects/breadarr/breadarrd/src/importer/mkv.rs", "/home/breadway/Projects/breadarr/breadarrd/src/importer/mod.rs", "/home/breadway/Projects/breadarr/breadarrd/src/jellyfin.rs", "/home/breadway/Projects/breadarr/breadarrd/src/library_scan.rs", "/home/breadway/Projects/breadarr/breadarrd/src/main.rs", "/home/breadway/Projects/breadarr/breadarrd/src/matcher/embed.rs", "/home/breadway/Projects/breadarr/breadarrd/src/matcher/mod.rs", "/home/breadway/Projects/breadarr/breadarrd/src/metadata/anime_map.rs", "/home/breadway/Projects/breadarr/breadarrd/src/metadata/mod.rs", "/home/breadway/Projects/breadarr/breadarrd/src/metadata/tmdb.rs", "/home/breadway/Projects/breadarr/breadarrd/src/metadata/tvdb.rs", "/home/breadway/Projects/breadarr/breadarrd/src/notify.rs", "/home/breadway/Projects/breadarr/breadarrd/src/parser/mod.rs", "/home/breadway/Projects/breadarr/breadarrd/src/parser/tokens.rs", "/home/breadway/Projects/breadarr/breadarrd/src/qbit/mod.rs", "/home/breadway/Projects/breadarr/breadarrd/src/scheduler.rs", "/home/breadway/Projects/breadarr/breadarrd/src/scoring/gate.rs", "/home/breadway/Projects/breadarr/breadarrd/src/scoring/mod.rs", "/home/breadway/Projects/breadarr/breadarrd/src/scoring/profile.rs", "/home/breadway/Projects/breadarr/breadarrd/src/scoring/score.rs", "/home/breadway/Projects/breadarr/breadarrd/src/sources/mod.rs", "/home/breadway/Projects/breadarr/breadarrd/src/sources/rss.rs", "/home/breadway/Projects/breadarr/breadarrd/src/sources/scrape.rs", "/home/breadway/Projects/breadarr/breadarrd/src/sources/tpb.rs", "/home/breadway/Projects/breadarr/breadarrd/src/transcode/mod.rs"], "document": ["/home/breadway/Projects/breadarr/README.md"], "paper": [], "image": [], "video": []}, "total_files": 46, "total_words": 84649, "needs_graph": true, "warning": null, "skipped_sensitive": [], "unclassified": ["/home/breadway/Projects/breadarr/.gitignore", "/home/breadway/Projects/breadarr/Cargo.toml", "/home/breadway/Projects/breadarr/LICENSE", "/home/breadway/Projects/breadarr/breadarr-shared/Cargo.toml", "/home/breadway/Projects/breadarr/breadarr-tui/Cargo.toml", "/home/breadway/Projects/breadarr/breadarrd/Cargo.toml", "/home/breadway/Projects/breadarr/breadarrd/src/src.tar.xz", "/home/breadway/Projects/breadarr/config.example.toml", "/home/breadway/Projects/breadarr/packaging/systemd/breadarrd.service"], "walk_errors": [], "ignored": ["/home/breadway/Projects/breadarr/.claude/scheduled_tasks.lock", "/home/breadway/Projects/breadarr/CLAUDE.md"], "pruned_noise_dirs": ["/home/breadway/Projects/breadarr/.git/", "/home/breadway/Projects/breadarr/.idea/", "/home/breadway/Projects/breadarr/graphify-out/", "/home/breadway/Projects/breadarr/target/"], "graphifyignore_patterns": 16, "scan_root": "/home/breadway/Projects/breadarr"}
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
{
|
|
||||||
"0": "scheduler.rs",
|
|
||||||
"1": "api/mod.rs",
|
|
||||||
"2": "Connection",
|
|
||||||
"3": "fetch_candidates",
|
|
||||||
"4": "enumerate_upgrade_targets",
|
|
||||||
"5": "rss.rs",
|
|
||||||
"6": "TitleMatcher",
|
|
||||||
"7": "parser/mod.rs",
|
|
||||||
"8": "config.rs",
|
|
||||||
"9": "transcode/mod.rs",
|
|
||||||
"10": "ffprobe.rs",
|
|
||||||
"11": "importer/mod.rs",
|
|
||||||
"12": "Result",
|
|
||||||
"13": "process_pending_grabs",
|
|
||||||
"14": "Connection",
|
|
||||||
"15": "import_one",
|
|
||||||
"16": "find_relinkable_episode_files",
|
|
||||||
"17": "main.rs",
|
|
||||||
"18": "import_season_pack",
|
|
||||||
"19": "QbitClient",
|
|
||||||
"20": "db.rs",
|
|
||||||
"21": "String",
|
|
||||||
"22": "enumerate_search_targets",
|
|
||||||
"23": "seeded_conn"
|
|
||||||
}
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
{"0": "a20711b8a258bdba", "1": "00eec605035731e0", "2": "fe27e60c0c69f644", "3": "aab173d1c84b4fa5", "4": "fcbc321c7cc48bd5", "5": "f2c1c699c8bbb3f1", "6": "edcd439ab7a6e602", "7": "045ecbe6e4c68626", "8": "1bc76ecd7d47170b", "9": "41ae8ae078021091", "10": "b5579e7d3cb56163", "11": "6e96670b2e6365c8", "12": "86a8b42c2c2db791", "13": "afab72e500fc3a5e", "14": "d44144490006b82e", "15": "03a7fd4f5097c30c", "16": "742257b51006ffce", "17": "c4a1f2a2c670d77d", "18": "6e74d48dd61b8d86", "19": "4fe857e1f7ed4db3", "20": "416c05e55e9af1a8", "21": "61f849337b416151", "22": "fe71e07eb838ebc3", "23": "ef859e5fff310b02"}
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
/home/breadway/.cache/uv/archive-v0/4yQjntA8tzDKxQRL/bin/python
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
/home/breadway/Projects/breadarr
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
/home/breadway/Projects/breadarr/README.md
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
{
|
|
||||||
"0": "scheduler.rs",
|
|
||||||
"1": "api/mod.rs",
|
|
||||||
"2": "Connection",
|
|
||||||
"3": "execute_search_targets",
|
|
||||||
"4": "enumerate_search_targets",
|
|
||||||
"5": "rss.rs",
|
|
||||||
"6": "matcher/mod.rs",
|
|
||||||
"7": "parser/mod.rs",
|
|
||||||
"8": "config.rs",
|
|
||||||
"9": "transcode/mod.rs",
|
|
||||||
"10": "ffprobe.rs",
|
|
||||||
"11": "importer/mod.rs",
|
|
||||||
"12": "Result",
|
|
||||||
"13": "process_pending_grabs",
|
|
||||||
"14": "Connection",
|
|
||||||
"15": "import_one",
|
|
||||||
"16": "find_relinkable_episode_files",
|
|
||||||
"17": "fetch_candidates",
|
|
||||||
"18": "import_season_pack"
|
|
||||||
}
|
|
||||||
|
|
@ -1,159 +0,0 @@
|
||||||
# Graph Report - breadarr (2026-08-03)
|
|
||||||
|
|
||||||
## Corpus Check
|
|
||||||
- 46 files · ~90,608 words
|
|
||||||
- Verdict: corpus is large enough that graph structure adds value.
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
- 605 nodes · 1557 edges · 19 communities
|
|
||||||
- Extraction: 100% EXTRACTED · 0% INFERRED · 0% AMBIGUOUS · INFERRED: 2 edges (avg confidence: 0.8)
|
|
||||||
- Token cost: 0 input · 0 output
|
|
||||||
|
|
||||||
## Graph Freshness
|
|
||||||
- Built from commit: `109b29ee`
|
|
||||||
- Run `git rev-parse HEAD` and compare to check if the graph is stale.
|
|
||||||
- Run `graphify update .` after code changes (no API cost).
|
|
||||||
|
|
||||||
## Community Hubs (Navigation)
|
|
||||||
- scheduler.rs
|
|
||||||
- api/mod.rs
|
|
||||||
- Connection
|
|
||||||
- execute_search_targets
|
|
||||||
- enumerate_search_targets
|
|
||||||
- rss.rs
|
|
||||||
- matcher/mod.rs
|
|
||||||
- parser/mod.rs
|
|
||||||
- config.rs
|
|
||||||
- transcode/mod.rs
|
|
||||||
- ffprobe.rs
|
|
||||||
- importer/mod.rs
|
|
||||||
- Result
|
|
||||||
- process_pending_grabs
|
|
||||||
- Connection
|
|
||||||
- import_one
|
|
||||||
- find_relinkable_episode_files
|
|
||||||
- fetch_candidates
|
|
||||||
- import_season_pack
|
|
||||||
|
|
||||||
## God Nodes (most connected - your core abstractions)
|
|
||||||
1. `import_one()` - 30 edges
|
|
||||||
2. `process_item()` - 30 edges
|
|
||||||
3. `TranscodeConfig` - 24 edges
|
|
||||||
4. `process_pending_grabs()` - 24 edges
|
|
||||||
5. `parse()` - 22 edges
|
|
||||||
6. `AppState` - 19 edges
|
|
||||||
7. `encode_and_verify()` - 18 edges
|
|
||||||
8. `fetch_candidates()` - 18 edges
|
|
||||||
9. `import_season_pack_file()` - 17 edges
|
|
||||||
10. `execute_search_targets()` - 17 edges
|
|
||||||
|
|
||||||
## Surprising Connections (you probably didn't know these)
|
|
||||||
- `import_one()` --references--> `TranscodeConfig` [EXTRACTED]
|
|
||||||
breadarrd/src/importer/mod.rs → breadarr-shared/src/config.rs
|
|
||||||
- `import_season_pack()` --references--> `TranscodeConfig` [EXTRACTED]
|
|
||||||
breadarrd/src/importer/mod.rs → breadarr-shared/src/config.rs
|
|
||||||
- `import_season_pack_file()` --references--> `TranscodeConfig` [EXTRACTED]
|
|
||||||
breadarrd/src/importer/mod.rs → breadarr-shared/src/config.rs
|
|
||||||
- `maybe_enqueue_transcode()` --references--> `TranscodeConfig` [EXTRACTED]
|
|
||||||
breadarrd/src/importer/mod.rs → breadarr-shared/src/config.rs
|
|
||||||
- `process_pending_grabs()` --references--> `TranscodeConfig` [EXTRACTED]
|
|
||||||
breadarrd/src/importer/mod.rs → breadarr-shared/src/config.rs
|
|
||||||
|
|
||||||
## Import Cycles
|
|
||||||
- None detected.
|
|
||||||
|
|
||||||
## Communities (19 total, 0 thin omitted)
|
|
||||||
|
|
||||||
### Community 0 - "scheduler.rs"
|
|
||||||
Cohesion: 0.07
|
|
||||||
Nodes (24): a_second_prepare_call_on_an_already_claimed_review_sees_not_pending(), count_monitored_missing_episodes_in_season_counts_correctly(), does_not_find_an_unmonitored_or_already_have_episode(), find_episode_id_ignores_monitored_and_has_file_state(), finds_a_monitored_missing_episode(), insert_pending_review(), movie_does_not_need_grab_once_it_has_a_file(), movie_does_not_need_grab_when_unmonitored() (+16 more)
|
|
||||||
|
|
||||||
### Community 1 - "api/mod.rs"
|
|
||||||
Cohesion: 0.09
|
|
||||||
Nodes (37): AppState, BackgroundRequest, constant_time_eq(), CycleRecord, CycleStatus, require_api_token(), router(), Connection (+29 more)
|
|
||||||
|
|
||||||
### Community 2 - "Connection"
|
|
||||||
Cohesion: 0.16
|
|
||||||
Nodes (37): ApprovalPrep, best_existing_movie_score(), best_existing_score(), best_existing_season_pack_score(), count_monitored_missing_episodes_in_season(), finalize_review_approval(), find_episode_id(), find_media_item_id_by_title() (+29 more)
|
|
||||||
|
|
||||||
### Community 3 - "execute_search_targets"
|
|
||||||
Cohesion: 0.24
|
|
||||||
Nodes (17): TitleMatcher, execute_search_targets(), GrabCycleStats, is_seen(), mark_seen(), QbitClient, run_grab_cycle(), run_search_cycle() (+9 more)
|
|
||||||
|
|
||||||
### Community 4 - "enumerate_search_targets"
|
|
||||||
Cohesion: 0.13
|
|
||||||
Nodes (30): build_movie_query(), build_tv_query(), cadence_stays_backed_off_at_a_high_search_count_instead_of_overflowing(), enumerate_search_targets(), enumerate_search_targets_excludes_owned_movies_includes_missing(), enumerate_search_targets_excludes_unaired_inflight_and_anime(), enumerate_search_targets_for_media_item(), enumerate_search_targets_for_media_item_routes_anime_movie_via_tmdb_table() (+22 more)
|
|
||||||
|
|
||||||
### Community 5 - "rss.rs"
|
|
||||||
Cohesion: 0.07
|
|
||||||
Nodes (29): parse_human_size(), RawReleaseItem, Option, String, urlencode(), accumulate_field(), build_search_url(), ItemFields (+21 more)
|
|
||||||
|
|
||||||
### Community 6 - "matcher/mod.rs"
|
|
||||||
Cohesion: 0.14
|
|
||||||
Nodes (17): download(), ensure_model(), MatchCandidate, MatchOutcome, queue_for_review(), Connection, Option, Path (+9 more)
|
|
||||||
|
|
||||||
### Community 7 - "parser/mod.rs"
|
|
||||||
Cohesion: 0.08
|
|
||||||
Nodes (42): a_genuine_season_only_pack_with_no_dash_episode_still_has_no_episode(), brackets_still_take_priority_over_a_coincidental_bare_year(), Codec, does_not_mistake_a_year_titled_movie_for_a_bare_year(), does_not_mistake_a_yyyy_mm_dd_date_for_an_episode_range(), does_not_panic_on_real_corpus(), does_not_panic_on_unparsable_manga_release(), extracts_a_bare_year_from_a_scene_style_movie_name() (+34 more)
|
|
||||||
|
|
||||||
### Community 8 - "config.rs"
|
|
||||||
Cohesion: 0.06
|
|
||||||
Nodes (56): Config, config_path(), DaemonConfig, default_1337x_mirrors(), default_anime_svtav1_max_threads(), default_anime_svtav1_preset(), default_av1_efficiency_factor(), default_config_has_expected_values() (+48 more)
|
|
||||||
|
|
||||||
### Community 9 - "transcode/mod.rs"
|
|
||||||
Cohesion: 0.09
|
|
||||||
Nodes (63): Arc, TranscodeConfig, BacklogCandidate, cfg(), claim_pending_jobs(), claim_pending_jobs_claims_nothing_when_already_at_the_cap(), claim_pending_jobs_enforces_live_action_and_anime_caps_independently(), claim_pending_jobs_respects_already_running_jobs_as_a_global_cap() (+55 more)
|
|
||||||
|
|
||||||
### Community 10 - "ffprobe.rs"
|
|
||||||
Cohesion: 0.12
|
|
||||||
Nodes (27): AudioStream, build_media_probe(), DecodeCheck, generate_clip(), generate_test_clip(), is_english(), MediaProbe, parse_frame_rate_fraction() (+19 more)
|
|
||||||
|
|
||||||
### Community 11 - "importer/mod.rs"
|
|
||||||
Cohesion: 0.10
|
|
||||||
Nodes (25): a_fresh_grab_is_not_stalled(), a_grab_untouched_past_the_threshold_is_stalled(), a_torrent_missing_past_the_grace_period_is_failed(), a_torrent_missing_within_the_grace_period_is_not_yet_failed(), fail_grab_reopens_the_release_for_search(), find_relinkable_episode_files_falls_back_to_the_unpadded_season_folder(), find_relinkable_episode_files_skips_an_ambiguous_match_rather_than_guessing(), media_item_with_root() (+17 more)
|
|
||||||
|
|
||||||
### Community 12 - "Result"
|
|
||||||
Cohesion: 0.15
|
|
||||||
Nodes (24): copy_via_temp_file(), copy_via_temp_file_writes_through_a_part_file_and_renames_into_place(), find_by_basename(), find_by_stem(), import_season_pack_file(), insufficient_space(), largest_video_file(), locate_video_file() (+16 more)
|
|
||||||
|
|
||||||
### Community 13 - "process_pending_grabs"
|
|
||||||
Cohesion: 0.20
|
|
||||||
Nodes (13): a_persistently_failing_import_escalates_to_failed_instead_of_retrying_forever(), does_not_import_a_torrent_while_it_is_still_being_physically_moved(), fail_grab(), fetch_pending_grabs(), ImportStats, PendingGrab, process_pending_grabs(), process_pending_grabs_routes_each_grab_by_torrent_state() (+5 more)
|
|
||||||
|
|
||||||
### Community 14 - "Connection"
|
|
||||||
Cohesion: 0.17
|
|
||||||
Nodes (16): ensure_probed(), ensure_probed_does_not_flag_a_wide_aspect_ratio_file_as_under_quality(), generate_dual_audio_clip(), grab_is_stalled(), grab_missing_past_grace(), probe_library(), probe_library_reports_probed_vs_skipped_up_to_date(), ProbeSweepReport (+8 more)
|
|
||||||
|
|
||||||
### Community 15 - "import_one"
|
|
||||||
Cohesion: 0.16
|
|
||||||
Nodes (14): a_drifted_root_folder_does_not_defeat_the_dest_collision_check(), a_strictly_better_release_replaces_the_existing_file_via_a_real_move(), an_upgrade_swap_sweeps_every_duplicate_episode_file_row_not_just_one(), ensure_probed_flags_a_low_resolution_file_as_under_quality(), ensure_probed_skips_reprobing_an_unchanged_file(), generate_test_clip(), import_one(), import_one_enqueues_a_transcode_job_when_transcode_is_enabled() (+6 more)
|
|
||||||
|
|
||||||
### Community 16 - "find_relinkable_episode_files"
|
|
||||||
Cohesion: 0.20
|
|
||||||
Nodes (13): collect_video_files(), deterministic_filename(), deterministic_movie_filename(), find_relinkable_episode_files(), find_relinkable_episode_files_finds_a_file_with_no_tracked_row(), has_cjk(), ProbeFields, relink_episode_files() (+5 more)
|
|
||||||
|
|
||||||
### Community 17 - "fetch_candidates"
|
|
||||||
Cohesion: 0.17
|
|
||||||
Nodes (12): fetch_candidates(), infer_has_english_audio(), is_anime(), load_quality_profile(), load_quality_profile_applies_a_stored_override(), load_quality_profile_uses_defaults_for_the_seeded_empty_weights_row(), passes_relevance_filter(), release_sort_key() (+4 more)
|
|
||||||
|
|
||||||
### Community 18 - "import_season_pack"
|
|
||||||
Cohesion: 0.43
|
|
||||||
Nodes (7): import_season_pack(), import_season_pack_fails_when_nothing_in_it_matches_a_tracked_episode(), import_season_pack_imports_every_file_and_marks_episodes_owned(), import_season_pack_skips_an_upgrade_locked_episode_even_with_a_lower_scoring_existing_release(), import_season_pack_skips_episodes_that_already_have_a_better_file(), SeasonPackImportOutcome, seeded_season_pack_conn()
|
|
||||||
|
|
||||||
## Suggested Questions
|
|
||||||
_Questions this graph is uniquely positioned to answer:_
|
|
||||||
|
|
||||||
- **Why does `TranscodeConfig` connect `transcode/mod.rs` to `config.rs`, `Result`, `process_pending_grabs`, `import_one`, `import_season_pack`?**
|
|
||||||
_High betweenness centrality (0.462) - this node is a cross-community bridge._
|
|
||||||
- **Why does `SearchCycleStats` connect `execute_search_targets` to `scheduler.rs`, `api/mod.rs`?**
|
|
||||||
_High betweenness centrality (0.316) - this node is a cross-community bridge._
|
|
||||||
- **Why does `AppState` connect `api/mod.rs` to `transcode/mod.rs`?**
|
|
||||||
_High betweenness centrality (0.195) - this node is a cross-community bridge._
|
|
||||||
- **Should `scheduler.rs` be split into smaller, more focused modules?**
|
|
||||||
_Cohesion score 0.0666049953746531 - nodes in this community are weakly interconnected._
|
|
||||||
- **Should `api/mod.rs` be split into smaller, more focused modules?**
|
|
||||||
_Cohesion score 0.08527131782945736 - nodes in this community are weakly interconnected._
|
|
||||||
- **Should `enumerate_search_targets` be split into smaller, more focused modules?**
|
|
||||||
_Cohesion score 0.12643678160919541 - nodes in this community are weakly interconnected._
|
|
||||||
- **Should `rss.rs` be split into smaller, more focused modules?**
|
|
||||||
_Cohesion score 0.06570048309178744 - nodes in this community are weakly interconnected._
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,232 +0,0 @@
|
||||||
{
|
|
||||||
"breadarr-shared/src/client.rs": {
|
|
||||||
"mtime": 1784176136.6198714,
|
|
||||||
"ast_hash": "383a3e626e611317f06b28984d72ab9e",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarr-shared/src/config.rs": {
|
|
||||||
"mtime": 1785696395.8005114,
|
|
||||||
"ast_hash": "f6b6fb2eb97829e12f88821408ad1f02",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarr-shared/src/dto.rs": {
|
|
||||||
"mtime": 1784908572.450929,
|
|
||||||
"ast_hash": "aa32217f2eb0f9f0dd3cb5384063e22f",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarr-shared/src/lib.rs": {
|
|
||||||
"mtime": 1783781950.6175613,
|
|
||||||
"ast_hash": "a2ab57e66492dd8a3dd59c60f9821b27",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarr-tui/src/app.rs": {
|
|
||||||
"mtime": 1784638215.5754883,
|
|
||||||
"ast_hash": "ca1205c8be64ec95230c75db46ceac51",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarr-tui/src/main.rs": {
|
|
||||||
"mtime": 1784638316.713315,
|
|
||||||
"ast_hash": "5fd64e003fb112a15f45e26b70a0e286",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarr-tui/src/ui.rs": {
|
|
||||||
"mtime": 1784638447.1445198,
|
|
||||||
"ast_hash": "44d8a181957b8c1e644a45dff91e9a76",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/api/mod.rs": {
|
|
||||||
"mtime": 1785696056.3421187,
|
|
||||||
"ast_hash": "3868111877263996f985e7297f566569",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/api/routes/calendar.rs": {
|
|
||||||
"mtime": 1784033809.0910208,
|
|
||||||
"ast_hash": "4d04ad23ceba40405f5a015525753b5c",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/api/routes/health.rs": {
|
|
||||||
"mtime": 1784908576.1114342,
|
|
||||||
"ast_hash": "55d7e27388131a844315e941095f2127",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/api/routes/library_health.rs": {
|
|
||||||
"mtime": 1784113370.1363761,
|
|
||||||
"ast_hash": "1e2fc6fff36cbf091a539000ede57611",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/api/routes/media.rs": {
|
|
||||||
"mtime": 1784171609.152028,
|
|
||||||
"ast_hash": "c1e84c6268e624f1eae46fd70033fa04",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/api/routes/mod.rs": {
|
|
||||||
"mtime": 1784175849.5137725,
|
|
||||||
"ast_hash": "46b952bdd0b9cbb8d0418a350e814459",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/api/routes/quality_profiles.rs": {
|
|
||||||
"mtime": 1784176136.6858702,
|
|
||||||
"ast_hash": "2dfbf6122c323b7521df3087e739f62f",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/api/routes/releases.rs": {
|
|
||||||
"mtime": 1783782116.7739515,
|
|
||||||
"ast_hash": "0eb53bc15913f1445687f8de7e4040ad",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/api/routes/review.rs": {
|
|
||||||
"mtime": 1785695923.5693555,
|
|
||||||
"ast_hash": "907f8d308d2942eb3abb92482ffca187",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/api/routes/search.rs": {
|
|
||||||
"mtime": 1783841453.3332663,
|
|
||||||
"ast_hash": "7d82ca20febd26e9020642aecbab313c",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/api/routes/stuck.rs": {
|
|
||||||
"mtime": 1784638231.949136,
|
|
||||||
"ast_hash": "46ff0112d0302f18ed8ad0d2c47d9f3b",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/db.rs": {
|
|
||||||
"mtime": 1785332784.4654708,
|
|
||||||
"ast_hash": "54d92eeae67311c25b0dec690302f6c1",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/importer/ffprobe.rs": {
|
|
||||||
"mtime": 1785695262.529791,
|
|
||||||
"ast_hash": "c9749d92cd5bffb5e7633facb369f051",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/importer/mkv.rs": {
|
|
||||||
"mtime": 1784175099.8600957,
|
|
||||||
"ast_hash": "99c3e8684a7081eda3730e6ecee4d862",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/importer/mod.rs": {
|
|
||||||
"mtime": 1785695989.9557424,
|
|
||||||
"ast_hash": "3f763d44c853bcd5d0cdfba4a60783c6",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/jellyfin.rs": {
|
|
||||||
"mtime": 1784911004.2736921,
|
|
||||||
"ast_hash": "245f75ff1d6f3c86e15635537f9234a5",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/library_scan.rs": {
|
|
||||||
"mtime": 1784632454.081846,
|
|
||||||
"ast_hash": "b35f50965f0e39bacf99059c84d18006",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/main.rs": {
|
|
||||||
"mtime": 1785677152.6518264,
|
|
||||||
"ast_hash": "3e5baa3fdebcfe6172675afe575b4505",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/matcher/embed.rs": {
|
|
||||||
"mtime": 1784269828.2931612,
|
|
||||||
"ast_hash": "e0a425d2e351589b6e56af02fe8ad396",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/matcher/mod.rs": {
|
|
||||||
"mtime": 1785695690.847343,
|
|
||||||
"ast_hash": "e71d4677d9f421b7ef8b84354ac9fc4d",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/metadata/anime_map.rs": {
|
|
||||||
"mtime": 1783855515.7563374,
|
|
||||||
"ast_hash": "46b90382ecc49a1452d580f61d7806a4",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/metadata/mod.rs": {
|
|
||||||
"mtime": 1784636520.9888098,
|
|
||||||
"ast_hash": "bc4ebde5b9e89515ee87ea6a5ffd3c10",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/metadata/tmdb.rs": {
|
|
||||||
"mtime": 1783802179.6291993,
|
|
||||||
"ast_hash": "02ff58e5db664e947f486bb0e685f5b4",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/metadata/tvdb.rs": {
|
|
||||||
"mtime": 1784635726.8710334,
|
|
||||||
"ast_hash": "cd21d4943f6a05269a46d95bae368758",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/notify.rs": {
|
|
||||||
"mtime": 1784033287.1188982,
|
|
||||||
"ast_hash": "35b8279fb3d6f05892bda5401541c838",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/parser/mod.rs": {
|
|
||||||
"mtime": 1785695514.945018,
|
|
||||||
"ast_hash": "5d42da21fd9eae954c0b1d0633d7ba45",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/parser/tokens.rs": {
|
|
||||||
"mtime": 1785695636.4009888,
|
|
||||||
"ast_hash": "88b08a119a5285c9e640d2dfe59dbd85",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/qbit/mod.rs": {
|
|
||||||
"mtime": 1785662339.058403,
|
|
||||||
"ast_hash": "cd3e6c110e9cf757815d4bfb62faca8e",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/scheduler.rs": {
|
|
||||||
"mtime": 1785695949.7192466,
|
|
||||||
"ast_hash": "99233bde5f082ea7ed9b9065ff4943a0",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/scoring/gate.rs": {
|
|
||||||
"mtime": 1784117113.4110034,
|
|
||||||
"ast_hash": "f6090fd0a592380dc3ce045decf6932d",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/scoring/mod.rs": {
|
|
||||||
"mtime": 1784175656.340803,
|
|
||||||
"ast_hash": "f00b3e1527789cc6ab5ac6e384c451c0",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/scoring/profile.rs": {
|
|
||||||
"mtime": 1784175744.9204473,
|
|
||||||
"ast_hash": "5f637981034d7c4edb9e7f70aebc2e36",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/scoring/score.rs": {
|
|
||||||
"mtime": 1784015678.9287148,
|
|
||||||
"ast_hash": "6ca1852f1e611ac7046127ab2b60524e",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/sources/mod.rs": {
|
|
||||||
"mtime": 1785696200.501458,
|
|
||||||
"ast_hash": "d4efc09eab30de5fee6d25d000ca2b0c",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/sources/rss.rs": {
|
|
||||||
"mtime": 1785696625.7660108,
|
|
||||||
"ast_hash": "4f3fd2400a3cc2cf3e24900f1335e884",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/sources/scrape.rs": {
|
|
||||||
"mtime": 1784177018.756294,
|
|
||||||
"ast_hash": "df885de8d787d6c2af3d814f0831ee6e",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/sources/tpb.rs": {
|
|
||||||
"mtime": 1785696280.7010753,
|
|
||||||
"ast_hash": "8b6896a57f82268a0e1c6924054ce968",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/transcode/mod.rs": {
|
|
||||||
"mtime": 1785696564.622992,
|
|
||||||
"ast_hash": "fd89d1494a14380c2d758a7d90312a1b",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"README.md": {
|
|
||||||
"mtime": 1784175535.4875388,
|
|
||||||
"ast_hash": "62f6240c65824f55179178205fb74b90",
|
|
||||||
"semantic_hash": ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,188 +0,0 @@
|
||||||
# Graph Report - breadarr (2026-08-03)
|
|
||||||
|
|
||||||
## Corpus Check
|
|
||||||
- 46 files · ~90,608 words
|
|
||||||
- Verdict: corpus is large enough that graph structure adds value.
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
- 693 nodes · 1770 edges · 24 communities
|
|
||||||
- Extraction: 100% EXTRACTED · 0% INFERRED · 0% AMBIGUOUS · INFERRED: 2 edges (avg confidence: 0.8)
|
|
||||||
- Token cost: 0 input · 0 output
|
|
||||||
|
|
||||||
## Graph Freshness
|
|
||||||
- Built from commit: `d110556a`
|
|
||||||
- Run `git rev-parse HEAD` and compare to check if the graph is stale.
|
|
||||||
- Run `graphify update .` after code changes (no API cost).
|
|
||||||
|
|
||||||
## Community Hubs (Navigation)
|
|
||||||
- scheduler.rs
|
|
||||||
- api/mod.rs
|
|
||||||
- Connection
|
|
||||||
- fetch_candidates
|
|
||||||
- enumerate_upgrade_targets
|
|
||||||
- rss.rs
|
|
||||||
- TitleMatcher
|
|
||||||
- parser/mod.rs
|
|
||||||
- config.rs
|
|
||||||
- transcode/mod.rs
|
|
||||||
- ffprobe.rs
|
|
||||||
- importer/mod.rs
|
|
||||||
- Result
|
|
||||||
- process_pending_grabs
|
|
||||||
- Connection
|
|
||||||
- import_one
|
|
||||||
- find_relinkable_episode_files
|
|
||||||
- main.rs
|
|
||||||
- import_season_pack
|
|
||||||
- QbitClient
|
|
||||||
- db.rs
|
|
||||||
- String
|
|
||||||
- enumerate_search_targets
|
|
||||||
- seeded_conn
|
|
||||||
|
|
||||||
## God Nodes (most connected - your core abstractions)
|
|
||||||
1. `import_one()` - 30 edges
|
|
||||||
2. `process_item()` - 30 edges
|
|
||||||
3. `TranscodeConfig` - 24 edges
|
|
||||||
4. `process_pending_grabs()` - 24 edges
|
|
||||||
5. `main()` - 23 edges
|
|
||||||
6. `parse()` - 22 edges
|
|
||||||
7. `AppState` - 19 edges
|
|
||||||
8. `encode_and_verify()` - 18 edges
|
|
||||||
9. `fetch_candidates()` - 18 edges
|
|
||||||
10. `QbitClient` - 17 edges
|
|
||||||
|
|
||||||
## Surprising Connections (you probably didn't know these)
|
|
||||||
- `import_one()` --references--> `TranscodeConfig` [EXTRACTED]
|
|
||||||
breadarrd/src/importer/mod.rs → breadarr-shared/src/config.rs
|
|
||||||
- `import_season_pack()` --references--> `TranscodeConfig` [EXTRACTED]
|
|
||||||
breadarrd/src/importer/mod.rs → breadarr-shared/src/config.rs
|
|
||||||
- `import_season_pack_file()` --references--> `TranscodeConfig` [EXTRACTED]
|
|
||||||
breadarrd/src/importer/mod.rs → breadarr-shared/src/config.rs
|
|
||||||
- `maybe_enqueue_transcode()` --references--> `TranscodeConfig` [EXTRACTED]
|
|
||||||
breadarrd/src/importer/mod.rs → breadarr-shared/src/config.rs
|
|
||||||
- `process_pending_grabs()` --references--> `TranscodeConfig` [EXTRACTED]
|
|
||||||
breadarrd/src/importer/mod.rs → breadarr-shared/src/config.rs
|
|
||||||
|
|
||||||
## Import Cycles
|
|
||||||
- None detected.
|
|
||||||
|
|
||||||
## Communities (24 total, 0 thin omitted)
|
|
||||||
|
|
||||||
### Community 0 - "scheduler.rs"
|
|
||||||
Cohesion: 0.07
|
|
||||||
Nodes (21): a_second_prepare_call_on_an_already_claimed_review_sees_not_pending(), insert_pending_review(), load_quality_profile(), load_quality_profile_applies_a_stored_override(), load_quality_profile_uses_defaults_for_the_seeded_empty_weights_row(), movie_does_not_need_grab_once_it_has_a_file(), movie_does_not_need_grab_when_unmonitored(), movie_does_not_need_grab_while_a_release_is_in_flight() (+13 more)
|
|
||||||
|
|
||||||
### Community 1 - "api/mod.rs"
|
|
||||||
Cohesion: 0.09
|
|
||||||
Nodes (36): AppState, BackgroundRequest, constant_time_eq(), CycleRecord, CycleStatus, require_api_token(), router(), Connection (+28 more)
|
|
||||||
|
|
||||||
### Community 2 - "Connection"
|
|
||||||
Cohesion: 0.18
|
|
||||||
Nodes (30): best_existing_movie_score(), best_existing_score(), best_existing_season_pack_score(), count_monitored_missing_episodes_in_season(), find_episode_id(), find_media_item_id_by_title(), find_monitored_episode(), find_monitored_missing_episode() (+22 more)
|
|
||||||
|
|
||||||
### Community 3 - "fetch_candidates"
|
|
||||||
Cohesion: 0.20
|
|
||||||
Nodes (18): execute_search_targets(), fetch_candidates(), infer_has_english_audio(), is_anime(), looks_like_season_pack(), passes_relevance_filter(), release_sort_key(), ReleaseCandidate (+10 more)
|
|
||||||
|
|
||||||
### Community 4 - "enumerate_upgrade_targets"
|
|
||||||
Cohesion: 0.21
|
|
||||||
Nodes (15): build_tv_query(), enumerate_search_targets_for_media_item(), enumerate_search_targets_for_media_item_routes_anime_movie_via_tmdb_table(), enumerate_upgrade_targets(), enumerate_upgrade_targets_excludes_an_upgrade_locked_episode(), enumerate_upgrade_targets_prioritizes_a_probe_flagged_episode_over_a_clean_one(), enumerate_upgrade_targets_returns_both_when_budget_allows(), relevance_filter_accepts_a_genuine_match() (+7 more)
|
|
||||||
|
|
||||||
### Community 5 - "rss.rs"
|
|
||||||
Cohesion: 0.07
|
|
||||||
Nodes (29): parse_human_size(), RawReleaseItem, Option, String, urlencode(), accumulate_field(), build_search_url(), ItemFields (+21 more)
|
|
||||||
|
|
||||||
### Community 6 - "TitleMatcher"
|
|
||||||
Cohesion: 0.15
|
|
||||||
Nodes (19): download(), ensure_model(), MatchCandidate, MatchOutcome, queue_for_review(), Connection, Option, Path (+11 more)
|
|
||||||
|
|
||||||
### Community 7 - "parser/mod.rs"
|
|
||||||
Cohesion: 0.08
|
|
||||||
Nodes (42): a_genuine_season_only_pack_with_no_dash_episode_still_has_no_episode(), brackets_still_take_priority_over_a_coincidental_bare_year(), Codec, does_not_mistake_a_year_titled_movie_for_a_bare_year(), does_not_mistake_a_yyyy_mm_dd_date_for_an_episode_range(), does_not_panic_on_real_corpus(), does_not_panic_on_unparsable_manga_release(), extracts_a_bare_year_from_a_scene_style_movie_name() (+34 more)
|
|
||||||
|
|
||||||
### Community 8 - "config.rs"
|
|
||||||
Cohesion: 0.06
|
|
||||||
Nodes (56): Config, config_path(), DaemonConfig, default_1337x_mirrors(), default_anime_svtav1_max_threads(), default_anime_svtav1_preset(), default_av1_efficiency_factor(), default_config_has_expected_values() (+48 more)
|
|
||||||
|
|
||||||
### Community 9 - "transcode/mod.rs"
|
|
||||||
Cohesion: 0.09
|
|
||||||
Nodes (63): Arc, TranscodeConfig, BacklogCandidate, cfg(), claim_pending_jobs(), claim_pending_jobs_claims_nothing_when_already_at_the_cap(), claim_pending_jobs_enforces_live_action_and_anime_caps_independently(), claim_pending_jobs_respects_already_running_jobs_as_a_global_cap() (+55 more)
|
|
||||||
|
|
||||||
### Community 10 - "ffprobe.rs"
|
|
||||||
Cohesion: 0.12
|
|
||||||
Nodes (27): AudioStream, build_media_probe(), DecodeCheck, generate_clip(), generate_test_clip(), is_english(), MediaProbe, parse_frame_rate_fraction() (+19 more)
|
|
||||||
|
|
||||||
### Community 11 - "importer/mod.rs"
|
|
||||||
Cohesion: 0.10
|
|
||||||
Nodes (25): a_fresh_grab_is_not_stalled(), a_grab_untouched_past_the_threshold_is_stalled(), a_torrent_missing_past_the_grace_period_is_failed(), a_torrent_missing_within_the_grace_period_is_not_yet_failed(), fail_grab_reopens_the_release_for_search(), find_relinkable_episode_files_falls_back_to_the_unpadded_season_folder(), find_relinkable_episode_files_skips_an_ambiguous_match_rather_than_guessing(), media_item_with_root() (+17 more)
|
|
||||||
|
|
||||||
### Community 12 - "Result"
|
|
||||||
Cohesion: 0.15
|
|
||||||
Nodes (24): copy_via_temp_file(), copy_via_temp_file_writes_through_a_part_file_and_renames_into_place(), find_by_basename(), find_by_stem(), import_season_pack_file(), insufficient_space(), largest_video_file(), locate_video_file() (+16 more)
|
|
||||||
|
|
||||||
### Community 13 - "process_pending_grabs"
|
|
||||||
Cohesion: 0.20
|
|
||||||
Nodes (13): a_persistently_failing_import_escalates_to_failed_instead_of_retrying_forever(), does_not_import_a_torrent_while_it_is_still_being_physically_moved(), fail_grab(), fetch_pending_grabs(), ImportStats, PendingGrab, process_pending_grabs(), process_pending_grabs_routes_each_grab_by_torrent_state() (+5 more)
|
|
||||||
|
|
||||||
### Community 14 - "Connection"
|
|
||||||
Cohesion: 0.17
|
|
||||||
Nodes (16): ensure_probed(), ensure_probed_does_not_flag_a_wide_aspect_ratio_file_as_under_quality(), generate_dual_audio_clip(), grab_is_stalled(), grab_missing_past_grace(), probe_library(), probe_library_reports_probed_vs_skipped_up_to_date(), ProbeSweepReport (+8 more)
|
|
||||||
|
|
||||||
### Community 15 - "import_one"
|
|
||||||
Cohesion: 0.16
|
|
||||||
Nodes (14): a_drifted_root_folder_does_not_defeat_the_dest_collision_check(), a_strictly_better_release_replaces_the_existing_file_via_a_real_move(), an_upgrade_swap_sweeps_every_duplicate_episode_file_row_not_just_one(), ensure_probed_flags_a_low_resolution_file_as_under_quality(), ensure_probed_skips_reprobing_an_unchanged_file(), generate_test_clip(), import_one(), import_one_enqueues_a_transcode_job_when_transcode_is_enabled() (+6 more)
|
|
||||||
|
|
||||||
### Community 16 - "find_relinkable_episode_files"
|
|
||||||
Cohesion: 0.20
|
|
||||||
Nodes (13): collect_video_files(), deterministic_filename(), deterministic_movie_filename(), find_relinkable_episode_files(), find_relinkable_episode_files_finds_a_file_with_no_tracked_row(), has_cjk(), ProbeFields, relink_episode_files() (+5 more)
|
|
||||||
|
|
||||||
### Community 17 - "main.rs"
|
|
||||||
Cohesion: 0.17
|
|
||||||
Nodes (35): BackgroundRequest, background_loop(), debug_1337x_search(), debug_anime_map_refresh(), debug_grab_cycle(), debug_import_cycle(), debug_jellyfin_refresh(), debug_match_title() (+27 more)
|
|
||||||
|
|
||||||
### Community 18 - "import_season_pack"
|
|
||||||
Cohesion: 0.43
|
|
||||||
Nodes (7): import_season_pack(), import_season_pack_fails_when_nothing_in_it_matches_a_tracked_episode(), import_season_pack_imports_every_file_and_marks_episodes_owned(), import_season_pack_skips_an_upgrade_locked_episode_even_with_a_lower_scoring_existing_release(), import_season_pack_skips_episodes_that_already_have_a_better_file(), SeasonPackImportOutcome, seeded_season_pack_conn()
|
|
||||||
|
|
||||||
### Community 19 - "QbitClient"
|
|
||||||
Cohesion: 0.10
|
|
||||||
Nodes (18): AddTorrentResponse, extract_btih(), MagnetRejected, QbitClient, Mutex, Option, Result, TorrentInfo (+10 more)
|
|
||||||
|
|
||||||
### Community 20 - "db.rs"
|
|
||||||
Cohesion: 0.25
|
|
||||||
Nodes (18): add_column_if_missing(), backup_before_open(), backup_before_open_copies_an_existing_database(), backup_before_open_is_a_noop_when_no_database_exists_yet(), backup_before_open_prunes_beyond_max_backups(), init(), init_is_idempotent(), prune_old_backups() (+10 more)
|
|
||||||
|
|
||||||
### Community 21 - "String"
|
|
||||||
Cohesion: 0.23
|
|
||||||
Nodes (13): ApprovalPrep, build_movie_query(), finalize_review_approval(), get_media_item(), grab_and_capture_hash(), grab_candidate(), grab_prepared_approval(), MediaItemRow (+5 more)
|
|
||||||
|
|
||||||
### Community 22 - "enumerate_search_targets"
|
|
||||||
Cohesion: 0.33
|
|
||||||
Nodes (11): cadence_stays_backed_off_at_a_high_search_count_instead_of_overflowing(), enumerate_search_targets(), enumerate_search_targets_excludes_owned_movies_includes_missing(), enumerate_search_targets_excludes_unaired_inflight_and_anime(), enumerate_search_targets_prioritizes_never_searched_first(), enumerate_search_targets_respects_budget(), enumerate_search_targets_routes_anime_movies_to_nyaa_search(), record_search_attempt() (+3 more)
|
|
||||||
|
|
||||||
### Community 23 - "seeded_conn"
|
|
||||||
Cohesion: 0.22
|
|
||||||
Nodes (10): count_monitored_missing_episodes_in_season_counts_correctly(), does_not_find_an_unmonitored_or_already_have_episode(), find_episode_id_ignores_monitored_and_has_file_state(), finds_a_monitored_missing_episode(), record_grab(), record_grab_still_logs_to_torrent_fetch_when_hash_capture_failed(), record_grab_writes_both_a_release_row_and_a_torrent_fetch_audit_row(), resolves_absolute_episode_via_anime_map() (+2 more)
|
|
||||||
|
|
||||||
## Knowledge Gaps
|
|
||||||
- **1 isolated node(s):** `AddTorrentResponse`
|
|
||||||
These have ≤1 connection - possible missing edges or undocumented components.
|
|
||||||
|
|
||||||
## Suggested Questions
|
|
||||||
_Questions this graph is uniquely positioned to answer:_
|
|
||||||
|
|
||||||
- **Why does `TranscodeConfig` connect `transcode/mod.rs` to `config.rs`, `Result`, `process_pending_grabs`, `import_one`, `import_season_pack`?**
|
|
||||||
_High betweenness centrality (0.409) - this node is a cross-community bridge._
|
|
||||||
- **Why does `SearchCycleStats` connect `fetch_candidates` to `scheduler.rs`, `api/mod.rs`?**
|
|
||||||
_High betweenness centrality (0.271) - this node is a cross-community bridge._
|
|
||||||
- **Why does `AppState` connect `api/mod.rs` to `transcode/mod.rs`, `main.rs`?**
|
|
||||||
_High betweenness centrality (0.217) - this node is a cross-community bridge._
|
|
||||||
- **What connects `AddTorrentResponse` to the rest of the system?**
|
|
||||||
_1 weakly-connected nodes found - possible documentation gaps or missing edges._
|
|
||||||
- **Should `scheduler.rs` be split into smaller, more focused modules?**
|
|
||||||
_Cohesion score 0.06976744186046512 - nodes in this community are weakly interconnected._
|
|
||||||
- **Should `api/mod.rs` be split into smaller, more focused modules?**
|
|
||||||
_Cohesion score 0.08826945412311266 - nodes in this community are weakly interconnected._
|
|
||||||
- **Should `rss.rs` be split into smaller, more focused modules?**
|
|
||||||
_Cohesion score 0.06570048309178744 - nodes in this community are weakly interconnected._
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1 +0,0 @@
|
||||||
{"nodes": [{"id": "$graphify-root$_breadarrd_src_scoring_mod_rs", "label": "mod.rs", "file_type": "code", "source_file": "breadarrd/src/scoring/mod.rs", "source_location": "L1"}], "edges": [{"source": "$graphify-root$_breadarrd_src_scoring_mod_rs", "target": "gate", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "breadarrd/src/scoring/mod.rs", "source_location": "L5", "weight": 1.0, "context": "import"}, {"source": "$graphify-root$_breadarrd_src_scoring_mod_rs", "target": "profile", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "breadarrd/src/scoring/mod.rs", "source_location": "L6", "weight": 1.0, "context": "import"}, {"source": "$graphify-root$_breadarrd_src_scoring_mod_rs", "target": "score", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "breadarrd/src/scoring/mod.rs", "source_location": "L7", "weight": 1.0, "context": "import"}], "raw_calls": []}
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1 +0,0 @@
|
||||||
{"nodes": [{"id": "$graphify-root$_breadarr_shared_src_lib_rs", "label": "lib.rs", "file_type": "code", "source_file": "breadarr-shared/src/lib.rs", "source_location": "L1"}], "edges": [{"source": "$graphify-root$_breadarr_shared_src_lib_rs", "target": "daemonclient", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "breadarr-shared/src/lib.rs", "source_location": "L5", "weight": 1.0, "context": "import"}, {"source": "$graphify-root$_breadarr_shared_src_lib_rs", "target": "config", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "breadarr-shared/src/lib.rs", "source_location": "L6", "weight": 1.0, "context": "import"}], "raw_calls": []}
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1 +0,0 @@
|
||||||
{"nodes": [{"id": "$graphify-root$_breadarrd_src_api_routes_mod_rs", "label": "mod.rs", "file_type": "code", "source_file": "breadarrd/src/api/routes/mod.rs", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
graphify-out/cache/stat-index.json
vendored
1
graphify-out/cache/stat-index.json
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
28321
graphify-out/graph.json
28321
graphify-out/graph.json
File diff suppressed because it is too large
Load diff
|
|
@ -1,232 +0,0 @@
|
||||||
{
|
|
||||||
"breadarr-shared/src/client.rs": {
|
|
||||||
"mtime": 1784176136.6198714,
|
|
||||||
"ast_hash": "383a3e626e611317f06b28984d72ab9e",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarr-shared/src/config.rs": {
|
|
||||||
"mtime": 1785696395.8005114,
|
|
||||||
"ast_hash": "f6b6fb2eb97829e12f88821408ad1f02",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarr-shared/src/dto.rs": {
|
|
||||||
"mtime": 1784908572.450929,
|
|
||||||
"ast_hash": "aa32217f2eb0f9f0dd3cb5384063e22f",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarr-shared/src/lib.rs": {
|
|
||||||
"mtime": 1783781950.6175613,
|
|
||||||
"ast_hash": "a2ab57e66492dd8a3dd59c60f9821b27",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarr-tui/src/app.rs": {
|
|
||||||
"mtime": 1784638215.5754883,
|
|
||||||
"ast_hash": "ca1205c8be64ec95230c75db46ceac51",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarr-tui/src/main.rs": {
|
|
||||||
"mtime": 1784638316.713315,
|
|
||||||
"ast_hash": "5fd64e003fb112a15f45e26b70a0e286",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarr-tui/src/ui.rs": {
|
|
||||||
"mtime": 1784638447.1445198,
|
|
||||||
"ast_hash": "44d8a181957b8c1e644a45dff91e9a76",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/api/mod.rs": {
|
|
||||||
"mtime": 1785696056.3421187,
|
|
||||||
"ast_hash": "3868111877263996f985e7297f566569",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/api/routes/calendar.rs": {
|
|
||||||
"mtime": 1784033809.0910208,
|
|
||||||
"ast_hash": "4d04ad23ceba40405f5a015525753b5c",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/api/routes/health.rs": {
|
|
||||||
"mtime": 1784908576.1114342,
|
|
||||||
"ast_hash": "55d7e27388131a844315e941095f2127",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/api/routes/library_health.rs": {
|
|
||||||
"mtime": 1784113370.1363761,
|
|
||||||
"ast_hash": "1e2fc6fff36cbf091a539000ede57611",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/api/routes/media.rs": {
|
|
||||||
"mtime": 1784171609.152028,
|
|
||||||
"ast_hash": "c1e84c6268e624f1eae46fd70033fa04",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/api/routes/mod.rs": {
|
|
||||||
"mtime": 1784175849.5137725,
|
|
||||||
"ast_hash": "46b952bdd0b9cbb8d0418a350e814459",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/api/routes/quality_profiles.rs": {
|
|
||||||
"mtime": 1784176136.6858702,
|
|
||||||
"ast_hash": "2dfbf6122c323b7521df3087e739f62f",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/api/routes/releases.rs": {
|
|
||||||
"mtime": 1783782116.7739515,
|
|
||||||
"ast_hash": "0eb53bc15913f1445687f8de7e4040ad",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/api/routes/review.rs": {
|
|
||||||
"mtime": 1785695923.5693555,
|
|
||||||
"ast_hash": "907f8d308d2942eb3abb92482ffca187",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/api/routes/search.rs": {
|
|
||||||
"mtime": 1783841453.3332663,
|
|
||||||
"ast_hash": "7d82ca20febd26e9020642aecbab313c",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/api/routes/stuck.rs": {
|
|
||||||
"mtime": 1784638231.949136,
|
|
||||||
"ast_hash": "46ff0112d0302f18ed8ad0d2c47d9f3b",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/db.rs": {
|
|
||||||
"mtime": 1785332784.4654708,
|
|
||||||
"ast_hash": "54d92eeae67311c25b0dec690302f6c1",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/importer/ffprobe.rs": {
|
|
||||||
"mtime": 1785695262.529791,
|
|
||||||
"ast_hash": "c9749d92cd5bffb5e7633facb369f051",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/importer/mkv.rs": {
|
|
||||||
"mtime": 1784175099.8600957,
|
|
||||||
"ast_hash": "99c3e8684a7081eda3730e6ecee4d862",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/importer/mod.rs": {
|
|
||||||
"mtime": 1785695989.9557424,
|
|
||||||
"ast_hash": "3f763d44c853bcd5d0cdfba4a60783c6",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/jellyfin.rs": {
|
|
||||||
"mtime": 1784911004.2736921,
|
|
||||||
"ast_hash": "245f75ff1d6f3c86e15635537f9234a5",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/library_scan.rs": {
|
|
||||||
"mtime": 1784632454.081846,
|
|
||||||
"ast_hash": "b35f50965f0e39bacf99059c84d18006",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/main.rs": {
|
|
||||||
"mtime": 1785677152.6518264,
|
|
||||||
"ast_hash": "3e5baa3fdebcfe6172675afe575b4505",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/matcher/embed.rs": {
|
|
||||||
"mtime": 1784269828.2931612,
|
|
||||||
"ast_hash": "e0a425d2e351589b6e56af02fe8ad396",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/matcher/mod.rs": {
|
|
||||||
"mtime": 1785695690.847343,
|
|
||||||
"ast_hash": "e71d4677d9f421b7ef8b84354ac9fc4d",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/metadata/anime_map.rs": {
|
|
||||||
"mtime": 1783855515.7563374,
|
|
||||||
"ast_hash": "46b90382ecc49a1452d580f61d7806a4",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/metadata/mod.rs": {
|
|
||||||
"mtime": 1784636520.9888098,
|
|
||||||
"ast_hash": "bc4ebde5b9e89515ee87ea6a5ffd3c10",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/metadata/tmdb.rs": {
|
|
||||||
"mtime": 1783802179.6291993,
|
|
||||||
"ast_hash": "02ff58e5db664e947f486bb0e685f5b4",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/metadata/tvdb.rs": {
|
|
||||||
"mtime": 1784635726.8710334,
|
|
||||||
"ast_hash": "cd21d4943f6a05269a46d95bae368758",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/notify.rs": {
|
|
||||||
"mtime": 1784033287.1188982,
|
|
||||||
"ast_hash": "35b8279fb3d6f05892bda5401541c838",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/parser/mod.rs": {
|
|
||||||
"mtime": 1785695514.945018,
|
|
||||||
"ast_hash": "5d42da21fd9eae954c0b1d0633d7ba45",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/parser/tokens.rs": {
|
|
||||||
"mtime": 1785695636.4009888,
|
|
||||||
"ast_hash": "88b08a119a5285c9e640d2dfe59dbd85",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/qbit/mod.rs": {
|
|
||||||
"mtime": 1785662339.058403,
|
|
||||||
"ast_hash": "cd3e6c110e9cf757815d4bfb62faca8e",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/scheduler.rs": {
|
|
||||||
"mtime": 1785695949.7192466,
|
|
||||||
"ast_hash": "99233bde5f082ea7ed9b9065ff4943a0",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/scoring/gate.rs": {
|
|
||||||
"mtime": 1784117113.4110034,
|
|
||||||
"ast_hash": "f6090fd0a592380dc3ce045decf6932d",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/scoring/mod.rs": {
|
|
||||||
"mtime": 1784175656.340803,
|
|
||||||
"ast_hash": "f00b3e1527789cc6ab5ac6e384c451c0",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/scoring/profile.rs": {
|
|
||||||
"mtime": 1784175744.9204473,
|
|
||||||
"ast_hash": "5f637981034d7c4edb9e7f70aebc2e36",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/scoring/score.rs": {
|
|
||||||
"mtime": 1784015678.9287148,
|
|
||||||
"ast_hash": "6ca1852f1e611ac7046127ab2b60524e",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/sources/mod.rs": {
|
|
||||||
"mtime": 1785696200.501458,
|
|
||||||
"ast_hash": "d4efc09eab30de5fee6d25d000ca2b0c",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/sources/rss.rs": {
|
|
||||||
"mtime": 1785696625.7660108,
|
|
||||||
"ast_hash": "4f3fd2400a3cc2cf3e24900f1335e884",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/sources/scrape.rs": {
|
|
||||||
"mtime": 1784177018.756294,
|
|
||||||
"ast_hash": "df885de8d787d6c2af3d814f0831ee6e",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/sources/tpb.rs": {
|
|
||||||
"mtime": 1785696280.7010753,
|
|
||||||
"ast_hash": "8b6896a57f82268a0e1c6924054ce968",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"breadarrd/src/transcode/mod.rs": {
|
|
||||||
"mtime": 1785696564.622992,
|
|
||||||
"ast_hash": "fd89d1494a14380c2d758a7d90312a1b",
|
|
||||||
"semantic_hash": ""
|
|
||||||
},
|
|
||||||
"README.md": {
|
|
||||||
"mtime": 1784175535.4875388,
|
|
||||||
"ast_hash": "62f6240c65824f55179178205fb74b90",
|
|
||||||
"semantic_hash": ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue