Exemple #1
0
        private Task DeleteItem(Guid id)
        {
            var item = _libraryManager.GetItemById(id);

            if (item == null)
            {
                return(Task.FromResult(true));
            }

            return(_libraryManager.DeleteItem(item, new DeleteOptions
            {
                DeleteFileLocation = false
            }));
        }
Exemple #2
0
        private void DeleteItem(Guid id)
        {
            var item = _libraryManager.GetItemById(id);

            if (item == null)
            {
                return;
            }

            _libraryManager.DeleteItem(item, new DeleteOptions
            {
                DeleteFileLocation = false
            }, false);
        }
Exemple #3
0
        private async Task CleanDeadItems(CancellationToken cancellationToken, IProgress <double> progress)
        {
            var itemIds = _libraryManager.GetItemIds(new InternalItemsQuery
            {
                HasDeadParentId = true
            });

            var numComplete = 0;
            var numItems    = itemIds.Count;

            _logger.Debug("Cleaning {0} items with dead parent links", numItems);

            foreach (var itemId in itemIds)
            {
                cancellationToken.ThrowIfCancellationRequested();

                var item = _libraryManager.GetItemById(itemId);

                if (item != null)
                {
                    _logger.Debug("Cleaning item {0} type: {1} path: {2}", item.Name, item.GetType().Name, item.Path ?? string.Empty);

                    await _libraryManager.DeleteItem(item, new DeleteOptions
                    {
                        DeleteFileLocation = false
                    });
                }

                numComplete++;
                double percent = numComplete;
                percent /= numItems;
                progress.Report(percent * 100);
            }

            progress.Report(100);
        }
Exemple #4
0
        private void OnLibraryManagerItemUpdated(object?sender, ItemChangeEventArgs itemChangeEventArgs)
        {
            // Only interested in real Season and Episode items
            if (itemChangeEventArgs.Item.IsVirtualItem ||
                !(itemChangeEventArgs.Item is Season || itemChangeEventArgs.Item is Episode))
            {
                return;
            }

            if (!IsEnabledForLibrary(itemChangeEventArgs.Item))
            {
                return;
            }

            var indexNumber = itemChangeEventArgs.Item.IndexNumber;

            // If the item is an Episode, filter on ParentIndexNumber as well (season number)
            int?parentIndexNumber = null;

            if (itemChangeEventArgs.Item is Episode)
            {
                parentIndexNumber = itemChangeEventArgs.Item.ParentIndexNumber;
            }

            var query = new InternalItemsQuery
            {
                IsVirtualItem                = true,
                IndexNumber                  = indexNumber,
                ParentIndexNumber            = parentIndexNumber,
                IncludeItemTypes             = new[] { itemChangeEventArgs.Item.GetType().Name },
                Parent                       = itemChangeEventArgs.Parent,
                GroupByPresentationUniqueKey = false,
                DtoOptions                   = new DtoOptions(true)
            };

            var existingVirtualItems = _libraryManager.GetItemList(query);

            var deleteOptions = new DeleteOptions
            {
                DeleteFileLocation = true
            };

            // Remove the virtual season/episode that matches the newly updated item
            for (var i = 0; i < existingVirtualItems.Count; i++)
            {
                _libraryManager.DeleteItem(existingVirtualItems[i], deleteOptions);
            }
        }
Exemple #5
0
        /// <summary>
        /// Deletes the specified request.
        /// </summary>
        /// <param name="request">The request.</param>
        public void Delete(DeleteItem request)
        {
            var item = _libraryManager.GetItemById(request.Id);

            var auth = _authContext.GetAuthorizationInfo(Request);
            var user = _userManager.GetUserById(auth.UserId);

            if (!item.CanDelete(user))
            {
                throw new UnauthorizedAccessException();
            }

            var task = _libraryManager.DeleteItem(item);

            Task.WaitAll(task);
        }
