Example #1
0
        /// <summary>
        /// Create user from Contract_JMMUser
        /// </summary>
        /// <returns></returns>
        private object CreateUser()
        {
            Request     request = this.Request;
            SVR_JMMUser _user   = (SVR_JMMUser)this.Context.CurrentUser;

            if (_user.IsAdmin == 1)
            {
                JMMUser user = this.Bind();
                user.Password       = Digest.Hash(user.Password);
                user.HideCategories = "";
                user.PlexUsers      = "";
                if (new ShokoServiceImplementation().SaveUser(user) == "")
                {
                    return(APIStatus.statusOK());
                }
                else
                {
                    return(APIStatus.internalError());
                }
            }
            else
            {
                return(APIStatus.adminNeeded());
            }
        }
Example #2
0
        public List <SVR_VideoLocal> GetMostRecentlyAdded(int maxResults, int jmmuserID)
        {
            SVR_JMMUser user = RepoFactory.JMMUser.GetByID(jmmuserID);

            if (user == null)
            {
                lock (Cache)
                {
                    if (maxResults == -1)
                    {
                        return(Cache.Values.OrderByDescending(a => a.DateTimeCreated).ToList());
                    }
                    return(Cache.Values.OrderByDescending(a => a.DateTimeCreated).Take(maxResults).ToList());
                }
            }
            if (maxResults == -1)
            {
                return(Cache.Values.OrderByDescending(a => a.DateTimeCreated).Where(a =>
                {
                    var series = a.GetAnimeEpisodes().Select(b => b.GetAnimeSeries()).Where(b => b != null)
                                 .DistinctBy(b => b.AniDB_ID);
                    return series.All(user.AllowedSeries);
                }).ToList());
            }
            return(Cache.Values.OrderByDescending(a => a.DateTimeCreated).Where(a =>
            {
                var series = a.GetAnimeEpisodes().Select(b => b.GetAnimeSeries()).Where(b => b != null)
                             .DistinctBy(b => b.AniDB_ID);
                return series.All(user.AllowedSeries);
            }).Take(maxResults).ToList());
        }
Example #3
0
        private void CreateInitialUsers()
        {
            if (RepoFactory.JMMUser.GetAll().Count() > 0)
            {
                return;
            }

            Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(ServerSettings.Culture);

            SVR_JMMUser defaultUser = new SVR_JMMUser();

            defaultUser.CanEditServerSettings = 1;
            defaultUser.HideCategories        = "";
            defaultUser.IsAdmin     = 1;
            defaultUser.IsAniDBUser = 1;
            defaultUser.IsTraktUser = 1;
            defaultUser.Password    = "";
            defaultUser.Username    = Shoko.Commons.Properties.Resources.Users_Default;
            RepoFactory.JMMUser.Save(defaultUser, true);

            SVR_JMMUser familyUser = new SVR_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    = Shoko.Commons.Properties.Resources.Users_FamilyFriendly;
            RepoFactory.JMMUser.Save(familyUser, true);
        }
Example #4
0
        private void CreateInitialUsers()
        {
            if (RepoFactory.JMMUser.GetAll().Any())
            {
                return;
            }

            Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(ServerSettings.Culture);

            SVR_JMMUser defaultUser = new SVR_JMMUser
            {
                CanEditServerSettings = 1,
                HideCategories        = string.Empty,
                IsAdmin     = 1,
                IsAniDBUser = 1,
                IsTraktUser = 1,
                Password    = ServerSettings.DefaultUserPassword,
                Username    = ServerSettings.DefaultUserUsername
            };

            RepoFactory.JMMUser.Save(defaultUser, true);

            SVR_JMMUser familyUser = new SVR_JMMUser
            {
                CanEditServerSettings = 1,
                HideCategories        = "ecchi,nudity,sex,sexual abuse,horror,erotic game,incest,18 restricted",
                IsAdmin     = 1,
                IsAniDBUser = 1,
                IsTraktUser = 1,
                Password    = string.Empty,
                Username    = Commons.Properties.Resources.Users_FamilyFriendly
            };

            RepoFactory.JMMUser.Save(familyUser, true);
        }
Example #5
0
        public List <Dashboard.EpisodeDetails> GetAniDBCalendarInDays([FromQuery] int numberOfDays = 7, [FromQuery] bool showAll = false)
        {
            SVR_JMMUser user        = HttpContext.GetUser();
            var         episodeList = RepoFactory.AniDB_Episode.GetForDate(DateTime.Today, DateTime.Today.AddDays(numberOfDays)).ToList();
            var         animeDict   = episodeList
                                      .Select(episode => RepoFactory.AniDB_Anime.GetByAnimeID(episode.AnimeID))
                                      .Distinct()
                                      .ToDictionary(anime => anime.AnimeID);
            var seriesDict = episodeList
                             .Select(episode => RepoFactory.AnimeSeries.GetByAnimeID(episode.AnimeID))
                             .Where(series => series != null)
                             .Distinct()
                             .ToDictionary(anime => anime.AniDB_ID);

            return(episodeList
                   .Where(episode => user.AllowedAnime(animeDict[episode.AnimeID]) && (showAll || seriesDict.Keys.Contains(episode.AnimeID)))
                   .OrderBy(episode => episode.GetAirDateAsDate())
                   .Select(episode =>
            {
                var anime = animeDict[episode.AnimeID];
                if (seriesDict.TryGetValue(episode.AnimeID, out var series))
                {
                    return new Dashboard.EpisodeDetails(episode, anime, series);
                }
                return new Dashboard.EpisodeDetails(episode, anime);
            })
                   .ToList());
        }
