private static void Main(string[] args) { // Configure the console Console.Title = "PlexBird"; // Configure the logger var xmlDocument = new XmlDocument(); xmlDocument.LoadXml(Resources.log4net); XmlConfigurator.Configure(xmlDocument.DocumentElement); Logger.Info("Launching PlexBird.."); // Check config var configPath = Path.Combine(Environment.CurrentDirectory, "config.json"); if (!File.Exists(configPath)) { var defaultConfiguration = new Configuration { Username = string.Empty, AuthToken = string.Empty, PlexUrl = "http://localhost:32400/", AnimeLibrary = "Anime TV Series", SyncTime = 60 }; File.WriteAllText(configPath, JsonConvert.SerializeObject(defaultConfiguration, Formatting.Indented)); Logger.Error("Configuration file not found!"); Logger.Warn("A configuration file has been generated, please edit config.json!"); Console.ReadKey(); return; } Configuration = JsonConvert.DeserializeObject <Configuration>(File.ReadAllText(configPath)); if (Configuration.Username.Equals(string.Empty) || Configuration.AuthToken.Equals(string.Empty)) { Logger.Info("No AuthToken was found, you have to sign in to connect with HummingBird."); while (Configuration.AuthToken.Equals(string.Empty)) { Console.Write("HummingBird username: "******"HummingBird password: "******"Wrong username/password combination, try again."); } } } HummingBird.SetAuthenticationToken(Configuration.AuthToken); User = HummingBird.GetUser(Configuration.Username); Logger.Info($"Connected to HummingBird as the user {User.Name}, you've spent {User.LifeSpentOnAnime} minutes watching anime."); // Check database var databasePath = Path.Combine(Environment.CurrentDirectory, "database.json"); if (!File.Exists(databasePath)) { Database = new Dictionary <string, DatabaseEntry>(); SaveDatabase(); } else { Database = JsonConvert.DeserializeObject <Dictionary <string, DatabaseEntry> >(File.ReadAllText(databasePath)); } // Setup plex var plex = new Plex(Configuration.PlexUrl); PlexLibrary plexLibrary = null; // Search plexLibrary foreach (var plexLib in plex.GetLibraries()) { if (!plexLib.Title.Equals(Configuration.AnimeLibrary)) { continue; } if (plexLibrary != null) { throw new Exception($"The plex installation '{plex.Information.FriendlyName}' seems to have more than one '{plexLib.Title}' library."); } plexLibrary = plexLib; } if (plexLibrary == null) { throw new Exception($"The plex installation '{plex.Information.FriendlyName}' seems to not have '{Configuration.AnimeLibrary}' library."); } // Connected new Thread(SynchronizeLibrary).Start(new ThreadData { Plex = plex, PlexLibrary = plexLibrary }); // End SaveDatabase(); }
private static void SynchronizeLibrary(object objThreadData) { var plex = ((ThreadData)objThreadData).Plex; var plexLibrary = ((ThreadData)objThreadData).PlexLibrary; Logger.Info($"Connected to {plex.Information.FriendlyName} running {plex.Information.Platform} {plex.Information.PlatformVersion}."); while (true) { try { // Iterate through all (anime) shows foreach (var plexShow in plex.GetAllShows(plexLibrary.Key)) { var plexShowData = plex.GetShowData(plexShow.RatingKey); if (plexShowData.Seasons.Count != 1) { throw new Exception($"The show '{plexLibrary.Title}' seems to have more than or less than one season."); } var plexSeason = plexShowData.Seasons[0]; var lastViewedEpisode = 0; // Iterate through all episodes of a show foreach (var plexEpisode in plex.GetAllEpisodes(plexSeason.RatingKey)) { if (plexEpisode.ViewCount > 0 && plexEpisode.Index > lastViewedEpisode) { lastViewedEpisode = plexEpisode.Index; } } // Find the show on HummingBird if (!Database.ContainsKey(plexShowData.Key)) { var animeShows = HummingBird.SearchAnime(plexShow.Title); HBAnime animeShow = null; // If there is just one result, it MUST be the anime we're looking for. if (animeShows.Count == 1) { animeShow = animeShows[0]; } if (animeShows.Count > 1) { foreach (var hbAnime in animeShows) { if (hbAnime.Title.ToLower().Equals(plexShow.Title.ToLower())) // Check if title equals local title { animeShow = hbAnime; break; } if (hbAnime.TitleAlternate != null && hbAnime.TitleAlternate.ToLower().Equals(plexShow.Title.ToLower())) // Check if alternate title equals local title { animeShow = hbAnime; break; } // Maybe missing a random character? (e.g. https://hummingbird.me/anime/isshuukan-friends) if (hbAnime.Title.Contains(plexShow.Title) && hbAnime.Title.Replace(plexShow.Title, string.Empty).Length <= 1) { animeShow = hbAnime; break; } if (hbAnime.TitleAlternate != null && hbAnime.TitleAlternate.Contains(plexShow.Title) && hbAnime.TitleAlternate.Replace(plexShow.Title, string.Empty).Length <= 1) { animeShow = hbAnime; break; } } } // Add to the database if (animeShow != null) { if (Database.ContainsKey(plexShowData.Key)) { continue; } Database.Add(plexShowData.Key, new DatabaseEntry { EpisodesWatched = lastViewedEpisode, LastUpdateEpisodesWatched = 0, Anime = animeShow }); } else { Logger.Info($"Couldn't find the show '{StringUtil.RemoveNonASCII(plexShow.Title)}' on HummingBird."); } } // Update! if (Database.ContainsKey(plexShowData.Key)) { var databaseEntry = Database[plexShowData.Key]; if (databaseEntry.EpisodesWatched != databaseEntry.Anime.EpisodeCount) // Not yet completed { if (lastViewedEpisode > databaseEntry.EpisodesWatched) { databaseEntry.EpisodesWatched = lastViewedEpisode; } if (databaseEntry.EpisodesWatched != databaseEntry.LastUpdateEpisodesWatched) { var episodesWatched = databaseEntry.EpisodesWatched; // Apply manual fixes if (databaseEntry.Anime.Title.Equals("Gakusen Toshi Asterisk 2nd Season")) { episodesWatched = episodesWatched - 12; } var libraryItem = HummingBird.UpdateLibrary(databaseEntry.Anime, episodesWatched); if (libraryItem != null) { Database[plexShowData.Key].LastUpdateEpisodesWatched = databaseEntry.EpisodesWatched; Logger.Info($"Synchronized '{StringUtil.RemoveNonASCII(plexShow.Title)}'."); } else { Logger.Info($"Something went wrong with '{StringUtil.RemoveNonASCII(plexShow.Title)}'."); } } } SaveDatabase(); } } } catch (Exception ex) { Logger.Warn("Something went wrong in the sync thread, trying again in 5 minutes.", ex); Thread.Sleep(1000 * 60 * 5); continue; } // Sleepwell for 1 minute. Thread.Sleep(1000 * Configuration.SyncTime); } }