Exemple #6
0
        /// <summary>
        /// Deletes the specified request.
        /// </summary>
        /// <param name="request">The request.</param>
        public void Delete(DeleteItem request)
        {
            var item = _libraryManager.GetItemById(request.Id);
            var auth = _authContext.GetAuthorizationInfo(Request);
            var user = _userManager.GetUserById(auth.UserId);

            if (!item.CanDelete(user))
            {
                throw new SecurityException("Unauthorized access");
            }

            if (item is ILiveTvRecording)
            {
                var task = _liveTv.DeleteRecording(request.Id);
                Task.WaitAll(task);
            }
            else
            {
                var task = _libraryManager.DeleteItem(item);
                Task.WaitAll(task);
            }
        }
        /// <summary>
        /// Removes the virtual entry after a corresponding physical version has been added
        /// </summary>
        private async Task <bool> RemoveObsoleteOrMissingEpisodes(IEnumerable <Series> series,
                                                                  IEnumerable <Tuple <int, int> > episodeLookup,
                                                                  bool forceRemoveAll)
        {
            var existingEpisodes = (from s in series
                                    let seasonOffset = TvdbSeriesProvider.GetSeriesOffset(s.ProviderIds) ?? ((s.AnimeSeriesIndex ?? 1) - 1)
                                                       from c in s.RecursiveChildren.OfType <Episode>()
                                                       select new { SeasonOffset = seasonOffset, Episode = c })
                                   .ToList();

            var physicalEpisodes = existingEpisodes
                                   .Where(i => i.Episode.LocationType != LocationType.Virtual)
                                   .ToList();

            var virtualEpisodes = existingEpisodes
                                  .Where(i => i.Episode.LocationType == LocationType.Virtual)
                                  .ToList();

            var episodesToRemove = virtualEpisodes
                                   .Where(i =>
            {
                if (forceRemoveAll)
                {
                    return(true);
                }

                if (i.Episode.IndexNumber.HasValue && i.Episode.ParentIndexNumber.HasValue)
                {
                    var seasonNumber  = i.Episode.ParentIndexNumber.Value + i.SeasonOffset;
                    var episodeNumber = i.Episode.IndexNumber.Value;

                    // If there's a physical episode with the same season and episode number, delete it
                    if (physicalEpisodes.Any(p =>
                                             p.Episode.ParentIndexNumber.HasValue && (p.Episode.ParentIndexNumber.Value + p.SeasonOffset) == seasonNumber &&
                                             p.Episode.ContainsEpisodeNumber(episodeNumber)))
                    {
                        return(true);
                    }

                    // If the episode no longer exists in the remote lookup, delete it
                    if (!episodeLookup.Any(e => e.Item1 == seasonNumber && e.Item2 == episodeNumber))
                    {
                        return(true);
                    }

                    return(false);
                }

                return(true);
            })
                                   .ToList();

            var hasChanges = false;

            foreach (var episodeToRemove in episodesToRemove.Select(e => e.Episode))
            {
                _logger.Info("Removing missing/unaired episode {0} {1}x{2}", episodeToRemove.Series.Name, episodeToRemove.ParentIndexNumber, episodeToRemove.IndexNumber);

                await _libraryManager.DeleteItem(episodeToRemove).ConfigureAwait(false);

                hasChanges = true;
            }

            return(hasChanges);
        }
Exemple #8
0
        /// <summary>
        /// Runs the specified progress.
        /// </summary>
        /// <param name="progress">The progress.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>Task.</returns>
        public async Task Run(IProgress <double> progress, CancellationToken cancellationToken)
        {
            var items = _libraryManager.RootFolder.GetRecursiveChildren(i => i is IHasMusicGenres)
                        .SelectMany(i => i.Genres)
                        .DistinctNames()
                        .ToList();

            var numComplete = 0;
            var count       = items.Count;

            var validIds = new List <Guid>();

            foreach (var name in items)
            {
                try
                {
                    var itemByName = _libraryManager.GetMusicGenre(name);

                    validIds.Add(itemByName.Id);

                    await itemByName.RefreshMetadata(cancellationToken).ConfigureAwait(false);
                }
                catch (OperationCanceledException)
                {
                    // Don't clutter the log
                    break;
                }
                catch (Exception ex)
                {
                    _logger.ErrorException("Error refreshing {0}", ex, name);
                }

                numComplete++;
                double percent = numComplete;
                percent /= count;
                percent *= 100;

                progress.Report(percent);
            }

            var allIds = _libraryManager.GetItemIds(new InternalItemsQuery
            {
                IncludeItemTypes = new[] { typeof(MusicGenre).Name }
            });

            var invalidIds = allIds
                             .Except(validIds)
                             .ToList();

            foreach (var id in invalidIds)
            {
                cancellationToken.ThrowIfCancellationRequested();

                var item = _libraryManager.GetItemById(id);

                await _libraryManager.DeleteItem(item, new DeleteOptions
                {
                    DeleteFileLocation = false
                }).ConfigureAwait(false);
            }

            progress.Report(100);
        }
