예제 #1
0
        public static FileType CheckFileType(string extension)
        {
            if (!string.IsNullOrEmpty(extension))
            {
                FileExtension file = new FileExtension(extension.ToUpper(), FileType.UnKnow, "");

                if (VideoFileExtensions.Contains(file))
                {
                    return(FileType.Video);
                }
                else if (SubTitleFileExtensions.Contains(file))
                {
                    return(FileType.SubTitle);
                }
                else if (AudioFileExtensions.Contains(file))
                {
                    return(FileType.Audio);
                }
                else if (ImageFileExtensions.Contains(file))
                {
                    return(FileType.Image);
                }
            }

            return(FileType.UnKnow);
        }
    private async Task <Either <BaseError, Unit> > ScanEpisodes(
        LibraryPath libraryPath,
        string ffmpegPath,
        string ffprobePath,
        Season season,
        string seasonPath,
        CancellationToken cancellationToken)
    {
        var allSeasonFiles = _localFileSystem.ListSubdirectories(seasonPath)
                             .Map(_localFileSystem.ListFiles)
                             .Flatten()
                             .Append(_localFileSystem.ListFiles(seasonPath))
                             .Filter(f => VideoFileExtensions.Contains(Path.GetExtension(f)))
                             .Filter(f => !Path.GetFileName(f).StartsWith("._"))
                             .OrderBy(identity)
                             .ToList();

        foreach (string file in allSeasonFiles)
        {
            // TODO: figure out how to rebuild playlists
            Either <BaseError, Episode> maybeEpisode = await _televisionRepository
                                                       .GetOrAddEpisode(season, libraryPath, file)
                                                       .BindT(
                episode => UpdateStatistics(new MediaItemScanResult <Episode>(episode), ffmpegPath, ffprobePath)
                .MapT(_ => episode))
                                                       .BindT(UpdateMetadata)
                                                       .BindT(e => UpdateThumbnail(e, cancellationToken))
                                                       .BindT(UpdateSubtitles)
                                                       .BindT(e => FlagNormal(new MediaItemScanResult <Episode>(e)))
                                                       .MapT(r => r.Item);

            foreach (BaseError error in maybeEpisode.LeftToSeq())
            {
                _logger.LogWarning("Error processing episode at {Path}: {Error}", file, error.Value);
            }

            foreach (Episode episode in maybeEpisode.RightToSeq())
            {
                await _searchIndex.UpdateItems(_searchRepository, new List <MediaItem> {
                    episode
                });
            }
        }

        // TODO: remove missing episodes?

        return(Unit.Default);
    }
예제 #3
0
        /// <summary>
        /// Gets the files from directory.
        /// </summary>
        /// <param name="dir">The dir.</param>
        private void GetFilesFromDirectory(string dir)
        {
            // files for the current folder only
            List <string> files = new List <string>();

            try
            {
                // ignore the folder?
                if (File.Exists(Path.Combine(dir, "tv.ignore")))
                {
                    return;
                }

                // add files only
                files = Directory.GetFiles(dir).ToList();
            }
            catch
            {
                return;
            }

            // Add files to list.
            foreach (var file in files)
            {
                string extension = "*" + Path.GetExtension(file);

                if (extension != "*." && VideoFileExtensions.Contains(extension))
                {
                    FileCacheEntities.Add(new FileCacheEntity()
                    {
                        FileNameOnly    = Path.GetFileNameWithoutExtension(file),
                        FullPathAndName = file
                    });
                }
            }

            // Do same for child folders.
            foreach (var childDir in Directory.GetDirectories(dir))
            {
                GetFilesFromDirectory(childDir);
            }
        }
        private async Task <Unit> ScanEpisodes(
            LibraryPath libraryPath,
            string ffprobePath,
            Season season,
            string seasonPath)
        {
            foreach (string file in _localFileSystem.ListFiles(seasonPath)
                     .Filter(f => VideoFileExtensions.Contains(Path.GetExtension(f))).OrderBy(identity))
            {
                // TODO: figure out how to rebuild playlists
                Either <BaseError, Episode> maybeEpisode = await _televisionRepository
                                                           .GetOrAddEpisode(season, libraryPath, file)
                                                           .BindT(
                    episode => UpdateStatistics(new MediaItemScanResult <Episode>(episode), ffprobePath)
                    .MapT(_ => episode))
                                                           .BindT(UpdateMetadata)
                                                           .BindT(UpdateThumbnail);

                maybeEpisode.IfLeft(
                    error => _logger.LogWarning("Error processing episode at {Path}: {Error}", file, error.Value));
            }

            return(Unit.Default);
        }
