Exemplo n.º 1
0
        public Contract_GroupFilterExtended ToContractExtended(ISession session, JMMUser user)
        {
            Contract_GroupFilterExtended contract = new Contract_GroupFilterExtended();

            contract.GroupFilter = this.ToContract();
            contract.GroupCount  = 0;
            contract.SeriesCount = 0;

            // find all the groups for thise group filter
            AnimeGroupRepository repGroups = new AnimeGroupRepository();
            List <AnimeGroup>    allGrps   = repGroups.GetAll(session);

            if ((StatsCache.Instance.StatUserGroupFilter.ContainsKey(user.JMMUserID)) && (StatsCache.Instance.StatUserGroupFilter[user.JMMUserID].ContainsKey(this.GroupFilterID)))
            {
                HashSet <int> groups = StatsCache.Instance.StatUserGroupFilter[user.JMMUserID][GroupFilterID];
                foreach (AnimeGroup grp in allGrps)
                {
                    if (groups.Contains(grp.AnimeGroupID))
                    {
                        contract.GroupCount++;
                    }
                }
            }
            return(contract);
        }
Exemplo n.º 2
0
 public Contract_GroupFilterExtended ToContractExtended(JMMUser user)
 {
     using (var session = JMMService.SessionFactory.OpenSession())
     {
         return(ToContractExtended(session, user));
     }
 }
Exemplo n.º 3
0
        public void UpdateGroupFilter(HashSet <GroupFilterConditionType> types)
        {
            AnimeGroup grp = RepoFactory.AnimeGroup.GetByID(AnimeGroupID);
            JMMUser    usr = RepoFactory.JMMUser.GetByID(JMMUserID);

            if (grp != null && usr != null)
            {
                grp.UpdateGroupFilters(types, usr);
            }
        }
Exemplo n.º 4
0
        public void UpdateGroupFilter(HashSet <GroupFilterConditionType> types)
        {
            AnimeSeries ser = RepoFactory.AnimeSeries.GetByID(AnimeSeriesID);
            JMMUser     usr = RepoFactory.JMMUser.GetByID(JMMUserID);

            if (ser != null && usr != null)
            {
                ser.UpdateGroupFilters(types, usr);
            }
        }
Exemplo n.º 5
0
        public void UpdateGroupFilter(HashSet <GroupFilterConditionType> types)
        {
            AnimeSeriesRepository repo     = new AnimeSeriesRepository();
            JMMUserRepository     repouser = new JMMUserRepository();
            AnimeSeries           ser      = repo.GetByID(AnimeSeriesID);
            JMMUser usr = repouser.GetByID(JMMUserID);

            if (ser != null && usr != null)
            {
                ser.UpdateGroupFilters(types, usr);
            }
        }
Exemplo n.º 6
0
		public void Save(JMMUser obj)
		{
			using (var session = JMMService.SessionFactory.OpenSession())
			{
				// populate the database
				using (var transaction = session.BeginTransaction())
				{
					session.SaveOrUpdate(obj);
					transaction.Commit();
				}
			}
            logger.Trace("Updating group filter stats by user from JMMUserRepository.Save: {0}", obj.JMMUserID);
            StatsCache.Instance.UpdateGroupFilterUsingUser(obj.JMMUserID);
		}
