Refactor manual inbox handling: update ImportManualInboxCommand to accept ManualImportItem objects, enhancing file import functionality with explicit season and episode numbers. Implement CleanupManualLeftoversAsync in IMediaStorage to remove unnecessary files after import. Update frontend components to support new import structure and improve user experience in manual media management.
build / backend (push) Successful in 1m11s
build / frontend (push) Successful in 34s
tests / backend-tests (push) Successful in 1m38s

This commit is contained in:
Leonid Pershin
2026-07-26 15:41:31 +03:00
parent 74bca20a3c
commit a95c0540d9
13 changed files with 585 additions and 90 deletions
@@ -1,4 +1,5 @@
using TeleWave.Application.Common.Interfaces;
using TeleWave.Application.Media;
using TeleWave.Domain.Media;
namespace TeleWave.Infrastructure.Media;
@@ -71,6 +72,58 @@ public sealed class FileSystemMediaStorage(MediaPathResolver paths) : IMediaStor
.ToList();
}
public Task CleanupManualLeftoversAsync(
string relativePath,
CancellationToken cancellationToken
)
{
var path = paths.ManualPath(relativePath);
var directory = Path.GetDirectoryName(path);
if (directory is null || !Directory.Exists(directory))
return Task.CompletedTask;
// Спутник — файл, чьё имя начинается с имени забранного (без расширения) и точки:
// так ловятся и «Серия.srt», и «Серия.ru.srt». Видеофайлы исключены намеренно —
// «Серия.Extended.mkv» это не мусор, а другой материал.
// Отбор — своим сравнением, а не маской поиска: в имени файла на Linux законно встречается
// «*», и маска захватила бы чужие файлы. Код удаляет — он обязан быть буквальным.
var prefix = Path.GetFileNameWithoutExtension(path) + ".";
foreach (var sibling in Directory.EnumerateFiles(directory))
{
var name = Path.GetFileName(sibling);
if (!name.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
continue;
if (MediaFormats.IsAllowed(name))
continue;
try
{
File.Delete(sibling);
}
catch (IOException)
{
// Файл занят или уже удалён — не повод валить импорт целиком.
}
}
// Опустевший подкаталог тоже мусор. Корень manual/ не трогаем: он нужен всегда.
if (
!string.Equals(directory, paths.ManualDir, StringComparison.Ordinal)
&& !Directory.EnumerateFileSystemEntries(directory).Any()
)
{
try
{
Directory.Delete(directory);
}
catch (IOException)
{
// Каталог занят — оставим как есть.
}
}
return Task.CompletedTask;
}
public Task PromoteToOriginalAsync(
MediaSource source,
string sourceToken,