using Newtonsoft.Json; using System; using System.Collections.Generic; using System.IO; using System.Reflection; using System.Text.RegularExpressions; using System.Threading.Tasks; namespace EpisodeRenamer { class EpisodeRenamer { private class PatternConfig { public bool Enabled { get; set; } 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 Patterns { get; set; } public List Extensions { get; set; } } private static List patterns; private static HashSet 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; Start = start; End = end; } } static async Task Main(string[] args) { if (args.Length > 0) { try { LoadConfiguration(); } catch (Exception ex) { Console.WriteLine($"Ошибка загрузки конфигурации: {ex.Message}"); Console.WriteLine("Программа будет завершена."); Console.ReadLine(); return; } foreach (string line in args) if (Directory.Exists(line)) { ProcessFolder(line); Console.WriteLine(); } Console.ReadLine(); return; } // Создаем экземпляр слушателя using (var hotkeyListener = new HotkeyListener()) { // Подписываемся на события hotkeyListener.OnHotkeyPressed += (combination) => { switch (combination) { case "ControlAltR": LoadConfiguration(true); break; case "ControlC": Environment.Exit(0); break; default: break; } }; hotkeyListener.OnError += (ex) => Console.WriteLine($"Ошибка: {ex.Message}"); hotkeyListener.RegisterCombination("ControlAltR", new[] { HotkeyListener.VirtualKeyCodes.VK_CONTROL, HotkeyListener.VirtualKeyCodes.VK_ALT, HotkeyListener.VirtualKeyCodes.VK_R }); hotkeyListener.RegisterCombination("ControlC", new[] { HotkeyListener.VirtualKeyCodes.VK_CONTROL, 0x43 }); // Запускаем прослушивание await hotkeyListener.StartListeningAsync(); // Основной цикл программы await RunMainProgramLoop(); // Останавливаем слушатель (автоматически вызывается в Dispose) await hotkeyListener.StopListeningAsync(); } } static async Task RunMainProgramLoop() { Console.Title = Assembly.GetExecutingAssembly().GetCustomAttribute().Title; try { LoadConfiguration(); } catch (Exception ex) { Console.WriteLine($"Ошибка загрузки конфигурации: {ex.Message}"); Console.WriteLine("Программа будет завершена."); Console.ReadLine(); return; } Console.WriteLine("Чтобы оставить текущую директорию нажмите 'Enter'"); Console.WriteLine("Чтобы перезагрузить конфигурацию нажмите 'Ctrl + Alt + R'"); Console.WriteLine("Чтобы выйти нажмите 'Ctrl + C'"); while (true) { try { Console.Write("\nВведите путь до папки с эпизодами: "); string input = Console.ReadLine() ?? string.Empty; input = input.Trim(); 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 void LoadConfiguration(bool reloadConfiguration = false) { if (reloadConfiguration) { extensions.Clear(); patterns.Clear(); GC.Collect(); 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(json); // Загружаем расширения файлов extensions = new HashSet(config.Extensions, StringComparer.OrdinalIgnoreCase); // Загружаем паттерны patterns = new List(); 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; } } }