TrackedFile NewTrackedFile(EpisodeTrackerDBContext db, MonitoredFile mon)
        {
            var tracked = new TrackedFile {
                FileName = mon.FileName,
                Start = DateTime.Now,
                Stop = DateTime.Now,
                LengthSeconds = mon.Length.TotalSeconds
            };
            db.TrackedFile.Add(tracked);

            if(mon.TvMatch != null) {
                var seriesQuery = db.Series.Include(s => s.Episodes);
                Series series = null;
                if(mon.Series != null) {
                    series = db.Series.SingleOrDefault(s => s.ID == mon.Series.ID);
                } else {
                    // Series is only null when no TVDB match was found
                    series = seriesQuery.SingleOrDefault(s => s.Name == mon.TvMatch.Name);
                }

                if(series == null) {
                    series = new Series {
                        Name = mon.TvMatch.Name,
                        Added = DateTime.Now
                    };

                    db.Series.Add(series);
                }

                IEnumerable<Episode> episodes = null;
                if(mon.Episodes != null) {
                    var ids = mon.Episodes.Select(e => e.ID);
                    episodes = series.Episodes
                        .Where(ep => ids.Contains(ep.ID));

                    foreach(var ep in episodes) {
                        tracked.Episodes.Add(new TrackedEpisode {
                            Episode = ep,
             							Added = DateTime.Now,
                            Updated = DateTime.Now
                        });
                    }
                } else {
                    // Check for loose reference
                    episodes = series.Episodes
                        .Where(ep =>
                            ep.Season == mon.TvMatch.Season
                            && (
                                !mon.TvMatch.ToEpisode.HasValue && ep.Number == mon.TvMatch.Episode
                                || mon.TvMatch.ToEpisode.HasValue && ep.Number >= mon.TvMatch.Episode && ep.Number <= mon.TvMatch.ToEpisode.Value
                            )
                        );

                    for(var i = mon.TvMatch.Episode; i < (mon.TvMatch.ToEpisode ?? mon.TvMatch.Episode); i++) {
                        var episode = series.Episodes.SingleOrDefault(ep => ep.Season == mon.TvMatch.Season && ep.Number == i);
                        if(episode == null) {
                            episode = new Episode {
                                Season = mon.TvMatch.Season ?? 0,
                                Number = i,
                                Added = DateTime.Now
                            };

                            series.Episodes.Add(episode);
                        }

                        episode.Updated = DateTime.Now;
                        tracked.Episodes.Add(new TrackedEpisode {
                            Episode = episode,
                            Added = DateTime.Now,
                            Updated = DateTime.Now
                        });
                    }

                    series.Updated = DateTime.Now;
                }

                mon.Series = series;
                mon.Episodes = tracked.Episodes.Select(te => te.Episode);
            }

            db.SaveChanges();
            return tracked;
        }
        void SyncEpisode(TVDBEpisode tvDBEpisode, Series series)
        {
            Episode episode = null;

            episode = series.Episodes
                .SingleOrDefault(ep => ep.TVDBID == tvDBEpisode.ID);

            if(episode == null) {
                episode = series.Episodes
                    .SingleOrDefault(ep =>
                        ep.Number == tvDBEpisode.Number
                        && ep.Season == tvDBEpisode.Season
                    );
            }

            if(episode == null) {
                episode = new Episode {
                    Season = tvDBEpisode.Season,
                    Number = tvDBEpisode.Number,
                    Added = DateTime.Now
                };
                series.Episodes.Add(episode);
            }

            episode.TVDBID = tvDBEpisode.ID;
            episode.Name = tvDBEpisode.Name;
            episode.Overview = tvDBEpisode.Overview;
            episode.Aired = tvDBEpisode.Aired <= SqlDateTime.MaxValue.Value && tvDBEpisode.Aired >= SqlDateTime.MinValue.Value ? tvDBEpisode.Aired : default(DateTime?);
            episode.AbsoluteNumber = tvDBEpisode.AbsoluteNumber;
            episode.Rating = tvDBEpisode.Rating;
            episode.Updated = DateTime.Now;
        }
        private void Sync(SyncInfo syncInfo)
        {
            if(syncInfo.Complete) return;

            var tvdbSeries = new TVDBRequest().Series(TVDBID, true);
            Series series = null;

            using(var db = new EpisodeTrackerDBContext()) {
                var seriesQuery = db.Series
                    .Include(s => s.Episodes)
                    .Include(s => s.Aliases);

                series = seriesQuery.SingleOrDefault(s => s.TVDBID == tvdbSeries.ID);

                if(series == null) {
                    series = seriesQuery
                        .SingleOrDefault(s =>
                            s.Name == tvdbSeries.Name
                            || s.Name == Name
                            || s.Aliases.Any(a => a.Name == Name)
                        );
                }

                if(series == null) {
                    series = new Series {
                        Added = DateTime.Now
                    };
                    db.Series.Add(series);
                }

                series.TVDBID = tvdbSeries.ID;
                series.Name = tvdbSeries.Name;
                series.AirsDay = tvdbSeries.AirsDay;
                series.AirsTime = tvdbSeries.AirsTime;
                series.Status = tvdbSeries.Status;
                series.Overview = tvdbSeries.Overview;
                series.LengthMinutes = tvdbSeries.LengthMinutes;
                series.Rating = tvdbSeries.Rating;

                if(Name != null && !Name.Equals(series.Name, StringComparison.OrdinalIgnoreCase) && !db.SeriesAliases.Any(a => a.Name == Name)) {
                    series.Aliases.Add(new SeriesAlias { Name = Name });
                }

                series.Genres.Clear();
                GenresSync(series, tvdbSeries.Genres);

                series.Updated = DateTime.Now;

                foreach(var ep in tvdbSeries.Episodes) {
                    SyncEpisode(ep, series);
                }

                db.SaveChanges();
            }

            if(DownloadBanners || DownloadBannersAsync) {
                // Do this after saving so we can use the ID
                var syncBanners = SyncBannersAsync(series, tvdbSeries);
                if(!DownloadBannersAsync) syncBanners.Wait();
            }

            lock(syncing) {
                syncing.Remove(TVDBID);
            }

            syncInfo.Complete = true;
        }
        Task SyncBannersAsync(Series series, TVDBSeries tvdbSeries)
        {
            var root = @"Resources\Series\" + series.ID;
            if(!Directory.Exists(root)) Directory.CreateDirectory(root);

            var tasks = new List<Task>();
            tasks.Add(DownloadBanner(tvdbSeries.BannerPath, root, "banner.jpg"));
            tasks.Add(DownloadBanner(tvdbSeries.FanartPath, root, "fanart.jpg"));

            foreach(var ep in series.Episodes) {
                if(!ep.TVDBID.HasValue) continue;
                var tvdbEP = tvdbSeries.Episodes.SingleOrDefault(te => te.ID == ep.TVDBID.Value);
                if(tvdbEP == null) continue;
                tasks.Add(DownloadBanner(tvdbEP.Filename, root, ep.ID + ".jpg"));
            }

            return Task.WhenAll(tasks);
        }
        void GenresSync(Series series, string[] genres)
        {
            lock(genreLock) {
                var dbGenres = new List<Genre>();
                using(var db = new EpisodeTrackerDBContext()) {
                    foreach(var tvdbGenre in genres) {
                        var genre = db.Genres.SingleOrDefault(g => g.Name == tvdbGenre);
                        if(genre == null) {
                            genre = new Genre {
                                Name = tvdbGenre
                            };
                            db.Genres.Add(genre);
                        }
                        dbGenres.Add(genre);
                    }
                    db.SaveChanges();
                }

                foreach(var genre in dbGenres) {
                    series.Genres.Add(new SeriesGenre {
                        GenreID = genre.ID
                    });
                }
            }
        }