Exemplo n.º 7
0
        public void ToggleWatchedStatus(bool watched, bool updateOnline, DateTime?watchedDate, bool updateStats,
                                        bool updateStatsCache, int userID,
                                        bool syncTrakt, bool updateWatchedDate)
        {
            JMMUser user = RepoFactory.JMMUser.GetByID(userID);

            if (user == null)
            {
                return;
            }

            List <JMMUser> aniDBUsers = RepoFactory.JMMUser.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 = RepoFactory.AniDB_File.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;
                    }


                    RepoFactory.AniDB_File.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          = RepoFactory.CrossRef_File_Episode.GetByHash(this.Hash);
            Dictionary <int, AnimeSeries> toUpdateSeries = new Dictionary <int, AnimeSeries>();

            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 episodes for this file, may be more than one (One Piece x Toriko)
                    AnimeEpisode ep = RepoFactory.AnimeEpisode.GetByAniDBEpisodeID(xref.EpisodeID);
                    // 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 && vidUser.WatchedDate.HasValue)
                        {
                            // if not null means it is watched
                            epPercentWatched += filexref.Percentage;
                        }

                        if (epPercentWatched > 95)
                        {
                            break;
                        }
                    }

                    if (epPercentWatched > 95)
                    {
                        ser = ep.GetAnimeSeries();
                        if (!toUpdateSeries.ContainsKey(ser.AnimeSeriesID))
                        {
                            toUpdateSeries.Add(ser.AnimeSeriesID, ser);
                        }
                        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 (syncTrakt && ServerSettings.Trakt_IsEnabled &&
                            !string.IsNullOrEmpty(ServerSettings.Trakt_AuthToken))
                        {
                            CommandRequest_TraktHistoryEpisode cmdSyncTrakt =
                                new CommandRequest_TraktHistoryEpisode(ep.AnimeEpisodeID, TraktSyncAction.Add);
                            cmdSyncTrakt.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)
                {
                    // get the episodes for this file, may be more than one (One Piece x Toriko)
                    AnimeEpisode ep = RepoFactory.AnimeEpisode.GetByAniDBEpisodeID(xrefEp.EpisodeID);
                    ser = ep.GetAnimeSeries();
                    if (!toUpdateSeries.ContainsKey(ser.AnimeSeriesID))
                    {
                        toUpdateSeries.Add(ser.AnimeSeriesID, ser);
                    }
                    // 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 && vidUser.WatchedDate.HasValue)
                        {
                            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);
                                }
                            }
                        }

                        if (syncTrakt && ServerSettings.Trakt_IsEnabled &&
                            !string.IsNullOrEmpty(ServerSettings.Trakt_AuthToken))
                        {
                            CommandRequest_TraktHistoryEpisode cmdSyncTrakt =
                                new CommandRequest_TraktHistoryEpisode(ep.AnimeEpisodeID, TraktSyncAction.Remove);
                            cmdSyncTrakt.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 (toUpdateSeries.Count > 0 && updateStats)
            {
                foreach (AnimeSeries s in toUpdateSeries.Values)
                {
                    // update all the groups above this series in the heirarchy
                    s.UpdateStats(true, true, true);
                }
                //ser.TopLevelAnimeGroup.UpdateStatsFromTopLevel(true, true, true);
            }

            //if (ser != null && updateStatsCache)
            //StatsCache.Instance.UpdateUsingSeries(ser.AnimeSeriesID);
        }
Exemplo n.º 8
0
        public Contract_GroupFilterExtended ToContractExtended(ISession session, JMMUser user)
        {
            Contract_GroupFilterExtended contract = new Contract_GroupFilterExtended();
            contract.GroupFilter = this.ToContract();
            contract.GroupCount = 0;
            contract.SeriesCount = 0;

            if (GroupsIds.ContainsKey(user.JMMUserID))
            {
                contract.GroupCount = GroupsIds[user.JMMUserID].Count;
            }
            /*
            // find all the groups for thise group filter
            AnimeGroupRepository repGroups = new AnimeGroupRepository();
            List<AnimeGroup> allGrps = repGroups.GetAll(session);

            if ((StatsCache.Instance.StatUserGroupFilter.ContainsKey(user.JMMUserID)) && (StatsCache.Instance.StatUserGroupFilter[user.JMMUserID].ContainsKey(this.GroupFilterID)))
            {
                HashSet<int> groups = StatsCache.Instance.StatUserGroupFilter[user.JMMUserID][GroupFilterID];
                foreach (AnimeGroup grp in allGrps)
                {
                    if (groups.Contains(grp.AnimeGroupID))
                        contract.GroupCount++;
                }
            }*/
            return contract;
        }
Exemplo n.º 9
0
 public Contract_GroupFilterExtended ToContractExtended(JMMUser user)
 {
     using (var session = DatabaseFactory.SessionFactory.OpenSession())
     {
         return ToContractExtended(session, user);
     }
 }
