Skip to content

Commit

Permalink
Working PoC
Browse files Browse the repository at this point in the history
Add:
- Search is working
- Nfo saving is working
- Nix builds should be back

TODO:
- Specific actions to search and replace only, downloading artworks etc
- Develop the information provided in the UI (movie details, etc...)
  • Loading branch information
fusetim committed Jul 29, 2023
1 parent fcb52c6 commit d8fa3fc
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 45 deletions.
7 changes: 4 additions & 3 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 20 additions & 5 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
};

fenix = {
url = "github:nix-community/fenix";
url = "github:nix-community/fenix/monthly";
inputs.nixpkgs.follows = "nixpkgs";
inputs.rust-analyzer-src.follows = "";
};
Expand All @@ -21,16 +21,25 @@
url = "github:rustsec/advisory-db";
flake = false;
};

};

outputs = { self, nixpkgs, crane, fenix, flake-utils, advisory-db, ... }:
flake-utils.lib.eachDefaultSystem (system:
let
toolchain = (fenix.packages.${system}.toolchainOf {
channel = "nightly";
date = "2023-07-27";
sha256 = "1bUA3mqH455LncZMMH1oEBFLWu5TOluJeDZ8iwAsBGs=";
});
in
let
pkgs = import nixpkgs {
inherit system;
};

inherit (pkgs) lib;
inherit toolchain;

craneLib = crane.lib.${system};
src = craneLib.cleanCargoSource (craneLib.path ./.);
Expand All @@ -52,8 +61,7 @@
# MY_CUSTOM_VAR = "some value";
};

craneLibLLvmTools = craneLib.overrideToolchain
(fenix.packages.${system}.complete.withComponents [
craneLibLLvmTools = craneLib.overrideToolchain (toolchain.withComponents [
"cargo"
"llvm-tools"
"rustc"
Expand All @@ -77,6 +85,8 @@
pkgs.llvmPackages_latest.libclang
pkgs.rustPlatform.bindgenHook
pkgs.openssl
pkgs.samba
pkgs.samba.dev
];
});
in
Expand Down Expand Up @@ -145,8 +155,10 @@

# Extra inputs can be added here
nativeBuildInputs = with pkgs; [
cargo
rustc
(toolchain.withComponents [
"cargo"
"rustc"
])
pkg-config
];

Expand All @@ -158,6 +170,8 @@
pkgs.llvmPackages_latest.bintools
pkgs.clang_16
pkgs.openssl
pkgs.samba
pkgs.samba.dev
];

LIBCLANG_PATH = "${pkgs.llvmPackages_latest.libclang.lib}/lib";
Expand All @@ -167,6 +181,7 @@
# add dev libraries here (e.g. pkgs.libvmi.dev)
pkgs.glibc.dev
pkgs.ffmpeg.dev
pkgs.samba.dev
])
# Includes with special directory paths
++ [
Expand Down
4 changes: 2 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ pub async fn try_open_nfo(lfs: &mut MultiFs, mut path: PathBuf) -> Result<nfo::M
Err(anyhow!("No nfo available."))
}

async fn get_metadata(lfs: &mut MultiFs, base_url: Url, path: PathBuf) -> Result<nfo::FileInfo> {
pub async fn get_metadata(lfs: &mut MultiFs, base_url: Url, path: PathBuf) -> Result<nfo::FileInfo> {
use metadata::media_file::MediaFileMetadata;
use metadata::stream::StreamMetadata;

Expand Down Expand Up @@ -165,7 +165,7 @@ async fn get_metadata(lfs: &mut MultiFs, base_url: Url, path: PathBuf) -> Result
})
}