예제 #5
0
    private async Task <Either <BaseError, Unit> > ScanMusicVideos(
        LibraryPath libraryPath,
        string ffmpegPath,
        string ffprobePath,
        Artist artist,
        string artistFolder,
        CancellationToken cancellationToken)
    {
        var folderQueue = new Queue <string>();

        folderQueue.Enqueue(artistFolder);

        while (folderQueue.Count > 0)
        {
            if (cancellationToken.IsCancellationRequested)
            {
                return(new ScanCanceled());
            }

            string musicVideoFolder = folderQueue.Dequeue();
            // _logger.LogDebug("Scanning music video folder {Folder}", musicVideoFolder);

            var allFiles = _localFileSystem.ListFiles(musicVideoFolder)
                           .Filter(f => VideoFileExtensions.Contains(Path.GetExtension(f)))
                           .Filter(f => !Path.GetFileName(f).StartsWith("._"))
                           .ToList();

            foreach (string subdirectory in _localFileSystem.ListSubdirectories(musicVideoFolder)
                     .OrderBy(identity))
            {
                folderQueue.Enqueue(subdirectory);
            }

            string etag = FolderEtag.Calculate(musicVideoFolder, _localFileSystem);
            Option <LibraryFolder> knownFolder = libraryPath.LibraryFolders
                                                 .Filter(f => f.Path == musicVideoFolder)
                                                 .HeadOrNone();

            // skip folder if etag matches
            if (await knownFolder.Map(f => f.Etag ?? string.Empty).IfNoneAsync(string.Empty) == etag)
            {
                continue;
            }

            foreach (string file in allFiles.OrderBy(identity))
            {
                // TODO: figure out how to rebuild playouts
                Either <BaseError, MediaItemScanResult <MusicVideo> > maybeMusicVideo = await _musicVideoRepository
                                                                                        .GetOrAdd(artist, libraryPath, file)
                                                                                        .BindT(musicVideo => UpdateStatistics(musicVideo, ffmpegPath, ffprobePath))
                                                                                        .BindT(UpdateMetadata)
                                                                                        .BindT(result => UpdateThumbnail(result, cancellationToken))
                                                                                        .BindT(UpdateSubtitles)
                                                                                        .BindT(FlagNormal);

                foreach (BaseError error in maybeMusicVideo.LeftToSeq())
                {
                    _logger.LogWarning("Error processing music video at {Path}: {Error}", file, error.Value);
                }

                foreach (MediaItemScanResult <MusicVideo> result in maybeMusicVideo.RightToSeq())
                {
                    if (result.IsAdded)
                    {
                        await _searchIndex.AddItems(_searchRepository, new List <MediaItem> {
                            result.Item
                        });
                    }
                    else if (result.IsUpdated)
                    {
                        await _searchIndex.UpdateItems(_searchRepository, new List <MediaItem> {
                            result.Item
                        });
                    }

                    await _libraryRepository.SetEtag(libraryPath, knownFolder, musicVideoFolder, etag);
                }
            }
        }

        return(Unit.Default);
    }
예제 #6
0
        public async Task <Either <BaseError, Unit> > ScanFolder(LibraryPath libraryPath, string ffprobePath)
        {
            if (!_localFileSystem.IsLibraryPathAccessible(libraryPath))
            {
                return(new MediaSourceInaccessible());
            }

            var folderQueue = new Queue <string>();

            foreach (string folder in _localFileSystem.ListSubdirectories(libraryPath.Path).OrderBy(identity))
            {
                folderQueue.Enqueue(folder);
            }

            while (folderQueue.Count > 0)
            {
                string movieFolder = folderQueue.Dequeue();

                var allFiles = _localFileSystem.ListFiles(movieFolder)
                               .Filter(f => VideoFileExtensions.Contains(Path.GetExtension(f)))
                               .Filter(
                    f => !ExtraFiles.Any(
                        e => Path.GetFileNameWithoutExtension(f).EndsWith(e, StringComparison.OrdinalIgnoreCase)))
                               .ToList();

                if (allFiles.Count == 0)
                {
                    foreach (string subdirectory in _localFileSystem.ListSubdirectories(movieFolder).OrderBy(identity))
                    {
                        folderQueue.Enqueue(subdirectory);
                    }

                    continue;
                }

                foreach (string file in allFiles.OrderBy(identity))
                {
                    // TODO: figure out how to rebuild playlists
                    Either <BaseError, MediaItemScanResult <Movie> > maybeMovie = await _movieRepository
                                                                                  .GetOrAdd(libraryPath, file)
                                                                                  .BindT(movie => UpdateStatistics(movie, ffprobePath))
                                                                                  .BindT(UpdateMetadata)
                                                                                  .BindT(movie => UpdateArtwork(movie, ArtworkKind.Poster))
                                                                                  .BindT(movie => UpdateArtwork(movie, ArtworkKind.FanArt));

                    await maybeMovie.Match(
                        async result =>
                    {
                        if (result.IsAdded)
                        {
                            await _searchIndex.AddItems(new List <MediaItem> {
                                result.Item
                            });
                        }
                        else if (result.IsUpdated)
                        {
                            await _searchIndex.UpdateItems(new List <MediaItem> {
                                result.Item
                            });
                        }
                    },
                        error =>
                    {
                        _logger.LogWarning("Error processing movie at {Path}: {Error}", file, error.Value);
                        return(Task.CompletedTask);
                    });
                }
            }

            foreach (string path in await _movieRepository.FindMoviePaths(libraryPath))
            {
                if (!_localFileSystem.FileExists(path))
                {
                    _logger.LogInformation("Removing missing movie at {Path}", path);
                    List <int> ids = await _movieRepository.DeleteByPath(libraryPath, path);

                    await _searchIndex.RemoveItems(ids);
                }
            }

            return(Unit.Default);
        }