Example #6
0
        /// <summary>
        ///  change current user password
        /// </summary>
        /// <returns></returns>
        private object ChangePassword()
        {
            Request     request = this.Request;
            SVR_JMMUser user    = (SVR_JMMUser)this.Context.CurrentUser;

            return(ChangePassword(user.JMMUserID));
        }
Example #7
0
        public ActionResult ChangePassword(int userID, [FromBody] string password, bool revokeAPIKeys = true)
        {
            try
            {
                SVR_JMMUser jmmUser = RepoFactory.JMMUser.GetByID(userID);
                if (jmmUser == null)
                {
                    return(BadRequest("User not found"));
                }
                if (jmmUser.JMMUserID != User.JMMUserID && !User.IsAdminUser())
                {
                    return(Unauthorized());
                }

                jmmUser.Password = Digest.Hash(password);
                RepoFactory.JMMUser.Save(jmmUser, false);
                if (revokeAPIKeys)
                {
                    RepoFactory.AuthTokens.DeleteAllWithUserID(jmmUser.JMMUserID);
                }
            }
            catch (Exception ex)
            {
                return(InternalError(ex.ToString()));
            }

            return(Ok());
        }
Example #8
0
        private static List <SearchResult> SearchTagsEquals(string query, int limit, SVR_JMMUser user, ParallelQuery <AniDB_Tag> allTags)
        {
            List <SearchResult>     series     = new List <SearchResult>();
            IEnumerable <CustomTag> customTags = RepoFactory.CustomTag.GetAll();

            series.AddRange(customTags.Where(a => a.TagName.Equals(query, StringComparison.InvariantCultureIgnoreCase)).SelectMany(tag =>
            {
                return(RepoFactory.CrossRef_CustomTag.GetByCustomTagID(tag.CustomTagID)
                       .Select(xref =>
                {
                    if (xref.CrossRefType != (int)CustomTagCrossRefType.Anime)
                    {
                        return null;
                    }
                    var anime = RepoFactory.AnimeSeries.GetByAnimeID(xref.CrossRefID);
                    // Because we are searching tags, then getting series from it, we need to make sure it's allowed
                    // for example, p**n could have the drugs tag, even though it's not a "p**n tag"
                    if (anime?.GetAnime()?.GetAllTags().FindInEnumerable(user.GetHideCategories()) ?? true)
                    {
                        return null;
                    }
                    return new SearchResult
                    {
                        Distance = 0,
                        Index = 0,
                        Match = tag.TagName,
                        Result = anime,
                        ExactMatch = true
                    };
                }).Where(a => a != null).OrderBy(a => a.Distance).ThenBy(a => a.Result.GetSeriesName()));
            }).Take(limit));

            limit -= series.Count;

            series.AddRange(allTags.Where(a => a.TagName.Equals(query, StringComparison.InvariantCultureIgnoreCase)).SelectMany(tag =>
            {
                return(RepoFactory.AniDB_Anime_Tag.GetByTagID(tag.TagID)
                       .Select(xref =>
                {
                    var anime = RepoFactory.AnimeSeries.GetByAnimeID(xref.AnimeID);
                    // Because we are searching tags, then getting series from it, we need to make sure it's allowed
                    // for example, p**n could have the drugs tag, even though it's not a "p**n tag"
                    if (anime?.GetAnime()?.GetAllTags().FindInEnumerable(user.GetHideCategories()) ?? true)
                    {
                        return null;
                    }
                    return new SearchResult
                    {
                        Distance = (600 - xref.Weight) / 600D,
                        Index = 0,
                        Match = tag.TagName,
                        Result = anime,
                        ExactMatch = true
                    };
                }).Where(a => a != null).OrderBy(a => a.Distance).ThenBy(a => a.Result.GetSeriesName()));
            }).Take(limit));
            return(series);
        }
Example #9
0
        /// <summary>
        /// Delete user from his ID
        /// </summary>
        /// <returns></returns>
        private object DeleteUser()
        {
            SVR_JMMUser _user = (SVR_JMMUser)Context.CurrentUser;

            if (_user.IsAdmin == 1)
            {
                SVR_JMMUser user = this.Bind();
                return(new ShokoServiceImplementation().DeleteUser(user.JMMUserID) == string.Empty
                    ? APIStatus.OK()
                    : APIStatus.InternalError());
            }

            return(APIStatus.AdminNeeded());
        }
Example #10
0
            public SVR_JMMUser GetServerModel()
            {
                var user = new SVR_JMMUser
                {
                    Username              = Username,
                    JMMUserID             = ID,
                    Password              = Password,
                    HideCategories        = string.Join(',', TagBlacklist),
                    IsAdmin               = IsAdmin ? 1 : 0,
                    IsTraktUser           = CommunitySites.Contains(global::Shoko.Models.Enums.CommunitySites.Trakt) ? 1 : 0,
                    CanEditServerSettings = IsAdmin ? 1 : 0,
                    IsAniDBUser           = CommunitySites.Contains(global::Shoko.Models.Enums.CommunitySites.AniDB) ? 1 : 0,
                };

                return(user);
            }
