feat: групповая отправка заданий по 3 для сохранения порядка в очереди

- Отправка заданий группами по 3 (оригинал + today + tomorrow) параллельно
- Сохранение порядка внутри группы для корректной FIFO очереди Nexrender
- Добавлен UID в логи отправки заданий
- Версия обновлена до 0.2.4
This commit is contained in:
2026-04-17 18:29:04 +03:00
parent 6f13679dd9
commit e0fa57a36e
4 changed files with 66 additions and 63 deletions

2
Cargo.lock generated
View File

@@ -10,7 +10,7 @@ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]] [[package]]
name = "ae_anons" name = "ae_anons"
version = "0.2.3" version = "0.2.4"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"askama", "askama",

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "ae_anons" name = "ae_anons"
version = "0.2.3" version = "0.2.4"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
authors = ["Alexey Barabanov <a.barabanov@tvstart.ru>"] authors = ["Alexey Barabanov <a.barabanov@tvstart.ru>"]

View File

@@ -12,17 +12,10 @@
Автоматизированная система для создания спортивных анонсов с использованием шаблонов After Effects через Nexrender, с данными из электронных таблиц Synology Office. Автоматизированная система для создания спортивных анонсов с использованием шаблонов After Effects через Nexrender, с данными из электронных таблиц Synology Office.
## Новое в версии 0.2.3 ## Новое в версии v0.2.4
- Веб-интерфейс с настраиваемым портом (WEB_PORT) - Групповая отправка заданий по 3 (оригинал + today + tomorrow) для сохранения порядка в очереди
- Тёмная/светлая тема с автоопределением ОС - Улучшена читаемость логов отправки (добавлен UID)
- Сортировка по всем колонкам
- Фильтрация по имени файла/UID
- Остановка всех активных заданий
- Автообновление каждые 60 секунд
- Улучшенное debug-логирование
- Исправлена обработка UNC/SMB путей
- Нормализация множественных пробелов в именах файлов"
## Обзор ## Обзор
@@ -576,6 +569,7 @@ ae_anons/
## 🙏 Благодарности ## 🙏 Благодарности
### Nexrender ### Nexrender
Особая благодарность проекту **[Nexrender](https://github.com/inlife/nexrender)** Особая благодарность проекту **[Nexrender](https://github.com/inlife/nexrender)**
([@inlife](https://github.com/inlife) и контрибьюторам) за создание надёжной платформы ([@inlife](https://github.com/inlife) и контрибьюторам) за создание надёжной платформы
для автоматизации After Effects. для автоматизации After Effects.
@@ -587,7 +581,7 @@ ae_anons/
- **Иконки**: [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) - **Иконки**: [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/)
- **Шрифты**: [SIL OFL 1.1](https://scripts.sil.org/OFL) - **Шрифты**: [SIL OFL 1.1](https://scripts.sil.org/OFL)
© 2023 Fonticons, Inc. https://fontawesome.com © 2023 Fonticons, Inc. <https://fontawesome.com>
## Лицензия ## Лицензия
@@ -616,7 +610,12 @@ ae_anons/
## История изменений ## История изменений
### v0.2.3 (текущая) ### v0.2.4 (текущая)
- Групповая отправка заданий по 3 (оригинал + today + tomorrow) для сохранения порядка в очереди
- Улучшена читаемость логов отправки (добавлен UID)
### v0.2.3
- Веб-интерфейс с настраиваемым портом (WEB_PORT) - Веб-интерфейс с настраиваемым портом (WEB_PORT)
- Улучшенное debug-логирование - Улучшенное debug-логирование

View File

@@ -303,13 +303,11 @@ async fn generate_and_submit_jobs(
// Вспомогательная функция для поиска значения по части ключа // Вспомогательная функция для поиска значения по части ключа
fn get_cell_fuzzy(row: &HashMap<String, String>, key_part: &str) -> Option<String> { fn get_cell_fuzzy(row: &HashMap<String, String>, key_part: &str) -> Option<String> {
// Точное совпадение
if let Some(val) = row.get(key_part) { if let Some(val) = row.get(key_part) {
if !val.is_empty() { if !val.is_empty() {
return Some(val.clone()); return Some(val.clone());
} }
} }
// Поиск по содержанию (без учёта регистра)
for (k, v) in row.iter() { for (k, v) in row.iter() {
if k.to_lowercase().contains(&key_part.to_lowercase()) && !v.is_empty() { if k.to_lowercase().contains(&key_part.to_lowercase()) && !v.is_empty() {
return Some(v.clone()); return Some(v.clone());
@@ -427,20 +425,24 @@ async fn generate_and_submit_jobs(
let http_client = Client::new(); let http_client = Client::new();
let mut submitted_jobs: Vec<(String, String)> = Vec::with_capacity(jobs.len()); let mut submitted_jobs: Vec<(String, String)> = Vec::with_capacity(jobs.len());
let mut tasks = Vec::with_capacity(jobs.len()); // Отправляем задания группами по 3 (оригинал + today + tomorrow)
for job in jobs { for chunk in jobs.chunks(3) {
let mut tasks = Vec::with_capacity(3);
for job in chunk {
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 config_clone = config.clone(); let config_clone = config.clone();
let job_owned = job.clone();
tasks.push(tokio::spawn(async move { tasks.push(tokio::spawn(async move {
let nexrender_job = job.to_nexrender_job(&config_clone); let nexrender_job = job_owned.to_nexrender_job(&config_clone);
info!("Submitting job: {}", job.outfile_name); info!("Submitting job: {}", job_owned.outfile_name);
if log_enabled!(Level::Debug) { if log_enabled!(Level::Debug) {
debug!( debug!(
"Job details: sport='{}', league='{}', channel='{}', team_a='{}', team_b='{}'", "Job details: sport='{}', league='{}', channel='{}', team_a='{}', team_b='{}'",
job.sport, job.league, job.channel, job.team_a, job.team_b job_owned.sport, job_owned.league, job_owned.channel, job_owned.team_a, job_owned.team_b
); );
} }
@@ -449,7 +451,7 @@ async fn generate_and_submit_jobs(
if response.status().is_success() { if response.status().is_success() {
let result: Value = response.json().await?; let result: Value = response.json().await?;
if let Some(uid) = result.get("uid").and_then(|u| u.as_str()) { if let Some(uid) = result.get("uid").and_then(|u| u.as_str()) {
Ok::<_, anyhow::Error>(Some((uid.to_string(), job.outfile_name))) Ok::<_, anyhow::Error>(Some((uid.to_string(), job_owned.outfile_name)))
} else { } else {
Ok(None) Ok(None)
} }
@@ -462,10 +464,11 @@ async fn generate_and_submit_jobs(
})); }));
} }
// Ждём завершения группы
for task in tasks { for task in tasks {
match task.await { match task.await {
Ok(Ok(Some((uid, outfile_name)))) => { Ok(Ok(Some((uid, outfile_name)))) => {
info!("Job submitted: {}", outfile_name); info!("Job submitted: {} (UID: {})", outfile_name, uid);
submitted_jobs.push((uid, outfile_name)); submitted_jobs.push((uid, outfile_name));
} }
Ok(Ok(None)) => { Ok(Ok(None)) => {
@@ -479,6 +482,7 @@ async fn generate_and_submit_jobs(
} }
} }
} }
}
info!( info!(
"Successfully submitted {} jobs to Nexrender", "Successfully submitted {} jobs to Nexrender",