From 4f1c6d1ab774c6f2e5b18365f258d1cc2db5c72a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B5=D0=B9=20=D0=91=D0=B0?= =?UTF-8?q?=D1=80=D0=B0=D0=B1=D0=B0=D0=BD=D0=BE=D0=B2?= Date: Fri, 17 Apr 2026 10:34:25 +0300 Subject: [PATCH] v 0.1.1 --- .env.example | 23 +++++++++++------ README.md | 47 +++++++++++++++++++++++++++------- src/main.rs | 10 +++++--- src/nexrender.rs | 65 +++++++++++++++++++++++++++++++++--------------- 4 files changed, 105 insertions(+), 40 deletions(-) diff --git a/.env.example b/.env.example index de92aed..4b535ed 100644 --- a/.env.example +++ b/.env.example @@ -1,12 +1,19 @@ # Synology NAS -NAS_FQDN=https://your-nas.domain.com -NAS_USER=your_username -NAS_PASS=your_password -NAS_FILE=/team-folders/path/to/your/file.osheet +NAS_FQDN="https://your-nas.example.com" +NAS_USER="your_username" +NAS_PASS="your_password" +NAS_FILE="/Team Folder/path/to/file.osheet" + +#Loging +RUST_LOG="info" # Nexrender -NEXRENDER_API_URL=http://your-nexrender-server:3000/api/v1/jobs -OUTPUT_FOLDER=//your-storage/path/to/output +NEXRENDER_API_URL="http://nexrender-server:3050/api/v1/jobs" +OUTPUT_FOLDER="/path/to/output" -# Logging -RUST_LOG=info \ No newline at end of file +# After Effects Templates +TEMPLATE_DOUBLE_SRC="file:///path/to/double_team_template.aepx" +TEMPLATE_SINGLE_SRC="file:///path/to/single_team_template.aepx" +TEMPLATE_COMPOSITION="main" +TEMPLATE_OUTPUT_MODULE="h264" +TEMPLATE_OUTPUT_EXT="mp4" \ No newline at end of file diff --git a/README.md b/README.md index 8e2ef0c..0f8f464 100644 --- a/README.md +++ b/README.md @@ -113,15 +113,44 @@ RUST_LOG=debug cargo run ### Переменные окружения -| Переменная | Обязательна | Описание | -|---------------------|-------------|------------------------------------------------------| -| `NAS_FQDN` | Да | URL NAS Synology с протоколом и портом | -| `NAS_USER` | Да | Имя пользователя учетной записи Synology | -| `NAS_PASS` | Да | Пароль учетной записи Synology | -| `NAS_FILE` | Да | Полный путь к файлу .osheet на NAS | -| `NEXRENDER_API_URL` | Да | Конечная точка API сервера Nexrender | -| `OUTPUT_FOLDER` | Да | Сетевой путь для рендеренных видео | -| `RUST_LOG` | Нет | Уровень детализации логирования (по умолчанию: info) | +| Переменная | Обязательна | Описание | +|-------------------------|-------------|------------------------------------------------------| +| `NAS_FQDN` | Да | URL NAS Synology с протоколом и портом | +| `NAS_USER` | Да | Имя пользователя учетной записи Synology | +| `NAS_PASS` | Да | Пароль учетной записи Synology | +| `NAS_FILE` | Да | Полный путь к файлу .osheet на NAS | +| `NEXRENDER_API_URL` | Да | Конечная точка API сервера Nexrender | +| `OUTPUT_FOLDER` | Да | Сетевой путь для рендеренных видео | +| `RUST_LOG` | Нет | Уровень детализации логирования (по умолчанию: info) | +| `TEMPLATE_DOUBLE_SRC` | Да | Путь к AEP-шаблону для двух команд | +| `TEMPLATE_SINGLE_SRC` | Да | Путь к AEP-шаблону для одной команды | +| `TEMPLATE_COMPOSITION` | Да | Имя композиции в проекте AE (например, `main`) | +| `TEMPLATE_OUTPUT_MODULE`| Да | Имя модуля вывода в AE (например, `h264`) | +| `TEMPLATE_OUTPUT_EXT` | Да | Расширение выходного файла (например, `mp4`) | + +### Пример файла `.env` + +```env +# Synology NAS +NAS_FQDN="https://your-nas.example.com" +NAS_USER="your_username" +NAS_PASS="your_password" +NAS_FILE="/Team Folder/path/to/file.osheet" + +#Loging +RUST_LOG="info" + +# Nexrender +NEXRENDER_API_URL="http://nexrender-server:3050/api/v1/jobs" +OUTPUT_FOLDER="/path/to/output" + +# After Effects Templates +TEMPLATE_DOUBLE_SRC="file:///path/to/double_team_template.aepx" +TEMPLATE_SINGLE_SRC="file:///path/to/single_team_template.aepx" +TEMPLATE_COMPOSITION="main" +TEMPLATE_OUTPUT_MODULE="h264" +TEMPLATE_OUTPUT_EXT="mp4" +``` ## Структура электронной таблицы diff --git a/src/main.rs b/src/main.rs index ce9fcad..3a1fbd5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -357,10 +357,12 @@ async fn process_nexrender_jobs(workbook: &ExcelWorkbook, config: &Config) -> Re for job in jobs { let client = http_client.clone(); let api_url = config.nexrender_api_url.clone(); - let output_folder = config.output_folder.clone(); + let config_clone = config.clone(); // ✅ Клонируем весь конфиг tasks.push(tokio::spawn(async move { - let nexrender_job = job.to_nexrender_job(&output_folder); + let nexrender_job = job.to_nexrender_job(&config_clone); // ✅ Передаём Config + info!("Submitting job: {}", job.outfile_name); + let response = client.post(&api_url).json(&nexrender_job).send().await?; if response.status().is_success() { @@ -371,7 +373,9 @@ async fn process_nexrender_jobs(workbook: &ExcelWorkbook, config: &Config) -> Re Ok(None) } } else { - error!("Failed to submit job: {}", job.outfile_name); + let status = response.status(); + let text = response.text().await.unwrap_or_default(); + error!("Failed to submit job ({}): {}", status, text); Ok(None) } })); diff --git a/src/nexrender.rs b/src/nexrender.rs index 311c8cd..c9ac6d8 100644 --- a/src/nexrender.rs +++ b/src/nexrender.rs @@ -90,22 +90,32 @@ pub struct Template { pub output_ext: String, } -impl Default for Template { - fn default() -> Self { +impl Template { + pub fn double( + src: String, + composition: String, + output_module: String, + output_ext: String, + ) -> Self { Self { - src: "file:///c:/users/virtVmix-2/Downloads/PackShot_DOUBLE.aepx".to_string(), - composition: "pack".to_string(), - output_module: "Start_h264".to_string(), - output_ext: "mp4".to_string(), + src, + composition, + output_module, + output_ext, } } -} -impl Template { - pub fn single() -> Self { + pub fn single( + src: String, + composition: String, + output_module: String, + output_ext: String, + ) -> Self { Self { - src: "file:///c:/users/virtVmix-2/Downloads/PackShot_SINGLE.aepx".to_string(), - ..Default::default() + src, + composition, + output_module, + output_ext, } } } @@ -292,17 +302,32 @@ impl JobData { variants } - pub fn to_nexrender_job(&self, output_folder: &str) -> NexrenderJob { + // ЕДИНСТВЕННАЯ реализация метода + pub fn to_nexrender_job(&self, config: &crate::config::Config) -> NexrenderJob { let template = if self.team_b.is_empty() { - Template::single() + Template::single( + config.template_single_src.clone(), + config.template_composition.clone(), + config.template_output_module.clone(), + config.template_output_ext.clone(), + ) } else { - Template::default() + Template::double( + config.template_double_src.clone(), + config.template_composition.clone(), + config.template_output_module.clone(), + config.template_output_ext.clone(), + ) }; - let output_path = format!("{}/{}.mp4", output_folder, self.outfile_name); + let output_path = format!( + "{}/{}.{}", + config.output_folder, self.outfile_name, config.template_output_ext + ); + let mut actions = Actions::default(); actions.postrender.push(PostrenderAction::Copy { - input: "encoded.mp4".to_string(), + input: format!("encoded.{}", config.template_output_ext), output: output_path, }); @@ -503,16 +528,16 @@ impl LogoRegistry { Self::default() } - pub fn insert(&mut self, team: String, sport: String, link: String) { - self.logos.insert((team, sport), link); - } - pub fn with_capacity(capacity: usize) -> Self { Self { logos: HashMap::with_capacity(capacity), } } + pub fn insert(&mut self, team: String, sport: String, link: String) { + self.logos.insert((team, sport), link); + } + pub fn find(&self, team: &str, sport: &str) -> Option { if let Some(link) = self.logos.get(&(team.to_string(), sport.to_string())) { return Some(link.clone());