/// <summary> /// Updates the status message on configures social networks. /// </summary> /// <param name="file">The identified file.</param> public static void PostToSocial(ShowFile file) { if (Settings.Get("Post only recent", true) && (DateTime.Now - file.Airdate).TotalDays > 21) { Log.Debug("Not posting " + file + " to social networks because it is not a recent episode."); return; } var listed = Settings.Get("Post restrictions list", new List <int>()) .Contains(Database.TVShows.Values.First(x => x.Title == file.Show).ID); switch (Settings.Get("Post restrictions list type", "black")) { case "black": if (listed) { Log.Debug("Not posting " + file + " to social networks because the show is blacklisted."); return; } break; case "white": if (!listed) { Log.Debug("Not posting " + file + " to social networks because the show is not whitelisted."); return; } break; } foreach (var engine in Extensibility.GetNewInstances <SocialEngine>()) { if (!Settings.Get <bool>("Post to " + engine.Name)) { continue; } if (engine is OAuthEngine) { var tokens = Settings.Get <List <string> >(engine.Name + " OAuth"); if (tokens != null && tokens.Count != 0) { ((OAuthEngine)engine).Tokens = tokens; } else { Log.Debug("Not posting " + file + " to " + engine.Name + " because it required OAuth tokens are missing."); continue; } } var format = Settings.Get(engine.Name + " Status Format", engine.DefaultStatusFormat); if (string.IsNullOrWhiteSpace(format)) { return; } try { engine.PostMessage(FileNames.Parser.FormatFileName(format, file)); Log.Debug("Successfully posted " + file + " to " + engine.Name + "."); } catch (Exception ex) { Log.Warn("Unhandled exception while posting " + file + " to " + engine.Name + ".", ex); } } }
/// <summary> /// Checks for open files on the specified processes and marks them if recognized. /// </summary> public static void CheckOpenFiles() { if (!Settings.Get("Monitor Processes", true)) { return; } Log.Debug("Checking for open files..."); var alt = Settings.Get("Process Monitoring Method", "Internal") == "WindowTitle"; var netmon = Settings.Get <bool>("Monitor Network Shares"); if (!alt && !Utils.IsAdmin) { Log.Info("You are not running with administrator rights but requested file handle monitoring."); } var procs = new List <string>(); procs.AddRange(Settings.Get <List <string> >("Processes to Monitor")); try { procs.AddRange(Utils.GetDefaultVideoPlayers().Select(Path.GetFileName)); } catch (Exception ex) { Log.Warn("Error while getting list of default video players.", ex); } if (Log.IsTraceEnabled) { Log.Trace("Requested processes:", procs); } if (!procs.Any() && !netmon && !UPnP.IsRunning) { Log.Debug("No processes specified to monitor and network share monitoring is disabled."); return; } var pids = GetPIDs(procs).Distinct().ToList(); if (Log.IsTraceEnabled) { Log.Trace("Running PIDs:", pids); } if (!pids.Any() && !netmon && !UPnP.IsRunning) { Log.Debug("Unable to get at least one PID for the specified processes and network share monitoring is disabled."); return; } var files = new List <FileInfo>(); var titles = new List <string>(); if (pids.Any()) { if (!alt) { using (var mre = new ManualResetEvent(false)) { var thd = new Thread(() => { try { files.AddRange(GetHandleList(pids)); } finally { mre.Set(); } }) { IsBackground = true }; thd.Start(); if (!mre.WaitOne(TimeSpan.FromMinutes(3))) { Log.Error("Process monitoring method timed out after 3 minutes. Please consider switching implementations from the Settings menu."); try { thd.Interrupt(); thd.Abort(); } catch { } } } if (files.Count != 0) { Log.Debug(Utils.FormatNumber(files.Count, "file handle", true) + " open for the PID" + (pids.Count != 1 ? "s" : string.Empty) + ": " + string.Join(", ", pids).TrimEnd(", ".ToCharArray()) + "."); } } else { titles.AddRange(GetWindownTitles(pids)); if (titles.Count != 0) { Log.Debug(Utils.FormatNumber(files.Count, "window title", true) + " found for the PID" + (pids.Count != 1 ? "s" : string.Empty) + ": " + string.Join(", ", pids).TrimEnd(", ".ToCharArray()) + "."); } if (Log.IsTraceEnabled) { Log.Trace("Window titles:", titles); } } } if (Signature.IsActivated && UPnP.IsRunning) { try { var cnt = files.Count; files.AddRange(UPnP.GetActiveTransfers()); if (files.Count != cnt) { Log.Debug(Utils.FormatNumber(files.Count - cnt, "file handle", true) + " open through the UPnP/DLNA server."); } } catch (Exception ex) { Log.Warn("Cannot get active transfer list from the UPnP/DLNA server due to an exception.", ex); } } if (Signature.IsActivated && netmon) { try { var cnt = files.Count; files.AddRange(NetworkShares.GetActiveTransfers()); if (files.Count != cnt) { Log.Debug(Utils.FormatNumber(files.Count - cnt, "file handle", true) + " open through Windows file sharing."); } } catch (Exception ex) { Log.Warn("Cannot get active network share transfer list from Windows due to an exception.", ex); } } if (Log.IsTraceEnabled) { Log.Trace("Open file handles:", files.Select(fi => fi.FullName)); } if (files.Count == 0 && titles.Count == 0) { Log.Debug("No " + (alt ? "window titles" : "file handles open") + " for the specified processes (" + (pids.Count == 0 ? "none running" : "PID" + (pids.Count != 1 ? "s" : string.Empty) + ": " + string.Join(", ", pids).TrimEnd(", ".ToCharArray())) + ") and services."); return; } foreach (var show in Database.TVShows) { var titleRegex = Parser.GenerateTitleRegex(show.Value.Title, show.Value); var releaseExpr = string.Empty; var releaseRegex = show.Value.Data.TryGetValue("regex", out releaseExpr) && !string.IsNullOrWhiteSpace(releaseExpr) ? new Regex(releaseExpr) : null; foreach (var file in files) { if (Parser.IsMatch(file.DirectoryName + @"\" + file.Name, titleRegex) || (releaseRegex != null && Parser.IsMatch(file.DirectoryName + @"\" + file.Name, releaseRegex))) { var pf = FileNames.Parser.ParseFile(file.Name, file.DirectoryName.Split(Path.DirectorySeparatorChar), false); if (pf.Success && show.Value.Title == pf.Show) { Log.Debug("Identified open file " + file.Name + " as " + pf + "."); if (!OpenFiles.Contains(file.ToString())) { // add to open files list // 5 minutes later we'll check again, and if it's still open we'll mark it as seen // the reason for this is that an episode will be marked as seen only if you're watching it for more than 10 minutes (5 minute checks twice) OpenFiles.Add(file.ToString()); } else { MarkAsSeen(show.Value.ID, pf); } } } } foreach (var title in titles) { if (Parser.IsMatch(title, titleRegex, null, false) || (releaseRegex != null && Parser.IsMatch(title, releaseRegex, null, false))) { var pf = FileNames.Parser.ParseFile(title, null, false); if (pf.Success && show.Value.Title == pf.Show) { Log.Debug("Identified window title " + title + " as " + pf + "."); if (!OpenFiles.Contains(pf.ToString())) { OpenFiles.Add(pf.ToString()); } else { MarkAsSeen(show.Value.ID, pf); } } } } } }
/// <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) { try { ep.Airdate = DateTime.Parse(ep.Airdate.ToString("yyyy-MM-dd ") + tv.AirTime).ToLocalTimeZone(tv.TimeZone); } catch { } } } 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> /// Handles the Elapsed event of the ParserTimer control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="System.Timers.ElapsedEventArgs"/> instance containing the event data.</param> private void ParserTimerElapsed(object sender, ElapsedEventArgs e) { if (_parsing) { return; } _parsing = true; var files = FilesListViewItemCollection.Where(f => !f.Processed).ToList(); if (files.Count == 0) { goto end; } Log.Info("Starting to identify files in renamer queue..."); var st = DateTime.Now; foreach (var file in files) { SetStatus("Identifying " + file.Information.Name + "...", true); Log.Debug("Identifying file " + file.Information.Name + "..."); try { file.Information = FileNames.Parser.ParseFile(file.Information.Name, Path.GetDirectoryName(file.Location).Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries)); file.Checked = file.Recognized = file.Enabled = file.Information.Success; file.Processed = true; if (file.Information.Success) { file.ShowStatusImage = "Collapsed"; file.ShowCheckBox = "Visible"; Log.Debug("Identified file " + file.Information.Name + " as " + file.Information + "."); } else { file.StatusImage = "/RSTVShowTracker;component/Images/exclamation-red.png"; Log.Debug("Unable to identify file " + file.Information.Name + ": " + file.Information + "."); } } catch (Exception ex) { file.Information.ParseError = ShowFile.FailureReasons.ExceptionOccurred; file.Checked = file.Recognized = file.Enabled = false; file.Processed = true; file.StatusImage = "/RSTVShowTracker;component/Images/exclamation-red.png"; Log.Warn("Exception while identifying file " + file.Information.Name + ".", ex); } file.RefreshEnabled(); } Log.Info("File identification queue finished in " + (DateTime.Now - st).TotalSeconds + "s."); SetStatus(); end: _parsing = false; }
/// <summary> /// The tasks which will run periodically. /// </summary> /// <param name="sender">The sender.</param> /// <param name="e">The <see cref="System.Timers.ElapsedEventArgs"/> instance containing the event data.</param> public static void Tasks(object sender = null, ElapsedEventArgs e = null) { if (InProgress) { if (_inProgressCount == 5) { Log.Warn("It is time to run Tasks(), but the last one didn't finish yet. Thread will be killed and a new Tasks() will run. (6/6)"); try { _lastThd.Abort(); _lastThd = null; } catch (Exception ex) { Log.Debug("Exception on stuck Tasks() thread abort.", ex); } } else { Log.Warn("It is time to run Tasks(), but the last one didn't finish yet so it'll be dropped. (" + (_inProgressCount + 1) + "/6)"); _inProgressCount++; return; } } _lastThd = Thread.CurrentThread; var st = DateTime.Now; Log.Debug("Running background tasks..."); InProgress = true; _inProgressCount = 0; try { Library.StartWatching(); } catch (Exception ex) { Log.Error("Unhandled exception while reindexing the library during background tasks.", ex); } try { CheckSoftwareUpdate(); } catch (Exception ex) { Log.Error("Unhandled exception while checking for software update during background tasks.", ex); } try { CheckDatabaseUpdate(); } catch (Exception ex) { Log.Error("Unhandled exception while checking for database update during background tasks.", ex); } try { CheckShowListUpdate(); } catch (Exception ex) { Log.Error("Unhandled exception while checking for show list update during background tasks.", ex); } try { ProcessMonitor.CheckOpenFiles(); } catch (Exception ex) { Log.Error("Unhandled exception while checking for open files during background tasks.", ex); } try { //AutoDownloader.SearchForMissingEpisodes(); } catch (Exception ex) { Log.Error("Unhandled exception while searching for missing episodes during background tasks.", ex); } try { RestartIfNeeded(); } catch (Exception ex) { Log.Error("Unhandled exception while checking if a software restart is required during background tasks.", ex); } InProgress = false; Log.Debug("Background tasks completed in " + (DateTime.Now - st).TotalSeconds + "s."); }
/// <summary> /// Gets the cover of the specified show. /// </summary> /// <param name="show">The show to get covers for.</param> /// <param name="status">The method to call when reporting a status change.</param> /// <returns> /// Cover of the specified show or null. /// </returns> public static string GetCover(string show, Action <string> status) { var cover = GetCoverLocation(show); if (File.Exists(cover)) { goto success; } Log.Info("Getting cover for " + show + "..."); string url; // try to find it on The TVDB status("Searching for cover on The TVDB..."); try { if ((url = GetCoverFromTVDB(show)) != null) { status("Downloading cover from " + new Uri(url).Host + "..."); if (DownloadCover(url, cover)) { goto success; } } } catch (Exception ex) { Log.Warn("Exception while searching for cover for " + show + " on TVDB.", ex); } // try to find it on IMDb status("Searching for cover on IMDb..."); try { if ((url = GetCoverFromIMDb(show)) != null) { status("Downloading cover from " + new Uri(url).Host + "..."); if (DownloadCover(url, cover)) { goto success; } } } catch (Exception ex) { Log.Warn("Exception while searching for cover for " + show + " on IMDb.", ex); } // try to find it on Amazon status("Searching for cover on Amazon..."); try { if ((url = GetCoverFromAmazon(show)) != null) { status("Downloading cover from " + new Uri(url).Host + "..."); if (DownloadCover(url, cover)) { goto success; } } } catch (Exception ex) { Log.Warn("Exception while searching for cover for " + show + " on Amazon.", ex); } // we were out of luck, but f**k that, we'll draw our own cover! with blackjack. and hookers. in fact, forget the cover. status("Drawing a cover..."); DrawCover(show, cover); success: return(cover); }