public Task <object> GetImage(ImageRequest request, User user, bool isHeadRequest) { var imageInfo = GetImageInfo(request, user); TimeSpan?cacheDuration = null; if (!string.IsNullOrEmpty(request.Tag)) { cacheDuration = TimeSpan.FromDays(365); } var responseHeaders = new Dictionary <string, string> { { "transferMode.dlna.org", "Interactive" }, { "realTimeInfo.dlna.org", "DLNA.ORG_TLAG=*" } }; var outputFormats = GetOutputFormats(request); return(GetImageResult(user.Id, request, imageInfo, outputFormats, cacheDuration, responseHeaders, isHeadRequest)); }
/// <summary> /// Count media items and call <see cref="SyncMovies"/> and <see cref="SyncShows"/> /// </summary> /// <returns></returns> private async Task SyncUserLibrary( Jellyfin.Data.Entities.User user, TraktUser traktUser, ISplittableProgress <double> progress, CancellationToken cancellationToken) { await SyncMovies(user, traktUser, progress.Split(2), cancellationToken).ConfigureAwait(false); await SyncShows(user, traktUser, progress.Split(2), cancellationToken).ConfigureAwait(false); }
/// <summary> Gets report result. </summary> /// <param name="request"> The request. </param> /// <returns> The report result. </returns> private ReportResult GetReportResult(GetItemReport request, User user) { ReportBuilder reportBuilder = new ReportBuilder(_libraryManager); QueryResult <BaseItem> queryResult = GetQueryResult(request, user); ReportResult reportResult = reportBuilder.GetResult(queryResult.Items, request); reportResult.TotalRecordCount = queryResult.TotalRecordCount; return(reportResult); }
private async Task PostImage(User user, Stream inputStream, string mimeType) { var memoryStream = await GetMemoryStream(inputStream); // Handle image/png; charset=utf-8 mimeType = mimeType.Split(';').FirstOrDefault(); var userDataPath = Path.Combine(ServerConfigurationManager.ApplicationPaths.UserConfigurationDirectoryPath, user.Username); user.ProfileImage = new Jellyfin.Data.Entities.ImageInfo(Path.Combine(userDataPath, "profile" + MimeTypes.ToExtension(mimeType))); await _providerManager .SaveImage(user, memoryStream, mimeType, user.ProfileImage.Path) .ConfigureAwait(false); await _userManager.UpdateUserAsync(user); }
private static ItemImageInfo GetImageInfo(ImageRequest request, User user) { var info = new ItemImageInfo { Path = user.ProfileImage.Path, Type = ImageType.Primary, DateModified = user.ProfileImage.LastModified, }; if (request.Width.HasValue) { info.Width = request.Width.Value; } if (request.Height.HasValue) { info.Height = request.Height.Value; } return(info); }
private long?GetMaxBitrate(long?clientMaxBitrate, Jellyfin.Data.Entities.User user) { var maxBitrate = clientMaxBitrate; var remoteClientMaxBitrate = user?.RemoteClientBitrateLimit ?? 0; if (remoteClientMaxBitrate <= 0) { remoteClientMaxBitrate = ServerConfigurationManager.Configuration.RemoteClientBitrateLimit; } if (remoteClientMaxBitrate > 0) { var isInLocalNetwork = _networkManager.IsInLocalNetwork(Request.RemoteIp); Logger.LogInformation("RemoteClientBitrateLimit: {0}, RemoteIp: {1}, IsInLocalNetwork: {2}", remoteClientMaxBitrate, Request.RemoteIp, isInLocalNetwork); if (!isInLocalNetwork) { maxBitrate = Math.Min(maxBitrate ?? remoteClientMaxBitrate, remoteClientMaxBitrate); } } return(maxBitrate); }
/// <summary> /// Sync watched and collected status of <see cref="Movie"/>s with trakt. /// </summary> private async Task SyncShows( Jellyfin.Data.Entities.User user, TraktUser traktUser, ISplittableProgress <double> progress, CancellationToken cancellationToken) { var traktWatchedShows = await _traktApi.SendGetWatchedShowsRequest(traktUser).ConfigureAwait(false); var traktCollectedShows = await _traktApi.SendGetCollectedShowsRequest(traktUser).ConfigureAwait(false); var episodeItems = _libraryManager.GetItemList( new InternalItemsQuery(user) { IncludeItemTypes = new[] { typeof(Episode).Name }, IsVirtualItem = false, OrderBy = new[] { new ValueTuple <string, SortOrder>(ItemSortBy.SeriesSortName, SortOrder.Ascending) } }) .Where(x => _traktApi.CanSync(x, traktUser)) .ToList(); var collectedEpisodes = new List <Episode>(); var playedEpisodes = new List <Episode>(); var unplayedEpisodes = new List <Episode>(); var decisionProgress = progress.Split(4).Split(episodeItems.Count); foreach (var child in episodeItems) { cancellationToken.ThrowIfCancellationRequested(); var episode = child as Episode; var userData = _userDataManager.GetUserData(user.Id, episode); var isPlayedTraktTv = false; var traktWatchedShow = SyncFromTraktTask.FindMatch(episode.Series, traktWatchedShows); if (traktWatchedShow?.seasons != null && traktWatchedShow.seasons.Count > 0) { isPlayedTraktTv = traktWatchedShow.seasons.Any( season => season.number == episode.GetSeasonNumber() && season.episodes != null && season.episodes.Any(te => te.number == episode.IndexNumber && te.plays > 0)); } // if the show has been played locally and is unplayed on trakt.tv then add it to the list if (userData != null && userData.Played && !isPlayedTraktTv) { if (traktUser.PostWatchedHistory) { playedEpisodes.Add(episode); } else if (!traktUser.SkipUnwatchedImportFromTrakt) { if (userData.Played) { userData.Played = false; _userDataManager.SaveUserData( user.Id, episode, userData, UserDataSaveReason.Import, cancellationToken); } } } else if (userData != null && !userData.Played && isPlayedTraktTv && traktUser.PostUnwatchedHistory) { // If the show has not been played locally but is played on trakt.tv then add it to the unplayed list unplayedEpisodes.Add(episode); } if (traktUser.SynchronizeCollections) { var traktCollectedShow = SyncFromTraktTask.FindMatch(episode.Series, traktCollectedShows); if (traktCollectedShow?.seasons == null || traktCollectedShow.seasons.All(x => x.number != episode.ParentIndexNumber) || traktCollectedShow.seasons.First(x => x.number == episode.ParentIndexNumber) .episodes.All(e => e.number != episode.IndexNumber)) { collectedEpisodes.Add(episode); } } decisionProgress.Report(100); } if (traktUser.SynchronizeCollections) { await SendEpisodeCollectionUpdates(true, traktUser, collectedEpisodes, progress.Split(4), cancellationToken).ConfigureAwait(false); } await SendEpisodePlaystateUpdates(true, traktUser, playedEpisodes, progress.Split(4), cancellationToken).ConfigureAwait(false); await SendEpisodePlaystateUpdates(false, traktUser, unplayedEpisodes, progress.Split(4), cancellationToken).ConfigureAwait(false); }
/// <summary> /// Sync watched and collected status of <see cref="Movie"/>s with trakt. /// </summary> private async Task SyncMovies( Jellyfin.Data.Entities.User user, TraktUser traktUser, ISplittableProgress <double> progress, CancellationToken cancellationToken) { /* * In order to sync watched status to trakt.tv we need to know what's been watched on Trakt already. This * will stop us from endlessly incrementing the watched values on the site. */ var traktWatchedMovies = await _traktApi.SendGetAllWatchedMoviesRequest(traktUser).ConfigureAwait(false); var traktCollectedMovies = await _traktApi.SendGetAllCollectedMoviesRequest(traktUser).ConfigureAwait(false); var libraryMovies = _libraryManager.GetItemList( new InternalItemsQuery(user) { IncludeItemTypes = new[] { typeof(Movie).Name }, IsVirtualItem = false, OrderBy = new [] { new ValueTuple <string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending) } }) .Where(x => _traktApi.CanSync(x, traktUser)) .ToList(); var collectedMovies = new List <Movie>(); var playedMovies = new List <Movie>(); var unplayedMovies = new List <Movie>(); var decisionProgress = progress.Split(4).Split(libraryMovies.Count); foreach (var child in libraryMovies) { cancellationToken.ThrowIfCancellationRequested(); var libraryMovie = child as Movie; var userData = _userDataManager.GetUserData(user.Id, child); if (traktUser.SynchronizeCollections) { // if movie is not collected, or (export media info setting is enabled and every collected matching movie has different metadata), collect it var collectedMathingMovies = SyncFromTraktTask.FindMatches(libraryMovie, traktCollectedMovies).ToList(); if (!collectedMathingMovies.Any() || (traktUser.ExportMediaInfo && collectedMathingMovies.All( collectedMovie => collectedMovie.MetadataIsDifferent(libraryMovie)))) { collectedMovies.Add(libraryMovie); } } var movieWatched = SyncFromTraktTask.FindMatch(libraryMovie, traktWatchedMovies); // if the movie has been played locally and is unplayed on trakt.tv then add it to the list if (userData.Played) { if (movieWatched == null) { if (traktUser.PostWatchedHistory) { playedMovies.Add(libraryMovie); } else if (!traktUser.SkipUnwatchedImportFromTrakt) { if (userData.Played) { userData.Played = false; _userDataManager.SaveUserData( user.Id, libraryMovie, userData, UserDataSaveReason.Import, cancellationToken); } } } } else { // If the show has not been played locally but is played on trakt.tv then add it to the unplayed list if (movieWatched != null && traktUser.PostUnwatchedHistory) { unplayedMovies.Add(libraryMovie); } } decisionProgress.Report(100); } // send movies to mark collected if (traktUser.SynchronizeCollections) { await SendMovieCollectionUpdates(true, traktUser, collectedMovies, progress.Split(4), cancellationToken).ConfigureAwait(false); } // send movies to mark watched await SendMoviePlaystateUpdates(true, traktUser, playedMovies, progress.Split(4), cancellationToken).ConfigureAwait(false); // send movies to mark unwatched await SendMoviePlaystateUpdates(false, traktUser, unplayedMovies, progress.Split(4), cancellationToken).ConfigureAwait(false); }
private async Task SyncTraktDataForUser(Jellyfin.Data.Entities.User user, double currentProgress, CancellationToken cancellationToken, IProgress <double> progress, double percentPerUser) { var traktUser = UserHelper.GetTraktUser(user); List <TraktMovieWatched> traktWatchedMovies; List <TraktShowWatched> traktWatchedShows; try { /* * In order to be as accurate as possible. We need to download the users show collection & the users watched shows. * It's unfortunate that trakt.tv doesn't explicitly supply a bulk method to determine shows that have not been watched * like they do for movies. */ traktWatchedMovies = await _traktApi.SendGetAllWatchedMoviesRequest(traktUser).ConfigureAwait(false); traktWatchedShows = await _traktApi.SendGetWatchedShowsRequest(traktUser).ConfigureAwait(false); } catch (Exception ex) { _logger.LogError(ex, "Exception handled"); throw; } _logger.LogInformation("Trakt.tv watched Movies count = " + traktWatchedMovies.Count); _logger.LogInformation("Trakt.tv watched Shows count = " + traktWatchedShows.Count); var mediaItems = _libraryManager.GetItemList( new InternalItemsQuery(user) { IncludeItemTypes = new[] { typeof(Movie).Name, typeof(Episode).Name }, IsVirtualItem = false, OrderBy = new[] { new ValueTuple <string, SortOrder>(ItemSortBy.SeriesSortName, SortOrder.Ascending), new ValueTuple <string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending) } }) .Where(i => _traktApi.CanSync(i, traktUser)).ToList(); // purely for progress reporting var percentPerItem = percentPerUser / mediaItems.Count; foreach (var movie in mediaItems.OfType <Movie>()) { cancellationToken.ThrowIfCancellationRequested(); var matchedMovie = FindMatch(movie, traktWatchedMovies); if (matchedMovie != null) { _logger.LogDebug("Movie is in Watched list " + movie.Name); var userData = _userDataManager.GetUserData(user.Id, movie); bool changed = false; DateTime?tLastPlayed = null; if (DateTime.TryParse(matchedMovie.last_watched_at, out var value)) { tLastPlayed = value; } // set movie as watched if (!userData.Played) { userData.Played = true; userData.LastPlayedDate = tLastPlayed ?? DateTime.Now; changed = true; } // keep the highest play count if (userData.PlayCount < matchedMovie.plays) { userData.PlayCount = matchedMovie.plays; changed = true; } // Update last played if remote time is more recent if (tLastPlayed != null && userData.LastPlayedDate < tLastPlayed) { userData.LastPlayedDate = tLastPlayed; changed = true; } // Only process if there's a change if (changed) { _userDataManager.SaveUserData( user.Id, movie, userData, UserDataSaveReason.Import, cancellationToken); } } else { //_logger.LogInformation("Failed to match " + movie.Name); } // purely for progress reporting currentProgress += percentPerItem; progress.Report(currentProgress); } foreach (var episode in mediaItems.OfType <Episode>()) { cancellationToken.ThrowIfCancellationRequested(); var matchedShow = FindMatch(episode.Series, traktWatchedShows); if (matchedShow != null) { var matchedSeason = matchedShow.seasons.FirstOrDefault( tSeason => tSeason.number == (episode.ParentIndexNumber == 0 ? 0 : (episode.ParentIndexNumber ?? 1))); // if it's not a match then it means trakt doesn't know about the season, leave the watched state alone and move on if (matchedSeason != null) { // episode is in users libary. Now we need to determine if it's watched var userData = _userDataManager.GetUserData(user.Id, episode); bool changed = false; var matchedEpisode = matchedSeason.episodes.FirstOrDefault(x => x.number == (episode.IndexNumber ?? -1)); if (matchedEpisode != null) { _logger.LogDebug("Episode is in Watched list " + GetVerboseEpisodeData(episode)); if (!traktUser.SkipWatchedImportFromTrakt) { DateTime?tLastPlayed = null; if (DateTime.TryParse(matchedEpisode.last_watched_at, out var value)) { tLastPlayed = value; } // Set episode as watched if (!userData.Played) { userData.Played = true; userData.LastPlayedDate = tLastPlayed ?? DateTime.Now; changed = true; } // keep the highest play count if (userData.PlayCount < matchedEpisode.plays) { userData.PlayCount = matchedEpisode.plays; changed = true; } // Update last played if remote time is more recent if (tLastPlayed != null && userData.LastPlayedDate < tLastPlayed) { userData.LastPlayedDate = tLastPlayed; changed = true; } } } else if (!traktUser.SkipUnwatchedImportFromTrakt) { userData.Played = false; userData.PlayCount = 0; userData.LastPlayedDate = null; changed = true; } // only process if changed if (changed) { _userDataManager.SaveUserData( user.Id, episode, userData, UserDataSaveReason.Import, cancellationToken); } } else { _logger.LogDebug("No Season match in Watched shows list " + GetVerboseEpisodeData(episode)); } } else { _logger.LogDebug("No Show match in Watched shows list " + GetVerboseEpisodeData(episode)); } // purely for progress reporting currentProgress += percentPerItem; progress.Report(currentProgress); } // _logger.LogInformation(syncItemFailures + " items not parsed"); }
private QueryResult <BaseItem> GetQueryResult(BaseReportRequest request, User user) { // all report queries currently need this because it's not being specified request.Recursive = true; BaseItem item = null; if (!string.IsNullOrEmpty(request.ParentId)) { item = _libraryManager.GetItemById(request.ParentId); } if (string.Equals(request.IncludeItemTypes, "Playlist", StringComparison.OrdinalIgnoreCase)) { //item = user == null ? _libraryManager.RootFolder : user.RootFolder; } else if (string.Equals(request.IncludeItemTypes, "BoxSet", StringComparison.OrdinalIgnoreCase)) { item = _libraryManager.GetUserRootFolder(); } // Default list type = children Folder folder = item as Folder; if (folder is null) { folder = _libraryManager.GetUserRootFolder(); } if (!string.IsNullOrEmpty(request.Ids)) { request.Recursive = true; var query = GetItemsQuery(request, user); var result = folder.GetItems(query); if (string.IsNullOrWhiteSpace(request.SortBy)) { var ids = query.ItemIds.ToList(); // Try to preserve order result.Items = result.Items.OrderBy(i => ids.IndexOf(i.Id)).ToArray(); } return(result); } if (request.Recursive) { return(folder.GetItems(GetItemsQuery(request, user))); } if (user == null) { return(folder.GetItems(GetItemsQuery(request, null))); } var userRoot = item as UserRootFolder; if (userRoot == null) { return(folder.GetItems(GetItemsQuery(request, user))); } IEnumerable <BaseItem> items = folder.GetChildren(user, true); var itemsArray = items.ToArray(); return(new QueryResult <BaseItem> { Items = itemsArray, TotalRecordCount = itemsArray.Length }); }
private InternalItemsQuery GetItemsQuery(BaseReportRequest request, User user) { var query = new InternalItemsQuery(user) { IsPlayed = request.IsPlayed, MediaTypes = request.GetMediaTypes(), IncludeItemTypes = request.GetIncludeItemTypes(), ExcludeItemTypes = request.GetExcludeItemTypes(), Recursive = request.Recursive, OrderBy = request.GetOrderBy(), IsFavorite = request.IsFavorite, StartIndex = request.StartIndex, IsMissing = request.IsMissing, IsUnaired = request.IsUnaired, CollapseBoxSetItems = request.CollapseBoxSetItems, NameLessThan = request.NameLessThan, NameStartsWith = request.NameStartsWith, NameStartsWithOrGreater = request.NameStartsWithOrGreater, HasImdbId = request.HasImdbId, IsPlaceHolder = request.IsPlaceHolder, IsLocked = request.IsLocked, IsHD = request.IsHD, Is3D = request.Is3D, HasTvdbId = request.HasTvdbId, HasTmdbId = request.HasTmdbId, HasOverview = request.HasOverview, HasOfficialRating = request.HasOfficialRating, HasParentalRating = request.HasParentalRating, HasSpecialFeature = request.HasSpecialFeature, HasSubtitles = request.HasSubtitles, HasThemeSong = request.HasThemeSong, HasThemeVideo = request.HasThemeVideo, HasTrailer = request.HasTrailer, Tags = request.GetTags(), OfficialRatings = request.GetOfficialRatings(), Genres = request.GetGenres(), GenreIds = request.GetGenreIds(), StudioIds = request.GetStudioIds(), Person = request.Person, PersonIds = request.GetPersonIds(), PersonTypes = request.GetPersonTypes(), Years = request.GetYears(), ImageTypes = request.GetImageTypes().ToArray(), VideoTypes = request.GetVideoTypes().ToArray(), AdjacentTo = request.AdjacentTo, ItemIds = request.GetItemIds(), MinCommunityRating = request.MinCommunityRating, MinCriticRating = request.MinCriticRating, ParentId = string.IsNullOrWhiteSpace(request.ParentId) ? Guid.Empty : new Guid(request.ParentId), ParentIndexNumber = request.ParentIndexNumber, EnableTotalRecordCount = request.EnableTotalRecordCount }; if (request.Limit == -1) { query.Limit = null; } if (!string.IsNullOrWhiteSpace(request.Ids)) { query.CollapseBoxSetItems = false; } query.IsFavorite = null; if (request.IsFavorite == true) { query.IsFavorite = true; } else if (request.IsNotFavorite == true) { query.IsFavorite = false; } foreach (var filter in request.GetFilters()) { switch (filter) { case ItemFilter.Dislikes: query.IsLiked = false; break; case ItemFilter.IsFavoriteOrLikes: query.IsFavoriteOrLiked = true; break; case ItemFilter.IsFolder: query.IsFolder = true; break; case ItemFilter.IsNotFolder: query.IsFolder = false; break; case ItemFilter.IsPlayed: query.IsPlayed = true; break; case ItemFilter.IsResumable: query.IsResumable = true; break; case ItemFilter.IsUnplayed: query.IsPlayed = false; break; case ItemFilter.Likes: query.IsLiked = true; break; } } if (!string.IsNullOrEmpty(request.MinPremiereDate)) { query.MinPremiereDate = DateTime.Parse(request.MinPremiereDate, null, DateTimeStyles.RoundtripKind).ToUniversalTime(); } if (!string.IsNullOrEmpty(request.MaxPremiereDate)) { query.MaxPremiereDate = DateTime.Parse(request.MaxPremiereDate, null, DateTimeStyles.RoundtripKind).ToUniversalTime(); } // Filter by Series Status if (!string.IsNullOrEmpty(request.SeriesStatus)) { query.SeriesStatuses = request.SeriesStatus.Split(',').Select(d => (SeriesStatus)Enum.Parse(typeof(SeriesStatus), d, true)).ToArray(); } // ExcludeLocationTypes if (!string.IsNullOrEmpty(request.ExcludeLocationTypes)) { var excludeLocationTypes = request.ExcludeLocationTypes.Split(',').Select(d => (LocationType)Enum.Parse(typeof(LocationType), d, true)).ToArray(); if (excludeLocationTypes.Contains(LocationType.Virtual)) { query.IsVirtualItem = false; } } // Min official rating if (!string.IsNullOrWhiteSpace(request.MinOfficialRating)) { query.MinParentalRating = _localization.GetRatingLevel(request.MinOfficialRating); } // Max official rating if (!string.IsNullOrWhiteSpace(request.MaxOfficialRating)) { query.MaxParentalRating = _localization.GetRatingLevel(request.MaxOfficialRating); } query.CollapseBoxSetItems = false; if (request.Limit > -1 && request.Limit < int.MaxValue) { query.Limit = request.Limit; } return(query); }
/// <summary> Gets an option. </summary> /// <param name="header"> The header. </param> /// <param name="sortField"> The sort field. </param> /// <returns> The option. </returns> private ReportOptions <ActivityLogEntry> GetOption(HeaderMetadata header, string sortField = "") { HeaderMetadata internalHeader = header; ReportOptions <ActivityLogEntry> option = new ReportOptions <ActivityLogEntry>() { Header = new ReportHeader { HeaderFieldType = ReportFieldType.String, SortField = sortField, Type = "", ItemViewType = ItemViewType.None } }; switch (header) { case HeaderMetadata.Name: option.Column = (i, r) => i.Name; option.Header.SortField = ""; break; case HeaderMetadata.Overview: option.Column = (i, r) => i.Overview; option.Header.SortField = ""; option.Header.CanGroup = false; break; case HeaderMetadata.ShortOverview: option.Column = (i, r) => i.ShortOverview; option.Header.SortField = ""; option.Header.CanGroup = false; break; case HeaderMetadata.Type: option.Column = (i, r) => i.Type; option.Header.SortField = ""; break; case HeaderMetadata.Date: option.Column = (i, r) => i.Date; option.Header.SortField = ""; option.Header.HeaderFieldType = ReportFieldType.DateTime; option.Header.Type = ""; break; case HeaderMetadata.UserPrimaryImage: //option.Column = (i, r) => i.UserPrimaryImageTag; option.Header.DisplayType = ReportDisplayType.Screen; option.Header.ItemViewType = ItemViewType.UserPrimaryImage; option.Header.ShowHeaderLabel = false; internalHeader = HeaderMetadata.User; option.Header.CanGroup = false; option.Column = (i, r) => { if (i.UserId != Guid.Empty) { Jellyfin.Data.Entities.User user = _userManager.GetUserById(i.UserId); if (user != null) { var dto = _userManager.GetUserDto(user); return(dto.PrimaryImageTag); } } return(string.Empty); }; option.Header.SortField = ""; break; case HeaderMetadata.Severity: option.Column = (i, r) => i.Severity; option.Header.SortField = ""; break; case HeaderMetadata.Item: option.Column = (i, r) => i.ItemId; option.Header.SortField = ""; break; case HeaderMetadata.User: option.Column = (i, r) => { if (i.UserId != Guid.Empty) { Jellyfin.Data.Entities.User user = _userManager.GetUserById(i.UserId); if (user != null) { return(user.Username); } } return(string.Empty); }; option.Header.SortField = ""; break; case HeaderMetadata.UserId: option.Column = (i, r) => i.UserId; option.Header.SortField = ""; break; } option.Header.Name = GetLocalizedHeader(internalHeader); option.Header.FieldName = header; return(option); }