Exemple #9
0
        public async System.Threading.Tasks.Task Execute(CancellationToken cancellationToken, IProgress <double> progress)
        {
            if (VersionCheck.IsVersionValid(_appHost.ApplicationVersion, _appHost.SystemUpdateLevel) == false)
            {
                _logger.Info("ERROR : Plugin not compatible with this server version");
                return;
            }

            // query the user playback info for the most active movies
            ActivityRepository    repository = new ActivityRepository(_logger, _config.ApplicationPaths, _fileSystem);
            ReportPlaybackOptions config     = _config.GetReportPlaybackOptions();

            foreach (var activity_playlist in config.ActivityPlaylists)
            {
                string list_name = activity_playlist.Name;
                string list_type = activity_playlist.Type;
                int    list_days = activity_playlist.Days;
                int    list_size = activity_playlist.Size;

                _logger.Info("Activity Playlist - Name:" + list_name + " Type:" + list_type + " Days:" + list_days);

                string sql = "";
                sql += "SELECT ItemId, ";
                sql += "COUNT(DISTINCT(UserId)) as count, ";
                sql += "AVG(CAST(strftime('%Y%m%d%H%M', 'now', 'localtime') AS int) - CAST(strftime('%Y%m%d%H%M', DateCreated) AS int)) as av_age ";
                sql += "FROM PlaybackActivity ";
                sql += "WHERE ItemType = '" + list_type + "' ";
                sql += "AND DateCreated > datetime('now', '-" + list_days + " day', 'localtime') ";
                sql += "GROUP BY ItemId ";
                sql += "ORDER BY count DESC, av_age ASC ";
                sql += "LIMIT " + list_size;

                List <string>         cols          = new List <string>();
                List <List <Object> > query_results = new List <List <object> >();
                repository.RunCustomQuery(sql, cols, query_results);

                List <long> items = new List <long>();
                foreach (List <Object> row in query_results)
                {
                    long item_id = long.Parse((string)row[0]);
                    items.Add(item_id);
                }

                // create a playlist with the most active movies
                string             playlist_name = list_name;
                InternalItemsQuery query         = new InternalItemsQuery();
                query.IncludeItemTypes = new string[] { "Playlist" };
                query.Name             = playlist_name;

                BaseItem[] results = _libraryManager.GetItemList(query, false);
                foreach (BaseItem item in results)
                {
                    _logger.Info("Deleting Existing Movie Playlist : " + item.InternalId);
                    DeleteOptions delete_options = new DeleteOptions();
                    delete_options.DeleteFileLocation = true;
                    _libraryManager.DeleteItem(item, delete_options);
                }

                _logger.Info("Creating Movie Playlist");
                PlaylistCreationRequest create_options = new PlaylistCreationRequest();
                create_options.Name = playlist_name;

                if (list_type == "Movie")
                {
                    create_options.MediaType = "Movie";
                }
                else
                {
                    create_options.MediaType = "Episode";
                }

                create_options.ItemIdList = items.ToArray();
                await _playlistman.CreatePlaylist(create_options).ConfigureAwait(false);
            }
        }
Exemple #10
0
        /// <summary>
        /// Removes the virtual entry after a corresponding physical version has been added
        /// </summary>
        private bool RemoveObsoleteOrMissingEpisodes(Series series,
                                                     IList <BaseItem> allRecursiveChildren,
                                                     IEnumerable <Tuple <int, int> > episodeLookup,
                                                     bool allowMissingEpisodes)
        {
            var existingEpisodes = allRecursiveChildren.OfType <Episode>()
                                   .ToList();

            var physicalEpisodes = existingEpisodes
                                   .Where(i => i.LocationType != LocationType.Virtual)
                                   .ToList();

            var virtualEpisodes = existingEpisodes
                                  .Where(i => i.LocationType == LocationType.Virtual)
                                  .ToList();

            var episodesToRemove = virtualEpisodes
                                   .Where(i =>
            {
                if (i.IndexNumber.HasValue && i.ParentIndexNumber.HasValue)
                {
                    var seasonNumber  = i.ParentIndexNumber.Value;
                    var episodeNumber = i.IndexNumber.Value;

                    // If there's a physical episode with the same season and episode number, delete it
                    if (physicalEpisodes.Any(p =>
                                             p.ParentIndexNumber.HasValue && (p.ParentIndexNumber.Value) == seasonNumber &&
                                             p.ContainsEpisodeNumber(episodeNumber)))
                    {
                        return(true);
                    }

                    // If the episode no longer exists in the remote lookup, delete it
                    if (!episodeLookup.Any(e => e.Item1 == seasonNumber && e.Item2 == episodeNumber))
                    {
                        return(true);
                    }

                    if (!allowMissingEpisodes && i.IsMissingEpisode)
                    {
                        // If it's missing, but not unaired, remove it
                        if (!i.PremiereDate.HasValue || i.PremiereDate.Value.ToLocalTime().Date.AddDays(UnairedEpisodeThresholdDays) < DateTime.Now.Date)
                        {
                            return(true);
                        }
                    }

                    return(false);
                }

                return(true);
            })
                                   .ToList();

            var hasChanges = false;

            foreach (var episodeToRemove in episodesToRemove)
            {
                _libraryManager.DeleteItem(episodeToRemove, new DeleteOptions
                {
                    DeleteFileLocation = true
                }, false);

                hasChanges = true;
            }

            return(hasChanges);
        }