예제 #7
0
    public async Task <Either <BaseError, Unit> > ScanFolder(
        LibraryPath libraryPath,
        string ffmpegPath,
        string ffprobePath,
        decimal progressMin,
        decimal progressMax,
        CancellationToken cancellationToken)
    {
        try
        {
            decimal progressSpread = progressMax - progressMin;

            var foldersCompleted = 0;

            var folderQueue = new Queue <string>();

            if (ShouldIncludeFolder(libraryPath.Path))
            {
                folderQueue.Enqueue(libraryPath.Path);
            }

            foreach (string folder in _localFileSystem.ListSubdirectories(libraryPath.Path)
                     .Filter(ShouldIncludeFolder)
                     .OrderBy(identity))
            {
                folderQueue.Enqueue(folder);
            }

            while (folderQueue.Count > 0)
            {
                if (cancellationToken.IsCancellationRequested)
                {
                    return(new ScanCanceled());
                }

                decimal percentCompletion = (decimal)foldersCompleted / (foldersCompleted + folderQueue.Count);
                await _mediator.Publish(
                    new LibraryScanProgress(libraryPath.LibraryId, progressMin + percentCompletion *progressSpread),
                    cancellationToken);

                string otherVideoFolder = folderQueue.Dequeue();
                foldersCompleted++;

                var filesForEtag = _localFileSystem.ListFiles(otherVideoFolder).ToList();

                var allFiles = filesForEtag
                               .Filter(f => VideoFileExtensions.Contains(Path.GetExtension(f)))
                               .Filter(f => !Path.GetFileName(f).StartsWith("._"))
                               .ToList();

                foreach (string subdirectory in _localFileSystem.ListSubdirectories(otherVideoFolder)
                         .Filter(ShouldIncludeFolder)
                         .OrderBy(identity))
                {
                    folderQueue.Enqueue(subdirectory);
                }

                string etag = FolderEtag.Calculate(otherVideoFolder, _localFileSystem);
                Option <LibraryFolder> knownFolder = libraryPath.LibraryFolders
                                                     .Filter(f => f.Path == otherVideoFolder)
                                                     .HeadOrNone();

                // skip folder if etag matches
                if (!allFiles.Any() || await knownFolder.Map(f => f.Etag ?? string.Empty).IfNoneAsync(string.Empty) ==
                    etag)
                {
                    continue;
                }

                _logger.LogDebug(
                    "UPDATE: Etag has changed for folder {Folder}",
                    otherVideoFolder);

                foreach (string file in allFiles.OrderBy(identity))
                {
                    Either <BaseError, MediaItemScanResult <OtherVideo> > maybeVideo = await _otherVideoRepository
                                                                                       .GetOrAdd(libraryPath, file)
                                                                                       .BindT(video => UpdateStatistics(video, ffmpegPath, ffprobePath))
                                                                                       .BindT(UpdateMetadata)
                                                                                       .BindT(UpdateSubtitles)
                                                                                       .BindT(FlagNormal);

                    foreach (BaseError error in maybeVideo.LeftToSeq())
                    {
                        _logger.LogWarning("Error processing other video at {Path}: {Error}", file, error.Value);
                    }

                    foreach (MediaItemScanResult <OtherVideo> result in maybeVideo.RightToSeq())
                    {
                        if (result.IsAdded)
                        {
                            await _searchIndex.AddItems(_searchRepository, new List <MediaItem> {
                                result.Item
                            });
                        }
                        else if (result.IsUpdated)
                        {
                            await _searchIndex.UpdateItems(_searchRepository, new List <MediaItem> {
                                result.Item
                            });
                        }

                        await _libraryRepository.SetEtag(libraryPath, knownFolder, otherVideoFolder, etag);
                    }
                }
            }

            foreach (string path in await _otherVideoRepository.FindOtherVideoPaths(libraryPath))
            {
                if (!_localFileSystem.FileExists(path))
                {
                    _logger.LogInformation("Flagging missing other video at {Path}", path);
                    List <int> otherVideoIds = await FlagFileNotFound(libraryPath, path);

                    await _searchIndex.RebuildItems(_searchRepository, otherVideoIds);
                }
                else if (Path.GetFileName(path).StartsWith("._"))
                {
                    _logger.LogInformation("Removing dot underscore file at {Path}", path);
                    List <int> otherVideoIds = await _otherVideoRepository.DeleteByPath(libraryPath, path);

                    await _searchIndex.RemoveItems(otherVideoIds);
                }
            }

            await _libraryRepository.CleanEtagsForLibraryPath(libraryPath);

            return(Unit.Default);
        }
        catch (Exception ex) when(ex is TaskCanceledException or OperationCanceledException)
        {
            return(new ScanCanceled());
        }
        finally
        {
            _searchIndex.Commit();
        }
    }