Compare commits

...

14 commits
2.2.2 ... main

Author SHA1 Message Date
49de0ce13c
build: Bumped version to 2.3.2 2025-04-07 21:26:27 -05:00
cd0dbcd47b
build: Updated deps to mitigate an issue in tokio 2025-04-07 21:25:19 -05:00
3757dd0a47
fix: Improved compatibility of built package 2025-03-08 16:37:23 -06:00
cf8a9ee764
build: Bumped version to 2.3.1 2025-03-08 16:06:18 -06:00
80fdc110ae
fix: Adapt to changes in deps 2025-03-08 16:05:53 -06:00
c5b1f51233
build: Updated deps 2025-03-08 15:53:54 -06:00
Sayantan Santra
3e7b8fbd5d
Merge pull request #7 from SinTan1729/dependabot/cargo/h2-0.3.26
build(deps): bump h2 from 0.3.24 to 0.3.26
2024-04-05 11:47:00 -05:00
dependabot[bot]
f979559b50
build(deps): bump h2 from 0.3.24 to 0.3.26
Bumps [h2](https://github.com/hyperium/h2) from 0.3.24 to 0.3.26.
- [Release notes](https://github.com/hyperium/h2/releases)
- [Changelog](https://github.com/hyperium/h2/blob/v0.3.26/CHANGELOG.md)
- [Commits](https://github.com/hyperium/h2/compare/v0.3.24...v0.3.26)

---
updated-dependencies:
- dependency-name: h2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-05 16:18:23 +00:00
443c8e6cc9
new: Print some message when flags are set 2024-03-11 18:23:16 -05:00
4f3f349292
chg: Slightly modified the manpage 2024-03-08 10:45:04 -06:00
51e0c84eb4
build: Bumped version to 2.3.0 2024-03-06 15:42:36 -06:00
83bf1f7af6
new: Autocomplete files generation 2024-03-06 15:41:45 -06:00
b4073357f7
fix: No need for the unwrap_or()s 2024-03-06 03:02:53 -06:00
67dcd9e378
new: Do argparsing using clap-rs instead of handwritten parser 2024-03-06 02:56:48 -06:00
8 changed files with 1174 additions and 508 deletions

1463
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,7 @@
[package]
name = "movie-rename"
version = "2.2.2"
version = "2.3.2"
build = "build.rs"
edition = "2021"
authors = ["Sayantan Santra <sayantan[dot]santra689[at]gmail[dot]com"]
license = "GPL-3.0"
@ -16,10 +17,15 @@ categories = ["command-line-utilities"]
[dependencies]
torrent-name-parser = "0.12.1"
tmdb-api = "0.5.0"
inquire = "0.6.2"
tmdb-api = "0.8.0"
inquire = "0.7.5"
load_file = "1.0.1"
tokio = { version = "1.32.0", features = ["macros", "rt-multi-thread"] }
clap = { version = "4.5.1", features = ["cargo"] }
[build-dependencies]
clap = { version = "4.5.1", features = ["cargo"] }
clap_complete = "4.5.1"
[profile.release]
strip = true

View file

@ -2,7 +2,7 @@ PREFIX := /usr/local
PKGNAME := movie-rename
build:
cargo build --release
cargo zigbuild --release --target x86_64-unknown-linux-gnu.2.34
build-debug:
cargo build
@ -19,6 +19,6 @@ uninstall:
rm -f "$(DESTDIR)$(PREFIX)/man/man1/$(PKGNAME).1"
aur: build
tar --transform 's/.*\///g' -czf $(PKGNAME).tar.gz target/release/$(PKGNAME) $(PKGNAME).1
tar --transform 's/.*\///g' -czf $(PKGNAME).tar.gz target/x86_64-unknown-linux-gnu/release/$(PKGNAME) target/autocomplete/* $(PKGNAME).1
.PHONY: build build-debug install clean uninstall aur

21
build.rs Normal file
View file

@ -0,0 +1,21 @@
use clap_complete::generate_to;
use clap_complete::shells::{Bash, Fish, Zsh};
use std::env;
use std::ffi::OsString;
use std::fs::{create_dir, remove_dir_all};
use std::io::Error;
include!("src/args.rs");
fn main() -> Result<(), Error> {
let target = "./target/autocomplete";
remove_dir_all(target).ok();
create_dir(target)?;
let outdir = OsString::from(target);
let mut cmd = get_command();
generate_to(Bash, &mut cmd, "movie-rename", &outdir)?;
generate_to(Fish, &mut cmd, "movie-rename", &outdir)?;
generate_to(Zsh, &mut cmd, "movie-rename", &outdir)?;
Ok(())
}

View file

@ -17,9 +17,6 @@ Performs a dry run, without actually renaming anything.
-d, --directory
Runs in directory mode. In this mode, it is assumed that the arguments are directory names, which contain exactly one movie and optionally subtitles.
.TP
-dn, -nd
Performs a dry run in directory mode.
.TP
-h, --help
Print help information.
.TP

46
src/args.rs Normal file
View file

@ -0,0 +1,46 @@
use clap::{arg, command, ArgAction, Command, ValueHint};
use std::collections::HashMap;
// Bare command generation function to help with autocompletion
pub fn get_command() -> Command {
command!()
.name("movie-rename")
.author("Sayantan Santra <sayantan.santra@gmail.com>")
.about("A simple tool to rename movies, written in Rust.")
.arg(arg!(-d --directory "Run in directory mode").action(ArgAction::SetTrue))
.arg(arg!(-n --"dry-run" "Do a dry run").action(ArgAction::SetTrue))
.arg(arg!(-l --"i-feel-lucky" "Always choose the first option").action(ArgAction::SetTrue))
.arg(
arg!([entries] "The files/directories to be processed")
.trailing_var_arg(true)
.num_args(1..)
.value_hint(ValueHint::AnyPath)
.required(true),
)
// Use -v instead of -V for version
.disable_version_flag(true)
.arg(arg!(-v --version "Print version").action(ArgAction::Version))
.arg_required_else_help(true)
}
// Function to process the passed arguments
pub fn process_args() -> (Vec<String>, HashMap<String, bool>) {
let matches = get_command().get_matches();
// Generate the settings HashMap from read flags
let mut settings = HashMap::new();
for id in matches.ids().map(|x| x.as_str()) {
if id != "entries" {
settings.insert(id.to_string(), matches.get_flag(id));
}
}
// Every unmatched argument should be treated as a file entry
let entries: Vec<String> = matches
.get_many::<String>("entries")
.expect("No entries provided!")
.cloned()
.collect();
(entries, settings)
}

View file

@ -2,23 +2,20 @@ use inquire::{
ui::{Color, IndexPrefix, RenderConfig, Styled},
InquireError, Select,
};
use std::{collections::HashMap, fs, path::Path, process::exit};
use std::{collections::HashMap, fs, path::Path};
use tmdb_api::{
client::{reqwest::ReqwestExecutor, Client},
movie::{credits::MovieCredits, search::MovieSearch},
prelude::Command,
Client,
};
use torrent_name_parser::Metadata;
use crate::structs::{get_long_lang, Language, MovieEntry};
// Get the version from Cargo.toml
const VERSION: &str = env!("CARGO_PKG_VERSION");
// Function to process movie entries
pub async fn process_file(
filename: &String,
tmdb: &Client,
tmdb: &Client<ReqwestExecutor>,
pattern: &str,
dry_run: bool,
lucky: bool,
@ -208,86 +205,8 @@ pub async fn process_file(
(filename_without_ext, Some(new_name_base), true)
}
// Function to process the passed arguments
pub fn process_args(mut args: Vec<String>) -> (Vec<String>, HashMap<&'static str, bool>) {
// Remove the entry corresponding to the running process
args.remove(0);
let mut entries = Vec::new();
let mut settings = HashMap::from([("dry_run", false), ("directory", false), ("lucky", false)]);
for arg in args {
match arg.as_str() {
"--help" | "-h" => {
println!(" The expected syntax is:");
println!(
" movie-rename <filename(s)> [-n|--dry-run] [-d|--directory] [-l|--i-feel-lucky] [-v|--version]"
);
println!(
" There needs to be a config file named config in the $XDG_CONFIG_HOME/movie-rename/ directory."
);
println!(" It should consist of two lines. The first line should have your TMDb API key.");
println!(
" The second line should have a pattern, that will be used for the rename."
);
println!(" In the pattern, the variables need to be enclosed in {{}}, the supported variables are `title`, `year` and `director`.");
println!(
" Default pattern is `{{title}} ({{year}}) - {{director}}`. Extension is always kept."
);
println!(" Passing --directory or -d assumes that the arguments are directory names, which contain exactly one movie and optionally subtitles.");
println!(" Passing --dry-run or -n does a dry tun and only prints out the new names, without actually doing anything.");
println!(
" You can join the short flags -d, -n and -l together (e.g. -dn or -dln)."
);
println!(" Passing --version or -v shows version and exits.");
println!(" Pass --help to get this again.");
exit(0);
}
"--version" | "-v" => {
println!("movie-rename {VERSION}");
exit(0);
}
"--dry-run" => {
println!("Doing a dry run...");
settings.entry("dry_run").and_modify(|x| *x = true);
}
"--i-feel-lucky" => {
println!("Choosing the first option automatically...");
settings.entry("lucky").and_modify(|x| *x = true);
}
"--directory" => {
println!("Running in directory mode...");
settings.entry("directory").and_modify(|x| *x = true);
}
other => {
if other.starts_with('-') {
if !other.starts_with("--") && other.len() < 5 {
// Process short args
if other.contains('l') {
println!("Choosing the first option automatically...");
settings.entry("lucky").and_modify(|x| *x = true);
}
if other.contains('d') {
println!("Running in directory mode...");
settings.entry("directory").and_modify(|x| *x = true);
}
if other.contains('n') {
println!("Doing a dry run...");
settings.entry("dry_run").and_modify(|x| *x = true);
}
} else {
eprintln!("Unknown argument passed: {other}");
exit(1);
}
} else {
entries.push(arg);
}
}
}
}
(entries, settings)
}
// RenderConfig for the menu items
fn get_render_config() -> RenderConfig {
fn get_render_config() -> RenderConfig<'static> {
let mut render_config = RenderConfig::default();
render_config.option_index_prefix = IndexPrefix::Simple;

View file

@ -1,19 +1,31 @@
use load_file::{self, load_str};
use std::{collections::HashMap, env, fs, path::Path, process::exit};
use tmdb_api::Client;
use tmdb_api::client::{reqwest::ReqwestExecutor, Client};
// Import all the modules
mod functions;
use functions::{process_args, process_file};
use functions::process_file;
mod args;
mod structs;
#[tokio::main]
async fn main() {
// Read arguments from commandline
let args: Vec<String> = env::args().collect();
// Process the passed arguments
let (entries, settings) = process_args(args);
let (entries, settings) = args::process_args();
let flag_dry_run = settings["dry-run"];
let flag_directory = settings["directory"];
let flag_lucky = settings["i-feel-lucky"];
// Print some message when flags are set.
if flag_dry_run {
println!("Doing a dry run. No files will be modified.")
}
if flag_directory {
println!("Running in directory mode...")
}
if flag_lucky {
println!("Automatically selecting the first entry...")
}
// Try to read config file, or display error
let mut config_file = env::var("XDG_CONFIG_HOME").unwrap_or(String::from("$HOME"));
@ -38,25 +50,17 @@ async fn main() {
}
// Create TMDb object for API calls
let tmdb = Client::new(String::from(api_key));
let tmdb = Client::<ReqwestExecutor>::new(String::from(api_key));
// Iterate over entries
for entry in entries {
// Check if the file/directory exists on disk and run necessary commands
match settings["directory"] {
match flag_directory {
// Normal file
false => {
if Path::new(entry.as_str()).is_file() {
// Process the filename for movie entries
process_file(
&entry,
&tmdb,
pattern,
settings["dry_run"],
settings["lucky"],
None,
)
.await;
process_file(&entry, &tmdb, pattern, flag_dry_run, flag_lucky, None).await;
} else {
eprintln!("The file {entry} wasn't found on disk, skipping...");
continue;
@ -77,8 +81,8 @@ async fn main() {
&filename,
&tmdb,
pattern,
settings["dry_run"],
settings["lucky"],
flag_dry_run,
flag_lucky,
Some(&movie_list),
)
.await;
@ -109,7 +113,7 @@ async fn main() {
);
} else {
println!("[directory] '{entry_clean}' -> '{name}'",);
if !settings["dry_run"] {
if !flag_dry_run {
if !Path::new(name.as_str()).is_dir() {
fs::rename(entry, name)
.expect("Unable to rename directory!");