Exemple #11
0
        /// <summary>
        /// Validates the people.
        /// </summary>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <param name="progress">The progress.</param>
        /// <returns>Task.</returns>
        public async Task ValidatePeople(CancellationToken cancellationToken, IProgress <double> progress)
        {
            var people = _libraryManager.GetPeopleNames(new InternalPeopleQuery());

            var numComplete = 0;

            var numPeople = people.Count;

            _logger.LogDebug("Will refresh {0} people", numPeople);

            foreach (var person in people)
            {
                cancellationToken.ThrowIfCancellationRequested();

                try
                {
                    var item = _libraryManager.GetPerson(person);

                    var options = new MetadataRefreshOptions(new DirectoryService(_fileSystem))
                    {
                        ImageRefreshMode    = MetadataRefreshMode.ValidationOnly,
                        MetadataRefreshMode = MetadataRefreshMode.ValidationOnly
                    };

                    await item.RefreshMetadata(options, cancellationToken).ConfigureAwait(false);
                }
                catch (OperationCanceledException)
                {
                    throw;
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "Error validating IBN entry {Person}", person);
                }

                // Update progress
                numComplete++;
                double percent = numComplete;
                percent /= numPeople;

                progress.Report(100 * percent);
            }

            var deadEntities = _libraryManager.GetItemList(new InternalItemsQuery
            {
                IncludeItemTypes = new[] { BaseItemKind.Person },
                IsDeadPerson     = true,
                IsLocked         = false
            });

            foreach (var item in deadEntities)
            {
                _logger.LogInformation(
                    "Deleting dead {2} {0} {1}.",
                    item.Id.ToString("N", CultureInfo.InvariantCulture),
                    item.Name,
                    item.GetType().Name);

                _libraryManager.DeleteItem(
                    item,
                    new DeleteOptions
                {
                    DeleteFileLocation = false
                },
                    false);
            }

            progress.Report(100);

            _logger.LogInformation("People validation complete");
        }
Exemple #12
0
        /// <summary>
        /// Validates the people.
        /// </summary>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <param name="progress">The progress.</param>
        /// <returns>Task.</returns>
        public async Task ValidatePeople(CancellationToken cancellationToken, IProgress <double> progress)
        {
            var innerProgress = new ActionableProgress <double>();

            innerProgress.RegisterAction(pct => progress.Report(pct * .15));

            var peopleOptions = _config.Configuration.PeopleMetadataOptions;

            var people = _libraryManager.GetAllPeople();

            var dict = new Dictionary <string, bool>(StringComparer.OrdinalIgnoreCase);

            foreach (var person in people)
            {
                var isMetadataEnabled = DownloadMetadata(person, peopleOptions);

                bool currentValue;
                if (dict.TryGetValue(person.Name, out currentValue))
                {
                    if (!currentValue && isMetadataEnabled)
                    {
                        dict[person.Name] = true;
                    }
                }
                else
                {
                    dict[person.Name] = isMetadataEnabled;
                }
            }

            var numComplete = 0;
            var validIds    = new List <Guid>();

            foreach (var person in dict)
            {
                cancellationToken.ThrowIfCancellationRequested();

                try
                {
                    var item = _libraryManager.GetPerson(person.Key);

                    validIds.Add(item.Id);

                    var options = new MetadataRefreshOptions(_fileSystem)
                    {
                        MetadataRefreshMode = person.Value ? MetadataRefreshMode.Default : MetadataRefreshMode.ValidationOnly,
                        ImageRefreshMode    = person.Value ? ImageRefreshMode.Default : ImageRefreshMode.ValidationOnly
                    };

                    await item.RefreshMetadata(options, cancellationToken).ConfigureAwait(false);
                }
                catch (OperationCanceledException)
                {
                    throw;
                }
                catch (Exception ex)
                {
                    _logger.ErrorException("Error validating IBN entry {0}", ex, person);
                }

                // Update progress
                numComplete++;
                double percent = numComplete;
                percent /= people.Count;

                progress.Report(100 * percent);
            }

            var allIds = _libraryManager.GetItemIds(new InternalItemsQuery
            {
                IncludeItemTypes = new[] { typeof(Person).Name }
            });

            var invalidIds = allIds
                             .Except(validIds)
                             .ToList();

            foreach (var id in invalidIds)
            {
                cancellationToken.ThrowIfCancellationRequested();

                var item = _libraryManager.GetItemById(id);

                if (item != null)
                {
                    await _libraryManager.DeleteItem(item, new DeleteOptions
                    {
                        DeleteFileLocation = false
                    }).ConfigureAwait(false);
                }
            }

            progress.Report(100);

            _logger.Info("People validation complete");

            // Bad practice, i know. But we keep a lot in memory, unfortunately.
            GC.Collect(2, GCCollectionMode.Forced, true);
            GC.Collect(2, GCCollectionMode.Forced, true);
        }
