PageRenderTime 46ms CodeModel.GetById 10ms RepoModel.GetById 1ms app.codeStats 0ms

/Renamer/Renamer/Form1.cs

https://bitbucket.org/floAr/personal
C# | 435 lines | 335 code | 61 blank | 39 comment | 74 complexity | 624b8ff307e66ac52ae2370457d86c7d MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Data;
  5. using System.Drawing;
  6. using System.Linq;
  7. using System.Text;
  8. using System.Windows.Forms;
  9. using System.IO;
  10. namespace Renamer
  11. {
  12. public partial class Form1 : Form
  13. {
  14. Video[] _videos;
  15. public Form1()
  16. {
  17. InitializeComponent();
  18. }
  19. /// <summary>
  20. /// Renames all the files using the new editted values.
  21. /// </summary>
  22. private void OnRenameClick(object sender, EventArgs e)
  23. {
  24. foreach (Video video in _videos)
  25. {
  26. File.Move(video.OriginalPath, video.NewPath);
  27. }
  28. }
  29. #region Populate Data
  30. /// <summary>
  31. /// Opens a directory for processing.
  32. /// </summary>
  33. private void OnOpenClick(object sender, EventArgs args)
  34. {
  35. OpenFileDialog dialog = new OpenFileDialog();
  36. DialogResult dialogResult = dialog.ShowDialog();
  37. if (dialogResult == System.Windows.Forms.DialogResult.OK)
  38. {
  39. DirectoryInfo dir = new DirectoryInfo(Path.GetDirectoryName(dialog.FileName));
  40. _videos = FlattenDirectories(dir).SelectMany(d => ProcessFolder(d)).ToArray();
  41. PopulateDataGrid(_videos);
  42. }
  43. }
  44. /// <summary>
  45. /// Clears and repopulates the data grid with the videos.
  46. /// </summary>
  47. private void PopulateDataGrid(IEnumerable<Video> videos)
  48. {
  49. dataGrid.Rows.Clear();
  50. foreach (Video video in videos)
  51. {
  52. string season = video.Season == -1 ? string.Empty : video.Season.ToString();
  53. string episode = video.Episode == -1 ? string.Empty : video.Episode.ToString();
  54. dataGrid.Rows.Add(video.OriginalName, video.NewName, video.Show, season, episode);
  55. if (video.OriginalName == video.NewName)
  56. dataGrid.Rows[dataGrid.Rows.Count - 1].DefaultCellStyle.BackColor = Color.LightGray;
  57. }
  58. }
  59. /// <summary>
  60. /// Returns this directory and all descendant directories.
  61. /// </summary>
  62. private IEnumerable<DirectoryInfo> FlattenDirectories(DirectoryInfo dir)
  63. {
  64. Stack<DirectoryInfo> stack = new Stack<DirectoryInfo>();
  65. stack.Push(dir);
  66. while (stack.Count != 0)
  67. {
  68. DirectoryInfo current = stack.Pop();
  69. yield return current;
  70. foreach (DirectoryInfo child in current.GetDirectories())
  71. stack.Push(child);
  72. }
  73. }
  74. /// <summary>
  75. /// Gets the Video files from the given folder.
  76. /// The Show/Season/Episode values are filled in with guesses.
  77. /// </summary>
  78. private ICollection<Video> ProcessFolder(DirectoryInfo dir)
  79. {
  80. string[] files = dir.GetFiles().Select(f => f.FullName).ToArray();
  81. string[] names = files.Select(f => Path.GetFileNameWithoutExtension(f)).ToArray();
  82. string title = GetTitle(names);
  83. Tuple<int, int>[] episodes = GetEpisodeNumbers(names);
  84. List<Video> videos = new List<Video>();
  85. for (int i = 0; i < episodes.Length; i++)
  86. {
  87. videos.Add(new Video(title, episodes[i].Item1, episodes[i].Item2, files[i]));
  88. }
  89. return videos;
  90. }
  91. #endregion Populate Data
  92. #region Title/Episode Detection
  93. /// <summary>
  94. /// Guesses the title of the show given the raw input strings.
  95. /// </summary>
  96. private string GetTitle(string[] input)
  97. {
  98. List<string> names = new List<string>();
  99. // Split the name up into contiguous parts.
  100. // Tokens like (...), {...}, [...], and episode numbers are the delimiters.
  101. for (int i = 0; i < input.Length; i++)
  102. {
  103. names.AddRange(SplitName(input[i]));
  104. }
  105. // Use the most common value.
  106. Dictionary<string, int> votes = new Dictionary<string, int>();
  107. foreach (string name in names)
  108. {
  109. int voteCount;
  110. if (!votes.TryGetValue(name, out voteCount))
  111. {
  112. voteCount = 0;
  113. }
  114. votes[name] = voteCount + 1;
  115. }
  116. return votes.OrderByDescending(v => v.Value).Select(v => v.Key).FirstOrDefault();
  117. }
  118. /// <summary>
  119. /// Split the name up into contiguous parts.
  120. /// Tokens like (...), {...}, [...], and episode numbers are the delimiters.
  121. /// Replaces all seperator characters with spaces as well.
  122. /// </summary>
  123. private ICollection<string> SplitName(string input)
  124. {
  125. List<string> nonScoped = new List<string>();
  126. // Remove scopes: (..), {...}, etc.
  127. string scopeResult = string.Empty;
  128. for (int i = 0; i < input.Length; i++)
  129. {
  130. char c = input[i];
  131. if (c == '(')
  132. {
  133. nonScoped.Add(scopeResult);
  134. scopeResult = string.Empty;
  135. i = input.IndexOf(')', i) + 1;
  136. }
  137. else if (c == '{')
  138. {
  139. nonScoped.Add(scopeResult);
  140. scopeResult = string.Empty;
  141. i = input.IndexOf('}', i) + 1;
  142. }
  143. else if (c == '[')
  144. {
  145. nonScoped.Add(scopeResult);
  146. scopeResult = string.Empty;
  147. i = input.IndexOf(']', i) + 1;
  148. }
  149. else
  150. {
  151. scopeResult = scopeResult + c;
  152. }
  153. }
  154. nonScoped.Add(scopeResult);
  155. // Remove episodes
  156. List<string> results = new List<string>();
  157. foreach (string item in nonScoped)
  158. {
  159. string[] split = item.Split(Splitters, StringSplitOptions.RemoveEmptyEntries);
  160. string episodeResult = string.Empty;
  161. for (int i = 0; i < split.Length; i++)
  162. {
  163. if (IsEpisode(split[i]))
  164. {
  165. results.Add(episodeResult);
  166. episodeResult = string.Empty;
  167. }
  168. else
  169. {
  170. episodeResult += " " + split[i];
  171. }
  172. }
  173. results.Add(episodeResult);
  174. }
  175. return results.Where(s => !string.IsNullOrEmpty(s)).Select(s => s.Trim()).ToArray();
  176. }
  177. static readonly char[] Splitters = new[] { '.', '-', '_', ' ', '(', ')', '{', '}', '[', ']', ' ' };
  178. /// <summary>
  179. /// Gets the season and episode number for all of the given strings.
  180. /// </summary>
  181. private Tuple<int,int>[] GetEpisodeNumbers(string[] names)
  182. {
  183. Tuple<int,int>[] results = new Tuple<int,int>[names.Length];
  184. for (int i = 0; i < names.Length; i++)
  185. {
  186. string name = names[i];
  187. string result = string.Empty;
  188. string[] split = name.Split(Splitters, StringSplitOptions.RemoveEmptyEntries);
  189. int resultSeason = -1;
  190. int resultEpisode = -1;
  191. foreach (string piece in split)
  192. {
  193. if (TryParseEpisode(piece, out resultEpisode, out resultSeason))
  194. {
  195. break;
  196. }
  197. resultEpisode = -1;
  198. resultSeason = -1;
  199. }
  200. results[i] = Tuple.Create(resultSeason, resultEpisode);
  201. }
  202. return results;
  203. }
  204. private bool IsEpisode(string input)
  205. {
  206. int e, s;
  207. return TryParseEpisode(input, out e, out s);
  208. }
  209. /// <summary>
  210. /// Tries to parse the season and episode from the given string.
  211. /// </summary>
  212. private bool TryParseEpisode(string input, out int resultEpisode, out int resultSeason)
  213. {
  214. resultEpisode = -1;
  215. resultSeason = -1;
  216. int episode;
  217. int season;
  218. // "01" episode only
  219. if (int.TryParse(input, out episode) && episode < 1000)
  220. {
  221. // "301" Season/Episode in one number
  222. if (episode > 99)
  223. {
  224. resultSeason = episode / 100;
  225. resultEpisode = episode % 100;
  226. }
  227. else
  228. {
  229. resultEpisode = episode;
  230. }
  231. return true;
  232. }
  233. // "Ep01" episode only
  234. string[] episodeSplit = input.Split(new[] { "Episode", "Ep", "E" }, StringSplitOptions.RemoveEmptyEntries);
  235. if (episodeSplit.Length == 1 && int.TryParse(episodeSplit[0], out episode))
  236. {
  237. resultEpisode = episode;
  238. return true;
  239. }
  240. // "S3E01" Season/Episode
  241. string[] seasonSplit = input.Split(new[] { 'S', 's', 'E', 'e', 'X', 'x' }, StringSplitOptions.RemoveEmptyEntries);
  242. if (seasonSplit.Length == 2 && int.TryParse(seasonSplit[0], out season) && int.TryParse(seasonSplit[1], out episode))
  243. {
  244. resultSeason = season;
  245. resultEpisode = episode;
  246. return true;
  247. }
  248. return false;
  249. }
  250. #endregion Title/Episode Detection
  251. #region Editing
  252. private void UpdateSelectionEditor()
  253. {
  254. Video[] selectedVideos = this.SelectedVideos;
  255. if (selectedVideos.Length == 0)
  256. {
  257. selectedVideos = _videos;
  258. }
  259. showBox.Text = string.Empty;
  260. seasonBox.Text = string.Empty;
  261. episodeBox.Text = string.Empty;
  262. seFormatBox.Text = string.Empty;
  263. eFormatBox.Text = string.Empty;
  264. if (selectedVideos.Length > 0)
  265. {
  266. if (selectedVideos.All(v => v.Show == selectedVideos[0].Show))
  267. {
  268. showBox.Text = selectedVideos[0].Show;
  269. }
  270. else
  271. showBox.Text = "Various";
  272. if (selectedVideos.All(v => v.Season == selectedVideos[0].Season))
  273. {
  274. if (selectedVideos[0].Season != -1)
  275. seasonBox.Text = selectedVideos[0].Season.ToString();
  276. }
  277. else
  278. seasonBox.Text = "Various";
  279. if (selectedVideos.All(v => v.Episode == selectedVideos[0].Episode))
  280. {
  281. if (selectedVideos[0].Episode != -1)
  282. episodeBox.Text = selectedVideos[0].Episode.ToString();
  283. }
  284. else
  285. episodeBox.Text = "Various";
  286. if (selectedVideos.All(v => v.Format.SeasonEpisode == selectedVideos[0].Format.SeasonEpisode))
  287. {
  288. seFormatBox.Text = selectedVideos[0].Format.SeasonEpisode;
  289. }
  290. else
  291. seFormatBox.Text = "Various";
  292. if (selectedVideos.All(v => v.Format.EpisodeOnly == selectedVideos[0].Format.EpisodeOnly))
  293. {
  294. eFormatBox.Text = selectedVideos[0].Format.EpisodeOnly;
  295. }
  296. else
  297. eFormatBox.Text = "Various";
  298. }
  299. }
  300. private void dataGrid_SelectionChanged(object sender, EventArgs e)
  301. {
  302. UpdateSelectionEditor();
  303. }
  304. private Video[] SelectedVideos
  305. {
  306. get
  307. {
  308. return SelectedIndexes.Select(i => _videos[i]).ToArray();
  309. }
  310. }
  311. private int[] SelectedIndexes
  312. {
  313. get
  314. {
  315. List<int> indexes = new List<int>();
  316. for (int i = 0; i < dataGrid.SelectedRows.Count; i++)
  317. {
  318. indexes.Add(dataGrid.SelectedRows[i].Index);
  319. }
  320. return indexes.ToArray();
  321. }
  322. }
  323. private void editBtn_Click(object sender, EventArgs e)
  324. {
  325. int episode;
  326. int season;
  327. if (!int.TryParse(seasonBox.Text, out season))
  328. {
  329. if (string.IsNullOrEmpty(seasonBox.Text))
  330. season = -1;
  331. else if (seasonBox.Text == "Various")
  332. season = -2;
  333. else
  334. {
  335. MessageBox.Show("Season must be a number or empty.");
  336. return;
  337. }
  338. }
  339. if (!int.TryParse(episodeBox.Text, out episode))
  340. {
  341. if (string.IsNullOrEmpty(episodeBox.Text))
  342. episode = -1;
  343. else if (episodeBox.Text == "Various")
  344. episode = -2;
  345. else
  346. {
  347. MessageBox.Show("Episode must be a number or empty.");
  348. return;
  349. }
  350. }
  351. foreach (Video video in SelectedVideos)
  352. {
  353. if (showBox.Text != "Various")
  354. video.Show = showBox.Text;
  355. if (season != -2)
  356. video.Season = season;
  357. if (episode != -2)
  358. video.Episode = episode;
  359. if (seFormatBox.Text != "Various" && !string.IsNullOrEmpty(seFormatBox.Text))
  360. video.Format.SeasonEpisode = seFormatBox.Text;
  361. if (eFormatBox.Text != "Various" && !string.IsNullOrEmpty(eFormatBox.Text))
  362. video.Format.EpisodeOnly = eFormatBox.Text;
  363. }
  364. int[] indexes = SelectedIndexes;
  365. PopulateDataGrid(_videos);
  366. dataGrid.ClearSelection();
  367. foreach (int i in indexes)
  368. {
  369. dataGrid.Rows[i].Selected = true;
  370. }
  371. }
  372. #endregion Editing
  373. }
  374. }