async fn transform_as_nfo(client: &TmdbClient, tmdb_id: u64, lang: Option<String>) -> Result<nfo::Movie> {
pub async fn transform_as_nfo(client: &TmdbClient, tmdb_id: u64, lang: Option<String>) -> Result<nfo::Movie> {
let mdr = MovieDetails::new(tmdb_id).with_language(lang.clone());
let md = mdr.execute(&client).await
.map_err(|err| anyhow!("Failed to get movie details (id: {}), causes:\n{:?}", tmdb_id, err))?;
Expand Down
24 changes: 19 additions & 5 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,23 +157,37 @@ where B: tui::backend::Backend
..Default::default()
}
});
state.register_event(AppEvent::MovieManagerEvent(MovieManagerEvent::MovieDiscovered((movie, path))));
state.register_event(AppEvent::MovieManagerEvent(MovieManagerEvent::MovieDiscovered((movie, i, path))));
}
}
}
}
},
AppMessage::MovieManagerMessage(MovieManagerMessage::SearchTitle(title)) => {
use tmdb_api::movie::search::MovieSearch;
use tmdb_api::prelude::Command;
let ms = MovieSearch::new(title);
if let Some(results) = ms.execute(&tmdb_client).await {
if let Ok(results) = ms.execute(&tmdb_client).await {
state.register_event(AppEvent::MovieManagerEvent(MovieManagerEvent::SearchResults(results.results)));
} else {
// TODO
}
},
AppMessage::MovieManagerMessage(MovieManagerMessage::SaveNfo((nfo, path))) => {
// TODO
unimplemented!()
AppMessage::MovieManagerMessage(MovieManagerMessage::SaveNfo((id, fs_id, mut path))) => {
use std::io::Cursor;
use std::io::Seek;
let mut movie_nfo = mkube::transform_as_nfo(&tmdb_client, id, Some("fr".to_owned())).await?;
let mt = mkube::get_metadata(&mut state.conns[fs_id], (&state.libraries[fs_id]).try_into().expect("Cannot get a baseURL from library."), path.clone()).await?;
movie_nfo.fileinfo = Some(mt);
let nfo_string = quick_xml::se::to_string(&movie_nfo).expect("Failed to produce a valid nfo file.");

path.set_extension("nfo");
let mut buf = Cursor::new(Vec::new());
buf.write_all(br#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>"#).await?;
buf.write_all(nfo_string.as_bytes()).await?;
let _ = buf.rewind();
let _ = state.conns[fs_id].as_mut_rfs().create_file(&path, &Metadata::default(), Box::new(buf))
.map_err(|err| anyhow!("Can't open the nfo file., causes:\n{:?}", err))?;
},
AppMessage::Close => {
break;
Expand Down
27 changes: 22 additions & 5 deletions src/views/movie_manager/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ use tui::{
};

use std::path::PathBuf;
use crossterm::event::KeyCode;


pub mod details;
pub mod table;
Expand All @@ -14,6 +16,7 @@ pub mod search;
use table::{MovieTable, MovieTableState};
use search::{MovieSearch, MovieSearchState};
use crate::{AppEvent, AppState, AppMessage};
use crate::views::widgets::{Input, InputState};

#[derive(Clone, Debug, Default)]
pub struct MovieManager {
Expand All @@ -35,16 +38,16 @@ impl Default for MovieManagerState {
#[derive(Clone, Debug, PartialEq)]
pub enum MovieManagerEvent {
ClearMovieList,
MovieDiscovered((crate::nfo::Movie, PathBuf)),
MovieUpdated((crate::nfo::Movie, PathBuf)),
SearchMovie((crate::nfo::Movie, PathBuf)),
MovieDiscovered((crate::nfo::Movie, usize, PathBuf)),
MovieUpdated((crate::nfo::Movie, usize, PathBuf)),
SearchMovie((crate::nfo::Movie, usize, PathBuf)),
SearchResults(Vec<tmdb_api::movie::MovieShort>),
}
#[derive(Clone, Debug, PartialEq)]
pub enum MovieManagerMessage {
RefreshMovies,
SearchTitle(String),
SaveNfo((crate::nfo::Movie, PathBuf)),
SaveNfo((u64, usize, PathBuf)), // tmdb_id, movie_path
}

impl StatefulWidget for MovieManager {
Expand Down Expand Up @@ -76,19 +79,33 @@ impl MovieManagerState {
match self {
MovieManagerState::Table(ref mut state) => {
match app_event {
AppEvent::MovieManagerEvent(MovieManagerEvent::SearchMovie((movie, path))) => {
AppEvent::MovieManagerEvent(MovieManagerEvent::SearchMovie((movie, fs_id, path))) => {
let mut query_state = InputState::default();
query_state.set_value(&movie.title);
let new_state = MovieSearchState {
movie_path: path,
movie_fs_id: fs_id,
query_state,
..Default::default()
};
*self = MovieManagerState::Search(new_state);
true
}
_ => state.input(app_event),
}
},
MovieManagerState::Search(ref mut state) => {
if let AppEvent::KeyEvent(kev) = app_event {
if kev.code == KeyCode::Esc {
*self = Default::default();
true
} else {
state.input(app_event)
}
} else {
state.input(app_event)
}
},
_ => { false },
}
}
Expand Down
52 changes: 34 additions & 18 deletions src/views/movie_manager/search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,14 @@ impl Default for MovieSearch {

#[derive(Clone, Debug, Default)]
pub struct MovieSearchState {
table_state: TableState,
results: Vec<MovieShort>,
is_loading: bool,
query_state: InputState,
send_state: ButtonState,
selected: usize,
movie_path: PathBuf,
pub table_state: TableState,
pub results: Vec<MovieShort>,
pub is_loading: bool,
pub query_state: InputState,
pub send_state: ButtonState,
pub selected: usize,
pub movie_path: PathBuf,
pub movie_fs_id: usize,
}

impl StatefulWidget for MovieSearch {
Expand All @@ -54,9 +55,9 @@ impl StatefulWidget for MovieSearch {
Constraint::Percentage(100),
]).split(area);
let search_bar = Layout::default()
.direction(Direction::Vertical)
.direction(Direction::Horizontal)
.constraints(vec![
Constraint::Percentage(100),
Constraint::Length(chunks[0].width - 10),
Constraint::Min(2),
Constraint::Min(8),
]).split(chunks[0]);
Expand All @@ -69,7 +70,7 @@ impl StatefulWidget for MovieSearch {
state.query_state.set_focus(state.selected == 0);
state.send_state.focus(state.selected == 1);
StatefulWidget::render(self.query, search_bar[0], buf, &mut state.query_state);
StatefulWidget::render(self.send, search_bar[1], buf, &mut state.send_state);
StatefulWidget::render(self.send, search_bar[2], buf, &mut state.send_state);
search_block.render(chunks[1], buf);
if state.is_loading {
Paragraph::new("Searching...").render(inner, buf);
Expand All @@ -92,7 +93,7 @@ impl StatefulWidget for MovieSearch {
)
.widths(&[Constraint::Length(50), Constraint::Length(4), Constraint::Percentage(100)])
.column_spacing(1)
.highlight_style(Style::default().bg(Color::LightRed));
.highlight_style(Style::default().bg(if state.selected == 2 { Color::LightRed } else { Color::Gray }));
StatefulWidget::render(table, inner, buf, &mut state.table_state);
}
}
Expand All @@ -106,19 +107,22 @@ impl MovieSearchState {
if self.selected == 0 || self.selected == 1 {
let sender = MESSAGE_SENDER.get().unwrap();
sender.send(AppMessage::MovieManagerMessage(MovieManagerMessage::SearchTitle(self.query_state.get_value().to_owned()))).unwrap();
self.is_loading = true;
true
} else if self.selected == 2 {
/*let sender = MESSAGE_SENDER.get().unwrap();
sender.send(AppMessage::MovieManagerMessage(MovieManagerMessage::SaveNfo((nfo, self.movie_path.clone())))).unwrap();
*/
true
if let Some(index) = self.table_state.selected() {
let sender = MESSAGE_SENDER.get().unwrap();
sender.send(AppMessage::MovieManagerMessage(MovieManagerMessage::SaveNfo((self.results[index].inner.id, self.movie_fs_id, self.movie_path.clone())))).unwrap();
return true;
}
false
} else {
false
}
} else if kev.code == KeyCode::Up && self.results.len() > 0 {
} else if self.selected == 2 && kev.code == KeyCode::Up && self.results.len() > 0 {
self.table_state.select(self.table_state.selected().map(|c| (c + self.results.len() - 1) % self.results.len()));
true
} else if kev.code == KeyCode::Down && self.results.len() > 0 {
} else if self.selected == 2 && kev.code == KeyCode::Down && self.results.len() > 0 {
self.table_state.select(self.table_state.selected().map(|c| (c + 1) % self.results.len()).or(Some(0)));
true
} else if kev.code == KeyCode::Tab {
Expand All @@ -128,9 +132,21 @@ impl MovieSearchState {
self.selected = (self.selected + 2) % 3;
true
} else {
false
if self.selected == 0 {
self.query_state.input(kev)
} else if self.selected == 1 {
self.send_state.input(kev)
} else {
false
}
}
},
AppEvent::MovieManagerEvent(MovieManagerEvent::SearchResults(results)) => {
self.results = results;
self.table_state.select(None);
self.is_loading = false;
true
},
_ => { false },
}
}
Expand Down
14 changes: 7 additions & 7 deletions src/views/movie_manager/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub struct MovieTable {}
#[derive(Clone, Debug, Default)]
pub struct MovieTableState {
table_state: TableState,
movies: Vec<(Movie, PathBuf)>,
movies: Vec<(Movie, usize, PathBuf)>,
is_loading: bool,
}

Expand All @@ -35,7 +35,7 @@ impl StatefulWidget for MovieTable {
}

let rows : Vec<_> = state.movies.iter()
.map(|(m, _)| {
.map(|(m, _, _)| {
let title = m.title.clone();
let year = m.premiered.as_deref().unwrap_or("".into());
let source = m.source.as_deref().unwrap_or("".into());
Expand Down Expand Up @@ -63,7 +63,7 @@ impl MovieTableState {
pub fn input(&mut self, app_event: AppEvent) -> bool {
match app_event {
AppEvent::KeyEvent(kev) => {
if kev.code == KeyCode::Char('R') && kev.modifiers == (KeyModifiers::SHIFT | KeyModifiers::CONTROL) && (!self.is_loading) {
if kev.code == KeyCode::Char('r') && (!self.is_loading) {
let sender = MESSAGE_SENDER.get().unwrap();
sender.send(AppMessage::MovieManagerMessage(MovieManagerMessage::RefreshMovies)).unwrap();
self.is_loading = true;
Expand Down Expand Up @@ -96,12 +96,12 @@ impl MovieTableState {
self.movies.push(movie);
true
},
AppEvent::MovieManagerEvent(MovieManagerEvent::MovieUpdated((movie, path))) => {
AppEvent::MovieManagerEvent(MovieManagerEvent::MovieUpdated((movie, fs_id, path))) => {
self.is_loading = false;
if let Some((ind, _)) = self.movies.iter().enumerate().filter(|(_, (_,p))| p == &path).next() {
self.movies[ind] = (movie, path);
if let Some((ind, _)) = self.movies.iter().enumerate().filter(|(_, (_,fi,p))| p == &path && fi == &fs_id).next() {
self.movies[ind] = (movie, fs_id, path);
} else {
self.movies.push((movie, path));
self.movies.push((movie, fs_id, path));
}
true
},
Expand Down

0 comments on commit d8fa3fc

Please sign in to comment.