Exemple #13
0
        /// <summary>
        /// Runs the specified progress.
        /// </summary>
        /// <param name="progress">The progress.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>Task.</returns>
        public async Task Run(IProgress <double> progress, CancellationToken cancellationToken)
        {
            var names = _itemRepo.GetAllArtistNames();

            var numComplete = 0;
            var count       = names.Count;

            foreach (var name in names)
            {
                try
                {
                    var item = _libraryManager.GetArtist(name);

                    await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
                }
                catch (OperationCanceledException)
                {
                    // Don't clutter the log
                    throw;
                }
                catch (Exception ex)
                {
                    _logger.ErrorException("Error refreshing {0}", ex, name);
                }

                numComplete++;
                double percent = numComplete;
                percent /= count;
                percent *= 100;

                progress.Report(percent);
            }

            names = names.Select(i => i.RemoveDiacritics()).DistinctNames().ToList();

            var artistEntities = _libraryManager.GetItemList(new InternalItemsQuery
            {
                IncludeItemTypes = new[] { typeof(MusicArtist).Name }
            }).Cast <MusicArtist>().ToList();

            foreach (var artist in artistEntities)
            {
                if (!artist.IsAccessedByName)
                {
                    continue;
                }

                var name = (artist.Name ?? string.Empty).RemoveDiacritics();

                if (!names.Contains(name, StringComparer.OrdinalIgnoreCase))
                {
                    _logger.Info("Deleting dead artist {0} {1}.", artist.Id.ToString("N"), artist.Name);

                    await _libraryManager.DeleteItem(artist, new DeleteOptions
                    {
                        DeleteFileLocation = false
                    }).ConfigureAwait(false);
                }
            }

            progress.Report(100);
        }
Exemple #14
0
        public async Task <QueryResult <BaseItem> > GetChannelItemsInternal(InternalItemsQuery query, IProgress <double> progress, CancellationToken cancellationToken)
        {
            // Get the internal channel entity
            var channel = GetChannel(query.ChannelIds[0]);

            // Find the corresponding channel provider plugin
            var channelProvider = GetChannelProvider(channel);

            var user = query.User;

            ChannelItemSortField?sortField = null;
            var sortDescending             = false;

            var parentItem = query.ParentId.HasValue ? _libraryManager.GetItemById(query.ParentId.Value) : channel;

            var itemsResult = await GetChannelItems(channelProvider,
                                                    user,
                                                    parentItem is Channel?null : parentItem.ExternalId,
                                                    sortField,
                                                    sortDescending,
                                                    cancellationToken)
                              .ConfigureAwait(false);

            if (!query.ParentId.HasValue)
            {
                query.Parent = channel;
            }
            query.ChannelIds = new string[] { };

            // Not yet sure why this is causing a problem
            query.GroupByPresentationUniqueKey = false;

            //_logger.Debug("GetChannelItemsInternal");

            // null if came from cache
            if (itemsResult != null)
            {
                var internalItems = itemsResult.Items
                                    .Select(i => GetChannelItemEntity(i, channelProvider, channel.Id, parentItem.Id, cancellationToken))
                                    .ToArray();

                var existingIds = _libraryManager.GetItemIds(query);
                var deadIds     = existingIds.Except(internalItems.Select(i => i.Id))
                                  .ToArray();

                foreach (var deadId in deadIds)
                {
                    var deadItem = _libraryManager.GetItemById(deadId);
                    if (deadItem != null)
                    {
                        _libraryManager.DeleteItem(deadItem, new DeleteOptions
                        {
                            DeleteFileLocation         = false,
                            DeleteFromExternalProvider = false
                        }, parentItem, false);
                    }
                }
            }

            return(_libraryManager.GetItemsResult(query));
        }
        private Task DeleteItem(DeleteItem request)
        {
            var item = _dtoService.GetItemByDtoId(request.Id);

            return(_libraryManager.DeleteItem(item));
        }