/// <summary>
        /// Encodes a video and audio file.
        /// </summary>
        /// <param name="id">An ID used to track the item being encoded.</param>
        /// <param name="preset">The preset used to encode the given file.</param>
        /// <param name="file">The location of the file to encode.</param>
        /// <param name="command">The command used to tell Handbrake how to encode the video.</param>
        /// <param name="progress">The callback function fired when progress of a file's encoding is updated. The action is given the percentage complete.</param>
        /// <param name="complete">The callback function fired when encoding is complete. The action is given the location of the encoded file.</param>
        /// <returns>An ID used to track the item being encoded.</returns>
        public void Encode(Guid id, Preset preset, string file, string command, Action<EncodingMovieTask, double> progress, Action<EncodingMovieTask> complete)
        {
            if (string.IsNullOrEmpty(file))
                throw new ArgumentNullException("file");
            if (!System.IO.File.Exists(file))
                throw new FileNotFoundException(file);

            command = string.Format(command, "\"" + file + "\"", id.ToString("N") + ".mp4" + PresetIndicator.Get(preset));

            var task = new EncodingMovieTask {ID = id, File = file, PercentComplete = 0};

            Logger.Info("Beginning encoding: " + command);

            new Thread(() => {
                var process = new Process();
                process.StartInfo.FileName = @"c:\Code\showveoservice\ShowveoService.MVCApplication\Resources\HandbrakeCLI.exe";
                process.StartInfo.Arguments = command;
                process.StartInfo.RedirectStandardOutput = true;
                process.StartInfo.UseShellExecute = false;
                process.OutputDataReceived += (sender, args) => OnDataReceived(progress, task, args);
                process.ErrorDataReceived += (sender, args) => OnErrorReceived(args);
                process.Exited += (sender, args) => complete.Invoke(task);
                process.Start();

                process.BeginOutputReadLine();

                while (!process.HasExited) {}
            }).Start();
        }
        /// <summary>
        /// Adds or updates the encoding movie task to the progress list.
        /// </summary>
        /// <param name="task">The task to add or update.</param>
        /// <param name="percentChanged">The amount by which the task has completed.</param>
        public void AddOrUpdate(EncodingMovieTask task, double percentChanged = 0)
        {
            if (!_progress.ContainsKey(task.ID))
                _progress[task.ID] = task;

            _progress[task.ID].PercentComplete += percentChanged;
            if (_progress[task.ID].PercentComplete > 100)
                _progress[task.ID].PercentComplete = 100;

            _pusher.Push(_progress[task.ID]);
        }
        /// <summary>
        /// Fired after data has been received from an encoding operation
        /// </summary>
        private void OnDataReceived(Action<EncodingMovieTask, double> progress, EncodingMovieTask task, DataReceivedEventArgs e)
        {
            if (e.Data == null || !e.Data.Contains("%"))
                return;

            var begin = e.Data.IndexOf(",") + 1;
            var end = e.Data.IndexOf("%") - 1;
            var newPercent = Convert.ToDouble(e.Data.Substring(begin, end - begin).Replace(" ", ""));
            progress.Invoke(task, newPercent - _previousPercentage);

            _previousPercentage = newPercent;
        }
        /// <summary>
        /// Pushes information regarding the progress of an encoding movie task.
        /// </summary>
        /// <param name="task">The encoding movie task.</param>
        public void Push(EncodingMovieTask task)
        {
            if (task == null)
                throw new ArgumentNullException("task");

            _publisher.Publish(new Publication {
                Channel = _channel,
                DataJson = _serializer.Serialize(new {
                    task.ID,
                    File = Path.GetFileName(task.File),
                    task.PercentComplete
            })});
        }
        /// <summary>
        /// Encodes a file.
        /// </summary>
        /// <param name="file">The file to encode.</param>
        /// <returns>The ID of the encoding task.</returns>
        public void Encode(string file)
        {
            if (string.IsNullOrEmpty(file))
                throw new ArgumentNullException("file");
            if (!System.IO.File.Exists(file))
                throw new FileNotFoundException(file);

            var id = Guid.NewGuid();
            var tasks = _factory.CreateAll().Select(localEncoder => (Action) (() => localEncoder.Encode(id, file, OnProgressReceived, OnEncodingCompleted))).ToList();
            var task = new EncodingMovieTask {File = file, ID = id, PercentComplete = 0};
            _encodingProgressContainer.AddOrUpdate(task);

            if (_queue.Count() == 0)
                tasks.First().Invoke();

            _queue.Add(tasks);
        }
 /// <summary>
 /// Fired after progress has been received for an encoding operation.
 /// </summary>
 /// <param name="task">The encoding movie task for which progress should be updated.</param>
 /// <param name="percentChanged">The amount by which the task has completed.</param>
 private void OnProgressReceived(EncodingMovieTask task, double percentChanged)
 {
     percentChanged /= _factory.EncoderCount;
     _encodingProgressContainer.AddOrUpdate(task, percentChanged);
 }
        /// <summary>
        /// Fired after an encoding task has been completed.
        /// </summary>
        /// <param name="task">The completed task.</param>
        private void OnEncodingCompleted(EncodingMovieTask task)
        {
            var tasks = _queue[0];
            tasks.RemoveAt(0);
            if (tasks.Count > 0)
            {
                tasks.First().Invoke();
                return;
            }

            _encodingProgressContainer.AddOrUpdate(task, 100);
            _uncategorizedMovieRepository.Insert(new UncategorizedMovie { OriginalFile = task.File, EncodedFile = task.ID.ToString("N") });

            _queue.RemoveAt(0);
            if (_queue.Count > 0)
            {
                tasks = _queue[0];
                tasks.First().Invoke();
            }
        }