Example #11
0
        /// <summary>
        /// Create user from Contract_JMMUser
        /// </summary>
        /// <returns></returns>
        private object CreateUser()
        {
            SVR_JMMUser _user = (SVR_JMMUser)Context.CurrentUser;

            if (_user.IsAdmin == 1)
            {
                JMMUser user = this.Bind();
                user.Password       = Digest.Hash(user.Password);
                user.HideCategories = string.Empty;
                user.PlexUsers      = string.Empty;
                return(new ShokoServiceImplementation().SaveUser(user) == string.Empty
                    ? APIStatus.OK()
                    : APIStatus.InternalError());
            }

            return(APIStatus.AdminNeeded());
        }
Example #12
0
        /// <summary>
        /// Set settings for LogRotator
        /// </summary>
        /// <returns></returns>
        private object SetRotateLogs()
        {
            Request     request = Request;
            SVR_JMMUser user    = (SVR_JMMUser)Context.CurrentUser;
            Logs        rotator = this.Bind();

            if (user.IsAdmin == 1)
            {
                ServerSettings.RotateLogs             = rotator.rotate;
                ServerSettings.RotateLogs_Zip         = rotator.zip;
                ServerSettings.RotateLogs_Delete      = rotator.delete;
                ServerSettings.RotateLogs_Delete_Days = rotator.days.ToString();

                return(APIStatus.OK());
            }

            return(APIStatus.AdminNeeded());
        }
Example #13
0
        public SVR_JMMUser MergeServerModel(SVR_JMMUser existing)
        {
            var user = new SVR_JMMUser
            {
                Username              = Username,
                JMMUserID             = ID,
                HideCategories        = string.Join(',', TagBlacklist),
                IsAdmin               = IsAdmin ? 1 : 0,
                IsTraktUser           = CommunitySites.Contains(global::Shoko.Models.Enums.CommunitySites.Trakt) ? 1 : 0,
                CanEditServerSettings = IsAdmin ? 1 : 0,
                IsAniDBUser           = CommunitySites.Contains(global::Shoko.Models.Enums.CommunitySites.AniDB) ? 1 : 0,
                Password              = existing?.Password ?? string.Empty,
                PlexToken             = existing?.PlexToken,
                PlexUsers             = existing?.PlexUsers ?? string.Empty
            };

            return(user);
        }
Example #14
0
        public ActionResult <User> AddUser(User.FullUser user)
        {
            if (!ModelState.IsValid)
            {
                return(BadRequest(ModelState));
            }
            try
            {
                SVR_JMMUser jmmUser = user.GetServerModel();

                RepoFactory.JMMUser.Save(jmmUser);

                return(new User(jmmUser));
            }
            catch (Exception e)
            {
                return(InternalError(e.Message));
            }
        }
Example #15
0
 public User(SVR_JMMUser user)
 {
     ID             = user.JMMUserID;
     Username       = user.Username;
     IsAdmin        = user.IsAdmin == 1;
     CommunitySites = new List <CommunitySites>();
     if (user.IsAniDBUser == 1)
     {
         CommunitySites.Add(global::Shoko.Models.Enums.CommunitySites.AniDB);
     }
     if (user.IsTraktUser == 1)
     {
         CommunitySites.Add(global::Shoko.Models.Enums.CommunitySites.Trakt);
     }
     if (!string.IsNullOrEmpty(user.PlexToken))
     {
         CommunitySites.Add(global::Shoko.Models.Enums.CommunitySites.Plex);
     }
     TagBlacklist = user.GetHideCategories().ToList();
 }
Example #16
0
        /// <summary>
        /// change given user (by uid) password
        /// </summary>
        /// <returns></returns>
        private object ChangePassword(int uid)
        {
            SVR_JMMUser thisuser = (SVR_JMMUser)Context.CurrentUser;
            SVR_JMMUser user     = this.Bind();

            if (thisuser.IsAdmin == 1)
            {
                return(new ShokoServiceImplementation().ChangePassword(uid, user.Password) == string.Empty
                    ? APIStatus.OK()
                    : APIStatus.InternalError());
            }
            if (thisuser.JMMUserID == user.JMMUserID)
            {
                return(new ShokoServiceImplementation().ChangePassword(uid, user.Password) == string.Empty
                    ? APIStatus.OK()
                    : APIStatus.InternalError());
            }

            return(APIStatus.AdminNeeded());
        }
