diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..9675673 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,7 @@ +.github +.git +/screenshots +/target +README.md +CHANGELOG.md +LICENSE diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..87b7851 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,26 @@ +FROM clux/muslrust:stable AS builder +WORKDIR /usr/src + +# Download and compile Rust dependencies in an empty project and cache as a separate Docker layer +RUN USER=root cargo new --bin managarr-temp + +WORKDIR /usr/src/managarr-temp +COPY Cargo.* . +RUN cargo build --release --target x86_64-unknown-linux-musl +# remove src from empty project +RUN rm -r src +COPY src ./src +# remove previous deps +RUN rm ./target/x86_64-unknown-linux-musl/release/deps/managarr* + +RUN --mount=type=cache,target=/volume/target \ + --mount=type=cache,target=/root/.cargo/registry \ + cargo build --release --target x86_64-unknown-linux-musl --bin managarr +RUN mv target/x86_64-unknown-linux-musl/release/managarr . + +FROM debian:stable-slim + +# Copy the compiled binary from the builder container +COPY --from=builder --chown=nonroot:nonroot /usr/src/managarr-temp/managarr /usr/local/bin + +ENTRYPOINT [ "/usr/local/bin/managarr" ] diff --git a/Makefile b/Makefile index d5ed5ed..71fc07b 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,7 @@ #!make +VERSION := latest +IMG_NAME := darkalex17/managarr +IMAGE := ${IMG_NAME}:${VERSION} default: run @@ -14,6 +17,9 @@ test-cov: build: test @cargo build --release +docker: + @DOCKER_BUILDKIT=1 docker build --rm -t ${IMAGE} . + run: @CARGO_INCREMENTAL=1 cargo fmt && make lint && cargo run @@ -34,4 +40,5 @@ release: @git tag -a ${V} -m "Release ${V}" && git push origin ${V} delete-tag: - @git tag -d ${V} && git push --delete origin ${V} \ No newline at end of file + @git tag -d ${V} && git push --delete origin ${V} + diff --git a/README.md b/README.md index 212ee08..82b1ef9 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,23 @@ pleasant as possible! - ![bazarr_logo](logos/bazarr.png) [Bazarr](https://www.bazarr.media/) - ![tautulli_logo](logos/tautulli.png) [Tautulli](https://tautulli.com/) +## Try Before You Buy +To try out Managarr before linking it to your HTPC, you can use the purpose built [managarr-demo](https://github.com/Dark-Alex-17/managarr-demo) repository. +Simply run the following command to start a demo: + +```shell +curl https://raw.githubusercontent.com/Dark-Alex-17/managarr-demo/main/managarr-demo.sh > /tmp/managarr-demo.sh && bash /tmp/managarr-demo.sh +``` + +**Note**: This demo does download a handful of images to your local machine, so be sure to run the cleanup command from the [cleanup section](https://github.com/Dark-Alex-17/managarr-demo#Cleanup) of the +`managarr-demo` README to free space once you're done trying out Managarr: + +```shell +docker image rm lscr.io/linuxserver/radarr &&\ + docker image rm lscr.io/linuxserver/prowlarr &&\ + rm -rf /tmp/managarr-demo +``` + ## Features ### Radarr @@ -79,6 +96,16 @@ pleasant as possible! - [ ] Support for Tautulli +## Installation + +### Docker +Run Managarr as a docker container by mounting your `config.yml` file to `/root/.config/managarr/config.yml`. For example: +```shell +docker run --rm -it -v ~/.config/managarr:/root/.config/managarr darkalex17/managarr +``` + +You can also clone this repo and run `make docker` to build a docker image locally and run it using the above command. + # Configuration Managarr assumes reasonable defaults to connect to each service (i.e. Radarr is on localhost:7878), but all servers will require you to input the API token. @@ -165,4 +192,4 @@ tautulli: * [Tautulli >= v2](https://github.com/Tautulli/Tautulli/wiki/Tautulli-API-Reference) ## Creator -* [Alex Clarke](https://github.com/Dark-Alex-17) \ No newline at end of file +* [Alex Clarke](https://github.com/Dark-Alex-17) diff --git a/src/ui/radarr_ui/library/movie_details_ui.rs b/src/ui/radarr_ui/library/movie_details_ui.rs index 2888e59..909d55e 100644 --- a/src/ui/radarr_ui/library/movie_details_ui.rs +++ b/src/ui/radarr_ui/library/movie_details_ui.rs @@ -165,6 +165,8 @@ fn draw_file_info(f: &mut Frame<'_>, app: &App<'_>, area: Rect) { fn draw_movie_details(f: &mut Frame<'_>, app: &App<'_>, area: Rect) { let block = layout_block_top_border(); + let is_monitored = app.data.radarr_data.movies.current_selection().monitored; + let status = app.data.radarr_data.movies.current_selection().status.clone(); match app.data.radarr_data.movie_details_modal.as_ref() { Some(movie_details_modal) if !app.is_loading => { @@ -183,7 +185,7 @@ fn draw_movie_details(f: &mut Frame<'_>, app: &App<'_>, area: Rect) { .map(|line| { let split = line.split(':').collect::>(); let title = format!("{}:", split[0]); - let style = style_from_download_status(download_status); + let style = style_from_download_status(download_status, is_monitored, status.clone()); Line::from(vec![ title.bold().style(style), @@ -518,13 +520,15 @@ fn draw_manual_search_confirm_prompt(f: &mut Frame<'_>, app: &mut App<'_>) { } } -fn style_from_download_status(download_status: &str) -> Style { +fn style_from_download_status(download_status: &str, is_monitored: bool, status: String) -> Style { match download_status { - "Downloaded" => Style::new().success(), + "Downloaded" => Style::new().downloaded(), "Awaiting Import" => Style::new().awaiting_import(), - "Downloading" => Style::new().warning(), - "Missing" => Style::new().failure(), - _ => Style::new().success(), + "Downloading" => Style::new().downloading(), + _ if !is_monitored && download_status == "Missing" => Style::new().unmonitored_missing(), + _ if status != "released" && download_status == "Missing" => Style::new().unreleased(), + "Missing" => Style::new().missing(), + _ => Style::new().downloaded(), } } diff --git a/src/ui/radarr_ui/library/movie_details_ui_tests.rs b/src/ui/radarr_ui/library/movie_details_ui_tests.rs index 15f1077..f8b7ef4 100644 --- a/src/ui/radarr_ui/library/movie_details_ui_tests.rs +++ b/src/ui/radarr_ui/library/movie_details_ui_tests.rs @@ -1,10 +1,15 @@ #[cfg(test)] mod tests { + use pretty_assertions::assert_eq; + use ratatui::style::Style; + use ratatui::text::Text; + use rstest::rstest; use strum::IntoEnumIterator; use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, MOVIE_DETAILS_BLOCKS}; - use crate::ui::radarr_ui::library::movie_details_ui::MovieDetailsUi; + use crate::ui::radarr_ui::library::movie_details_ui::{decorate_peer_style, MovieDetailsUi, style_from_download_status}; use crate::ui::DrawUi; + use crate::ui::styles::ManagarrStyle; #[test] fn test_movie_details_ui_accepts() { @@ -16,4 +21,44 @@ mod tests { } }); } + + #[rstest] + #[case("Downloading", true, "", Style::new().downloading())] + #[case("Downloaded", true, "", Style::new().downloaded())] + #[case("Awaiting Import", true, "", Style::new().awaiting_import())] + #[case("Missing", false, "", Style::new().unmonitored_missing())] + #[case("Missing", false, "", Style::new().unmonitored_missing())] + #[case("Missing", true, "released", Style::new().missing())] + #[case("", true, "", Style::new().downloaded())] + fn test_style_from_download_status( + #[case] download_status: &str, + #[case] is_monitored: bool, + #[case] movie_status: &str, + #[case] expected_style: Style, + ) { + assert_eq!(style_from_download_status(download_status, is_monitored, movie_status.to_owned()), expected_style); + } + + #[rstest] + #[case(0, 0, PeerStyle::Failure)] + #[case(1, 2, PeerStyle::Warning)] + #[case(4, 2, PeerStyle::Success)] + fn test_decorate_peer_style( + #[case] seeders: u64, + #[case] leechers: u64, + #[case] expected_style: PeerStyle, + ) { + let text = Text::from("test"); + match expected_style { + PeerStyle::Failure => assert_eq!(decorate_peer_style(seeders, leechers, text.clone()), text.failure()), + PeerStyle::Warning => assert_eq!(decorate_peer_style(seeders, leechers, text.clone()), text.warning()), + PeerStyle::Success => assert_eq!(decorate_peer_style(seeders, leechers, text.clone()), text.success()), + } + } + + enum PeerStyle { + Failure, + Warning, + Success, + } } diff --git a/src/ui/radarr_ui/radarr_ui_tests.rs b/src/ui/radarr_ui/radarr_ui_tests.rs index 6269f24..8da8049 100644 --- a/src/ui/radarr_ui/radarr_ui_tests.rs +++ b/src/ui/radarr_ui/radarr_ui_tests.rs @@ -1,5 +1,6 @@ #[cfg(test)] mod tests { + use pretty_assertions::assert_eq; use ratatui::widgets::{Cell, Row}; use rstest::rstest; use strum::IntoEnumIterator;