/// <summary> /// Identifies the name of the show. /// </summary> /// <param name="name">The name of the show.</param> /// <param name="ep">The episode.</param> /// <param name="askRemote">if set to <c>true</c> lab.rolisoft.net's API will be asked to identify a show after the local database failed.</param> /// <returns> /// A tuple containing the show's and episode's title and airdate. /// </returns> private static Tuple<string, string, DateTime, TVShow, Episode> IdentifyShow(string name, ShowEpisode ep, bool askRemote = false) { var title = string.Empty; var date = DateTime.MinValue; var match = false; var ltvsh = default(TVShow); var lepis = default(Episode); // try to find show in local database foreach (var show in Database.TVShows.Values.ToList()) { var titleMatch = ShowNames.Parser.GenerateTitleRegex(show.Name).Match(name); var releaseMatch = !string.IsNullOrWhiteSpace(show.Release) ? Regex.Match(name, show.Release) : null; if ((titleMatch.Success && titleMatch.Value == name) || (releaseMatch != null && releaseMatch.Success && releaseMatch.Value == name)) { if (ep == null) { match = true; ltvsh = show; name = show.Name; break; } else if (ep.AirDate != null) { var episode = show.Episodes.Where(x => x.Airdate.ToOriginalTimeZone(x.Show.TimeZone).Date == ep.AirDate.Value.Date).ToList(); if (episode.Count != 0) { match = true; ltvsh = show; name = show.Name; lepis = episode[0]; title = episode[0].Name; date = episode[0].Airdate; ep.Season = episode[0].Season; ep.Episode = episode[0].Number; break; } } else { Episode episode; if (show.EpisodeByID.TryGetValue(ep.Season * 1000 + ep.Episode, out episode)) { match = true; ltvsh = show; name = show.Name; lepis = episode; title = episode.Name; date = episode.Airdate; break; } } } } // try to find show in the local cache of the list over at lab.rolisoft.net if (!match) { if (AllKnownTVShows.Count == 0) { var path = Path.Combine(Signature.InstallPath, @"misc\tvshows"); if (File.Exists(path) && new FileInfo(path).Length != 0) { using (var fs = File.OpenRead(path)) using (var br = new BinaryReader(fs)) { var ver = br.ReadByte(); var upd = br.ReadUInt32(); var cnt = br.ReadUInt32(); AllKnownTVShows = new List<KnownTVShow>(); for (var i = 0; i < cnt; i++) { var show = new KnownTVShow(); show.Title = br.ReadString(); show.Slug = br.ReadString(); show.Database = br.ReadString(); show.DatabaseID = br.ReadString(); AllKnownTVShows.Add(show); } } } else { try { GetAllKnownTVShows(); } catch { } } } var slug = Utils.CreateSlug(name); var matches = new List<KnownTVShow>(); foreach (var show in AllKnownTVShows) { if (show.Slug == slug) { matches.Add(show); } } if (matches.Count != 0 && ep == null) { match = true; name = matches[0].Title; } else if (matches.Count != 0 && ep != null) { TVShow local = null; foreach (var mtch in matches) { foreach (var show in Database.TVShows.Values) { if (show.Source == mtch.Database && show.SourceID == mtch.DatabaseID) { local = show; break; } } } if (local != null) { match = true; name = local.Name; if (ep.AirDate != null) { var eps = local.Episodes.Where(ch => ch.Airdate.Date == ep.AirDate.Value.Date).ToList(); if (eps.Count() != 0) { ltvsh = eps[0].Show; title = eps[0].Name; lepis = eps[0]; date = eps[0].Airdate; ep.Season = eps[0].Season; ep.Episode = eps[0].Number; } } else { var eps = local.Episodes.Where(ch => ch.Season == ep.Season && ch.Number == ep.Episode).ToList(); if (eps.Count() != 0) { ltvsh = eps[0].Show; title = eps[0].Name; lepis = eps[0]; date = eps[0].Airdate; } } } else if (ShowIDCache.ContainsKey(name) && TVShowCache.ContainsKey(name)) { match = true; name = ShowIDCache[name].Title; if (ep.AirDate != null) { var eps = TVShowCache[name].Episodes.Where(ch => ch.Airdate.Date == ep.AirDate.Value.Date).ToList(); if (eps.Count() != 0) { title = eps[0].Title; date = eps[0].Airdate; ep.Season = eps[0].Season; ep.Episode = eps[0].Number; } } else { var eps = TVShowCache[name].Episodes.Where(ch => ch.Season == ep.Season && ch.Number == ep.Episode).ToList(); if (eps.Count() != 0) { title = eps[0].Title; date = eps[0].Airdate; } } } else if (askRemote) { var guide = Updater.CreateGuide(matches[0].Database); var data = guide.GetData(matches[0].DatabaseID); ShowIDCache[name] = new ShowID { Title = data.Title }; match = true; name = data.Title; TVShowCache[name] = data; if (ep.AirDate != null) { var eps = data.Episodes.Where(ch => ch.Airdate.Date == ep.AirDate.Value.Date).ToList(); if (eps.Count() != 0) { title = eps[0].Title; date = eps[0].Airdate; ep.Season = eps[0].Season; ep.Episode = eps[0].Number; } } else { var eps = data.Episodes.Where(ch => ch.Season == ep.Season && ch.Number == ep.Episode).ToList(); if (eps.Count() != 0) { title = eps[0].Title; date = eps[0].Airdate; } } } } } // try to find show in cache if (!match && ShowIDCache.ContainsKey(name)) { match = true; name = ShowIDCache[name].Title; if (ep != null) { if (TVShowCache.ContainsKey(name)) { if (ep.AirDate != null) { var eps = TVShowCache[name].Episodes.Where(ch => ch.Airdate.Date == ep.AirDate.Value.Date).ToList(); if (eps.Count() != 0) { title = eps[0].Title; date = eps[0].Airdate; ep.Season = eps[0].Season; ep.Episode = eps[0].Number; } } else { var eps = TVShowCache[name].Episodes.Where(ch => ch.Season == ep.Season && ch.Number == ep.Episode).ToList(); if (eps.Count() != 0) { title = eps[0].Title; date = eps[0].Airdate; } } } else { match = false; } } } // try to identify show using lab.rolisoft.net's API if (!match && askRemote) { var req = Remote.API.GetShowInfo(name, new[] { "Title", "Source", "SourceID" }); if (req.Success) { if (ep == null) { ShowIDCache[name] = new ShowID { Title = req.Title }; match = true; name = req.Title; } else { var guide = Updater.CreateGuide(req.Source); var data = guide.GetData(req.SourceID); ShowIDCache[name] = new ShowID { Title = data.Title }; match = true; name = data.Title; TVShowCache[name] = data; if (ep.AirDate != null) { var eps = data.Episodes.Where(ch => ch.Airdate.Date == ep.AirDate.Value.Date).ToList(); if (eps.Count() != 0) { title = eps[0].Title; date = eps[0].Airdate; ep.Season = eps[0].Season; ep.Episode = eps[0].Number; } } else { var eps = data.Episodes.Where(ch => ch.Season == ep.Season && ch.Number == ep.Episode).ToList(); if (eps.Count() != 0) { title = eps[0].Title; date = eps[0].Airdate; } } } } } // return return match ? new Tuple<string, string, DateTime, TVShow, Episode>(name, title, date, ltvsh, lepis) : null; }
/// <summary> /// Adds the specified TV show to the database. /// </summary> /// <param name="sid">The show ID to add to the database.</param> /// <param name="callback">The status callback.</param> /// <returns>Added TV show or <c>null</c>.</returns> public static TVShow Add(ShowID sid, Action<int, string> callback = null) { Log.Info("Adding " + sid.Guide.Name + "/" + sid.ID + "..."); var st = DateTime.Now; if (callback != null) { callback(0, "Downloading guide from " + sid.Guide.Name + "..."); } TVShow tv; try { tv = sid.Guide.GetData(sid.ID, sid.Language); if (tv.Episodes.Count == 0) { Log.Error("Downloaded guide for " + tv.Title + " (" + sid.Guide.Name + "/" + sid.ID + ") has no episodes."); if (callback != null) { callback(-1, "No episodes listed for this show on " + sid.Guide.Name + "."); } return null; } } catch (Exception ex) { if (ex is ThreadAbortException) { Log.Warn("Thread aborted while downloading data from guide for " + sid.Guide.Name + "/" + sid.ID + ".", ex); return null; } Log.Error("Error while downloading data from guide for " + sid.Guide.Name + "/" + sid.ID + ".", ex); if (callback != null) { callback(-1, "Error while downloading data: " + ex.Message); } return null; } if (TVShows.Values.FirstOrDefault(x => x.Title == tv.Title) != null) { Log.Error("Duplicate entry detected for " + tv.Title + " (" + sid.Guide.Name + "/" + sid.ID + ")."); if (callback != null) { callback(-1, tv.Title + " is already in your database."); } return null; } foreach (var tvs in TVShows.Values) { tvs.RowID++; tvs.SaveData(); } tv.RowID = 0; tv.ID = TVShows.Values.Count > 0 ? TVShows.Values.Max(x => x.ID) + 1 : 1; tv.Data = new Dictionary<string, string>(); tv.Directory = Path.Combine(DataPath, Utils.CreateSlug(tv.Title, false)); tv.EpisodeByID = new Dictionary<int, Episode>(); if (Directory.Exists(tv.Directory)) { tv.Directory += "-" + tv.Source.ToLower(); } if (Directory.Exists(tv.Directory)) { tv.Directory += "-" + Utils.Rand.Next(); } try { Directory.CreateDirectory(tv.Directory); } catch (Exception ex) { Log.Error("Error while creating directory db\\" + Path.GetFileName(tv.Directory) + " for " + tv.Title + " (" + sid.Guide.Name + "/" + sid.ID + ").", ex); if (callback != null) { callback(-1, "Error while creating database."); } return null; } foreach (var ep in tv.Episodes) { ep.Show = tv; ep.ID = ep.Number + (ep.Season * 1000) + (tv.ID * 1000 * 1000); tv.EpisodeByID[ep.Number + (ep.Season * 1000)] = ep; if (!string.IsNullOrWhiteSpace(tv.AirTime) && ep.Airdate != Utils.UnixEpoch) { ep.Airdate = DateTime.Parse(ep.Airdate.ToString("yyyy-MM-dd ") + tv.AirTime).ToLocalTimeZone(tv.TimeZone); } } try { tv.Save(); tv.SaveTracking(); } catch (Exception ex) { Log.Error("Error while saving database to db\\" + Path.GetFileName(tv.Directory) + " for " + tv.Title + " (" + sid.Guide.Name + "/" + sid.ID + ").", ex); if (callback != null) { callback(-1, "Error while saving to database."); } return null; } TVShows[tv.ID] = tv; DataChange = DateTime.Now; if (tv.Language == "en") { Updater.UpdateRemoteCache(tv); } if (callback != null) { callback(1, "Show added successfully."); } Log.Debug("Successfully added " + tv.Title + " (" + sid.Guide.Name + "/" + sid.ID + ") in " + (DateTime.Now - st).TotalSeconds + "s."); return tv; }
/// <summary> /// Downloads and inserts the specified show into the database. /// </summary> /// <param name="show">The show.</param> private void AddShow(ShowID show) { working.Content = "Downloading guide..."; subworking.Content = show.Title; selectTabItem.Visibility = Visibility.Collapsed; workingTabItem.Visibility = Visibility.Visible; tabControl.SelectedIndex = 1; Utils.Win7Taskbar(state: TaskbarProgressBarState.Indeterminate); _worker = new Thread(() => { var tv = Database.Add(_guide.GetType().Name, show.ID, show.Language, (i, s) => { if (i == -1) { Dispatcher.Invoke((Action)(() => { workingTabItem.Visibility = Visibility.Collapsed; addTabItem.Visibility = Visibility.Visible; tabControl.SelectedIndex = 0; Utils.Win7Taskbar(state: TaskbarProgressBarState.NoProgress); })); TaskDialog.Show(new TaskDialogOptions { MainIcon = VistaTaskDialogIcon.Error, Title = "Error", MainInstruction = show.Title, Content = s, CustomButtons = new[] { "OK" } }); } }); if (tv == null) { return; } if (tv.Language == "en") { Updater.UpdateRemoteCache(tv); } _dbid = tv.ID; MainWindow.Active.DataChanged(); // show finish page Dispatcher.Invoke((Action)(() => { finishTitle.Content = tv.Title; workingTabItem.Visibility = Visibility.Collapsed; finishTabItem.Visibility = Visibility.Visible; tabControl.SelectedIndex = 3; Utils.Win7Taskbar(state: TaskbarProgressBarState.NoProgress); var shows = Database.TVShows[_dbid].Episodes.OrderByDescending(ep => ep.ID); markUntil.Items.Clear(); var gotLast = false; foreach (var item in shows) { markUntil.Items.Add("S{0:00}E{1:00} - {2}".FormatWith(item.Season, item.Number, item.Title)); if (!gotLast && item.Airdate < DateTime.Now && item.Airdate != Utils.UnixEpoch) { gotLast = true; markUntil.Items[markUntil.Items.Count - 1] += " [last aired episode]"; } } })); }); _worker.Start(); }
/// <summary> /// Downloads and inserts the specified show into the database. /// </summary> /// <param name="show">The show.</param> private void AddShow(ShowID show) { working.Content = "Downloading guide..."; subworking.Content = show.Title; selectTabItem.Visibility = Visibility.Collapsed; workingTabItem.Visibility = Visibility.Visible; tabControl.SelectedIndex = 1; Utils.Win7Taskbar(state: TaskbarProgressBarState.Indeterminate); var lang = (language.SelectedValue as StackPanel).Tag.ToString(); _worker = new Thread(() => { // get data from guide TVShow tv; try { tv = _guide.GetData(show.ID, lang); if (tv.Episodes.Count == 0) { throw new Exception("There aren't any episodes associated to this TV show on this database."); } } catch (Exception ex) { if (ex is ThreadAbortException) { return; } Dispatcher.Invoke((Action)(() => { workingTabItem.Visibility = Visibility.Collapsed; addTabItem.Visibility = Visibility.Visible; tabControl.SelectedIndex = 0; Utils.Win7Taskbar(state: TaskbarProgressBarState.NoProgress); })); new TaskDialog { Icon = TaskDialogStandardIcon.Error, Caption = "Couldn't grab TV show", InstructionText = show.Title, Text = "Couldn't download the episode listing and associated informations due to an unexpected error.", DetailsExpandedText = ex.Message, Cancelable = true }.Show(); return; } // try to see if duplicate if (Database.TVShows.Values.FirstOrDefault(x => x.Title == tv.Title) != null) { Dispatcher.Invoke((Action)(() => { workingTabItem.Visibility = Visibility.Collapsed; addTabItem.Visibility = Visibility.Visible; tabControl.SelectedIndex = 0; Utils.Win7Taskbar(state: TaskbarProgressBarState.NoProgress); })); new TaskDialog { Icon = TaskDialogStandardIcon.Error, Caption = "Duplicate entry", InstructionText = tv.Title, Text = "This TV show is already on your list.", Cancelable = true }.Show(); return; } // increment each rowid foreach (var tvs in Database.TVShows.Values) { tvs.RowID++; tvs.SaveData(); } // generate showid tv.RowID = 0; _dbid = tv.ID = Database.TVShows.Values.Max(x => x.ID) + 1; tv.Data = new Dictionary<string, string>(); tv.Directory = Path.Combine(Database.DataPath, Utils.CreateSlug(tv.Title, false)); if (Directory.Exists(tv.Directory)) { tv.Directory += "-" + tv.Source.ToLower(); } if (Directory.Exists(tv.Directory)) { tv.Directory += "-" + Utils.Rand.Next(); } Directory.CreateDirectory(tv.Directory); // apply timezone corrections foreach (var ep in tv.Episodes) { if (!string.IsNullOrWhiteSpace(tv.AirTime) && ep.Airdate != Utils.UnixEpoch) { ep.Airdate = DateTime.Parse(ep.Airdate.ToString("yyyy-MM-dd ") + tv.AirTime).ToLocalTimeZone(tv.TimeZone); } } // save tv.Save(); tv.SaveTracking(); // fire data change event Database.LoadDatabase(); MainWindow.Active.DataChanged(); // asynchronously update lab.rolisoft.net's cache if (tv.Language == "en") { Updater.UpdateRemoteCache(tv); } // show finish page Dispatcher.Invoke((Action)(() => { finishTitle.Content = tv.Title; workingTabItem.Visibility = Visibility.Collapsed; finishTabItem.Visibility = Visibility.Visible; tabControl.SelectedIndex = 3; Utils.Win7Taskbar(state: TaskbarProgressBarState.NoProgress); var shows = Database.TVShows[_dbid].Episodes.OrderByDescending(ep => ep.ID); markUntil.Items.Clear(); var gotLast = false; foreach (var item in shows) { markUntil.Items.Add("S{0:00}E{1:00} - {2}".FormatWith(item.Season, item.Number, item.Title)); if (!gotLast && item.Airdate < DateTime.Now && item.Airdate != Utils.UnixEpoch) { gotLast = true; markUntil.Items[markUntil.Items.Count - 1] += " [last aired episode]"; } } })); }); _worker.Start(); }
/// <summary> /// Downloads and inserts the specified show into the database. /// </summary> /// <param name="show">The show.</param> private void AddShow(ShowID show) { working.Content = "Downloading guide..."; subworking.Content = show.Title; selectTabItem.Visibility = Visibility.Collapsed; workingTabItem.Visibility = Visibility.Visible; tabControl.SelectedIndex = 1; Utils.Win7Taskbar(state: TaskbarProgressBarState.Indeterminate); var lang = (language.SelectedValue as StackPanel).Tag.ToString(); _worker = new Thread(() => { // get data from guide TVShow tv; try { tv = _guide.GetData(show.ID, lang); if (tv.Episodes.Count == 0) { throw new Exception("There aren't any episodes associated to this TV show on this database."); } } catch (Exception ex) { if (ex is ThreadAbortException) { return; } Dispatcher.Invoke((Action)(() => { workingTabItem.Visibility = Visibility.Collapsed; addTabItem.Visibility = Visibility.Visible; tabControl.SelectedIndex = 0; Utils.Win7Taskbar(state: TaskbarProgressBarState.NoProgress); })); new TaskDialog { Icon = TaskDialogStandardIcon.Error, Caption = "Couldn't grab TV show", InstructionText = show.Title, Text = "Couldn't download the episode listing and associated informations due to an unexpected error.", DetailsExpandedText = ex.Message, Cancelable = true }.Show(); return; } // try to see if duplicate if (Database.GetShowID(tv.Title) != int.MinValue) { Dispatcher.Invoke((Action)(() => { workingTabItem.Visibility = Visibility.Collapsed; addTabItem.Visibility = Visibility.Visible; tabControl.SelectedIndex = 0; Utils.Win7Taskbar(state: TaskbarProgressBarState.NoProgress); })); new TaskDialog { Icon = TaskDialogStandardIcon.Error, Caption = "Duplicate entry", InstructionText = tv.Title, Text = "This TV show is already on your list.", Cancelable = true }.Show(); return; } // create transaction SQLiteTransaction tr; try { tr = Database.Connection.BeginTransaction(); } catch (Exception ex) { if (ex is ThreadAbortException) { return; } Dispatcher.Invoke((Action)(() => { workingTabItem.Visibility = Visibility.Collapsed; addTabItem.Visibility = Visibility.Visible; tabControl.SelectedIndex = 0; Utils.Win7Taskbar(state: TaskbarProgressBarState.NoProgress); })); new TaskDialog { Icon = TaskDialogStandardIcon.Error, Caption = "Couldn't create transaction", InstructionText = tv.Title, Text = "Couldn't create transaction on the database to insert the episodes. The TV show is added to the list, however, there aren't any episodes associated to it. Run an update to add the episodes.", DetailsExpandedText = ex.Message, Cancelable = true }.Show(); return; } // insert into tvshows and let the autoincrementing field assign a showid Database.Execute("update tvshows set rowid = rowid + 1"); Database.Execute("insert into tvshows values (1, null, ?, ?)", tv.Title, show.Title != tv.Title ? ShowNames.Parser.GenerateTitleRegex(show.Title).ToString() : string.Empty); // then get that showid _dbid = Database.Query("select showid from tvshows where name = ? limit 1", tv.Title)[0]["showid"].ToInteger(); // insert guide fields var gname = _guide.GetType().Name; Database.ShowData(_dbid, "grabber", gname); Database.ShowData(_dbid, gname + ".id", show.ID); Database.ShowData(_dbid, gname + ".lang", lang); // insert showdata fields Database.ShowData(_dbid, "genre", tv.Genre); Database.ShowData(_dbid, "descr", tv.Description); Database.ShowData(_dbid, "cover", tv.Cover); Database.ShowData(_dbid, "airing", tv.Airing.ToString()); Database.ShowData(_dbid, "airtime", tv.AirTime); Database.ShowData(_dbid, "airday", tv.AirDay); Database.ShowData(_dbid, "network", tv.Network); Database.ShowData(_dbid, "timezone", tv.TimeZone); Database.ShowData(_dbid, "runtime", tv.Runtime.ToString()); Database.ShowData(_dbid, "url", tv.URL); // insert episodes foreach (var ep in tv.Episodes) { try { Database.ExecuteOnTransaction(tr, "insert into episodes values (?, ?, ?, ?, ?, ?, ?, ?, ?)", ep.Number + (ep.Season * 1000) + (_dbid * 100 * 1000), _dbid, ep.Season, ep.Number, tv.AirTime == String.Empty || ep.Airdate == Utils.UnixEpoch ? ep.Airdate.ToUnixTimestamp() : DateTime.Parse(ep.Airdate.ToString("yyyy-MM-dd ") + tv.AirTime).ToLocalTimeZone(tv.TimeZone).ToUnixTimestamp(), ep.Title, ep.Summary, ep.Picture, ep.URL); } catch { continue; } } // commit the changes tr.Commit(); // fire data change event Database.CopyToMemory(); MainWindow.Active.DataChanged(); // asynchronously update lab.rolisoft.net's cache Updater.UpdateRemoteCache(new Tuple<string, string>(_guide.GetType().Name, show.ID), tv); // show finish page Dispatcher.Invoke((Action)(() => { finishTitle.Content = tv.Title; workingTabItem.Visibility = Visibility.Collapsed; finishTabItem.Visibility = Visibility.Visible; tabControl.SelectedIndex = 3; Utils.Win7Taskbar(state: TaskbarProgressBarState.NoProgress); var shows = Database.Query("select season, episode, name, airdate from episodes where showid = ? order by (season * 1000 + episode) desc", _dbid); markUntil.Items.Clear(); var gotLast = false; foreach (var item in shows) { markUntil.Items.Add("S{0:00}E{1:00} - {2}".FormatWith(item["season"].ToInteger(), item["episode"].ToInteger(), item["name"])); if (!gotLast && item["airdate"].ToInteger() < DateTime.Now.ToUnixTimestamp() && item["airdate"] != "0") { gotLast = true; markUntil.Items[markUntil.Items.Count - 1] += " [last aired episode]"; } } })); }); _worker.Start(); }