Example #17
0
        public List <SVR_VideoLocal> GetMostRecentlyAdded(int take, int skip, int jmmuserID = -1)
        {
            if (skip < 0)
            {
                skip = 0;
            }
            if (take == 0)
            {
                return(new List <SVR_VideoLocal>());
            }

            SVR_JMMUser user = jmmuserID == -1 ? null : RepoFactory.JMMUser.GetByID(jmmuserID);

            if (user == null)
            {
                lock (Cache)
                {
                    if (take == -1)
                    {
                        return(Cache.Values.OrderByDescending(a => a.DateTimeCreated).Skip(skip).ToList());
                    }
                    return(Cache.Values.OrderByDescending(a => a.DateTimeCreated).Skip(skip).Take(take).ToList());
                }
            }

            if (take == -1)
            {
                return(Cache.Values
                       .Where(a => a.GetAnimeEpisodes().Select(b => b.GetAnimeSeries()).Where(b => b != null).DistinctBy(b => b.AniDB_ID).All(user.AllowedSeries))
                       .OrderByDescending(a => a.DateTimeCreated)
                       .Skip(skip)
                       .ToList());
            }

            return(Cache.Values
                   .Where(a => a.GetAnimeEpisodes().Select(b => b.GetAnimeSeries()).Where(b => b != null).DistinctBy(b => b.AniDB_ID).All(user.AllowedSeries))
                   .OrderByDescending(a => a.DateTimeCreated)
                   .Skip(skip)
                   .Take(take)
                   .ToList());
        }
Example #18
0
        /// <summary>
        /// Delete user from his ID
        /// </summary>
        /// <returns></returns>
        private object DeleteUser()
        {
            Request     request = this.Request;
            SVR_JMMUser _user   = (SVR_JMMUser)this.Context.CurrentUser;

            if (_user.IsAdmin == 1)
            {
                SVR_JMMUser user = this.Bind();
                if (new ShokoServiceImplementation().DeleteUser(user.JMMUserID) == "")
                {
                    return(APIStatus.statusOK());
                }
                else
                {
                    return(APIStatus.internalError());
                }
            }
            else
            {
                return(APIStatus.adminNeeded());
            }
        }
Example #19
0
        /// <summary>
        /// change given user (by uid) password
        /// </summary>
        /// <returns></returns>
        private object ChangePassword(int uid)
        {
            Request     request = this.Request;
            SVR_JMMUser _user   = (SVR_JMMUser)this.Context.CurrentUser;

            if (_user.IsAdmin == 1)
            {
                SVR_JMMUser user = this.Bind();
                if (new ShokoServiceImplementation().ChangePassword(uid, user.Password) == "")
                {
                    return(APIStatus.statusOK());
                }
                else
                {
                    return(APIStatus.internalError());
                }
            }
            else
            {
                return(APIStatus.adminNeeded());
            }
        }
Example #20
0
        private void CreateInitialUsers()
        {
            if (RepoFactory.JMMUser.GetAll().Any())
            {
                return;
            }

            Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(ServerSettings.Instance.Culture);

            string defaultPassword = ServerSettings.Instance.Database.DefaultUserPassword == ""
                ? ""
                : Digest.Hash(ServerSettings.Instance.Database.DefaultUserPassword);
            SVR_JMMUser defaultUser = new SVR_JMMUser
            {
                CanEditServerSettings = 1,
                HideCategories        = string.Empty,
                IsAdmin     = 1,
                IsAniDBUser = 1,
                IsTraktUser = 1,
                Password    = defaultPassword,
                Username    = ServerSettings.Instance.Database.DefaultUserUsername
            };

            RepoFactory.JMMUser.Save(defaultUser, true);

            SVR_JMMUser familyUser = new SVR_JMMUser
            {
                CanEditServerSettings = 1,
                HideCategories        = "ecchi,nudity,sex,sexual abuse,horror,erotic game,incest,18 restricted",
                IsAdmin     = 1,
                IsAniDBUser = 1,
                IsTraktUser = 1,
                Password    = string.Empty,
                Username    = "******"
            };

            RepoFactory.JMMUser.Save(familyUser, true);
        }
Example #21
0
        public static string ValidateUser(string username, string password, string device)
        {
            //in case of login before database have been loaded
            if (Users.Count == 0)
            {
                UserDatabase.Refresh();
            }

            var userRecord = Users.FirstOrDefault(u => u.Item2.Equals(username, StringComparison.OrdinalIgnoreCase) &&
                                                  u.Item3 == password);

            if (userRecord == null)
            {
                //if user is invalid try to refresh cache so we add any newly added users to cache just in case
                UserDatabase.Refresh();
                return(null);
            }

            int    uid    = new SVR_JMMUser(username).JMMUserID;
            string apiKey = "";

            try
            {
                var apiKeys = ActiveApiKeys.First(u => u.Item1 == uid && u.Item2 == device.ToLower());
                apiKey = apiKeys.Item3;
            }
            catch
            {
                apiKey = Guid.NewGuid().ToString();
                ActiveApiKeys.Add(new Tuple <int, string, string>(uid, device.ToLower(), apiKey));
                AuthTokens token = new AuthTokens {
                    UserID = uid, DeviceName = (device).ToLower(), Token = apiKey
                };
                RepoFactory.AuthTokens.Save(token);
            }

            return(apiKey);
        }
Example #22
0
        public Filter(HttpContext ctx, SVR_GroupFilter gf)
        {
            IDs = new IDs {
                ID = gf.GroupFilterID
            };
            Name = gf.GroupFilterName;
            SVR_JMMUser user = ctx.GetUser();

            Directory = ((GroupFilterType)gf.FilterType).HasFlag(GroupFilterType.Directory);
            // This can be used to exclude from client visibility for user
            Size = gf.GroupsIds.ContainsKey(user.JMMUserID) ? gf.GroupsIds[user.JMMUserID].Count : 0;
            if (Directory)
            {
                Size += RepoFactory.GroupFilter.GetByParentID(gf.GroupFilterID).Count;
            }

            ApplyAtSeriesLevel = gf.ApplyToSeries == 1;

            // It's never null, just marked Nullable for some reason
            Locked = gf.Locked != null && gf.Locked.Value == 1;

            HideInAPI = gf.InvisibleInClients == 1;
        }
