Merge pull request 'feature/bakery-integration' (#1) from feature/bakery-integration into main
Some checks failed
dev release / build (push) Failing after 2m8s

Reviewed-on: #1
This commit is contained in:
Breadway 2026-08-06 10:53:15 +08:00
commit fbf8d58587
95 changed files with 453 additions and 100420 deletions

View 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

View 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}"

View 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}"

View 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
View file

@ -6,3 +6,6 @@ config.toml
# Local hygiene notes (not for commit)
CLAUDE.md
# graphify knowledge-graph output (local tool cache, not for commit)
graphify-out/

44
bakery.toml Normal file
View 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",
]

View file

@ -27,19 +27,31 @@ pub struct Config {
pub transcode: TranscodeConfig,
}
/// Where the TUI's "add show" flow places new series by default. Sonarr/
/// Radarr let you pick a root folder per add; a single configured default
/// is a reasonable v1 simplification — per-add picking can follow later.
/// 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 per
/// 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)]
pub struct LibraryConfig {
#[serde(default = "default_root_folder")]
pub default_root_folder: String,
#[serde(default = "default_movies_root_folder")]
pub movies_root_folder: String,
}
fn default_root_folder() -> String {
"~/breadarr-library".to_string()
}
fn default_movies_root_folder() -> String {
"~/breadarr-library/Movies".to_string()
}
#[derive(Debug, Clone, Deserialize)]
pub struct SourcesConfig {
/// 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 {
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 {

View file

@ -6,6 +6,39 @@ use breadarr_shared::dto::{
};
use breadarr_shared::DaemonClient;
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)]
pub enum Tab {
@ -565,7 +598,7 @@ impl App {
return;
};
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 {
self.client
.unmonitor_season(media_item_id, season_number)
@ -659,7 +692,7 @@ impl App {
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 {
return;
};
@ -673,7 +706,7 @@ impl App {
title: result.title.clone(),
year: result.year,
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)
}
@ -682,7 +715,7 @@ impl App {
tmdb_id: result.external_id.clone(),
title: result.title.clone(),
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)
}

View file

@ -14,14 +14,17 @@ use crossterm::terminal::{
use ratatui::backend::CrosstermBackend;
use ratatui::Terminal;
use app::{App, Focus, StuckSection, Tab};
use app::{App, Focus, LibraryRoots, StuckSection, Tab};
#[tokio::main]
async fn main() -> Result<()> {
let config = Config::load()?;
let base_url = format!("http://{}", config.daemon.listen_addr);
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()?;
let mut stdout = io::stdout();
@ -30,7 +33,7 @@ async fn main() -> Result<()> {
let mut terminal = Terminal::new(backend)?;
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()?;
execute!(terminal.backend_mut(), LeaveAlternateScreen)?;
@ -42,7 +45,7 @@ async fn main() -> Result<()> {
async fn run(
terminal: &mut Terminal<CrosstermBackend<io::Stdout>>,
app: &mut App,
root_folder: &str,
roots: &LibraryRoots,
) -> Result<()> {
let mut last_refresh = tokio::time::Instant::now() - Duration::from_secs(10);
@ -60,7 +63,7 @@ async fn run(
if key.kind != KeyEventKind::Press {
continue;
}
handle_key(app, key.code, root_folder).await;
handle_key(app, key.code, roots).await;
if app.should_quit {
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.
if matches!(app.tab, Tab::Add) && matches!(app.focus, Focus::AddSearchInput) {
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::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,
},
Tab::Profiles if app.profile_detail.is_some() => {

View file

@ -99,14 +99,6 @@ impl PendingGrab {
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
@ -1615,7 +1607,7 @@ impl StaleFile {
/// later step (free-space check, the actual move) fails, so a failed
/// import never leaves neither the old file nor the new one behind.
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();
}
}
@ -1909,7 +1901,7 @@ fn import_one(
if let Some(row_id) = stale.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();
}
}
@ -2309,7 +2301,7 @@ fn import_season_pack_file(
if let Some(row_id) = stale.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();
}
}

View file

@ -633,10 +633,11 @@ async fn background_loop(
// repaired (a rename or an in-place transcode) gets re-probed
// immediately rather than waiting for its own turn a full
// interval later.
match {
let probe_result = {
let conn = conn.lock().await;
importer::probe_library(&conn)
} {
};
match probe_result {
Ok(report) if report.probed > 0 || report.failed > 0 => {
info!(?report, "media probe sweep complete");
}

View file

@ -41,6 +41,7 @@ pub struct EpisodeInfo {
/// `MutexGuard` across an `.await` point — this convenience wrapper is only
/// safe for callers with an owned, unshared `Connection` (e.g. the debug
/// CLI commands).
#[allow(clippy::too_many_arguments)]
pub async fn add_series(
conn: &Connection,
tvdb: &tvdb::TvdbClient,

View file

@ -1,7 +1,7 @@
use anyhow::{Context, Result};
use serde::Deserialize;
use super::{EpisodeInfo, MovieSearchResult, SeriesSearchResult};
use super::MovieSearchResult;
pub struct TmdbClient {
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>> {
#[derive(Deserialize)]
struct SearchResponse {
@ -96,48 +58,6 @@ impl TmdbClient {
})
.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> {

View file

@ -1,4 +1,3 @@
use anyhow::Result;
use serde::Serialize;
#[derive(Serialize)]

View file

@ -69,6 +69,9 @@ pub struct TorrentInfo {
pub name: String,
pub state: String,
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,
/// Full path to the torrent's content (file or directory root) —
/// qBittorrent resolves this for us, so importers don't need to guess

View file

@ -752,6 +752,10 @@ async fn grab_and_capture_hash(
#[derive(Debug, Default)]
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 new_items: usize,
pub grabbed: usize,
@ -851,6 +855,7 @@ enum SearchRoute {
/// (not just "no relevant results") — the mirror-rotation/cooldown
/// machinery already built for it is real resilience worth keeping,
/// just no longer the first choice given TPB's better precision.
#[allow(dead_code)]
X1337,
NyaaSearch,
}
@ -1658,16 +1663,16 @@ pub async fn execute_search_targets(
// with, since dedup (`is_seen`/`mark_seen`) and grab records
// are keyed by source id — attributing a 1337x-sourced guid to
// TPB's source id would silently break dedup between the two.
let (fetch_result, result_source_id) =
if primary_result.is_err() && matches!(target.route, SearchRoute::Tpb) {
let (fetch_result, result_source_id) = match primary_result {
Err(e) if matches!(target.route, SearchRoute::Tpb) => {
tracing::warn!(
query = %target.query,
error = %primary_result.as_ref().unwrap_err(),
error = %e,
"TPB search failed, falling back to 1337x"
);
(scrape.fetch(Some(&target.query)).await, scrape_source_id)
} else {
(primary_result, primary_id)
}
other => (other, primary_id),
};
match fetch_result {
@ -2821,7 +2826,7 @@ mod tests {
seeders: Some(500),
leechers: None,
};
let mut items = vec![episode.clone(), pack.clone()];
let mut items = [episode.clone(), pack.clone()];
items.sort_by_key(release_sort_key);
assert_eq!(items[0].guid, "pack");
assert_eq!(items[1].guid, "episode");
@ -2845,7 +2850,7 @@ mod tests {
seeders: Some(50),
leechers: None,
};
let mut items = vec![low.clone(), high.clone()];
let mut items = [low.clone(), high.clone()];
items.sort_by_key(release_sort_key);
assert_eq!(items[0].guid, "high");
assert_eq!(items[1].guid, "low");

View file

@ -337,6 +337,7 @@ enum EncodeOutcome {
/// 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
/// unconditionally.
#[allow(clippy::too_many_arguments)]
fn encode_and_verify(
input: PathBuf,
job_id: i64,
@ -726,6 +727,10 @@ pub fn find_oversized_av1_candidates(conn: &Connection, cfg: &TranscodeConfig) -
pub struct BacklogCandidate {
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 size_bytes: i64,
pub video_codec: Option<String>,

1
ci/bread-ecosystem.rev Normal file
View file

@ -0,0 +1 @@
147cfbbf96ae4b171027defa1130d2caddb934b1

21
ci/build.sh Executable file
View 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" "$@"

View file

@ -50,8 +50,13 @@ api_key = ""
bearer_token = ""
[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"
# 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]
# nyaa's English-translated anime category — the daemon polls this

File diff suppressed because it is too large Load diff

View file

@ -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
}

View file

@ -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"}

View file

@ -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"
}

View file

@ -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"}

View file

@ -1 +0,0 @@
/home/breadway/.cache/uv/archive-v0/4yQjntA8tzDKxQRL/bin/python

View file

@ -1 +0,0 @@
/home/breadway/Projects/breadarr

View file

@ -1 +0,0 @@
/home/breadway/Projects/breadarr/README.md

View file

@ -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"
}

View file

@ -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

View file

@ -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": ""
}
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

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 it is too large Load diff

View file

@ -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": ""
}
}