Ejemplo n.º 1
0
        public override void ProcessCommand()
        {
            logger.Info("Processing CommandRequest_MALDownloadStatusFromMAL");

            try
            {
                if (string.IsNullOrEmpty(ServerSettings.MAL_Username) || string.IsNullOrEmpty(ServerSettings.MAL_Password))
                    return;

                // find the latest eps to update
                AniDB_AnimeRepository repAnime = new AniDB_AnimeRepository();

                myanimelist mal = MALHelper.GetMALAnimeList();
                if (mal == null) return;
                if (mal.anime == null) return;

                CrossRef_AniDB_MALRepository repCrossRef = new CrossRef_AniDB_MALRepository();
                AniDB_EpisodeRepository repAniEps = new AniDB_EpisodeRepository();
                AnimeEpisodeRepository repEp = new AnimeEpisodeRepository();

                // find the anidb user
                JMMUserRepository repUsers = new JMMUserRepository();
                List<JMMUser> aniDBUsers = repUsers.GetAniDBUsers();
                if (aniDBUsers.Count == 0) return;

                JMMUser user = aniDBUsers[0];

                foreach (myanimelistAnime malAnime in mal.anime)
                {
                    // look up the anime
                    CrossRef_AniDB_MAL xref = repCrossRef.GetByMALID(malAnime.series_animedb_id);
                    if (xref == null) continue;

                    if (malAnime.series_animedb_id == 8107 || malAnime.series_animedb_id == 10737)
                    {
                        Console.Write("");
                    }

                    // check if this anime has any other links
                    List<CrossRef_AniDB_MAL> allXrefs = repCrossRef.GetByAnimeID(xref.AnimeID);
                    if (allXrefs.Count == 0) continue;

                    // find the range of watched episodes that this applies to
                    int startEpNumber = xref.StartEpisodeNumber;
                    int endEpNumber = GetUpperEpisodeLimit(allXrefs, xref);

                    List<AniDB_Episode> aniEps = repAniEps.GetByAnimeID(xref.AnimeID);
                    foreach (AniDB_Episode aniep in aniEps)
                    {
                        if (aniep.EpisodeType != xref.StartEpisodeType) continue;

                        AnimeEpisode ep = repEp.GetByAniDBEpisodeID(aniep.EpisodeID);
                        if (ep == null) continue;

                        int adjustedWatchedEps = malAnime.my_watched_episodes + xref.StartEpisodeNumber - 1;
                        int epNum = aniep.EpisodeNumber;

                        if (epNum < startEpNumber || epNum > endEpNumber) continue;

                        AnimeEpisode_User usrRec = ep.GetUserRecord(user.JMMUserID);

                        if (epNum <= adjustedWatchedEps)
                        {
                            // update if the user doesn't have a record (means not watched)
                            // or it is currently un-watched
                            bool update = false;
                            if (usrRec == null) update = true;
                            else
                            {
                                if (!usrRec.WatchedDate.HasValue) update = true;
                            }

                            if (update) ep.ToggleWatchedStatus(true, true, DateTime.Now, user.JMMUserID, false);
                        }
                        else
                        {
                            bool update = false;
                            if (usrRec != null)
                            {
                                if (usrRec.WatchedDate.HasValue) update = true;
                            }

                            if (update) ep.ToggleWatchedStatus(false, true, DateTime.Now, user.JMMUserID, false);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                logger.Error("Error processing CommandRequest_MALDownloadStatusFromMAL: {0}", ex.ToString());
                return;
            }
        }
Ejemplo n.º 2
0
		public void ToggleWatchedStatus(bool watched, bool updateOnline, DateTime? watchedDate, bool updateStats, bool updateStatsCache, int userID, 
			bool scrobbleTrakt, bool updateWatchedDate)
		{
			VideoLocalRepository repVids = new VideoLocalRepository();
			AnimeEpisodeRepository repEpisodes = new AnimeEpisodeRepository();
			AniDB_FileRepository repAniFile = new AniDB_FileRepository();
			CrossRef_File_EpisodeRepository repCross = new CrossRef_File_EpisodeRepository();
			VideoLocal_UserRepository repVidUsers = new VideoLocal_UserRepository();
			JMMUserRepository repUsers = new JMMUserRepository();
			AnimeEpisode_UserRepository repEpisodeUsers = new AnimeEpisode_UserRepository();

			JMMUser user = repUsers.GetByID(userID);
			if (user == null) return;

			List<JMMUser> aniDBUsers = repUsers.GetAniDBUsers();

			// update the video file to watched
			int mywatched = watched ? 1 : 0;

			if (user.IsAniDBUser == 0)
				SaveWatchedStatus(watched, userID, watchedDate, updateWatchedDate);
			else
			{
				// if the user is AniDB user we also want to update any other AniDB
				// users to keep them in sync
				foreach (JMMUser juser in aniDBUsers)
				{
					if (juser.IsAniDBUser == 1)
						SaveWatchedStatus(watched, juser.JMMUserID, watchedDate, updateWatchedDate);
				}
			}
			

			// now lets find all the associated AniDB_File record if there is one
			if (user.IsAniDBUser == 1)
			{
				AniDB_File aniFile = repAniFile.GetByHash(this.Hash);
				if (aniFile != null)
				{
					aniFile.IsWatched = mywatched;

					if (watched)
					{
						if (watchedDate.HasValue)
							aniFile.WatchedDate = watchedDate;
						else
							aniFile.WatchedDate = DateTime.Now;
					}
					else
						aniFile.WatchedDate = null;


					repAniFile.Save(aniFile, false);

					
				}

				if (updateOnline)
				{
					if ((watched && ServerSettings.AniDB_MyList_SetWatched) || (!watched && ServerSettings.AniDB_MyList_SetUnwatched))
					{
						CommandRequest_UpdateMyListFileStatus cmd = new CommandRequest_UpdateMyListFileStatus(this.Hash, watched, false, 
							watchedDate.HasValue ? Utils.GetAniDBDateAsSeconds(watchedDate) : 0);
						cmd.Save();
					}
				}
			}

			// now find all the episode records associated with this video file
			// but we also need to check if theer are any other files attached to this episode with a watched
			// status, 


			AnimeSeries ser = null;
			// get all files associated with this episode
			List<CrossRef_File_Episode> xrefs = repCross.GetByHash(this.Hash);
			if (watched)
			{
				// find the total watched percentage
				// eg one file can have a % = 100
				// or if 2 files make up one episodes they will each have a % = 50

				foreach (CrossRef_File_Episode xref in xrefs)
				{
					// get the episode for this file
					AnimeEpisode ep = repEpisodes.GetByAniDBEpisodeID(xref.EpisodeID);
					if (ep == null) continue;

					// get all the files for this episode
					int epPercentWatched = 0;
					foreach (CrossRef_File_Episode filexref in ep.FileCrossRefs)
					{
						VideoLocal_User vidUser = filexref.GetVideoLocalUserRecord(userID);
						if (vidUser != null)
						{
							// if not null means it is watched
							epPercentWatched += filexref.Percentage;
						}

						if (epPercentWatched > 95) break;
					}

					if (epPercentWatched > 95)
					{
						ser = ep.GetAnimeSeries();

						if (user.IsAniDBUser == 0)
							ep.SaveWatchedStatus(true, userID, watchedDate, updateWatchedDate);
						else
						{
							// if the user is AniDB user we also want to update any other AniDB
							// users to keep them in sync
							foreach (JMMUser juser in aniDBUsers)
							{
								if (juser.IsAniDBUser == 1)
									ep.SaveWatchedStatus(true, juser.JMMUserID, watchedDate, updateWatchedDate);
							}
						}

						if (scrobbleTrakt && !string.IsNullOrEmpty(ServerSettings.Trakt_Username) && !string.IsNullOrEmpty(ServerSettings.Trakt_Password))
						{
							CommandRequest_TraktShowScrobble cmdScrobble = new CommandRequest_TraktShowScrobble(ep.AnimeEpisodeID);
							cmdScrobble.Save();
						}

						if (!string.IsNullOrEmpty(ServerSettings.MAL_Username) && !string.IsNullOrEmpty(ServerSettings.MAL_Password))
						{
							CommandRequest_MALUpdatedWatchedStatus cmdMAL = new CommandRequest_MALUpdatedWatchedStatus(ser.AniDB_ID);
							cmdMAL.Save();
						}
					}
				}

				
			}
			else
			{
				// if setting a file to unwatched only set the episode unwatched, if ALL the files are unwatched
				foreach (CrossRef_File_Episode xrefEp in xrefs)
				{
					AnimeEpisode ep = repEpisodes.GetByAniDBEpisodeID(xrefEp.EpisodeID);
					if (ep == null) continue;
					ser = ep.GetAnimeSeries();

					// get all the files for this episode
					int epPercentWatched = 0;
					foreach (CrossRef_File_Episode filexref in ep.FileCrossRefs)
					{
						VideoLocal_User vidUser = filexref.GetVideoLocalUserRecord(userID);
						if (vidUser != null)
							epPercentWatched += filexref.Percentage;

						if (epPercentWatched > 95) break;
					}

					if (epPercentWatched < 95)
					{
						if (user.IsAniDBUser == 0)
							ep.SaveWatchedStatus(false, userID, watchedDate, true);
						else
						{
							// if the user is AniDB user we also want to update any other AniDB
							// users to keep them in sync
							foreach (JMMUser juser in aniDBUsers)
							{
								if (juser.IsAniDBUser == 1)
									ep.SaveWatchedStatus(false, juser.JMMUserID, watchedDate, true);
							}
						}

						CommandRequest_TraktShowEpisodeUnseen cmdUnseen = new CommandRequest_TraktShowEpisodeUnseen(ep.AnimeEpisodeID);
						cmdUnseen.Save();
					}
				}

				if (!string.IsNullOrEmpty(ServerSettings.MAL_Username) && !string.IsNullOrEmpty(ServerSettings.MAL_Password))
				{
					CommandRequest_MALUpdatedWatchedStatus cmdMAL = new CommandRequest_MALUpdatedWatchedStatus(ser.AniDB_ID);
					cmdMAL.Save();
				}
			}
			

			// update stats for groups and series
			if (ser != null && updateStats)
			{
				// update all the groups above this series in the heirarchy
				ser.UpdateStats(true, true, true);
				//ser.TopLevelAnimeGroup.UpdateStatsFromTopLevel(true, true, true);
			}

			if (ser != null && updateStatsCache)
				StatsCache.Instance.UpdateUsingSeries(ser.AnimeSeriesID);
		}
		public override void ProcessCommand()
		{
			logger.Info("Processing CommandRequest_AddFileToMyList: {0}", Hash);

			
			try
			{
				VideoLocalRepository repVids = new VideoLocalRepository();
				AnimeEpisodeRepository repEpisodes = new AnimeEpisodeRepository();

				vid = repVids.GetByHash(this.Hash);
                List<AnimeEpisode> animeEpisodes = new List<AnimeEpisode>();
                if (vid != null) animeEpisodes = vid.GetAnimeEpisodes();

				if (vid != null)
				{
					// when adding a file via the API, newWatchedStatus will return with current watched status on AniDB
					// if the file is already on the user's list

					bool isManualLink = false;
					List<CrossRef_File_Episode> xrefs = vid.EpisodeCrossRefs;
					if (xrefs.Count > 0)
						isManualLink = xrefs[0].CrossRefSource != (int)CrossRefSource.AniDB;

					// mark the video file as watched
					DateTime? watchedDate = null;
					bool newWatchedStatus = false;

					if (isManualLink)
						newWatchedStatus = JMMService.AnidbProcessor.AddFileToMyList(xrefs[0].AnimeID, xrefs[0].Episode.EpisodeNumber, ref watchedDate);
					else
						newWatchedStatus = JMMService.AnidbProcessor.AddFileToMyList(vid, ref watchedDate);

					// do for all AniDB users
					JMMUserRepository repUsers = new JMMUserRepository();
					List<JMMUser> aniDBUsers = repUsers.GetAniDBUsers();

					
					if (aniDBUsers.Count > 0)
					{
						JMMUser juser = aniDBUsers[0];
						vid.ToggleWatchedStatus(newWatchedStatus, false, watchedDate, false, false, juser.JMMUserID, false, true);
						logger.Info("Adding file to list: {0} - {1}", vid.ToString(), watchedDate);

						// if the the episode is watched we may want to set the file to watched as well
						if (ServerSettings.Import_UseExistingFileWatchedStatus && !newWatchedStatus)
						{
							
							if (animeEpisodes.Count > 0)
							{
								AnimeEpisode ep = animeEpisodes[0];
								AnimeEpisode_User epUser = null;

								foreach (JMMUser tempuser in aniDBUsers)
								{
									// only find the first user who watched this
									if (epUser == null)
										epUser = ep.GetUserRecord(tempuser.JMMUserID);
								}
								
								if (epUser != null)
								{
									logger.Info("Setting file as watched, because episode was already watched: {0} - user: {1}", vid.ToString(), juser.Username);
									vid.ToggleWatchedStatus(true, true, epUser.WatchedDate, false, false, epUser.JMMUserID, false, true);

								}

							}
						}
					}

					AnimeSeries ser = animeEpisodes[0].GetAnimeSeries();
					// all the eps should belong to the same anime
                    ser.QueueUpdateStats();
					//StatsCache.Instance.UpdateUsingSeries(ser.AnimeSeriesID);

                    // lets also try adding to the users trakt collecion
                    if (ser != null && ServerSettings.Trakt_IsEnabled && !string.IsNullOrEmpty(ServerSettings.Trakt_AuthToken))
                    {
                        foreach (AnimeEpisode aep in animeEpisodes)
                        {
                            CommandRequest_TraktCollectionEpisode cmdSyncTrakt = new CommandRequest_TraktCollectionEpisode(aep.AnimeEpisodeID, TraktSyncAction.Add);
                            cmdSyncTrakt.Save();
                        }

                    }

                    // sync the series on MAL
                    if (ser != null && !string.IsNullOrEmpty(ServerSettings.MAL_Username) && !string.IsNullOrEmpty(ServerSettings.MAL_Password))
                    {
                        CommandRequest_MALUpdatedWatchedStatus cmdMAL = new CommandRequest_MALUpdatedWatchedStatus(ser.AniDB_ID);
                        cmdMAL.Save();
                    }
				}
				

			}
			catch (Exception ex)
			{
				logger.Error("Error processing CommandRequest_AddFileToMyList: {0} - {1}", Hash, ex.ToString());
				return;
			}
		}
Ejemplo n.º 4
0
		public override void ProcessCommand()
		{
			logger.Info("Processing CommandRequest_SyncMyList");

			try
			{
				// we will always assume that an anime was downloaded via http first
				ScheduledUpdateRepository repSched = new ScheduledUpdateRepository();
				AniDB_FileRepository repAniFile = new AniDB_FileRepository();
				VideoLocalRepository repVidLocals = new VideoLocalRepository();

				ScheduledUpdate sched = repSched.GetByUpdateType((int)ScheduledUpdateType.AniDBMyListSync);
				if (sched == null)
				{
					sched = new ScheduledUpdate();
					sched.UpdateType = (int)ScheduledUpdateType.AniDBMyListSync;
					sched.UpdateDetails = "";
				}
				else
				{
					int freqHours = Utils.GetScheduledHours(ServerSettings.AniDB_MyList_UpdateFrequency);

					// if we have run this in the last 24 hours and are not forcing it, then exit
					TimeSpan tsLastRun = DateTime.Now - sched.LastUpdate;
					if (tsLastRun.TotalHours < freqHours)
					{
						if (!ForceRefresh) return;
					}
				}

				AniDBHTTPCommand_GetMyList cmd = new AniDBHTTPCommand_GetMyList();
				cmd.Init(ServerSettings.AniDB_Username, ServerSettings.AniDB_Password);
				enHelperActivityType ev = cmd.Process();
				if (ev == enHelperActivityType.GotMyListHTTP && cmd.MyListItems.Count > 1)
				{
					int totalItems = 0;
					int watchedItems = 0;
					int modifiedItems = 0;
					double pct = 0;

					// 2. find files locally for the user, which are not recorded on anidb
					//    and then add them to anidb
					Dictionary<int, Raw_AniDB_MyListFile> onlineFiles = new Dictionary<int, Raw_AniDB_MyListFile>();
					foreach (Raw_AniDB_MyListFile myitem in cmd.MyListItems)
						onlineFiles[myitem.FileID] = myitem;

					Dictionary<string, AniDB_File> dictAniFiles = new Dictionary<string, AniDB_File>();
					List<AniDB_File> allAniFiles = repAniFile.GetAll();
					foreach (AniDB_File anifile in allAniFiles)
						dictAniFiles[anifile.Hash] = anifile;

					int missingFiles = 0;
					foreach (VideoLocal vid in repVidLocals.GetAll())
					{
						if (!dictAniFiles.ContainsKey(vid.Hash)) continue;

						int fileID = dictAniFiles[vid.Hash].FileID;

						if (!onlineFiles.ContainsKey(fileID))
						{
							// means we have found a file in our local collection, which is not recorded online
							CommandRequest_AddFileToMyList cmdAddFile = new CommandRequest_AddFileToMyList(vid.Hash);
							cmdAddFile.Save();
							missingFiles++;
						}
					}
					logger.Info(string.Format("MYLIST Missing Files: {0} Added to queue for inclusion", missingFiles));

					JMMUserRepository repUsers = new JMMUserRepository();
					List<JMMUser> aniDBUsers = repUsers.GetAniDBUsers();

					VideoLocal_UserRepository repVidUsers = new VideoLocal_UserRepository();
					CrossRef_File_EpisodeRepository repFileEp = new CrossRef_File_EpisodeRepository();

					// 1 . sync mylist items
					foreach (Raw_AniDB_MyListFile myitem in cmd.MyListItems)
					{
						// ignore files mark as deleted by the user
						if (myitem.State == (int)AniDBFileStatus.Deleted) continue;

						totalItems++;
						if (myitem.IsWatched) watchedItems++;

						//calculate percentage
						pct = (double)totalItems / (double)cmd.MyListItems.Count * (double)100;
						string spct = pct.ToString("#0.0");

						string hash = string.Empty;

						AniDB_File anifile = repAniFile.GetByFileID(myitem.FileID);
						if (anifile != null)
							hash = anifile.Hash;
						else
						{
							// look for manually linked files
							List<CrossRef_File_Episode> xrefs = repFileEp.GetByEpisodeID(myitem.EpisodeID);
							foreach (CrossRef_File_Episode xref in xrefs)
							{
								if (xref.CrossRefSource != (int)CrossRefSource.AniDB)
								{
									hash = xref.Hash;
									break;
								}
							}
						}


						if (!string.IsNullOrEmpty(hash))
						{
							// find the video associated with this record
							VideoLocal vl = repVidLocals.GetByHash(hash);
							if (vl == null) continue;

							foreach (JMMUser juser in aniDBUsers)
							{
								bool localStatus = false;
								int? jmmUserID = null;

								// doesn't matter which anidb user we use
								jmmUserID = juser.JMMUserID;
								VideoLocal_User userRecord = vl.GetUserRecord(juser.JMMUserID);
								if (userRecord != null) localStatus = true;

								string action = "";
								if (localStatus != myitem.IsWatched)
								{
									if (localStatus == true)
									{
										// local = watched, anidb = unwatched
										if (ServerSettings.AniDB_MyList_ReadUnwatched)
										{
											modifiedItems++;
											if (jmmUserID.HasValue)
												vl.ToggleWatchedStatus(myitem.IsWatched, false, myitem.WatchedDate, false, false, jmmUserID.Value, false, true);
											action = "Used AniDB Status";
										}
									}
									else
									{
										// means local is un-watched, and anidb is watched
										if (ServerSettings.AniDB_MyList_ReadWatched)
										{
											modifiedItems++;
											if (jmmUserID.HasValue)
												vl.ToggleWatchedStatus(true, false, myitem.WatchedDate, false, false, jmmUserID.Value, false, true);
											action = "Updated Local record to Watched";
										}
									}

									string msg = string.Format("MYLISTDIFF:: File {0} - Local Status = {1}, AniDB Status = {2} --- {3}",
									vl.FullServerPath, localStatus, myitem.IsWatched, action);
									logger.Info(msg);
								}
							}

							

							//string msg = string.Format("MYLIST:: File {0} - Local Status = {1}, AniDB Status = {2} --- {3}",
							//	vl.FullServerPath, localStatus, myitem.IsWatched, action);
							//logger.Info(msg);
						}
					}

					


					// now update all stats
					Importer.UpdateAllStats();

					logger.Info("Process MyList: {0} Items, {1} Watched, {2} Modified", totalItems, watchedItems, modifiedItems);

					sched.LastUpdate = DateTime.Now;
					repSched.Save(sched);
				}
			}
			catch (Exception ex)
			{
				logger.Error("Error processing CommandRequest_SyncMyList: {0} ", ex.ToString());
				return;
			}
		}
Ejemplo n.º 5
0
		/*public static void UpdateWatchedStatus(int animeID, enEpisodeType epType, int lastWatchedEpNumber)
		{
			try
			{
				if (string.IsNullOrEmpty(ServerSettings.MAL_Username) || string.IsNullOrEmpty(ServerSettings.MAL_Password))
					return;

				AniDB_EpisodeRepository repAniEps = new AniDB_EpisodeRepository();
				List<AniDB_Episode> aniEps = repAniEps.GetByAnimeIDAndEpisodeTypeNumber(animeID, epType, lastWatchedEpNumber);
				if (aniEps.Count == 0) return;

				AnimeEpisodeRepository repEp = new AnimeEpisodeRepository();
				AnimeEpisode ep = repEp.GetByAniDBEpisodeID(aniEps[0].EpisodeID);
				if (ep == null) return;

				MALHelper.UpdateMAL(ep);
			}
			catch (Exception ex)
			{
				logger.ErrorException(ex.ToString(), ex);
			}
		}*/

		public static void UpdateMALSeries(AnimeSeries ser)
		{
			try
			{
				if (string.IsNullOrEmpty(ServerSettings.MAL_Username) || string.IsNullOrEmpty(ServerSettings.MAL_Password))
					return;

				// Populate MAL animelist hashtable if isNeverDecreaseWatched set
				Hashtable animeListHashtable = new Hashtable();
				if (ServerSettings.MAL_NeverDecreaseWatchedNums) //if set, check watched number before update: take some time, as user anime list must be loaded
				{
					myanimelist malAnimeList = GetMALAnimeList();
					if (malAnimeList != null && malAnimeList.anime != null)
					{
						for (int i = 0; i < malAnimeList.anime.Length; i++)
						{
							animeListHashtable.Add(malAnimeList.anime[i].series_animedb_id, malAnimeList.anime[i]);
						}
					}
				}

				// look for MAL Links
				List<CrossRef_AniDB_MAL> crossRefs = ser.GetAnime().GetCrossRefMAL();
				if (crossRefs == null || crossRefs.Count == 0)
				{
					logger.Warn("Could not find MAL link for : {0} ({1})", ser.GetAnime().GetFormattedTitle(), ser.GetAnime().AnimeID);
					return;
				}

				AnimeEpisodeRepository repEps = new AnimeEpisodeRepository();
				AniDB_FileRepository repFiles = new AniDB_FileRepository();

				List<AnimeEpisode> eps = ser.GetAnimeEpisodes();

				// find the anidb user
				JMMUserRepository repUsers = new JMMUserRepository();
				List<JMMUser> aniDBUsers = repUsers.GetAniDBUsers();
				if (aniDBUsers.Count == 0) return;

				JMMUser user = aniDBUsers[0];

				int score = 0;
				if (ser.GetAnime().UserVote != null)
					score = (int)(ser.GetAnime().UserVote.VoteValue / 100);

				// e.g.
				// AniDB - Code Geass R2
				// MAL Equivalent = AniDB Normal Eps 1 - 25 / Code Geass: Hangyaku no Lelouch R2 / hxxp://myanimelist.net/anime/2904/Code_Geass:_Hangyaku_no_Lelouch_R2
				// MAL Equivalent = AniDB Special Eps 1 - 9 / Code Geass: Hangyaku no Lelouch R2 Picture Drama / hxxp://myanimelist.net/anime/5163/Code_Geass:_Hangyaku_no_Lelouch_R2_Picture_Drama
				// MAL Equivalent = AniDB Special Eps 9 - 18 / Code Geass: Hangyaku no Lelouch R2: Flash Specials / hxxp://myanimelist.net/anime/9591/Code_Geass:_Hangyaku_no_Lelouch_R2:_Flash_Specials
				// MAL Equivalent = AniDB Special Eps 20 / Code Geass: Hangyaku no Lelouch - Kiseki no Birthday Picture Drama / hxxp://myanimelist.net/anime/8728/Code_Geass:_Hangyaku_no_Lelouch_-_Kiseki_no_Birthday_Picture_Drama

				foreach (CrossRef_AniDB_MAL xref in crossRefs)
				{
					// look for the right MAL id
					int malID = -1;
					int epNumber = -1;
					int totalEpCount = -1;

					List<string> fanSubGroups = new List<string>();

					// for each cross ref (which is a series on MAL) we need to update the data
					// so find all the episodes which apply to this cross ref
					int lastWatchedEpNumber = 0;
					int downloadedEps = 0;

					foreach (AnimeEpisode ep in eps)
					{
						int epNum = ep.AniDB_Episode.EpisodeNumber;
						if (xref.StartEpisodeType == (int)ep.EpisodeTypeEnum && epNum >= xref.StartEpisodeNumber &&
							epNum <= GetUpperEpisodeLimit(crossRefs, xref))
						{
							malID = xref.MALID;
							epNumber = epNum - xref.StartEpisodeNumber + 1;

							// find the total episode count
							if (totalEpCount < 0)
							{
								if (ep.EpisodeTypeEnum == AniDBAPI.enEpisodeType.Episode) totalEpCount = ser.GetAnime().EpisodeCountNormal;
								if (ep.EpisodeTypeEnum == AniDBAPI.enEpisodeType.Special) totalEpCount = ser.GetAnime().EpisodeCountSpecial;
								totalEpCount = totalEpCount - xref.StartEpisodeNumber + 1;
							}

							// any episodes here belong to the MAL series
							// find the latest watched episod enumber
							AnimeEpisode_User usrRecord = ep.GetUserRecord(user.JMMUserID);
							if (usrRecord != null && usrRecord.WatchedDate.HasValue && epNum > lastWatchedEpNumber)
							{
								lastWatchedEpNumber = epNum;
							}

							List<Contract_VideoDetailed> contracts = ep.GetVideoDetailedContracts(user.JMMUserID);

							// find the latest episode number in the collection
							if (contracts.Count > 0)
								downloadedEps++;

							foreach (Contract_VideoDetailed contract in contracts)
							{
								if (!string.IsNullOrEmpty(contract.AniDB_Anime_GroupNameShort) && !fanSubGroups.Contains(contract.AniDB_Anime_GroupNameShort))
									fanSubGroups.Add(contract.AniDB_Anime_GroupNameShort);
							}
						}
					}

					string fanSubs = "";
					foreach (string fgrp in fanSubGroups)
					{
						if (!string.IsNullOrEmpty(fanSubs)) fanSubs += ",";
						fanSubs += fgrp;
					}

					// determine status
					int status = 1; //watching
					if (animeListHashtable.ContainsKey(malID))
					{
						myanimelistAnime animeInList = (myanimelistAnime)animeListHashtable[malID];
						status = animeInList.my_status;
					}

					// over-ride is user has watched an episode
					// don't override on hold (3) or dropped (4) but do override plan to watch (6)
					if (status == 6 && lastWatchedEpNumber > 0) status = 1; //watching
					if (lastWatchedEpNumber == totalEpCount) status = 2; //completed

					if (lastWatchedEpNumber > totalEpCount)
					{
						logger.Error("updateMAL, episode number > matching anime episode total : {0} ({1}) / {2}", ser.GetAnime().GetFormattedTitle(), ser.GetAnime().AnimeID, epNumber);
						continue;
					}

					if (malID <= 0 || totalEpCount <= 0)
					{
						logger.Warn("Could not find MAL link for : {0} ({1})", ser.GetAnime().GetFormattedTitle(), ser.GetAnime().AnimeID);
						continue;
					}
					else
					{
						bool res = UpdateAnime(malID, lastWatchedEpNumber, status, score, downloadedEps, fanSubs);

						string confirmationMessage = string.Format("MAL successfully updated, mal id: {0}, ep: {1}, score: {2}", malID, lastWatchedEpNumber, score);
						if (res) logger.Trace(confirmationMessage);
					}
					
				}

				
			}
			catch (Exception ex)
			{
				logger.ErrorException(ex.ToString(), ex);
			}
		}