Example #23
0
        private static List <SearchResult> SearchTagsFuzzy(string query, int limit, SVR_JMMUser user, ParallelQuery <AniDB_Tag> allTags)
        {
            List <SearchResult> series = new List <SearchResult>();
            IEnumerable <Misc.SearchInfo <CustomTag> > customTags = RepoFactory.CustomTag.GetAll().Select(a =>
            {
                if (user.GetHideCategories().Contains(a.TagName))
                {
                    return(null);
                }
                Misc.SearchInfo <CustomTag> tag = Misc.DiceFuzzySearch(a.TagName, query, 0, a);
                if (tag.Index == -1 || tag.Result == null)
                {
                    return(null);
                }
                return(tag);
            }).Where(a => a != null).OrderBy(a => a.Distance);

            series.AddRange(customTags.SelectMany(tag =>
            {
                return(RepoFactory.CrossRef_CustomTag.GetByCustomTagID(tag.Result.CustomTagID)
                       .Select(xref =>
                {
                    if (xref.CrossRefType != (int)CustomTagCrossRefType.Anime)
                    {
                        return null;
                    }
                    SVR_AnimeSeries anime = RepoFactory.AnimeSeries.GetByAnimeID(xref.CrossRefID);
                    // Because we are searching tags, then getting series from it, we need to make sure it's allowed
                    // for example, p**n could have the drugs tag, even though it's not a "p**n tag"
                    if (anime?.GetAnime()?.GetAllTags().FindInEnumerable(user.GetHideCategories()) ?? true)
                    {
                        return null;
                    }
                    return new SearchResult
                    {
                        Distance = tag.Distance,
                        Index = tag.Index,
                        Match = tag.Result.TagName,
                        Result = anime,
                        ExactMatch = tag.ExactMatch
                    };
                }).Where(b => b != null).OrderBy(b => b.Distance).ThenBy(b => b.Result.GetSeriesName()).ToList());
            }).Take(limit));

            limit -= series.Count;

            var tags = allTags.Select(tag =>
            {
                var result = Misc.DiceFuzzySearch(tag.TagName, query, 0, tag);
                if (result.Index == -1 || result.Result == null)
                {
                    return(null);
                }
                return(result);
            }).Where(a => a != null).OrderBy(a => a.Distance);

            series.AddRange(tags.SelectMany(tag =>
            {
                return(RepoFactory.AniDB_Anime_Tag.GetByTagID(tag.Result.TagID)
                       .Select(xref =>
                {
                    var anime = RepoFactory.AnimeSeries.GetByAnimeID(xref.AnimeID);
                    // Because we are searching tags, then getting series from it, we need to make sure it's allowed
                    // for example, p**n could have the drugs tag, even though it's not a "p**n tag"
                    if (anime?.GetAnime()?.GetAllTags().FindInEnumerable(user.GetHideCategories()) ?? true)
                    {
                        return null;
                    }
                    return new SearchResult
                    {
                        Distance = (600D - xref.Weight) / 600,
                        Index = tag.Index,
                        Match = tag.Result.TagName,
                        Result = anime,
                        ExactMatch = tag.ExactMatch
                    };
                }).Where(a => a != null).OrderBy(a => a.Distance).ThenBy(a => a.Result.GetSeriesName()).ToList());
            }).Take(limit));
            return(series);
        }
        public override void ProcessCommand()
        {
            logger.Info("Processing CommandRequest_AddFileToMyList: {0}", Hash);


            try
            {
                vid = RepoFactory.VideoLocal.GetByHash(this.Hash);
                List <SVR_AnimeEpisode> animeEpisodes = new List <SVR_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 = ShokoService.AnidbProcessor.AddFileToMyList(xrefs[0].AnimeID,
                                                                                       xrefs[0].GetEpisode().EpisodeNumber,
                                                                                       ref watchedDate);
                    }
                    else
                    {
                        newWatchedStatus = ShokoService.AnidbProcessor.AddFileToMyList(vid, ref watchedDate);
                    }

                    // do for all AniDB users
                    List <SVR_JMMUser> aniDBUsers = RepoFactory.JMMUser.GetAniDBUsers();


                    if (aniDBUsers.Count > 0)
                    {
                        SVR_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)
                            {
                                SVR_AnimeEpisode      ep     = animeEpisodes[0];
                                SVR_AnimeEpisode_User epUser = null;

                                foreach (SVR_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);
                                }
                            }
                        }
                    }

                    SVR_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 (SVR_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;
            }
        }