Exemplo n.º 10
0
        public string SaveUser(Contract_JMMUser user)
        {
            JMMUserRepository repUsers = new JMMUserRepository();

            try
            {
                bool existingUser = false;
                bool updateStats = false;
                JMMUser jmmUser = null;
                if (user.JMMUserID.HasValue)
                {
                    jmmUser = repUsers.GetByID(user.JMMUserID.Value);
                    if (jmmUser == null) return "User not found";
                    existingUser = true;
                }
                else
                {
                    jmmUser = new JMMUser();
                    updateStats = true;
                }

                if (existingUser && jmmUser.IsAniDBUser != user.IsAniDBUser)
                    updateStats = true;

                jmmUser.HideCategories = user.HideCategories;
                jmmUser.IsAniDBUser = user.IsAniDBUser;
                jmmUser.IsTraktUser = user.IsTraktUser;
                jmmUser.IsAdmin = user.IsAdmin;
                jmmUser.Username = user.Username;
                jmmUser.CanEditServerSettings = user.CanEditServerSettings;
                jmmUser.PlexUsers = user.PlexUsers;
                if (string.IsNullOrEmpty(user.Password))
                    jmmUser.Password = "";

                // make sure that at least one user is an admin
                if (jmmUser.IsAdmin == 0)
                {
                    bool adminExists = false;
                    List<JMMUser> users = repUsers.GetAll();
                    foreach (JMMUser userOld in users)
                    {
                        if (userOld.IsAdmin == 1)
                        {
                            if (existingUser)
                            {
                                if (userOld.JMMUserID != jmmUser.JMMUserID) adminExists = true;
                            }
                            else
                                adminExists = true;

                        }
                    }

                    if (!adminExists) return "At least one user must be an administrator";
                }

                repUsers.Save(jmmUser);

                // update stats
                if (updateStats)
                {
                    AnimeSeriesRepository repSeries = new AnimeSeriesRepository();
                    foreach (AnimeSeries ser in repSeries.GetAll())
                        ser.QueueUpdateStats();
                }

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

            return "";
        }
Exemplo n.º 11
0
		private static void CreateInitialUsers()
		{
			JMMUserRepository repUsers = new JMMUserRepository();

			if (repUsers.GetAll().Count() > 0) return;

			JMMUser defaultUser = new JMMUser();
			defaultUser.CanEditServerSettings = 1;
			defaultUser.HideCategories = "";
			defaultUser.IsAdmin = 1;
			defaultUser.IsAniDBUser = 1;
			defaultUser.IsTraktUser = 1;
			defaultUser.Password = "";
			defaultUser.Username = "******";
			repUsers.Save(defaultUser);

			JMMUser familyUser = new JMMUser();
			familyUser.CanEditServerSettings = 1;
			familyUser.HideCategories = "Ecchi,Nudity,Sex,Sexual Abuse,Horror,Erotic Game,Incest,18 Restricted";
			familyUser.IsAdmin = 1;
			familyUser.IsAniDBUser = 1;
			familyUser.IsTraktUser = 1;
			familyUser.Password = "";
			familyUser.Username = "******";
			repUsers.Save(familyUser);
		}
Exemplo n.º 12
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);
            }
        }
