/// <summary> /// Creates an <see cref="SVR_AnimeGroup"/> instance. /// </summary> /// <remarks> /// This method only creates an <see cref="SVR_AnimeGroup"/> instance. It does NOT save it to the database. /// </remarks> /// <param name="session">The NHibernate session.</param> /// <param name="mainSeries">The <see cref="SVR_AnimeSeries"/> whose name will represent the group (Optional. Pass <c>null</c> if not available).</param> /// <param name="mainAnimeId">The ID of the anime whose name will represent the group if <paramref name="mainSeries"/> is <c>null</c>.</param> /// <param name="now">The current date/time.</param> /// <returns>The created <see cref="SVR_AnimeGroup"/>.</returns> private void CreateAnimeGroup_RA(SVR_AnimeGroup animeGroup_ra, SVR_AnimeSeries mainSeries, int mainAnimeId, DateTime now) { SVR_AnimeGroup animeGroup = new SVR_AnimeGroup(); string groupName; if (mainSeries != null) { animeGroup.Populate_RA(mainSeries, now); groupName = animeGroup.GroupName; } else // The anime chosen as the group's main anime doesn't actually have a series { SVR_AniDB_Anime mainAnime = Repo.Instance.AniDB_Anime.GetByID(mainAnimeId); animeGroup.Populate_RA(mainAnime, now); groupName = animeGroup.GroupName; } // If the title appears to end with a year suffix, then remove it groupName = _truncateYearRegex.Replace(groupName, string.Empty); animeGroup.GroupName = groupName; animeGroup.SortName = groupName; return; }
public Group(HttpContext ctx, SVR_AnimeGroup group) { int uid = ctx.GetUser()?.JMMUserID ?? 0; var allSeries = group.GetAllSeries(skipSorting: true); List <SVR_AnimeEpisode> ael = allSeries.SelectMany(a => a.GetAnimeEpisodes()).ToList(); IDs = new GroupIDs { ID = group.AnimeGroupID }; if (group.DefaultAnimeSeriesID != null) { IDs.DefaultSeries = group.DefaultAnimeSeriesID.Value; } if (group.AnimeGroupParentID.HasValue) { IDs.ParentGroup = group.AnimeGroupParentID.Value; } IDs.TopLevelGroup = group.TopLevelAnimeGroup.AnimeGroupID; Name = group.GroupName; SortName = group.SortName; Description = group.Description; Sizes = ModelHelper.GenerateSizes(ael, uid); Size = group.GetSeries().Count; HasCustomName = GetHasCustomName(group); }
/// <summary> /// Creates a single <see cref="SVR_AnimeGroup"/> for each <see cref="SVR_AnimeSeries"/> in <paramref name="seriesList"/>. /// </summary> /// <remarks> /// This method assumes that there are no active transactions on the specified <paramref name="session"/>. /// </remarks> /// <param name="session">The NHibernate session.</param> /// <param name="seriesList">The list of <see cref="SVR_AnimeSeries"/> to create groups for.</param> /// <returns>A sequence of the created <see cref="SVR_AnimeGroup"/>s.</returns> private IEnumerable <SVR_AnimeGroup> CreateGroupPerSeries(ISessionWrapper session, IReadOnlyList <SVR_AnimeSeries> seriesList) { _log.Info("Generating AnimeGroups for {0} AnimeSeries", seriesList.Count); DateTime now = DateTime.Now; var newGroupsToSeries = new Tuple <SVR_AnimeGroup, SVR_AnimeSeries> [seriesList.Count]; // Create one group per series for (int grp = 0; grp < seriesList.Count; grp++) { SVR_AnimeGroup group = new SVR_AnimeGroup(); SVR_AnimeSeries series = seriesList[grp]; @group.Populate(series, now); newGroupsToSeries[grp] = new Tuple <SVR_AnimeGroup, SVR_AnimeSeries>(group, series); } using (ITransaction trans = session.BeginTransaction()) { _animeGroupRepo.InsertBatch(session, newGroupsToSeries.Select(gts => gts.Item1).AsReadOnlyCollection()); trans.Commit(); } // Anime groups should have IDs now they've been inserted. Now assign the group ID's to their respective series // (The caller of this method will be responsible for saving the AnimeSeries) foreach (Tuple <SVR_AnimeGroup, SVR_AnimeSeries> groupAndSeries in newGroupsToSeries) { groupAndSeries.Item2.AnimeGroupID = groupAndSeries.Item1.AnimeGroupID; } _log.Info("Generated {0} AnimeGroups", newGroupsToSeries.Length); return(newGroupsToSeries.Select(gts => gts.Item1)); }
/// <summary> /// Creates an <see cref="SVR_AnimeGroup"/> instance. /// </summary> /// <remarks> /// This method only creates an <see cref="SVR_AnimeGroup"/> instance. It does NOT save it to the database. /// </remarks> /// <param name="session">The NHibernate session.</param> /// <param name="mainSeries">The <see cref="SVR_AnimeSeries"/> whose name will represent the group (Optional. Pass <c>null</c> if not available).</param> /// <param name="mainAnimeId">The ID of the anime whose name will represent the group if <paramref name="mainSeries"/> is <c>null</c>.</param> /// <param name="now">The current date/time.</param> /// <returns>The created <see cref="SVR_AnimeGroup"/>.</returns> private SVR_AnimeGroup CreateAnimeGroup(SVR_AnimeSeries mainSeries, int mainAnimeId, DateTime now) { SVR_AnimeGroup animeGroup = new SVR_AnimeGroup(); string groupName = null; if (mainSeries != null) { animeGroup.Populate(mainSeries, now); groupName = mainSeries.GetSeriesName(); } else // The anime chosen as the group's main anime doesn't actually have a series { SVR_AniDB_Anime mainAnime = _aniDbAnimeRepo.GetByAnimeID(mainAnimeId); animeGroup.Populate(mainAnime, now); groupName = mainAnime.GetFormattedTitle(); } groupName = _truncateYearRegex.Replace(groupName, String.Empty); // If the title appears to end with a year suffix, then remove it animeGroup.GroupName = groupName; animeGroup.SortName = groupName; return(animeGroup); }
private AnimeSeriesRepository() { BeginDeleteCallback = (cr) => { RepoFactory.AnimeSeries_User.Delete(RepoFactory.AnimeSeries_User.GetBySeriesID(cr.AnimeSeriesID)); lock (Changes) { Changes.Remove(cr.AnimeSeriesID); } }; EndDeleteCallback = (cr) => { cr.DeleteFromFilters(); if (cr.AnimeGroupID > 0) { logger.Trace("Updating group stats by group from AnimeSeriesRepository.Delete: {0}", cr.AnimeGroupID); SVR_AnimeGroup oldGroup = RepoFactory.AnimeGroup.GetByID(cr.AnimeGroupID); if (oldGroup != null) { RepoFactory.AnimeGroup.Save(oldGroup, true, true); } } }; }
private void UpdateAnimeGroupsAndTheirContracts(ISessionWrapper session, IReadOnlyCollection <SVR_AnimeGroup> groups) { ServerState.Instance.DatabaseBlocked = new ServerState.DatabaseBlockedInfo { Blocked = true, Status = "Updating statistics and contracts for AnimeGroups" }; _log.Info("Updating statistics and contracts for AnimeGroups"); var allCreatedGroupUsers = new ConcurrentBag <List <SVR_AnimeGroup_User> >(); // Update batches of AnimeGroup contracts in parallel. Each parallel branch requires it's own session since NHibernate sessions aren't thread safe. // The reason we're doing this in parallel is because updating contacts does a reasonable amount of work (including LZ4 compression) Parallel.ForEach(groups.Batch(DefaultBatchSize), new ParallelOptions { MaxDegreeOfParallelism = 4 }, localInit: () => DatabaseFactory.SessionFactory.OpenStatelessSession(), body: (groupBatch, state, localSession) => { var createdGroupUsers = new List <SVR_AnimeGroup_User>(groupBatch.Length); // We shouldn't need to keep track of updates to AnimeGroup_Users in the below call, because they should have all been deleted, // therefore they should all be new SVR_AnimeGroup.BatchUpdateStats(groupBatch, watchedStats: true, missingEpsStats: true, createdGroupUsers: createdGroupUsers); allCreatedGroupUsers.Add(createdGroupUsers); SVR_AnimeGroup.BatchUpdateContracts(localSession.Wrap(), groupBatch, updateStats: true); return(localSession); }, localFinally: localSession => { localSession.Dispose(); }); _animeGroupRepo.UpdateBatch(session, groups); _log.Info("AnimeGroup statistics and contracts have been updated"); ServerState.Instance.DatabaseBlocked = new ServerState.DatabaseBlockedInfo { Blocked = true, Status = "Creating AnimeGroup_Users and updating plex/kodi contracts" }; _log.Info("Creating AnimeGroup_Users and updating plex/kodi contracts"); List <SVR_AnimeGroup_User> animeGroupUsers = allCreatedGroupUsers.SelectMany(groupUsers => groupUsers) .ToList(); // Insert the AnimeGroup_Users so that they get assigned a primary key before we update plex/kodi contracts _animeGroupUserRepo.InsertBatch(session, animeGroupUsers); // We need to repopulate caches for AnimeGroup_User and AnimeGroup because we've updated/inserted them // and they need to be up to date for the plex/kodi contract updating to work correctly _animeGroupUserRepo.Populate(session, displayname: false); _animeGroupRepo.Populate(session, displayname: false); // NOTE: There are situations in which UpdatePlexKodiContracts will cause database database writes to occur, so we can't // use Parallel.ForEach for the time being (If it was guaranteed to only read then we'd be ok) foreach (SVR_AnimeGroup_User groupUser in animeGroupUsers) { groupUser.UpdatePlexKodiContracts(session); } _animeGroupUserRepo.UpdateBatch(session, animeGroupUsers); _log.Info("AnimeGroup_Users have been created"); }
private static CL_AniDB_Anime GetAnimeContractFromGroup(SVR_AnimeGroup grp) { var anime = grp.Anime.OrderBy(a => a.BeginYear) .ThenBy(a => a.AirDate ?? DateTime.MaxValue) .FirstOrDefault(); return(anime?.Contract.AniDBAnime); }
public static void Populate(this SVR_AnimeGroup agroup, SVR_AniDB_Anime anime, DateTime now) { agroup.Description = anime.Description; agroup.GroupName = anime.PreferredTitle; agroup.SortName = anime.PreferredTitle; agroup.DateTimeUpdated = now; agroup.DateTimeCreated = now; }
public static void Populate(this SVR_AnimeGroup agroup, SVR_AniDB_Anime anime, DateTime now) { agroup.Description = anime.Description; string name = anime.GetFormattedTitle(); agroup.GroupName = name; agroup.SortName = name; agroup.DateTimeUpdated = now; agroup.DateTimeCreated = now; }
public void UpdateGroupFilter(HashSet <GroupFilterConditionType> types) { SVR_AnimeGroup grp = RepoFactory.AnimeGroup.GetByID(AnimeGroupID); SVR_JMMUser usr = RepoFactory.JMMUser.GetByID(JMMUserID); if (grp != null && usr != null) { grp.UpdateGroupFilters(types, usr); } }
public void UpdatePlexKodiContracts_RA() { SVR_AnimeGroup grp = Repo.Instance.AnimeGroup.GetByID(AnimeGroupID); if (grp == null) { return; } List <SVR_AnimeSeries> series = grp.GetAllSeries(); PlexContract = Helper.GenerateFromAnimeGroup(grp, JMMUserID, series); }
public void UpdatePlexKodiContracts(ISessionWrapper session = null) { SVR_AnimeGroup grp = RepoFactory.AnimeGroup.GetByID(AnimeGroupID); if (grp == null) { return; } List <SVR_AnimeSeries> series = grp.GetAllSeries(); PlexContract = Helper.GenerateFromAnimeGroup(grp, JMMUserID, series, session); }
public bool AllowedGroup(SVR_AnimeGroup grp) { if (this.GetHideCategories().Count == 0) { return(true); } if (grp.Contract == null) { return(false); } return(!this.GetHideCategories().FindInEnumerable(grp.Contract.Stat_AllTags)); }
public static void Populate(this SVR_AnimeGroup agroup, SVR_AnimeSeries series, DateTime now) { SVR_AniDB_Anime anime = series.GetAnime(); agroup.Description = anime.Description; string name = series.GetSeriesName(); agroup.GroupName = name; agroup.SortName = name; agroup.DateTimeUpdated = now; agroup.DateTimeCreated = now; }
public static Filter FilterFromAnimeGroup(HttpContext ctx, SVR_AnimeGroup grp, int uid) { Filter ob = new Filter { name = grp.GroupName, id = grp.AnimeGroupID, url = APIHelper.ConstructFilterIdUrl(ctx, grp.AnimeGroupID), size = -1, viewed = -1 }; foreach (SVR_AnimeSeries ser in grp.GetSeries().Randomize()) { SVR_AniDB_Anime anim = ser.GetAnime(); if (anim != null) { ImageDetails fanart = anim.GetDefaultFanartDetailsNoBlanks(); ImageDetails banner = anim.GetDefaultWideBannerDetailsNoBlanks(); if (fanart != null) { ob.art.fanart.Add(new Art() { url = APIHelper.ConstructImageLinkFromTypeAndId(ctx, (int)fanart.ImageType, fanart.ImageID), index = ob.art.fanart.Count }); ob.art.thumb.Add(new Art() { url = APIHelper.ConstructImageLinkFromTypeAndId(ctx, (int)fanart.ImageType, fanart.ImageID), index = ob.art.thumb.Count }); } if (banner != null) { ob.art.banner.Add(new Art() { url = APIHelper.ConstructImageLinkFromTypeAndId(ctx, (int)banner.ImageType, banner.ImageID), index = ob.art.banner.Count }); } if (ob.art.fanart.Count > 0) { break; } } } return(ob); }
/// <summary> /// Creates <see cref="SVR_AnimeGroup"/> that contain <see cref="SVR_AnimeSeries"/> that appear to be related. /// </summary> /// <remarks> /// This method assumes that there are no active transactions on the specified <paramref name="session"/>. /// </remarks> /// <param name="session">The NHibernate session.</param> /// <param name="seriesList">The list of <see cref="SVR_AnimeSeries"/> to create groups for.</param> /// <returns>A sequence of the created <see cref="SVR_AnimeGroup"/>s.</returns> private IEnumerable <SVR_AnimeGroup> AutoCreateGroupsWithRelatedSeries(ISessionWrapper session, IReadOnlyCollection <SVR_AnimeSeries> seriesList) { ServerState.Instance.DatabaseBlocked = new ServerState.DatabaseBlockedInfo { Blocked = true, Status = "Auto-generating Groups based on Relation Trees" }; _log.Info("Auto-generating AnimeGroups for {0} AnimeSeries based on aniDB relationships", seriesList.Count); DateTime now = DateTime.Now; var grpCalculator = AutoAnimeGroupCalculator.CreateFromServerSettings(session); _log.Info( "The following exclusions will be applied when generating the groups: " + grpCalculator.Exclusions); // Group all of the specified series into their respective groups (keyed by the groups main anime ID) var seriesByGroup = seriesList.ToLookup(s => grpCalculator.GetGroupAnimeId(s.AniDB_ID)); var newGroupsToSeries = new List <Tuple <SVR_AnimeGroup, IReadOnlyCollection <SVR_AnimeSeries> > >(seriesList.Count); foreach (var groupAndSeries in seriesByGroup) { int mainAnimeId = groupAndSeries.Key; SVR_AnimeSeries mainSeries = groupAndSeries.FirstOrDefault(series => series.AniDB_ID == mainAnimeId); SVR_AnimeGroup animeGroup = CreateAnimeGroup(mainSeries, mainAnimeId, now); newGroupsToSeries.Add( new Tuple <SVR_AnimeGroup, IReadOnlyCollection <SVR_AnimeSeries> >(animeGroup, groupAndSeries.AsReadOnlyCollection())); } using (ITransaction trans = session.BeginTransaction()) { _animeGroupRepo.InsertBatch(session, newGroupsToSeries.Select(gts => gts.Item1).AsReadOnlyCollection()); trans.Commit(); } // Anime groups should have IDs now they've been inserted. Now assign the group ID's to their respective series // (The caller of this method will be responsible for saving the AnimeSeries) foreach (var groupAndSeries in newGroupsToSeries) { foreach (SVR_AnimeSeries series in groupAndSeries.Item2) { series.AnimeGroupID = groupAndSeries.Item1.AnimeGroupID; } } _log.Info("Generated {0} AnimeGroups", newGroupsToSeries.Count); return(newGroupsToSeries.Select(gts => gts.Item1)); }
private void UpdateAnimeGroupsAndTheirContracts(IEnumerable <SVR_AnimeGroup> groups) { _log.Info("Updating statistics and contracts for AnimeGroups"); var allCreatedGroupUsers = new ConcurrentBag <List <SVR_AnimeGroup_User> >(); // Update batches of AnimeGroup contracts in parallel. Each parallel branch requires it's own session since NHibernate sessions aren't thread safe. // The reason we're doing this in parallel is because updating contacts does a reasonable amount of work (including LZ4 compression) Parallel.ForEach(groups.Batch(DefaultBatchSize), new ParallelOptions { MaxDegreeOfParallelism = 4 }, body: (groupBatch, state, localSession) => { var createdGroupUsers = new List <SVR_AnimeGroup_User>(groupBatch.Length); // We shouldn't need to keep track of updates to AnimeGroup_Users in the below call, because they should have all been deleted, // therefore they should all be new SVR_AnimeGroup.BatchUpdateStats(groupBatch, watchedStats: true, missingEpsStats: true, createdGroupUsers: createdGroupUsers); allCreatedGroupUsers.Add(createdGroupUsers); SVR_AnimeGroup.BatchUpdateContracts(groupBatch, updateStats: true); }); _log.Info("AnimeGroup statistics and contracts have been updated"); _log.Info("Creating AnimeGroup_Users and updating plex/kodi contracts"); // Insert the AnimeGroup_Users so that they get assigned a primary key before we update plex/kodi contracts // We need to repopulate caches for AnimeGroup_User and AnimeGroup because we've updated/inserted them // and they need to be up to date for the plex/kodi contract updating to work correctly // NOTE: There are situations in which UpdatePlexKodiContracts will cause database database writes to occur, so we can't // use Parallel.ForEach for the time being (If it was guaranteed to only read then we'd be ok) List <int> ids = allCreatedGroupUsers.SelectMany(a => a).Select(a => a.AnimeGroup_UserID).ToList(); using (var upd = Repo.Instance.AnimeGroup_User.BeginBatchUpdate(() => Repo.Instance.AnimeGroup_User.GetMany(ids))) { foreach (SVR_AnimeGroup_User guser in upd) { guser.UpdatePlexKodiContracts_RA(); upd.Update(guser); } upd.Commit(); } _log.Info("AnimeGroup_Users have been created"); }
private static void GetValidVideoRecursive(IProvider prov, SVR_GroupFilter f, int userid, Directory pp) { List <SVR_GroupFilter> gfs = RepoFactory.GroupFilter.GetByParentID(f.GroupFilterID) .Where(a => a.GroupsIds.ContainsKey(userid) && a.GroupsIds[userid].Count > 0) .ToList(); foreach (SVR_GroupFilter gg in gfs.Where(a => (a.FilterType & (int)GroupFilterType.Directory) == 0)) { if (gg.GroupsIds.ContainsKey(userid)) { HashSet <int> groups = gg.GroupsIds[userid]; if (groups.Count != 0) { foreach (int grp in groups.Randomize(f.GroupFilterID)) { SVR_AnimeGroup ag = RepoFactory.AnimeGroup.GetByID(grp); Video v = ag.GetPlexContract(userid); if (v?.Art == null || v.Thumb == null) { continue; } pp.Art = prov.ReplaceSchemeHost(v.Art); pp.Thumb = prov.ReplaceSchemeHost(v.Thumb); break; } } } if (pp.Art != null) { break; } } if (pp.Art == null) { foreach (SVR_GroupFilter gg in gfs .Where(a => (a.FilterType & (int)GroupFilterType.Directory) == (int)GroupFilterType.Directory && a.InvisibleInClients == 0) .Randomize(f.GroupFilterID)) { GetValidVideoRecursive(prov, gg, userid, pp); if (pp.Art != null) { break; } } } pp.LeafCount = gfs.Count; pp.ViewedLeafCount = 0; }
private bool GetHasCustomName(SVR_AnimeGroup grp) { using (var session = DatabaseFactory.SessionFactory.OpenSession()) { var groupCalculator = AutoAnimeGroupCalculator.Create(session.Wrap(), AutoGroupExclude.None); int id = grp.GetSeries().FirstOrDefault()?.AniDB_ID ?? 0; if (id == 0) { return(true); } var ids = groupCalculator.GetIdsOfAnimeInSameGroup(id); return(!ids.Select(aid => RepoFactory.AniDB_Anime.GetByAnimeID(aid)).Where(anime => anime != null) .Any(anime => anime.GetAllTitles().Contains(grp.GroupName))); } }
public ActionResult <Group> SaveGroup(Group.FullGroup group) { SVR_AnimeGroup g = null; if (group.IDs.ID != 0) { g = RepoFactory.AnimeGroup.GetByID(group.IDs.ID); if (g == null) { return(BadRequest("No Group with ID")); } } g = group.ToServerModel(g); RepoFactory.AnimeGroup.Save(g); return(new Group(HttpContext, g)); }
/// <summary> /// Creates a new group that series will be put in during group re-calculation. /// </summary> /// <param name="session">The NHibernate session.</param> /// <returns>The temporary <see cref="SVR_AnimeGroup"/>.</returns> private SVR_AnimeGroup CreateTempAnimeGroup(ISessionWrapper session) { DateTime now = DateTime.Now; var tempGroup = new SVR_AnimeGroup { GroupName = TempGroupName, Description = TempGroupName, SortName = TempGroupName, DateTimeUpdated = now, DateTimeCreated = now }; // We won't use AnimeGroupRepository.Save because we don't need to perform all the extra stuff since this is for temporary use only session.Insert(tempGroup); return(tempGroup); }
private static bool GroupHasMostlyCompleteArt(SVR_AnimeGroup grp) { var anime = grp.Anime.OrderBy(a => a.BeginYear) .ThenBy(a => a.AirDate ?? DateTime.MaxValue) .FirstOrDefault(); var fanarts = anime?.Contract.AniDBAnime.Fanarts; if (fanarts == null || fanarts.Count <= 0) { return(false); } fanarts = anime.Contract.AniDBAnime.Banners; if (fanarts == null || fanarts.Count <= 0) { return(false); } return(true); }
/// <summary> /// Gets or creates an <see cref="SVR_AnimeGroup"/> for the specified series. /// </summary> /// <param name="session">The NHibernate session.</param> /// <param name="series">The series for which the group is to be created/retrieved (Must be initialised first).</param> /// <returns>The <see cref="SVR_AnimeGroup"/> to use for the specified series.</returns> /// <exception cref="ArgumentNullException"><paramref name="session"/> or <paramref name="series"/> is <c>null</c>.</exception> public SVR_AnimeGroup GetOrCreateSingleGroupForSeries(ISessionWrapper session, SVR_AnimeSeries series) { if (session == null) { throw new ArgumentNullException(nameof(session)); } if (series == null) { throw new ArgumentNullException(nameof(series)); } SVR_AnimeGroup animeGroup = null; if (_autoGroupSeries) { var grpCalculator = AutoAnimeGroupCalculator.CreateFromServerSettings(session); IReadOnlyList <int> grpAnimeIds = grpCalculator.GetIdsOfAnimeInSameGroup(series.AniDB_ID); // Try to find an existing AnimeGroup to add the series to // We basically pick the first group that any of the related series belongs to already animeGroup = grpAnimeIds.Where(id => id != series.AniDB_ID) .Select(id => RepoFactory.AnimeSeries.GetByAnimeID(id)) .Where(s => s != null) .Select(s => RepoFactory.AnimeGroup.GetByID(s.AnimeGroupID)) .FirstOrDefault(s => s != null); if (animeGroup == null) { // No existing group was found, so create a new one int mainAnimeId = grpCalculator.GetGroupAnimeId(series.AniDB_ID); SVR_AnimeSeries mainSeries = _animeSeriesRepo.GetByAnimeID(mainAnimeId); animeGroup = CreateAnimeGroup(mainSeries, mainAnimeId, DateTime.Now); RepoFactory.AnimeGroup.Save(animeGroup, true, true); } } else // We're not auto grouping (e.g. we're doing group per series) { animeGroup = new SVR_AnimeGroup(); animeGroup.Populate(series, DateTime.Now); RepoFactory.AnimeGroup.Save(animeGroup, true, true); } return(animeGroup); }
public static Directory DirectoryFromFilter(IProvider prov, SVR_GroupFilter gg, int userid) { Directory pp = new Directory { Type = "show" }; pp.Key = prov.ConstructFilterIdUrl(userid, gg.GroupFilterID); pp.Title = gg.GroupFilterName; pp.Id = gg.GroupFilterID; pp.AnimeType = AnimeTypes.AnimeGroupFilter.ToString(); if ((gg.FilterType & (int)GroupFilterType.Directory) == (int)GroupFilterType.Directory) { GetValidVideoRecursive(prov, gg, userid, pp); } else if (gg.GroupsIds.ContainsKey(userid)) { HashSet <int> groups = gg.GroupsIds[userid]; if (groups.Count == 0) { return(pp); } pp.LeafCount = groups.Count; pp.ViewedLeafCount = 0; foreach (int grp in groups.Randomize()) { SVR_AnimeGroup ag = RepoFactory.AnimeGroup.GetByID(grp); Video v = ag.GetPlexContract(userid); if (v?.Art == null || v.Thumb == null) { continue; } pp.Art = prov.ReplaceSchemeHost(v.Art); pp.Thumb = prov.ReplaceSchemeHost(v.Thumb); break; } return(pp); } return(pp); }
public void RecalculateStatsContractsForGroup(SVR_AnimeGroup group) { using (ISession sessionNotWrapped = DatabaseFactory.SessionFactory.OpenSession()) { var groups = new List <SVR_AnimeGroup> { group }; var session = sessionNotWrapped.Wrap(); var series = group.GetAllSeries(true); // recalculate series _log.Info($"Recalculating Series Stats and Contracts for Group: {group.GroupName} ({group.AnimeGroupID})"); using (ITransaction trans = session.BeginTransaction()) { UpdateAnimeSeriesContractsAndSave(session, series); trans.Commit(); } // Update Cache so that group can recalculate series.ForEach(a => _animeSeriesRepo.Cache.Update(a)); // Recalculate group _log.Info($"Recalculating Group Stats and Contracts for Group: {group.GroupName} ({group.AnimeGroupID})"); using (ITransaction trans = session.BeginTransaction()) { UpdateAnimeGroupsAndTheirContracts(session, groups); trans.Commit(); } // update cache _animeGroupRepo.Cache.Update(group); var groupsUsers = _animeGroupUserRepo.GetByGroupID(group.AnimeGroupID); groupsUsers.ForEach(a => _animeGroupUserRepo.Cache.Update(a)); // update filters _log.Info($"Recalculating Filters for Group: {group.GroupName} ({group.AnimeGroupID})"); UpdateGroupFilters(session); _log.Info($"Done Recalculating Stats and Contracts for Group: {group.GroupName} ({group.AnimeGroupID})"); } }
public static Filter FilterFromGroupFilter(HttpContext ctx, SVR_GroupFilter gg, int uid) { Filter ob = new Filter { name = gg.GroupFilterName, id = gg.GroupFilterID, url = APIHelper.ConstructFilterIdUrl(ctx, gg.GroupFilterID) }; if (gg.GroupsIds.ContainsKey(uid)) { HashSet <int> groups = gg.GroupsIds[uid]; if (groups.Count != 0) { ob.size = groups.Count; ob.viewed = 0; foreach (int grp in groups) { SVR_AnimeGroup ag = Repo.Instance.AnimeGroup.GetByID(grp); Video v = ag.GetPlexContract(uid); if (v?.Art != null && v.Thumb != null) { ob.art.fanart.Add(new Art() { url = APIHelper.ConstructImageLinkFromRest(ctx, v.Art), index = 0 }); ob.art.thumb.Add(new Art() { url = APIHelper.ConstructImageLinkFromRest(ctx, v.Thumb), index = 0 }); break; } } } } return(ob); }
public AnimeSeriesRepository() { BeginDeleteCallback = cr => { RepoFactory.AnimeSeries_User.Delete(RepoFactory.AnimeSeries_User.GetBySeriesID(cr.AnimeSeriesID)); Changes.Remove(cr.AnimeSeriesID); }; EndDeleteCallback = cr => { cr.DeleteFromFilters(); if (cr.AnimeGroupID <= 0) { return; } logger.Trace("Updating group stats by group from AnimeSeriesRepository.Delete: {0}", cr.AnimeGroupID); SVR_AnimeGroup oldGroup = RepoFactory.AnimeGroup.GetByID(cr.AnimeGroupID); if (oldGroup != null) { RepoFactory.AnimeGroup.Save(oldGroup, true, true); } }; }
/// <summary> /// Re-creates all AnimeGroups based on the existing AnimeSeries. /// </summary> /// <param name="session">The NHibernate session.</param> /// <exception cref="ArgumentNullException"><paramref name="session"/> is <c>null</c>.</exception> public void RecreateAllGroups(ISessionWrapper session) { if (session == null) { throw new ArgumentNullException(nameof(session)); } bool cmdProcGeneralPaused = ShokoService.CmdProcessorGeneral.Paused; bool cmdProcHasherPaused = ShokoService.CmdProcessorHasher.Paused; bool cmdProcImagesPaused = ShokoService.CmdProcessorImages.Paused; try { // Pause queues ShokoService.CmdProcessorGeneral.Paused = true; ShokoService.CmdProcessorHasher.Paused = true; ShokoService.CmdProcessorImages.Paused = true; _log.Info("Beginning re-creation of all groups"); IReadOnlyList <SVR_AnimeSeries> animeSeries = RepoFactory.AnimeSeries.GetAll(); IReadOnlyCollection <SVR_AnimeGroup> createdGroups = null; SVR_AnimeGroup tempGroup = null; using (ITransaction trans = session.BeginTransaction()) { tempGroup = CreateTempAnimeGroup(session); ClearGroupsAndDependencies(session, tempGroup.AnimeGroupID); trans.Commit(); } if (_autoGroupSeries) { createdGroups = AutoCreateGroupsWithRelatedSeries(session, animeSeries) .AsReadOnlyCollection(); } else // Standard group re-create { createdGroups = CreateGroupPerSeries(session, animeSeries) .AsReadOnlyCollection(); } using (ITransaction trans = session.BeginTransaction()) { UpdateAnimeSeriesContractsAndSave(session, animeSeries); session.Delete(tempGroup); // We should no longer need the temporary group we created earlier trans.Commit(); } // We need groups and series cached for updating of AnimeGroup contracts to work _animeGroupRepo.Populate(session, displayname: false); _animeSeriesRepo.Populate(session, displayname: false); using (ITransaction trans = session.BeginTransaction()) { UpdateAnimeGroupsAndTheirContracts(session, createdGroups); trans.Commit(); } // We need to update the AnimeGroups cache again now that the contracts have been saved // (Otherwise updating Group Filters won't get the correct results) _animeGroupRepo.Populate(session, displayname: false); _animeGroupUserRepo.Populate(session, displayname: false); _groupFilterRepo.Populate(session, displayname: false); using (ITransaction trans = session.BeginTransaction()) { UpdateGroupFilters(session); trans.Commit(); } _log.Info("Successfuly completed re-creating all groups"); } catch (Exception e) { _log.Error(e, "An error occurred while re-creating all groups"); try { // If an error occurs then chances are the caches are in an inconsistent state. So re-populate them _animeSeriesRepo.Populate(); _animeGroupRepo.Populate(); _groupFilterRepo.Populate(); _animeGroupUserRepo.Populate(); } catch (Exception ie) { _log.Warn(ie, "Failed to re-populate caches"); } throw; } finally { // Un-pause queues (if they were previously running) ShokoService.CmdProcessorGeneral.Paused = cmdProcGeneralPaused; ShokoService.CmdProcessorHasher.Paused = cmdProcHasherPaused; ShokoService.CmdProcessorImages.Paused = cmdProcImagesPaused; } }
public void Save(SVR_AnimeSeries obj, bool updateGroups, bool onlyupdatestats, bool skipgroupfilters = false, bool alsoupdateepisodes = false) { bool newSeries = false; SVR_AnimeGroup oldGroup = null; bool isMigrating = false; lock (obj) { if (obj.AnimeSeriesID == 0) { newSeries = true; // a new series } else { // get the old version from the DB SVR_AnimeSeries oldSeries; using (var session = DatabaseFactory.SessionFactory.OpenSession()) { lock (globalDBLock) { oldSeries = session.Get <SVR_AnimeSeries>(obj.AnimeSeriesID); } } if (oldSeries != null) { // means we are moving series to a different group if (oldSeries.AnimeGroupID != obj.AnimeGroupID) { oldGroup = RepoFactory.AnimeGroup.GetByID(oldSeries.AnimeGroupID); SVR_AnimeGroup newGroup = RepoFactory.AnimeGroup.GetByID(obj.AnimeGroupID); if (newGroup != null && newGroup.GroupName.Equals("AAA Migrating Groups AAA")) { isMigrating = true; } newSeries = true; } } } if (newSeries && !isMigrating) { obj.Contract = null; base.Save(obj); } HashSet <GroupFilterConditionType> types = obj.UpdateContract(onlyupdatestats); base.Save(obj); if (updateGroups && !isMigrating) { logger.Trace("Updating group stats by series from AnimeSeriesRepository.Save: {0}", obj.AnimeSeriesID); SVR_AnimeGroup grp = RepoFactory.AnimeGroup.GetByID(obj.AnimeGroupID); if (grp != null) { RepoFactory.AnimeGroup.Save(grp, true, true); } if (oldGroup != null) { logger.Trace("Updating group stats by group from AnimeSeriesRepository.Save: {0}", oldGroup.AnimeGroupID); RepoFactory.AnimeGroup.Save(oldGroup, true, true); } } if (!skipgroupfilters && !isMigrating) { int endyear = obj.Contract?.AniDBAnime?.AniDBAnime?.EndYear ?? 0; if (endyear == 0) { endyear = DateTime.Today.Year; } HashSet <int> allyears = null; if ((obj.Contract?.AniDBAnime?.AniDBAnime?.BeginYear ?? 0) != 0) { allyears = new HashSet <int>(Enumerable.Range(obj.Contract.AniDBAnime.AniDBAnime.BeginYear, endyear - obj.Contract.AniDBAnime.AniDBAnime.BeginYear + 1)); } //This call will create extra years or tags if the Group have a new year or tag RepoFactory.GroupFilter.CreateOrVerifyDirectoryFilters(false, obj.Contract?.AniDBAnime?.AniDBAnime?.GetAllTags(), allyears, obj.Contract?.AniDBAnime?.Stat_AllSeasons); // Update other existing filters obj.UpdateGroupFilters(types, null); } lock (Changes) { Changes.AddOrUpdate(obj.AnimeSeriesID); } } if (alsoupdateepisodes) { List <SVR_AnimeEpisode> eps = RepoFactory.AnimeEpisode.GetBySeriesID(obj.AnimeSeriesID); RepoFactory.AnimeEpisode.Save(eps); } }
public static Video GenerateFromAnimeGroup(SVR_AnimeGroup grp, int userid, List <SVR_AnimeSeries> allSeries, ISessionWrapper session = null) { CL_AnimeGroup_User cgrp = grp.GetUserContract(userid); int subgrpcnt = grp.GetAllChildGroups().Count; if ((cgrp.Stat_SeriesCount == 1) && (subgrpcnt == 0)) { SVR_AnimeSeries ser = ShokoServiceImplementation.GetSeriesForGroup(grp.AnimeGroupID, allSeries); CL_AnimeSeries_User cserie = ser?.GetUserContract(userid); if (cserie == null) { return(null); } Video v = GenerateFromSeries(cserie, ser, ser.GetAnime(), userid, session); v.AirDate = ser.AirDate; v.UpdatedAt = ser.LatestEpisodeAirDate.HasValue ? ser.LatestEpisodeAirDate.Value.ToUnixTime() : null; v.Group = cgrp; return(v); } else { SVR_AnimeSeries ser = grp.DefaultAnimeSeriesID.HasValue ? allSeries.FirstOrDefault(a => a.AnimeSeriesID == grp.DefaultAnimeSeriesID.Value) : allSeries.Find(a => a.AirDate != DateTime.MinValue); if (ser == null && allSeries.Count > 0) { ser = allSeries[0]; } CL_AnimeSeries_User cserie = ser?.GetUserContract(userid); Video v = FromGroup(cgrp, cserie, userid, subgrpcnt); v.Group = cgrp; v.AirDate = cgrp.Stat_AirDate_Min ?? DateTime.MinValue; v.UpdatedAt = cgrp.LatestEpisodeAirDate?.ToUnixTime(); v.Rating = (int)Math.Round((grp.AniDBRating / 100), 1); List <Tag> newTags = new List <Tag>(); foreach (AniDB_Tag tag in grp.Tags) { Tag newTag = new Tag(); TextInfo textInfo = new CultureInfo("en-US", false).TextInfo; newTag.Value = textInfo.ToTitleCase(tag.TagName.Trim()); if (!newTags.Contains(newTag)) { newTags.Add(newTag); } } v.Genres = newTags; if (ser == null) { return(v); } List <AnimeTitle> newTitles = ser.GetAnime() .GetTitles() .Select(title => new AnimeTitle { Title = title.Title, Language = title.Language, Type = title.TitleType }) .ToList(); v.Titles = newTitles; v.Roles = new List <RoleTag>(); //TODO Character implementation is limited in JMM, One Character, could have more than one Seiyuu if (ser.GetAnime()?.Contract?.AniDBAnime?.Characters != null) { foreach (CL_AniDB_Character c in ser.GetAnime().Contract.AniDBAnime.Characters) { string ch = c?.CharName; AniDB_Seiyuu seiyuu = c?.Seiyuu; if (String.IsNullOrEmpty(ch)) { continue; } RoleTag t = new RoleTag { Value = seiyuu?.SeiyuuName }; if (seiyuu != null) { t.TagPicture = ConstructSeiyuuImage(null, seiyuu.AniDB_SeiyuuID); } t.Role = ch; t.RoleDescription = c?.CharDescription; t.RolePicture = ConstructCharacterImage(null, c.CharID); v.Roles.Add(t); } } if (cserie?.AniDBAnime?.AniDBAnime?.Fanarts != null) { v.Fanarts = new List <Contract_ImageDetails>(); cserie?.AniDBAnime?.AniDBAnime?.Fanarts.ForEach( a => v.Fanarts.Add(new Contract_ImageDetails { ImageID = a.AniDB_Anime_DefaultImageID, ImageType = a.ImageType })); } if (cserie?.AniDBAnime?.AniDBAnime?.Banners == null) { return(v); } v.Banners = new List <Contract_ImageDetails>(); cserie?.AniDBAnime?.AniDBAnime?.Banners.ForEach( a => v.Banners.Add(new Contract_ImageDetails { ImageID = a.AniDB_Anime_DefaultImageID, ImageType = a.ImageType })); return(v); } }