Example #25
0
        /// <summary>
        ///     Search for series with given query in name or tag
        /// </summary>
        /// <param name="query">target string</param>
        /// <param name="userID">user id</param>
        /// <param name="limit">The number of results to return</param>
        /// <param name="flags">The SearchFlags to determine the type of search</param>
        /// <returns>
        ///     <see cref="List{SearchResult}" />
        /// </returns>
        public static List <SearchResult> Search(int userID, string query, int limit, SearchFlags flags, TagFilter.Filter tagFilter = TagFilter.Filter.None)
        {
            query = query.ToLowerInvariant();

            SVR_JMMUser user = RepoFactory.JMMUser.GetByID(userID);

            if (user == null)
            {
                throw new Exception("User not found");
            }

            ParallelQuery <SVR_AnimeSeries> allSeries =
                RepoFactory.AnimeSeries.GetAll().AsParallel().Where(a =>
                                                                    a?.GetAnime() != null && (a.GetAnime().GetAllTags().Count == 0 || !a.GetAnime().GetAllTags().FindInEnumerable(user.GetHideCategories())));

            ParallelQuery <AniDB_Tag> allTags = RepoFactory.AniDB_Tag.GetAll().AsParallel()
                                                .Where(a =>
            {
                List <string> _ = new List <string>();
                return(!user.GetHideCategories().Contains(a.TagName) &&
                       !TagFilter.IsTagBlackListed(a.TagName, tagFilter, ref _));
            });

            //search by anime id
            if (int.TryParse(query, out int aid))
            {
                var aidResults = SearchTitlesByAnimeID(aid, allSeries);
                if (aidResults.Count > 0)
                {
                    return(aidResults);
                }
            }

            #region Search_TitlesOnly

            switch (flags)
            {
            case SearchFlags.Titles:
                return(SearchTitlesIndexOf(query, limit, allSeries));

            case SearchFlags.Fuzzy | SearchFlags.Titles:
                return(SearchTitlesFuzzy(query, limit, allSeries));

            case SearchFlags.Tags:
                return(SearchTagsEquals(query, limit, user, allTags));

            case SearchFlags.Fuzzy | SearchFlags.Tags:
                return(SearchTagsFuzzy(query, limit, user, allTags));

            case SearchFlags.Tags | SearchFlags.Titles:
                List <SearchResult> titleResult = SearchTitlesIndexOf(query, limit, allSeries);

                int tagLimit = limit - titleResult.Count;
                if (tagLimit <= 0)
                {
                    return(titleResult);
                }
                titleResult.AddRange(SearchTagsEquals(query, tagLimit, user, allTags));
                return(titleResult);

            case SearchFlags.Fuzzy | SearchFlags.Tags | SearchFlags.Titles:
                List <SearchResult> titles = SearchTitlesFuzzy(query, limit, allSeries);

                int tagLimit2 = limit - titles.Count;
                if (tagLimit2 <= 0)
                {
                    return(titles);
                }
                titles.AddRange(SearchTagsFuzzy(query, tagLimit2, user, allTags));
                return(titles);
            }

            #endregion

            return(new List <SearchResult>());
        }
Example #26
0
        /// <summary>
        ///  change current user password
        /// </summary>
        /// <returns></returns>
        private object ChangePassword()
        {
            SVR_JMMUser user = this.Bind();

            return(ChangePassword(user.JMMUserID));
        }
Example #27
0
        public object GetStats()
        {
            SVR_JMMUser user = HttpContext.User.Identity as SVR_JMMUser;

            int    series_count;
            int    file_count;
            string size;

            int  watched_files  = 0;
            int  watched_series = 0;
            long hours          = 0;

            List <string> tags;

            if (user != null)
            {
                var series = Repo.Instance.AnimeSeries.GetAll().Where(a =>
                                                                      !a.GetAnime()?.GetAllTags().FindInEnumerable(user.GetHideCategories()) ?? false).ToList();
                series_count = series.Count;

                var files = series.SelectMany(a => a.GetAnimeEpisodes()).SelectMany(a => a.GetVideoLocals())
                            .DistinctBy(a => a.VideoLocalID).ToList();
                file_count = files.Count;
                size       = SizeSuffix(files.Sum(a => a.FileSize));

                var watched = Repo.Instance.VideoLocal_User.GetByUserID(user.JMMUserID)
                              .Where(a => a.WatchedDate != null).ToList();

                watched_files = watched.Count;

                watched_series = Repo.Instance.AnimeSeries.GetAll().Count(a =>
                {
                    var contract = a.GetUserContract(user.JMMUserID);
                    if (contract?.MissingEpisodeCount > 0)
                    {
                        return(false);
                    }
                    return(contract?.UnwatchedEpisodeCount == 0);
                });

                hours = watched.Select(a => Repo.Instance.VideoLocal.GetByID(a.VideoLocalID)).Where(a => a != null)
                        .Sum(a => a.Duration) / 3600000; // 1000ms * 60s * 60m = ?h

                tags = Repo.Instance.AniDB_Anime_Tag.GetAllForLocalSeries().GroupBy(a => a.TagID)
                       .ToDictionary(a => a.Key, a => a.Count()).OrderByDescending(a => a.Value)
                       .Select(a => Repo.Instance.AniDB_Tag.GetByID(a.Key)?.TagName)
                       .Where(a => a != null && !user.GetHideCategories().Contains(a)).ToList();
                var tagfilter = TagFilter.Filter.AnidbInternal | TagFilter.Filter.Misc | TagFilter.Filter.Source;
                tags = TagFilter.ProcessTags(tagfilter, tags).Take(10).ToList();
            }
            else
            {
                var series = Repo.Instance.AnimeSeries.GetAll();
                series_count = series.Count;

                var files = series.SelectMany(a => a.GetAnimeEpisodes()).SelectMany(a => a.GetVideoLocals())
                            .DistinctBy(a => a.VideoLocalID).ToList();
                file_count = files.Count;
                size       = SizeSuffix(files.Sum(a => a.FileSize));

                tags = Repo.Instance.AniDB_Anime_Tag.GetAllForLocalSeries().GroupBy(a => a.TagID)
                       .ToDictionary(a => a.Key, a => a.Count()).OrderByDescending(a => a.Value)
                       .Select(a => Repo.Instance.AniDB_Tag.GetByID(a.Key)?.TagName)
                       .Where(a => a != null).ToList();
                var tagfilter = TagFilter.Filter.AnidbInternal | TagFilter.Filter.Misc | TagFilter.Filter.Source;
                tags = TagFilter.ProcessTags(tagfilter, tags).Take(10).ToList();
            }

            return(new Dictionary <string, object>
            {
                { "queue", Repo.Instance.CommandRequest.GetByClasses().ToDictionary(a => FromLastPoint(a.Key), a => a.Value) },
                { "file_count", file_count },
                { "series_count", series_count },
                { "collection_size", size },
                { "watched_files", watched_files },
                { "watched_series", watched_series },
                { "hours_watched", hours },
                { "tags", tags }
            });
        }
