Update files
Signed-off-by: Lev Rusanov <30170278+JDM170@users.noreply.github.com>
This commit is contained in:
@@ -48,6 +48,7 @@
|
|||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="KeyListener.cs" />
|
||||||
<Compile Include="Program.cs" />
|
<Compile Include="Program.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
339
KeyListener.cs
Normal file
339
KeyListener.cs
Normal file
@@ -0,0 +1,339 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace EpisodeRenamer
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Класс для обработки горячих клавиш с использованием Task
|
||||||
|
/// </summary>
|
||||||
|
public class HotkeyListener : IDisposable
|
||||||
|
{
|
||||||
|
#region Windows API Imports
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
private static extern short GetAsyncKeyState(int vKey);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll")]
|
||||||
|
private static extern IntPtr GetConsoleWindow();
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
private static extern bool SetForegroundWindow(IntPtr hWnd);
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Key Codes
|
||||||
|
private static class VirtualKeyCodes
|
||||||
|
{
|
||||||
|
public const int VK_CONTROL = 0x11;
|
||||||
|
public const int VK_ALT = 0x12;
|
||||||
|
public const int VK_SHIFT = 0x10;
|
||||||
|
public const int VK_R = 0x52;
|
||||||
|
public const int VK_ESCAPE = 0x1B;
|
||||||
|
// Добавьте другие коды клавиш по необходимости
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Events and Delegates
|
||||||
|
/// <summary>
|
||||||
|
/// Делегат для события нажатия комбинации клавиш
|
||||||
|
/// </summary>
|
||||||
|
public delegate void HotkeyEventHandler(string combinationName);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Событие, возникающее при нажатии зарегистрированной комбинации клавиш
|
||||||
|
/// </summary>
|
||||||
|
public event HotkeyEventHandler OnHotkeyPressed;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Событие, возникающее при ошибке в работе слушателя
|
||||||
|
/// </summary>
|
||||||
|
public event Action<Exception> OnError;
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Private Fields
|
||||||
|
private readonly List<KeyCombination> _keyCombinations;
|
||||||
|
private CancellationTokenSource _cancellationTokenSource;
|
||||||
|
private Task _listenerTask;
|
||||||
|
private bool _isDisposed;
|
||||||
|
private readonly object _lockObject = new object();
|
||||||
|
private bool _isRunning;
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Key Combination Class
|
||||||
|
/// <summary>
|
||||||
|
/// Класс для описания комбинации клавиш
|
||||||
|
/// </summary>
|
||||||
|
private class KeyCombination
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public int[] KeyCodes { get; set; }
|
||||||
|
public int CooldownMs { get; set; } = 300;
|
||||||
|
public DateTime LastTriggered { get; set; } = DateTime.MinValue;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constructor
|
||||||
|
/// <summary>
|
||||||
|
/// Создает новый экземпляр слушателя горячих клавиш
|
||||||
|
/// </summary>
|
||||||
|
public HotkeyListener()
|
||||||
|
{
|
||||||
|
_keyCombinations = new List<KeyCombination>();
|
||||||
|
_cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
|
||||||
|
// Добавляем комбинацию по умолчанию
|
||||||
|
RegisterCombination("ControlAltR",
|
||||||
|
new[] { VirtualKeyCodes.VK_CONTROL, VirtualKeyCodes.VK_ALT, VirtualKeyCodes.VK_R });
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Public Methods
|
||||||
|
/// <summary>
|
||||||
|
/// Регистрирует новую комбинацию клавиш для отслеживания
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Имя комбинации</param>
|
||||||
|
/// <param name="keyCodes">Коды клавиш (все должны быть нажаты одновременно)</param>
|
||||||
|
/// <param name="cooldownMs">Время задержки между срабатываниями (мс)</param>
|
||||||
|
public void RegisterCombination(string name, int[] keyCodes, int cooldownMs = 300)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(name))
|
||||||
|
throw new ArgumentException("Имя комбинации не может быть пустым", nameof(name));
|
||||||
|
|
||||||
|
if (keyCodes == null || keyCodes.Length == 0)
|
||||||
|
throw new ArgumentException("Массив кодов клавиш не может быть пустым", nameof(keyCodes));
|
||||||
|
|
||||||
|
lock (_lockObject)
|
||||||
|
{
|
||||||
|
var existing = _keyCombinations.Find(k => k.Name == name);
|
||||||
|
if (existing != null)
|
||||||
|
{
|
||||||
|
_keyCombinations.Remove(existing);
|
||||||
|
}
|
||||||
|
|
||||||
|
_keyCombinations.Add(new KeyCombination
|
||||||
|
{
|
||||||
|
Name = name,
|
||||||
|
KeyCodes = keyCodes,
|
||||||
|
CooldownMs = cooldownMs
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Удаляет комбинацию клавиш из отслеживания
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Имя комбинации</param>
|
||||||
|
public void UnregisterCombination(string name)
|
||||||
|
{
|
||||||
|
lock (_lockObject)
|
||||||
|
{
|
||||||
|
var combination = _keyCombinations.Find(k => k.Name == name);
|
||||||
|
if (combination != null)
|
||||||
|
{
|
||||||
|
_keyCombinations.Remove(combination);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Запускает прослушивание горячих клавиш
|
||||||
|
/// </summary>
|
||||||
|
public async Task StartListeningAsync()
|
||||||
|
{
|
||||||
|
if (_isRunning)
|
||||||
|
return;
|
||||||
|
|
||||||
|
lock (_lockObject)
|
||||||
|
{
|
||||||
|
if (_isRunning)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
_isRunning = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Переводим консоль на передний план для лучшего захвата клавиш
|
||||||
|
BringConsoleToFront();
|
||||||
|
|
||||||
|
_listenerTask = Task.Run(() => ListenForHotkeys(_cancellationTokenSource.Token),
|
||||||
|
_cancellationTokenSource.Token);
|
||||||
|
|
||||||
|
await Task.CompletedTask;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
OnError?.Invoke(ex);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Останавливает прослушивание горячих клавиш
|
||||||
|
/// </summary>
|
||||||
|
public async Task StopListeningAsync()
|
||||||
|
{
|
||||||
|
if (!_isRunning)
|
||||||
|
return;
|
||||||
|
|
||||||
|
lock (_lockObject)
|
||||||
|
{
|
||||||
|
if (!_isRunning)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_isRunning = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_cancellationTokenSource?.Cancel();
|
||||||
|
|
||||||
|
if (_listenerTask != null && !_listenerTask.IsCompleted)
|
||||||
|
{
|
||||||
|
await _listenerTask.ContinueWith(t =>
|
||||||
|
{
|
||||||
|
// Игнорируем исключения отмены задачи
|
||||||
|
if (t.IsFaulted) OnError?.Invoke(t.Exception);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
// Ожидаемое исключение при отмене
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
OnError?.Invoke(ex);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Получает статус работы слушателя
|
||||||
|
/// </summary>
|
||||||
|
public bool IsListening => _isRunning;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Получает список зарегистрированных комбинаций
|
||||||
|
/// </summary>
|
||||||
|
public IReadOnlyList<string> RegisteredCombinations
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
lock (_lockObject)
|
||||||
|
{
|
||||||
|
return _keyCombinations.ConvertAll(k => k.Name).AsReadOnly();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Private Methods
|
||||||
|
private async Task ListenForHotkeys(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while (!cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
await Task.Delay(10, cancellationToken); // Небольшая задержка для снижения нагрузки
|
||||||
|
|
||||||
|
lock (_lockObject)
|
||||||
|
{
|
||||||
|
foreach (var combination in _keyCombinations)
|
||||||
|
{
|
||||||
|
if (IsCombinationPressed(combination.KeyCodes))
|
||||||
|
{
|
||||||
|
// Проверяем cooldown
|
||||||
|
if ((DateTime.Now - combination.LastTriggered).TotalMilliseconds >= combination.CooldownMs)
|
||||||
|
{
|
||||||
|
combination.LastTriggered = DateTime.Now;
|
||||||
|
TriggerHotkeyEvent(combination.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
// Ожидаемое исключение при отмене
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
OnError?.Invoke(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsCombinationPressed(int[] keyCodes)
|
||||||
|
{
|
||||||
|
foreach (var keyCode in keyCodes)
|
||||||
|
{
|
||||||
|
if (!IsKeyPressed(keyCode))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsKeyPressed(int keyCode)
|
||||||
|
{
|
||||||
|
return (GetAsyncKeyState(keyCode) & 0x8000) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TriggerHotkeyEvent(string combinationName)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
OnHotkeyPressed?.Invoke(combinationName);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
OnError?.Invoke(new InvalidOperationException(
|
||||||
|
$"Ошибка при обработке события для комбинации {combinationName}", ex));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BringConsoleToFront()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var consoleHandle = GetConsoleWindow();
|
||||||
|
SetForegroundWindow(consoleHandle);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// Не критичная ошибка, просто логируем
|
||||||
|
OnError?.Invoke(new InvalidOperationException("Не удалось перевести консоль на передний план", ex));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region IDisposable Implementation
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (!_isDisposed)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
StopListeningAsync().GetAwaiter().GetResult();
|
||||||
|
_cancellationTokenSource?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
_isDisposed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~HotkeyListener()
|
||||||
|
{
|
||||||
|
Dispose(false);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
356
Program.cs
356
Program.cs
@@ -1,185 +1,233 @@
|
|||||||
using System;
|
using Newtonsoft.Json;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using Newtonsoft.Json;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
class EpisodeRenamer
|
namespace EpisodeRenamer
|
||||||
{
|
{
|
||||||
private class PatternConfig
|
class EpisodeRenamer
|
||||||
{
|
{
|
||||||
public bool Enabled { get; set; }
|
private class PatternConfig
|
||||||
public string Regex { get; set; }
|
|
||||||
public int? Start { get; set; }
|
|
||||||
public int? End { get; set; }
|
|
||||||
public bool IgnoreCase { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
private class Config
|
|
||||||
{
|
|
||||||
public List<PatternConfig> Patterns { get; set; }
|
|
||||||
public List<string> Extensions { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
private static List<Pattern> patterns;
|
|
||||||
private static HashSet<string> extensions;
|
|
||||||
|
|
||||||
private class Pattern
|
|
||||||
{
|
|
||||||
public Regex Regex { get; }
|
|
||||||
public int? Start { get; }
|
|
||||||
public int? End { get; }
|
|
||||||
|
|
||||||
public Pattern(Regex regex, int? start, int? end)
|
|
||||||
{
|
{
|
||||||
Regex = regex;
|
public bool Enabled { get; set; }
|
||||||
Start = start;
|
public string Regex { get; set; }
|
||||||
End = end;
|
public int? Start { get; set; }
|
||||||
}
|
public int? End { get; set; }
|
||||||
}
|
public bool IgnoreCase { get; set; }
|
||||||
|
|
||||||
static void Main()
|
|
||||||
{
|
|
||||||
Console.Title = Assembly.GetExecutingAssembly().GetCustomAttribute<AssemblyTitleAttribute>().Title;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
LoadConfiguration();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Ошибка загрузки конфигурации: {ex.Message}");
|
|
||||||
Console.WriteLine("Программа будет завершена.");
|
|
||||||
Console.ReadLine();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.CancelKeyPress += (sender, e) =>
|
private class Config
|
||||||
{
|
{
|
||||||
Console.WriteLine("\nВыход из программы.");
|
public List<PatternConfig> Patterns { get; set; }
|
||||||
Environment.Exit(0);
|
public List<string> Extensions { get; set; }
|
||||||
};
|
}
|
||||||
|
|
||||||
Console.WriteLine("Чтобы оставить текущую директорию нажмите 'Enter'");
|
private static List<Pattern> patterns;
|
||||||
Console.WriteLine("Чтобы выйти нажмите 'Ctrl + C'");
|
private static HashSet<string> extensions;
|
||||||
|
|
||||||
while (true)
|
private class Pattern
|
||||||
{
|
{
|
||||||
|
public Regex Regex { get; }
|
||||||
|
public int? Start { get; }
|
||||||
|
public int? End { get; }
|
||||||
|
|
||||||
|
public Pattern(Regex regex, int? start, int? end)
|
||||||
|
{
|
||||||
|
Regex = regex;
|
||||||
|
Start = start;
|
||||||
|
End = end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async Task Main(string[] args)
|
||||||
|
{
|
||||||
|
// Создаем экземпляр слушателя
|
||||||
|
using (var hotkeyListener = new HotkeyListener())
|
||||||
|
{
|
||||||
|
// Подписываемся на события
|
||||||
|
hotkeyListener.OnHotkeyPressed += (combination) =>
|
||||||
|
{
|
||||||
|
switch (combination)
|
||||||
|
{
|
||||||
|
case "ControlAltR":
|
||||||
|
LoadConfiguration(true);
|
||||||
|
break;
|
||||||
|
case "ControlC":
|
||||||
|
Console.WriteLine("\nВыход из программы.");
|
||||||
|
Environment.Exit(0);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
hotkeyListener.OnError += (ex) => Console.WriteLine($"Ошибка: {ex.Message}");
|
||||||
|
hotkeyListener.RegisterCombination("ControlC", new[] { 0x11, 0x43 });
|
||||||
|
|
||||||
|
// Запускаем прослушивание
|
||||||
|
await hotkeyListener.StartListeningAsync();
|
||||||
|
|
||||||
|
// Основной цикл программы
|
||||||
|
await RunMainProgramLoop();
|
||||||
|
|
||||||
|
// Останавливаем слушатель (автоматически вызывается в Dispose)
|
||||||
|
await hotkeyListener.StopListeningAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async Task RunMainProgramLoop()
|
||||||
|
{
|
||||||
|
Console.Title = Assembly.GetExecutingAssembly().GetCustomAttribute<AssemblyTitleAttribute>().Title;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Console.Write("\nВведите путь до папки с эпизодами: ");
|
LoadConfiguration();
|
||||||
string input = Console.ReadLine().Trim() ?? string.Empty;
|
|
||||||
|
|
||||||
string folder = string.IsNullOrEmpty(input)
|
|
||||||
? Directory.GetCurrentDirectory()
|
|
||||||
: Path.GetFullPath(input);
|
|
||||||
|
|
||||||
if (Directory.Exists(folder))
|
|
||||||
{
|
|
||||||
ProcessFolder(folder);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Console.WriteLine("Указанная папка не существует.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Ошибка: {ex.Message}");
|
Console.WriteLine($"Ошибка загрузки конфигурации: {ex.Message}");
|
||||||
|
Console.WriteLine("Программа будет завершена.");
|
||||||
|
Console.ReadLine();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void LoadConfiguration()
|
//Console.CancelKeyPress += (sender, e) =>
|
||||||
{
|
//{
|
||||||
string configPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "config.json");
|
// Console.WriteLine("\nВыход из программы.");
|
||||||
|
// Environment.Exit(0);
|
||||||
|
//};
|
||||||
|
|
||||||
if (!File.Exists(configPath))
|
Console.WriteLine("Чтобы оставить текущую директорию нажмите 'Enter'");
|
||||||
{
|
Console.WriteLine("Чтобы перезагрузить конфигурацию нажмите 'Ctrl + Alt + R'");
|
||||||
throw new FileNotFoundException("Конфигурационный файл config.json не найден");
|
Console.WriteLine("Чтобы выйти нажмите 'Ctrl + C'");
|
||||||
}
|
|
||||||
|
|
||||||
string json = File.ReadAllText(configPath);
|
while (true)
|
||||||
Config config = JsonConvert.DeserializeObject<Config>(json);
|
|
||||||
|
|
||||||
// Загружаем расширения файлов
|
|
||||||
extensions = new HashSet<string>(config.Extensions, StringComparer.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
// Загружаем паттерны
|
|
||||||
patterns = new List<Pattern>();
|
|
||||||
foreach (PatternConfig patternConfig in config.Patterns)
|
|
||||||
{
|
|
||||||
if (!patternConfig.Enabled)
|
|
||||||
continue;
|
|
||||||
RegexOptions options = patternConfig.IgnoreCase ? RegexOptions.IgnoreCase : RegexOptions.None;
|
|
||||||
Regex regex = new Regex(patternConfig.Regex, options);
|
|
||||||
patterns.Add(new Pattern(regex, patternConfig.Start, patternConfig.End));
|
|
||||||
}
|
|
||||||
|
|
||||||
Console.WriteLine("Конфигурация успешно загружена.");
|
|
||||||
Console.WriteLine($"Загружено паттернов: {patterns.Count}; расширений: {extensions.Count}.");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ProcessFolder(string folder)
|
|
||||||
{
|
|
||||||
foreach (string filePath in Directory.GetFiles(folder))
|
|
||||||
{
|
|
||||||
string fileName = Path.GetFileName(filePath);
|
|
||||||
string extension = Path.GetExtension(fileName);
|
|
||||||
|
|
||||||
if (extensions.Contains(extension))
|
|
||||||
{
|
{
|
||||||
RenameFile(filePath, fileName, folder);
|
try
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void RenameFile(string filePath, string fileName, string folder)
|
|
||||||
{
|
|
||||||
string nameWithoutExt = Path.GetFileNameWithoutExtension(fileName);
|
|
||||||
|
|
||||||
foreach (var pattern in patterns)
|
|
||||||
{
|
|
||||||
Match match = pattern.Regex.Match(nameWithoutExt);
|
|
||||||
if (!match.Success)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
string found = match.Value;
|
|
||||||
int startIndex = AdjustIndex(pattern.Start, found.Length);
|
|
||||||
int endIndex = AdjustIndex(pattern.End, found.Length);
|
|
||||||
|
|
||||||
if (startIndex < 0 || endIndex < 0 || startIndex >= endIndex)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
string numberStr = found.Substring(
|
|
||||||
startIndex,
|
|
||||||
endIndex - startIndex
|
|
||||||
);
|
|
||||||
|
|
||||||
if (int.TryParse(numberStr, out int episode))
|
|
||||||
{
|
|
||||||
string newName = $"{episode:D2}{Path.GetExtension(fileName)}";
|
|
||||||
string newPath = Path.Combine(folder, newName);
|
|
||||||
|
|
||||||
if (!File.Exists(newPath))
|
|
||||||
{
|
{
|
||||||
File.Move(filePath, newPath);
|
Console.Write("\nВведите путь до папки с эпизодами: ");
|
||||||
Console.WriteLine($"\"{fileName}\" успешно переименован в \"{newName}\".");
|
string input = Console.ReadLine() ?? string.Empty;
|
||||||
return;
|
input = input.Trim();
|
||||||
}
|
|
||||||
|
|
||||||
Console.WriteLine($"Ошибка: файл \"{newName}\" уже существует.");
|
string folder = string.IsNullOrEmpty(input)
|
||||||
|
? Directory.GetCurrentDirectory()
|
||||||
|
: Path.GetFullPath(input);
|
||||||
|
|
||||||
|
if (Directory.Exists(folder))
|
||||||
|
{
|
||||||
|
ProcessFolder(folder);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine("Указанная папка не существует.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Ошибка: {ex.Message}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private static int AdjustIndex(int? index, int length)
|
private static void LoadConfiguration(bool reloadConfiguration = false)
|
||||||
{
|
{
|
||||||
if (!index.HasValue)
|
if (reloadConfiguration)
|
||||||
return length;
|
{
|
||||||
if (index.Value < 0)
|
extensions.Clear();
|
||||||
return length + index.Value;
|
patterns.Clear();
|
||||||
return index.Value;
|
Console.WriteLine("\nЗапрошена перезагрузка конфигурации.");
|
||||||
|
}
|
||||||
|
|
||||||
|
string configPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "config.json");
|
||||||
|
|
||||||
|
if (!File.Exists(configPath))
|
||||||
|
{
|
||||||
|
throw new FileNotFoundException("Конфигурационный файл config.json не найден");
|
||||||
|
}
|
||||||
|
|
||||||
|
string json = File.ReadAllText(configPath);
|
||||||
|
Config config = JsonConvert.DeserializeObject<Config>(json);
|
||||||
|
|
||||||
|
// Загружаем расширения файлов
|
||||||
|
extensions = new HashSet<string>(config.Extensions, StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
// Загружаем паттерны
|
||||||
|
patterns = new List<Pattern>();
|
||||||
|
foreach (PatternConfig patternConfig in config.Patterns)
|
||||||
|
{
|
||||||
|
if (!patternConfig.Enabled)
|
||||||
|
continue;
|
||||||
|
RegexOptions options = patternConfig.IgnoreCase ? RegexOptions.IgnoreCase : RegexOptions.None;
|
||||||
|
Regex regex = new Regex(patternConfig.Regex, options);
|
||||||
|
patterns.Add(new Pattern(regex, patternConfig.Start, patternConfig.End));
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine("Конфигурация успешно загружена.");
|
||||||
|
Console.WriteLine($"Загружено паттернов: {patterns.Count}; расширений: {extensions.Count}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ProcessFolder(string folder)
|
||||||
|
{
|
||||||
|
foreach (string filePath in Directory.GetFiles(folder))
|
||||||
|
{
|
||||||
|
string fileName = Path.GetFileName(filePath);
|
||||||
|
string extension = Path.GetExtension(fileName);
|
||||||
|
|
||||||
|
if (extensions.Contains(extension))
|
||||||
|
{
|
||||||
|
RenameFile(filePath, fileName, folder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RenameFile(string filePath, string fileName, string folder)
|
||||||
|
{
|
||||||
|
string nameWithoutExt = Path.GetFileNameWithoutExtension(fileName);
|
||||||
|
|
||||||
|
foreach (var pattern in patterns)
|
||||||
|
{
|
||||||
|
Match match = pattern.Regex.Match(nameWithoutExt);
|
||||||
|
if (!match.Success)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
string found = match.Value;
|
||||||
|
int startIndex = AdjustIndex(pattern.Start, found.Length);
|
||||||
|
int endIndex = AdjustIndex(pattern.End, found.Length);
|
||||||
|
|
||||||
|
if (startIndex < 0 || endIndex < 0 || startIndex >= endIndex)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
string numberStr = found.Substring(
|
||||||
|
startIndex,
|
||||||
|
endIndex - startIndex
|
||||||
|
);
|
||||||
|
|
||||||
|
if (int.TryParse(numberStr, out int episode))
|
||||||
|
{
|
||||||
|
string newName = $"{episode:D2}{Path.GetExtension(fileName)}";
|
||||||
|
string newPath = Path.Combine(folder, newName);
|
||||||
|
|
||||||
|
if (!File.Exists(newPath))
|
||||||
|
{
|
||||||
|
File.Move(filePath, newPath);
|
||||||
|
Console.WriteLine($"\"{fileName}\" успешно переименован в \"{newName}\".");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine($"Ошибка: файл \"{newName}\" уже существует.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int AdjustIndex(int? index, int length)
|
||||||
|
{
|
||||||
|
if (!index.HasValue)
|
||||||
|
return length;
|
||||||
|
if (index.Value < 0)
|
||||||
|
return length + index.Value;
|
||||||
|
return index.Value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user