private async Task <OrganizeSummary> OrganizeAsync(CopyItems items) { if (items == null) { throw new ArgumentNullException(nameof(items)); } OnProgress(this, PARSE_PROGRESS_FACTOR + 0.1, "Prepare destination"); try { OrganizeSummary summary = new OrganizeSummary(); await Task.Run(() => OrganizationThread(items, summary)); return(summary); } catch (Exception) { #if DEBUG throw; #else return(null); #endif } finally { OnProgress(this, 1.0, "Organization complete"); } }
public async Task OrganizeAsync() { if (workerRunning) { throw new InvalidOperationException("Cannot start parsing: worker currently running"); } workerRunning = true; workerAborted = false; try { dynamic temp = await ParseAsync(); OnParseDone(this, temp.Summary); if (workerAborted) { return; } OrganizeSummary organizeSummary = await OrganizeAsync(temp.Items); OnOrganizeDone(this, organizeSummary); } finally { Cleanup(); workerRunning = false; } }
private void OrganizationThread(CopyItems items, OrganizeSummary summary) { PrepareDestinationPath(items); int itemCount = items.Count; for (int i = 0; i < itemCount; i++) { if (workerAborted) { break; } float progress = (float)(i) / (float)itemCount; if ((int)(progress * 10) % 2 == 0) { OnProgress(this, PARSE_PROGRESS_FACTOR + 0.1 + (progress * (1.0 - PARSE_PROGRESS_FACTOR - 0.1)), $"Organizing {i + 1} of {itemCount}"); } CopySourceToDestination(items[i], summary); } }
private void CopySourceToDestination(CopyItem item, OrganizeSummary summary) { string destinationDirectory = Path.GetDirectoryName(item.destinationPath); if (!Directory.Exists(destinationDirectory)) { try { Directory.CreateDirectory(destinationDirectory); } catch (Exception ex) { if (ExceptionHandling == ExceptionHandling.Throw) { throw new MediaOrganizerException($"Failed to create directory: {destinationDirectory}", ex); } Trace.WriteLine($"[{nameof(MediaOrganizer)}] Ignored exception: {ex.Message}"); return; } } summary.sources.Add(item.sourcePath); summary.destinations.Add(item.destinationPath); bool overwrite = false; string targetDestinationPath = item.destinationPath; // Note: path may be altered later on to resolve conflicts bool fileExists = File.Exists(targetDestinationPath); switch (CopyMode) { case CopyMode.OverwriteExisting: overwrite = true; if (fileExists) { summary.overwritten[item.sourcePath] = targetDestinationPath; Trace.WriteLine($"[{nameof(MediaOrganizer)}] Force overwrite file: {targetDestinationPath}"); } break; case CopyMode.KeepExisting: if (fileExists) { summary.skipped[item.sourcePath] = targetDestinationPath; Trace.WriteLine($"[{nameof(MediaOrganizer)}] Keep existing file: {targetDestinationPath}"); return; } break; case CopyMode.KeepUnique: FileInfo sourceInfo = item.sourceInfo; FileInfo destinationInfo = new FileInfo(targetDestinationPath); if (fileExists) { // Potentially slow, therefore previous optimizations if (sourceInfo.AreFilesIdentical(destinationInfo, FileComparator)) { summary.duplicates[item.sourcePath] = targetDestinationPath; Trace.WriteLine($"[{nameof(MediaOrganizer)}] Duplicate file ignored: {item.sourcePath} (duplicate of {targetDestinationPath})"); return; } } if (sourceInfo.FileExistsInDirectory(destinationInfo.Directory, FileComparator)) { summary.duplicates[item.sourcePath] = destinationInfo.Directory.FullName; Trace.WriteLine($"[{nameof(MediaOrganizer)}] Duplicate file ignored: {item.sourcePath} (exists in {destinationInfo.Directory})"); return; // Source file already exists in target directory } // Find next unused filename string originalDestinationPath = targetDestinationPath; int index = 1; while (fileExists) { targetDestinationPath = destinationInfo.SuffixFileName(index++); fileExists = File.Exists(targetDestinationPath); } if (targetDestinationPath != originalDestinationPath) { summary.renamed[originalDestinationPath] = targetDestinationPath; Trace.WriteLine($"[{nameof(MediaOrganizer)}] Renamed to prevent conflict: {targetDestinationPath} (original: {originalDestinationPath})"); } break; default: throw new NotImplementedException($"CopyMode: {CopyMode}"); } try { File.Copy(item.sourcePath, targetDestinationPath, overwrite); summary.modified.Add(targetDestinationPath); if (FileVerification != FileComparator.None) { if (!item.sourceInfo.AreFilesIdentical(new FileInfo(targetDestinationPath), FileVerification)) { throw new MediaOrganizerException("File verification failed. Source: {0}. Destination: {1}", item.sourcePath, targetDestinationPath); } } } catch (Exception ex) { if (ExceptionHandling == ExceptionHandling.Throw) { throw new MediaOrganizerException($"Failed to copy file. Mode: {CopyMode}. Overwrite: {overwrite}. Source: {item.sourcePath}. Destination: {targetDestinationPath}", ex); } Trace.WriteLine($"[{nameof(MediaOrganizer)}] Ignored exception: {ex.Message}"); } }
private async void ParseComplete(MediaOrganizer organizer, OrganizeSummary summary) { if (InvokeRequired) { Action<MediaOrganizer, OrganizeSummary> action = new Action<MediaOrganizer, OrganizeSummary>(ParseComplete); BeginInvoke(action, organizer, summary); return; } if (MessageBox.Show(summary.ToString(), "Continue organization?", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No) { ProgressEnded(); return; } try { await organizer.OrganizeAsync(); OrganizeComplete(organizer); } catch (Exception ex) { OrganizeException(ex); } }
private async Task<List<CopyItem>> ParseItemsAsync(string sourcePath, string destinationPath, OrganizeSummary summary) { List<string> ignore = new List<string>(); if (IgnorePaths != null) ignore.AddRange(IgnorePaths); if (!sourcePath.DirectoryAreSame(destinationPath)) ignore.Add(destinationPath); IEnumerable<MetaData> data; try { MetaParserConfig config = new MetaParserConfig() { Recursive = Recursive, IgnorePaths = ignore }; data = await MetaParser.ParseAsync(sourcePath, config); } catch (MetaParseException ex) { throw new MediaOrganizerException("Failed to parse meta data", ex); } PatternPathParser parser = new PatternPathParser(Locale); parser.Preload(data); HashSet<string> files = new HashSet<string>(); HashSet<string> directories = new HashSet<string>(); HashSet<string> valid = new HashSet<string>(); HashSet<string> ignored = new HashSet<string>(); List<CopyItem> items = new List<CopyItem>(); foreach (MetaData meta in data) { if (workerAborted) break; string path = meta.Path; switch (meta.Type) { case MetaType.Directory: directories.Add(path); continue; case MetaType.File: files.Add(path); ignored.Add(path); continue; default: files.Add(path); break; } CopyItem item = ParseItem(parser, destinationPath, meta); items.Add(item); valid.Add(path); } summary.totalDirectories = directories.ToArray(); summary.totalFiles = files.ToArray(); summary.parsed = valid.ToArray(); summary.ignored = ignored.ToArray(); return items; }
public async Task<OrganizeSummary> ParseAsync() { if (sourcePath.DirectoryAreSame(destinationPath)) { // TODO: implement throw new NotSupportedException("TODO"); } if (workerRunning) throw new InvalidOperationException("Cannot start parsing: worker currently running"); workerRunning = true; workerAborted = false; OnProgress(this, 0.0, "Parsing source"); try { OrganizeSummary summary = new OrganizeSummary(); copyItems = new CopyItems(); copyItems.sourcePath = sourcePath; copyItems.destinationPath = destinationPath; copyItems.items = await ParseItemsAsync(sourcePath, destinationPath, summary); return summary; } catch (Exception) { #if DEBUG throw; #else return null; #endif } finally { workerRunning = false; OnProgress(this, PARSE_PROGRESS_FACTOR, "Parsing complete"); } }