Example #28
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.Error( ex,ex.ToString());
         *              }
         *      }*/

        public static void UpdateMALSeries(SVR_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();
                myanimelist malAnimeList       = GetMALAnimeList();
                if (ServerSettings.MAL_NeverDecreaseWatchedNums)
                //if set, check watched number before update: take some time, as user anime list must be loaded
                {
                    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;
                }

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

                // find the anidb user
                List <SVR_JMMUser> aniDBUsers = RepoFactory.JMMUser.GetAniDBUsers();
                if (aniDBUsers.Count == 0)
                {
                    return;
                }

                SVR_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 (SVR_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 == enEpisodeType.Episode)
                                {
                                    totalEpCount = ser.GetAnime().EpisodeCountNormal;
                                }
                                if (ep.EpisodeTypeEnum == 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
                            SVR_AnimeEpisode_User usrRecord = ep.GetUserRecord(user.JMMUserID);
                            if (usrRecord != null && usrRecord.WatchedDate.HasValue && epNum > lastWatchedEpNumber)
                            {
                                lastWatchedEpNumber = epNum;
                            }

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

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

                            foreach (CL_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;
                    }

                    string confirmationMessage = "";
                    if (animeListHashtable.ContainsKey(malID))
                    {
                        ModifyAnime(malID, lastWatchedEpNumber, status, score, downloadedEps, fanSubs);
                        confirmationMessage = string.Format(
                            "MAL successfully updated (MAL MODIFY), mal id: {0}, ep: {1}, score: {2}", malID,
                            lastWatchedEpNumber, score);
                    }
                    else
                    {
                        AddAnime(malID, lastWatchedEpNumber, status, score, downloadedEps, fanSubs);
                        confirmationMessage = string.Format(
                            "MAL successfully updated (MAL ADD), mal id: {0}, ep: {1}, score: {2}", malID,
                            lastWatchedEpNumber, score);
                    }
                    logger.Trace(confirmationMessage);
                }
            }
            catch (Exception ex)
            {
                logger.Error(ex, ex.ToString());
            }
        }
