webmaster442 / media

A CLI frontend for: ffmpeg, mpv, yt-dlp written in C#
MIT License
0 stars 0 forks source link

Browser #1

Closed webmaster442 closed 2 months ago

webmaster442 commented 2 months ago
using Spectre.Console;

namespace Browser;

internal sealed class Item
{
    public required string Name { get; init; }
    public required string FullPath { get; init; }
    public required string Icon { get; init; }
}

internal interface IBrowserMenuProvider
{
    IEnumerable<Item> ProvideItems(string currentPath);
    bool SelectionCanExit(Item selectedItem);
}

internal sealed class FileSystemBrowserMenuProvider : IBrowserMenuProvider
{
    private readonly HashSet<string> _extensionsToShow;

    public FileSystemBrowserMenuProvider(IEnumerable<string>? extensions = null)
    {
        if (extensions == null)
            _extensionsToShow = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
        else
            _extensionsToShow = new HashSet<string>(extensions, StringComparer.OrdinalIgnoreCase);
    }

    public IEnumerable<Item> ProvideItems(string currentPath)
    {
        if (string.IsNullOrEmpty(currentPath))
        {
            return GetDrives();
        }
        return GetFileSystemItems(currentPath);
    }

    private IEnumerable<Item> GetFileSystemItems(string currentPath)
    {
        var directoryInfo = new DirectoryInfo(currentPath);
        yield return new Item
        {
            Name = "..",
            FullPath = GetPreviousPath(currentPath),
            Icon = ""
        };
        foreach (var directory in directoryInfo.GetDirectories().OrderBy(x => x.Name))
        {
            if (!directory.Attributes.HasFlag(FileAttributes.Hidden)
                && !directory.Attributes.HasFlag(FileAttributes.System))
            {
                yield return CreateItem(directory);
            }
        }
        foreach (var file in directoryInfo.GetFiles().OrderBy(x => x.Name))
        {
            if (!file.Attributes.HasFlag(FileAttributes.Hidden)
                && !file.Attributes.HasFlag(FileAttributes.System))
            {
                if (_extensionsToShow.Count == 0)
                    yield return CreateItem(file);
                else if (_extensionsToShow.Contains(file.Extension))
                    yield return CreateItem(file);

            }
        }
    }

    private static string GetPreviousPath(string currentPath)
    {
        var parts = currentPath.Split(Path.DirectorySeparatorChar, StringSplitOptions.RemoveEmptyEntries);
        return string.Join(Path.DirectorySeparatorChar, parts.Take(parts.Length - 1));
    }

    private static Item CreateItem(FileSystemInfo item)
    {
        return new Item
        {
            FullPath = item.FullName,
            Name = item.Name,
            Icon = item is DirectoryInfo ? Emoji.Known.FileFolder : Emoji.Known.Memo
        };
    }

    private static IEnumerable<Item> GetDrives()
    {
        static string GetIcon(DriveType driveType)
        {
            return driveType switch
            {
                DriveType.Fixed => Emoji.Known.ComputerDisk,
                DriveType.Ram => Emoji.Known.DesktopComputer,
                DriveType.Removable => Emoji.Known.FloppyDisk,
                DriveType.CDRom => Emoji.Known.OpticalDisk,
                DriveType.NoRootDirectory => Emoji.Known.FileFolder,
                DriveType.Network => Emoji.Known.GlobeWithMeridians,
                _ => Emoji.Known.WhiteQuestionMark,
            };
        }

        foreach (var drive in DriveInfo.GetDrives())
        {
            yield return new Item
            {
                FullPath = drive.RootDirectory.FullName,
                Name = $"{drive.Name} - {drive.VolumeLabel}",
                Icon = GetIcon(drive.DriveType)
            };
        }
    }

    public bool SelectionCanExit(Item selectedItem)
        => File.Exists(selectedItem.FullPath);
}

internal sealed class BrowserMenu
{
    private string _currentPath;
    private readonly IBrowserMenuProvider _menuProvider;
    private readonly string _title;

    public BrowserMenu(IBrowserMenuProvider menuProvider,
                       string title)
    {
        _currentPath = string.Empty;
        _menuProvider = menuProvider;
        _title = title;
    }

    public async Task<Item> DoSelection()
    {
        while (true)
        {
            var selection = new SelectionPrompt<Item>()
                .PageSize(Console.WindowHeight - 5)
                .AddChoices(_menuProvider.ProvideItems(_currentPath))
                .Mode(SelectionMode.Independent)
                .Title($"{_title} - [green]{_currentPath}[/]")
                .UseConverter(Render);

            var item = await selection.ShowAsync(AnsiConsole.Console, CancellationToken.None);

            if (_menuProvider.SelectionCanExit(item))
            {
                return item;
            }
            else
            {
                _currentPath = item.FullPath;
            }
        }
    }

    private string Render(Item item)
        => $"{item.Icon} {item.Name}";
}