Exemplo n.º 13
0
		public bool EvaluateGroupFilter(GroupFilter gf, AnimeGroup grp, JMMUser curUser, AnimeGroup_User userRec)
		{
			// sub groups don't count
			if (grp.AnimeGroupParentID.HasValue) return false;

			// make sure the user has not filtered this out
			if (!curUser.AllowedGroup(grp, userRec)) return false;

			// first check for anime groups which are included exluded every time
			foreach (GroupFilterCondition gfc in gf.FilterConditions)
			{
				if (gfc.ConditionTypeEnum != GroupFilterConditionType.AnimeGroup) continue;

				int groupID = 0;
				int.TryParse(gfc.ConditionParameter, out groupID);
				if (groupID == 0) break;

				if (gfc.ConditionOperatorEnum == GroupFilterOperator.Equals)
					if (groupID == grp.AnimeGroupID) return true;

				if (gfc.ConditionOperatorEnum == GroupFilterOperator.NotEquals)
					if (groupID == grp.AnimeGroupID) return false;
			}

			NumberStyles style = NumberStyles.Number;
			CultureInfo culture = CultureInfo.CreateSpecificCulture("en-GB");

			if (gf.BaseCondition == (int)GroupFilterBaseCondition.Exclude) return false;

			Contract_AnimeGroup contractGroup = grp.ToContract(userRec);

			// now check other conditions
			foreach (GroupFilterCondition gfc in gf.FilterConditions)
			{
				switch (gfc.ConditionTypeEnum)
				{
					case GroupFilterConditionType.Favourite:
						if (userRec == null) return false;
						if (gfc.ConditionOperatorEnum == GroupFilterOperator.Include && userRec.IsFave == 0) return false;
						if (gfc.ConditionOperatorEnum == GroupFilterOperator.Exclude && userRec.IsFave == 1) return false;
						break;

					case GroupFilterConditionType.MissingEpisodes:
						if (gfc.ConditionOperatorEnum == GroupFilterOperator.Include && grp.HasMissingEpisodesAny == false) return false;
						if (gfc.ConditionOperatorEnum == GroupFilterOperator.Exclude && grp.HasMissingEpisodesAny == true) return false;
						break;

					case GroupFilterConditionType.MissingEpisodesCollecting:
						if (gfc.ConditionOperatorEnum == GroupFilterOperator.Include && grp.HasMissingEpisodesGroups == false) return false;
						if (gfc.ConditionOperatorEnum == GroupFilterOperator.Exclude && grp.HasMissingEpisodesGroups == true) return false;
						break;

						case GroupFilterConditionType.HasWatchedEpisodes:
						if (userRec == null) return false;
						if (gfc.ConditionOperatorEnum == GroupFilterOperator.Include && userRec.AnyFilesWatched == false) return false;
						if (gfc.ConditionOperatorEnum == GroupFilterOperator.Exclude && userRec.AnyFilesWatched == true) return false;
						break;

					case GroupFilterConditionType.HasUnwatchedEpisodes:
						if (userRec == null) return false;
						if (gfc.ConditionOperatorEnum == GroupFilterOperator.Include && userRec.HasUnwatchedFiles == false) return false;
						if (gfc.ConditionOperatorEnum == GroupFilterOperator.Exclude && userRec.HasUnwatchedFiles == true) return false;
						break;

					case GroupFilterConditionType.AssignedTvDBInfo:
						if (gfc.ConditionOperatorEnum == GroupFilterOperator.Include && contractGroup.Stat_HasTvDBLink == false) return false;
						if (gfc.ConditionOperatorEnum == GroupFilterOperator.Exclude && contractGroup.Stat_HasTvDBLink == true) return false;
						break;

					case GroupFilterConditionType.AssignedMALInfo:
						if (gfc.ConditionOperatorEnum == GroupFilterOperator.Include && contractGroup.Stat_HasMALLink == false) return false;
						if (gfc.ConditionOperatorEnum == GroupFilterOperator.Exclude && contractGroup.Stat_HasMALLink == true) return false;
						break;

					case GroupFilterConditionType.AssignedMovieDBInfo:
						if (gfc.ConditionOperatorEnum == GroupFilterOperator.Include && contractGroup.Stat_HasMovieDBLink == false) return false;
						if (gfc.ConditionOperatorEnum == GroupFilterOperator.Exclude && contractGroup.Stat_HasMovieDBLink == true) return false;
						break;

					case GroupFilterConditionType.AssignedTvDBOrMovieDBInfo:
						if (gfc.ConditionOperatorEnum == GroupFilterOperator.Include && contractGroup.Stat_HasMovieDBOrTvDBLink == false) return false;
						if (gfc.ConditionOperatorEnum == GroupFilterOperator.Exclude && contractGroup.Stat_HasMovieDBOrTvDBLink == true) return false;
						break;

					case GroupFilterConditionType.CompletedSeries:

						/*if (grp.IsComplete != grp.Stat_IsComplete)
						{
							Debug.Print("IsComplete DIFF  {0}", grp.GroupName);
						}*/

						if (gfc.ConditionOperatorEnum == GroupFilterOperator.Include && contractGroup.Stat_IsComplete == false) return false;
						if (gfc.ConditionOperatorEnum == GroupFilterOperator.Exclude && contractGroup.Stat_IsComplete == true) return false;
						break;

					case GroupFilterConditionType.FinishedAiring:
						if (gfc.ConditionOperatorEnum == GroupFilterOperator.Include && contractGroup.Stat_HasFinishedAiring == false) return false;
						if (gfc.ConditionOperatorEnum == GroupFilterOperator.Exclude && contractGroup.Stat_IsCurrentlyAiring == false) return false;
						break;

					case GroupFilterConditionType.UserVoted:
						if (gfc.ConditionOperatorEnum == GroupFilterOperator.Include && contractGroup.Stat_UserVotePermanent.HasValue == false) return false;
						if (gfc.ConditionOperatorEnum == GroupFilterOperator.Exclude && contractGroup.Stat_UserVotePermanent.HasValue == true) return false;
						break;

					case GroupFilterConditionType.UserVotedAny:
						if (gfc.ConditionOperatorEnum == GroupFilterOperator.Include && contractGroup.Stat_UserVoteOverall.HasValue == false) return false;
						if (gfc.ConditionOperatorEnum == GroupFilterOperator.Exclude && contractGroup.Stat_UserVoteOverall.HasValue == true) return false;
						break;

					case GroupFilterConditionType.AirDate:
						DateTime filterDate;
						if (gfc.ConditionOperatorEnum == GroupFilterOperator.LastXDays)
						{
							int days = 0;
							int.TryParse(gfc.ConditionParameter, out days);
							filterDate = DateTime.Today.AddDays(0 - days);
						}
						else
							filterDate = GetDateFromString(gfc.ConditionParameter);

						if (gfc.ConditionOperatorEnum == GroupFilterOperator.GreaterThan || gfc.ConditionOperatorEnum == GroupFilterOperator.LastXDays)
						{
							if (!contractGroup.Stat_AirDate_Min.HasValue || !contractGroup.Stat_AirDate_Max.HasValue) return false;
							if (contractGroup.Stat_AirDate_Max.Value < filterDate) return false;
						}
						if (gfc.ConditionOperatorEnum == GroupFilterOperator.LessThan)
						{
							if (!contractGroup.Stat_AirDate_Min.HasValue || !contractGroup.Stat_AirDate_Max.HasValue) return false;
							if (contractGroup.Stat_AirDate_Min.Value > filterDate) return false;
						}
						break;

					case GroupFilterConditionType.SeriesCreatedDate:
						DateTime filterDateSeries;
						if (gfc.ConditionOperatorEnum == GroupFilterOperator.LastXDays)
						{
							int days = 0;
							int.TryParse(gfc.ConditionParameter, out days);
							filterDateSeries = DateTime.Today.AddDays(0 - days);
						}
						else
							filterDateSeries = GetDateFromString(gfc.ConditionParameter);

						if (gfc.ConditionOperatorEnum == GroupFilterOperator.GreaterThan || gfc.ConditionOperatorEnum == GroupFilterOperator.LastXDays)
						{
							if (!contractGroup.Stat_SeriesCreatedDate.HasValue) return false;
							if (contractGroup.Stat_SeriesCreatedDate.Value < filterDateSeries) return false;
						}
						if (gfc.ConditionOperatorEnum == GroupFilterOperator.LessThan)
						{
							if (!contractGroup.Stat_SeriesCreatedDate.HasValue) return false;
							if (contractGroup.Stat_SeriesCreatedDate.Value > filterDateSeries) return false;
						}
						break;

					case GroupFilterConditionType.EpisodeWatchedDate:
						DateTime filterDateEpsiodeWatched;
						if (gfc.ConditionOperatorEnum == GroupFilterOperator.LastXDays)
						{
							int days = 0;
							int.TryParse(gfc.ConditionParameter, out days);
							filterDateEpsiodeWatched = DateTime.Today.AddDays(0 - days);
						}
						else
							filterDateEpsiodeWatched = GetDateFromString(gfc.ConditionParameter);

						if (gfc.ConditionOperatorEnum == GroupFilterOperator.GreaterThan || gfc.ConditionOperatorEnum == GroupFilterOperator.LastXDays)
						{
							if (userRec == null) return false;
							if (!userRec.WatchedDate.HasValue) return false;
							if (userRec.WatchedDate.Value < filterDateEpsiodeWatched) return false;
						}
						if (gfc.ConditionOperatorEnum == GroupFilterOperator.LessThan)
						{
							if (userRec == null) return false;
							if (!userRec.WatchedDate.HasValue) return false;
							if (userRec.WatchedDate.Value > filterDateEpsiodeWatched) return false;
						}
						break;

					case GroupFilterConditionType.EpisodeAddedDate:
						DateTime filterDateEpisodeAdded;
						if (gfc.ConditionOperatorEnum == GroupFilterOperator.LastXDays)
						{
							int days = 0;
							int.TryParse(gfc.ConditionParameter, out days);
							filterDateEpisodeAdded = DateTime.Today.AddDays(0 - days);
						}
						else
							filterDateEpisodeAdded = GetDateFromString(gfc.ConditionParameter);

						if (gfc.ConditionOperatorEnum == GroupFilterOperator.GreaterThan || gfc.ConditionOperatorEnum == GroupFilterOperator.LastXDays)
						{
							if (!grp.EpisodeAddedDate.HasValue) return false;
							if (grp.EpisodeAddedDate.Value < filterDateEpisodeAdded) return false;
						}
						if (gfc.ConditionOperatorEnum == GroupFilterOperator.LessThan)
						{
							if (!grp.EpisodeAddedDate.HasValue) return false;
							if (grp.EpisodeAddedDate.Value > filterDateEpisodeAdded) return false;
						}
						break;

					case GroupFilterConditionType.EpisodeCount:

						int epCount = -1;
						int.TryParse(gfc.ConditionParameter, out epCount);

						if (gfc.ConditionOperatorEnum == GroupFilterOperator.GreaterThan && contractGroup.Stat_EpisodeCount < epCount) return false;
						if (gfc.ConditionOperatorEnum == GroupFilterOperator.LessThan && contractGroup.Stat_EpisodeCount > epCount) return false;
						break;

					case GroupFilterConditionType.AniDBRating:

						decimal dRating = -1;
						decimal.TryParse(gfc.ConditionParameter, style, culture, out dRating);

						decimal thisRating = contractGroup.Stat_AniDBRating / (decimal)100;

						if (gfc.ConditionOperatorEnum == GroupFilterOperator.GreaterThan && thisRating < dRating) return false;
						if (gfc.ConditionOperatorEnum == GroupFilterOperator.LessThan && thisRating > dRating) return false;
						break;

					case GroupFilterConditionType.UserRating:

						if (!contractGroup.Stat_UserVoteOverall.HasValue) return false;

						decimal dUserRating = -1;
						decimal.TryParse(gfc.ConditionParameter, style, culture, out dUserRating);

						if (gfc.ConditionOperatorEnum == GroupFilterOperator.GreaterThan && contractGroup.Stat_UserVoteOverall.Value < dUserRating) return false;
						if (gfc.ConditionOperatorEnum == GroupFilterOperator.LessThan && contractGroup.Stat_UserVoteOverall.Value > dUserRating) return false;
						break;

					case GroupFilterConditionType.Category:

						string filterParm = gfc.ConditionParameter.Trim();

						string[] cats = filterParm.Split(',');
						bool foundCat = false;
						int index = 0;
						foreach (string cat in cats)
						{
							if (cat.Trim().Length == 0) continue;
							if (cat.Trim() == ",") continue;

							index = contractGroup.Stat_AllCategories.IndexOf(cat, 0, StringComparison.InvariantCultureIgnoreCase);
							if (index > -1)
							{
								foundCat = true;
								break;
							}
						}

						if (gfc.ConditionOperatorEnum == GroupFilterOperator.In)
							if (!foundCat) return false;

						if (gfc.ConditionOperatorEnum == GroupFilterOperator.NotIn)
							if (foundCat) return false;
						break;

					case GroupFilterConditionType.AnimeType:

						filterParm = gfc.ConditionParameter.Trim();
						List<string> grpTypeList = grp.AnimeTypesList;

						string[] atypes = filterParm.Split(',');
						bool foundAnimeType = false;
						index = 0;
						foreach (string atype in atypes)
						{
							if (atype.Trim().Length == 0) continue;
							if (atype.Trim() == ",") continue;

							foreach (string thisAType in grpTypeList)
							{
								if (string.Equals(thisAType, atype, StringComparison.InvariantCultureIgnoreCase))
								{
									foundAnimeType = true;
									break;
								}
							}
						}

						if (gfc.ConditionOperatorEnum == GroupFilterOperator.In)
							if (!foundAnimeType) return false;

						if (gfc.ConditionOperatorEnum == GroupFilterOperator.NotIn)
							if (foundAnimeType) return false;
						break;



					case GroupFilterConditionType.VideoQuality:

						filterParm = gfc.ConditionParameter.Trim();

						string[] vidQuals = filterParm.Split(',');
						bool foundVid = false;
						bool foundVidAllEps = false;
						index = 0;
						foreach (string vidq in vidQuals)
						{
							if (vidq.Trim().Length == 0) continue;
							if (vidq.Trim() == ",") continue;

							index = contractGroup.Stat_AllVideoQuality.IndexOf(vidq, 0, StringComparison.InvariantCultureIgnoreCase);
							if (index > -1) foundVid = true;

							index = contractGroup.Stat_AllVideoQuality_Episodes.IndexOf(vidq, 0, StringComparison.InvariantCultureIgnoreCase);
							if (index > -1) foundVidAllEps = true;

						}

						if (gfc.ConditionOperatorEnum == GroupFilterOperator.In)
							if (!foundVid) return false;

						if (gfc.ConditionOperatorEnum == GroupFilterOperator.NotIn)
							if (foundVid) return false;

						if (gfc.ConditionOperatorEnum == GroupFilterOperator.InAllEpisodes)
							if (!foundVidAllEps) return false;

						if (gfc.ConditionOperatorEnum == GroupFilterOperator.NotInAllEpisodes)
							if (foundVidAllEps) return false;

						break;

					case GroupFilterConditionType.AudioLanguage:
					case GroupFilterConditionType.SubtitleLanguage:

						filterParm = gfc.ConditionParameter.Trim();

						string[] languages = filterParm.Split(',');
						bool foundLan = false;
						index = 0;
						foreach (string lanName in languages)
						{
							if (lanName.Trim().Length == 0) continue;
							if (lanName.Trim() == ",") continue;

							if (gfc.ConditionTypeEnum == GroupFilterConditionType.AudioLanguage)
								index = contractGroup.Stat_AudioLanguages.IndexOf(lanName, 0, StringComparison.InvariantCultureIgnoreCase);

							if (gfc.ConditionTypeEnum == GroupFilterConditionType.SubtitleLanguage)
								index = contractGroup.Stat_SubtitleLanguages.IndexOf(lanName, 0, StringComparison.InvariantCultureIgnoreCase);

							if (index > -1) foundLan = true;

						}

						if (gfc.ConditionOperatorEnum == GroupFilterOperator.In)
							if (!foundLan) return false;

						if (gfc.ConditionOperatorEnum == GroupFilterOperator.NotIn)
							if (foundLan) return false;

						break;
				}
			}

			return true;
		}
        public string SaveUser(Contract_JMMUser user)
        {
            try
            {
                bool existingUser = false;
                bool updateStats = false;
                bool updateGf = false;
                JMMUser jmmUser = null;
                if (user.JMMUserID.HasValue)
                {
                    jmmUser = RepoFactory.JMMUser.GetByID(user.JMMUserID.Value);
                    if (jmmUser == null) return "User not found";
                    existingUser = true;
                }
                else
                {
                    jmmUser = new JMMUser();
                    updateStats = true;
                    updateGf = true;
                }

                if (existingUser && jmmUser.IsAniDBUser != user.IsAniDBUser)
                    updateStats = true;

                string hcat = string.Join(",", user.HideCategories);
                if (jmmUser.HideCategories != hcat)
                    updateGf = true;
                jmmUser.HideCategories = hcat;
                jmmUser.IsAniDBUser = user.IsAniDBUser;
                jmmUser.IsTraktUser = user.IsTraktUser;
                jmmUser.IsAdmin = user.IsAdmin;
                jmmUser.Username = user.Username;
                jmmUser.CanEditServerSettings = user.CanEditServerSettings;
                jmmUser.PlexUsers = string.Join(",", user.PlexUsers);
                if (string.IsNullOrEmpty(user.Password))
                {
                    jmmUser.Password = "";
                }
                else
                {
                    // Additional check for hashed password, if not hashed we hash it
                    if (user.Password.Length < 64)
                        jmmUser.Password = Digest.Hash(user.Password);
                    else
                        jmmUser.Password = user.Password;
                }

                // make sure that at least one user is an admin
                if (jmmUser.IsAdmin == 0)
                {
                    bool adminExists = false;
                    IReadOnlyList<JMMUser> users = RepoFactory.JMMUser.GetAll();
                    foreach (JMMUser userOld in users)
                    {
                        if (userOld.IsAdmin == 1)
                        {
                            if (existingUser)
                            {
                                if (userOld.JMMUserID != jmmUser.JMMUserID) adminExists = true;
                            }
                            else
                            {
                                //one admin account is needed
                                adminExists = true;
                                break;
                            }
                        }
                    }

                    if (!adminExists) return "At least one user must be an administrator";
                }

                RepoFactory.JMMUser.Save(jmmUser, updateGf);

                // update stats
                if (updateStats)
                {
                    foreach (AnimeSeries ser in RepoFactory.AnimeSeries.GetAll())
                        ser.QueueUpdateStats();
                }
            }
            catch (Exception ex)
            {
                logger.Error( ex,ex.ToString());
                return ex.Message;
            }

            return "";
        }