This commit is contained in:
2026-04-17 10:34:25 +03:00
parent 23ca2dc7d1
commit 010b3b02bd
4 changed files with 105 additions and 40 deletions

View File

@@ -1,12 +1,19 @@
# Synology NAS # Synology NAS
NAS_FQDN=https://your-nas.domain.com NAS_FQDN="https://your-nas.example.com"
NAS_USER=your_username NAS_USER="your_username"
NAS_PASS=your_password NAS_PASS="your_password"
NAS_FILE=/team-folders/path/to/your/file.osheet NAS_FILE="/Team Folder/path/to/file.osheet"
#Loging
RUST_LOG="info"
# Nexrender # Nexrender
NEXRENDER_API_URL=http://your-nexrender-server:3000/api/v1/jobs NEXRENDER_API_URL="http://nexrender-server:3050/api/v1/jobs"
OUTPUT_FOLDER=//your-storage/path/to/output OUTPUT_FOLDER="/path/to/output"
# Logging # After Effects Templates
RUST_LOG=info 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"

View File

@@ -113,15 +113,44 @@ RUST_LOG=debug cargo run
### Переменные окружения ### Переменные окружения
| Переменная | Обязательна | Описание | | Переменная | Обязательна | Описание |
|---------------------|-------------|------------------------------------------------------| |-------------------------|-------------|------------------------------------------------------|
| `NAS_FQDN` | Да | URL NAS Synology с протоколом и портом | | `NAS_FQDN` | Да | URL NAS Synology с протоколом и портом |
| `NAS_USER` | Да | Имя пользователя учетной записи Synology | | `NAS_USER` | Да | Имя пользователя учетной записи Synology |
| `NAS_PASS` | Да | Пароль учетной записи Synology | | `NAS_PASS` | Да | Пароль учетной записи Synology |
| `NAS_FILE` | Да | Полный путь к файлу .osheet на NAS | | `NAS_FILE` | Да | Полный путь к файлу .osheet на NAS |
| `NEXRENDER_API_URL` | Да | Конечная точка API сервера Nexrender | | `NEXRENDER_API_URL` | Да | Конечная точка API сервера Nexrender |
| `OUTPUT_FOLDER` | Да | Сетевой путь для рендеренных видео | | `OUTPUT_FOLDER` | Да | Сетевой путь для рендеренных видео |
| `RUST_LOG` | Нет | Уровень детализации логирования (по умолчанию: info) | | `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"
```
## Структура электронной таблицы ## Структура электронной таблицы

View File

@@ -357,10 +357,12 @@ async fn process_nexrender_jobs(workbook: &ExcelWorkbook, config: &Config) -> Re
for job in jobs { for job in jobs {
let client = http_client.clone(); let client = http_client.clone();
let api_url = config.nexrender_api_url.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 { 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?; let response = client.post(&api_url).json(&nexrender_job).send().await?;
if response.status().is_success() { if response.status().is_success() {
@@ -371,7 +373,9 @@ async fn process_nexrender_jobs(workbook: &ExcelWorkbook, config: &Config) -> Re
Ok(None) Ok(None)
} }
} else { } 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) Ok(None)
} }
})); }));

View File

@@ -90,22 +90,32 @@ pub struct Template {
pub output_ext: String, pub output_ext: String,
} }
impl Default for Template { impl Template {
fn default() -> Self { pub fn double(
src: String,
composition: String,
output_module: String,
output_ext: String,
) -> Self {
Self { Self {
src: "file:///c:/users/virtVmix-2/Downloads/PackShot_DOUBLE.aepx".to_string(), src,
composition: "pack".to_string(), composition,
output_module: "Start_h264".to_string(), output_module,
output_ext: "mp4".to_string(), output_ext,
} }
} }
}
impl Template { pub fn single(
pub fn single() -> Self { src: String,
composition: String,
output_module: String,
output_ext: String,
) -> Self {
Self { Self {
src: "file:///c:/users/virtVmix-2/Downloads/PackShot_SINGLE.aepx".to_string(), src,
..Default::default() composition,
output_module,
output_ext,
} }
} }
} }
@@ -292,17 +302,32 @@ impl JobData {
variants 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() { 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 { } 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(); let mut actions = Actions::default();
actions.postrender.push(PostrenderAction::Copy { actions.postrender.push(PostrenderAction::Copy {
input: "encoded.mp4".to_string(), input: format!("encoded.{}", config.template_output_ext),
output: output_path, output: output_path,
}); });
@@ -503,16 +528,16 @@ impl LogoRegistry {
Self::default() 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 { pub fn with_capacity(capacity: usize) -> Self {
Self { Self {
logos: HashMap::with_capacity(capacity), 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<String> { pub fn find(&self, team: &str, sport: &str) -> Option<String> {
if let Some(link) = self.logos.get(&(team.to_string(), sport.to_string())) { if let Some(link) = self.logos.get(&(team.to_string(), sport.to_string())) {
return Some(link.clone()); return Some(link.clone());