Example #29
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

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


                // find the anidb user
                List <SVR_JMMUser> aniDBUsers = RepoFactory.JMMUser.GetAniDBUsers();
                if (aniDBUsers.Count == 0)
                {
                    return;
                }

                SVR_JMMUser user = aniDBUsers[0];


                foreach (myanimelistAnime malAnime in mal.anime)
                {
                    // look up the anime
                    CrossRef_AniDB_MAL xref = RepoFactory.CrossRef_AniDB_MAL.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 = RepoFactory.CrossRef_AniDB_MAL.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 = RepoFactory.AniDB_Episode.GetByAnimeID(xref.AnimeID);
                    foreach (AniDB_Episode aniep in aniEps)
                    {
                        if (aniep.EpisodeType != xref.StartEpisodeType)
                        {
                            continue;
                        }

                        SVR_AnimeEpisode ep = RepoFactory.AnimeEpisode.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;
                        }

                        SVR_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
                        {
                            if (usrRec != null && usrRec.WatchedDate.HasValue)
                            {
                                ep.ToggleWatchedStatus(false, true, DateTime.Now, user.JMMUserID, false);
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                logger.Error("Error processing CommandRequest_MALDownloadStatusFromMAL: {0}", ex);
            }
        }
        public override void ProcessCommand()
        {
            logger.Info($"Processing CommandRequest_AddFileToMyList: {vid?.FileName} - {Hash} - {ReadStates}");

            try
            {
                if (vid == null)
                {
                    return;
                }

                // 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.DistinctBy(a => Tuple.Create(a.AnimeID, a.EpisodeID)).ToList();
                if (xrefs.Count > 0)
                {
                    isManualLink = xrefs[0].CrossRefSource != (int)CrossRefSource.AniDB;
                }

                // mark the video file as watched
                List <SVR_JMMUser> aniDBUsers          = RepoFactory.JMMUser.GetAniDBUsers();
                SVR_JMMUser        juser               = aniDBUsers.FirstOrDefault();
                DateTime?          originalWatchedDate = null;
                if (juser != null)
                {
                    originalWatchedDate = vid.GetUserRecord(juser.JMMUserID)?.WatchedDate?.ToUniversalTime();
                }

                DateTime?newWatchedDate = null;
                int?     lid            = null;
                // this only gets overwritten if the response is File Already in MyList
                AniDBFile_State?state = ServerSettings.Instance.AniDb.MyList_StorageState;

                if (isManualLink)
                {
                    foreach (var xref in xrefs)
                    {
                        (lid, newWatchedDate) = ShokoService.AnidbProcessor.AddFileToMyList(xref.AnimeID,
                                                                                            xref.GetEpisode().EpisodeNumber, originalWatchedDate, ref state);
                    }
                }
                else
                {
                    (lid, newWatchedDate) =
                        ShokoService.AnidbProcessor.AddFileToMyList(vid, originalWatchedDate, ref state);
                }

                // never true for Manual Links, so no worries about the loop overwriting it
                if (lid != null && lid.Value > 0)
                {
                    vid.MyListID = lid.Value;
                    RepoFactory.VideoLocal.Save(vid);
                }

                logger.Info($"Added File to MyList. File: {vid.FileName}  Manual Link: {isManualLink}  Watched Locally: {originalWatchedDate != null}  Watched AniDB: {newWatchedDate != null}  Local State: {ServerSettings.Instance.AniDb.MyList_StorageState}  AniDB State: {state}  ReadStates: {ReadStates}  ReadWatched Setting: {ServerSettings.Instance.AniDb.MyList_ReadWatched}  ReadUnwatched Setting: {ServerSettings.Instance.AniDb.MyList_ReadUnwatched}");
                if (juser != null)
                {
                    bool watched = newWatchedDate != null;

                    bool watchedLocally = originalWatchedDate != null;
                    bool watchedChanged = watched != watchedLocally;

                    if (ReadStates)
                    {
                        // handle import watched settings. Don't update AniDB in either case, we'll do that with the storage state
                        if (ServerSettings.Instance.AniDb.MyList_ReadWatched && watched && !watchedLocally)
                        {
                            vid.ToggleWatchedStatus(true, false, newWatchedDate, false, juser.JMMUserID,
                                                    false, false);
                        }
                        else if (ServerSettings.Instance.AniDb.MyList_ReadUnwatched && !watched && watchedLocally)
                        {
                            vid.ToggleWatchedStatus(false, false, null, false, juser.JMMUserID,
                                                    false, false);
                        }
                    }

                    // We should have a MyListID at this point, so hopefully this will prevent looping
                    if (watchedChanged || state != ServerSettings.Instance.AniDb.MyList_StorageState)
                    {
                        // if vid.MyListID > 0, isManualLink _should_ always be false, but _should_ isn't good enough
                        if (vid.MyListID > 0 && !isManualLink)
                        {
                            if (ServerSettings.Instance.AniDb.MyList_SetWatched && watchedLocally)
                            {
                                ShokoService.AnidbProcessor.UpdateMyListFileStatus(vid, true, originalWatchedDate);
                            }
                            else if (ServerSettings.Instance.AniDb.MyList_SetUnwatched && !watchedLocally)
                            {
                                ShokoService.AnidbProcessor.UpdateMyListFileStatus(vid, false);
                            }
                        }
                        else if (isManualLink)
                        {
                            foreach (var xref in xrefs)
                            {
                                if (ServerSettings.Instance.AniDb.MyList_SetWatched && watchedLocally)
                                {
                                    ShokoService.AnidbProcessor.UpdateMyListFileStatus(vid, xref.AnimeID, xref.GetEpisode().EpisodeNumber, true, originalWatchedDate);
                                }
                                else if (ServerSettings.Instance.AniDb.MyList_SetUnwatched && !watchedLocally)
                                {
                                    ShokoService.AnidbProcessor.UpdateMyListFileStatus(vid, xref.AnimeID, xref.GetEpisode().EpisodeNumber, false);
                                }
                            }
                        }
                    }
                }

                // if we don't have xrefs, then no series or eps.
                if (xrefs.Count <= 0)
                {
                    return;
                }

                SVR_AnimeSeries ser = RepoFactory.AnimeSeries.GetByAnimeID(xrefs[0].AnimeID);
                // 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 (ServerSettings.Instance.TraktTv.Enabled &&
                    !string.IsNullOrEmpty(ServerSettings.Instance.TraktTv.AuthToken))
                {
                    foreach (SVR_AnimeEpisode aep in vid.GetAnimeEpisodes())
                    {
                        CommandRequest_TraktCollectionEpisode cmdSyncTrakt =
                            new CommandRequest_TraktCollectionEpisode(aep.AnimeEpisodeID, TraktSyncAction.Add);
                        cmdSyncTrakt.Save();
                    }
                }
            }
            catch (Exception ex)
            {
                logger.Error($"Error processing CommandRequest_AddFileToMyList: {Hash} - {ex}");
            }
        }