Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Dec 3, 2025

Albums were loading with a hard-coded limit (previously 50, then 5000), preventing users with large libraries from accessing all their content. This implements infinite scroll pagination with server-side sorting.

Changes

API Layer (IJellyfinApiService, JellyfinApiService)

  • Added GetAlbumsPagedAsync(libraryId, startIndex, limit, sortBy, sortOrder) returning (List<Album>, int TotalCount)
  • Uses Jellyfin's StartIndex, Limit, SortBy, SortOrder query parameters

ViewModel (LibraryViewModel)

  • Pagination state: _albumsStartIndex, _totalAlbumCount, AlbumsPageSize=50
  • HasMoreAlbums computed property, IsLoadingMoreAlbums for loading state
  • LoadMoreAlbumsCommand appends next page to existing collection
  • LoadAlbumsAsync resets pagination on artist/sort changes
  • Server-side sorting: SortName/ProductionYear/IsFavorite mapped from UI sort status

UI (AlbumsListView)

  • Scroll detection triggers LoadMoreAlbumsCommand when within 200px of bottom
  • Loading indicator visible during fetch
  • Album count shows "X OF Y ALBUMS" when more available
// Usage in scroll handler
if (distanceFromBottom < ScrollThresholdPixels)
{
    if (vm.HasMoreAlbums && !vm.IsLoadingMoreAlbums)
        vm.LoadMoreAlbumsCommand?.Execute().Subscribe(_ => { }, _ => { });
}

Artist-specific albums still load all at once (typically small sets).

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • av-build-tel-api-v1.avaloniaui.net
    • Triggering command: /usr/share/dotnet/dotnet dotnet exec --runtimeconfig /home/REDACTED/.nuget/packages/avalonia.buildservices/0.0.31/tools/netstandard2.0/runtimeconfig.json /home/REDACTED/.nuget/packages/avalonia.buildservices/0.0.31/tools/netstandard2.0/Avalonia.BuildServices.Collector.dll (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Original prompt

Summary

Implement infinite scroll pagination for albums to replace the current hard-coded limit of 50 albums.

Current Problem

In JellyFinApiService.cs, the GetAlbumsAsync method has a hard-coded Limit=50 which only loads 50 albums at startup, even if the user has hundreds or thousands of albums in their library.

Implementation Requirements

1. API Service Changes (JellyFinApiService.cs)

Add a new paginated method to the API service:

public async Task<(List<Album> Albums, int TotalCount)> GetAlbumsPagedAsync(
    string libraryId, int startIndex = 0, int limit = 50)
{
    if (!IsAuthenticated || _httpClient is null)
    {
        return ([], 0);
    }

    var response = await _httpClient.GetAsync(
        $"Items?IncludeItemTypes=MusicAlbum&ParentId={libraryId}" +
        $"&Recursive=true&StartIndex={startIndex}&Limit={limit}");
    
    response.EnsureSuccessStatusCode();
    var stream = await response.Content.ReadAsStreamAsync();
    var result = await JsonSerializer.DeserializeAsync(stream, 
        AppJsonSerializerContext.Default.JellyfinResponseAlbum);
    
    return (result?.Items ?? [], result?.TotalRecordCount ?? 0);
}

Don't forget to add this to the IJellyfinApiService interface as well.

2. ViewModel Changes (LibraryViewModel.cs)

Add pagination state and commands:

private int _albumsStartIndex = 0;
private int _totalAlbumCount = 0;
private const int AlbumsPageSize = 50;
private bool _isLoadingMoreAlbums = false;

public bool HasMoreAlbums => Albums.Count < _totalAlbumCount;
public bool IsLoadingMoreAlbums { get => _isLoadingMoreAlbums; set => this.RaiseAndSetIfChanged(ref _isLoadingMoreAlbums, value); }

public ReactiveCommand<Unit, Unit> LoadMoreAlbumsCommand { get; }

Modify LoadAlbumsAsync to:

  • Reset pagination state when loading fresh (artist selection changed, sort changed)
  • Use the new paginated API method
  • Update the total count

Add LoadMoreAlbumsAsync method:

  • Increment _albumsStartIndex by page size
  • Fetch next page
  • Append to existing Albums collection (don't replace)
  • Handle sorting properly (may need to fetch all and sort client-side, OR request sorted from server using Jellyfin's SortBy parameter)

3. UI Changes (AlbumsListView.axaml and AlbumsListView.axaml.cs)

Add scroll detection to trigger loading more albums when user scrolls near the bottom:

In the XAML, give the ListBox a name and add a loading indicator:

<ListBox
    x:Name="AlbumsListBox"
    ItemsSource="{Binding Albums}"
    ...>
</ListBox>

<!-- Loading indicator at bottom -->
<StackPanel 
    Grid.Row="1"
    VerticalAlignment="Bottom"
    HorizontalAlignment="Center"
    IsVisible="{Binding IsLoadingMoreAlbums}">
    <TextBlock Text="Loading more albums..." />
</StackPanel>

In the code-behind, add scroll detection:

public AlbumsListView()
{
    InitializeComponent();
    
    // Find the ScrollViewer inside the ListBox and subscribe to scroll events
    this.AttachedToVisualTree += (_, _) =>
    {
        var listBox = this.FindControl<ListBox>("AlbumsListBox");
        var scrollViewer = listBox?.GetVisualDescendants().OfType<ScrollViewer>().FirstOrDefault();
        
        if (scrollViewer != null)
        {
            scrollViewer.ScrollChanged += OnScrollChanged;
        }
    };
}

private void OnScrollChanged(object? sender, ScrollChangedEventArgs e)
{
    if (sender is not ScrollViewer sv) return;
    
    // Check if we're near the bottom (within 200 pixels)
    var distanceFromBottom = sv.Extent.Height - sv.Offset.Y - sv.Viewport.Height;
    
    if (distanceFromBottom < 200)
    {
        if (DataContext is LibraryViewModel vm && vm.HasMoreAlbums && !vm.IsLoadingMoreAlbums)
        {
            vm.LoadMoreAlbumsCommand?.Execute().Subscribe();
        }
    }
}

4. Sorting Considerations

When sorting is applied, you have two options:

  1. Server-side sorting: Use Jellyfin's SortBy and SortOrder query parameters (e.g., SortBy=Name&SortOrder=Ascending)
  2. Client-side sorting: Fetch all albums first, then sort locally

For simplicity and consistency, consider using server-side sorting by adding SortBy parameters to the API call:

  • SortBy=Name for A-Z
  • SortBy=ProductionYear&SortOrder=Descending for BY RELEASE YEAR
  • SortBy=IsFavorite&SortOrder=Descending for BY RATING (if supported)

5. Edge Cases to Handle

  • Reset pagination when:
    • Artist selection changes
    • Sort order changes
    • Reset button is clicked
  • Prevent duplicate loading (use IsLoadingMoreAlbums flag)
  • Handle the case where GetAlbumsByArtistAsync is called (artist selected) - this probably doesn't need pagination as artists typically have fewer albums
  • Update AlbumCount display to show "X of Y ALBUMS" or similar to indicate more are available

Expected Outcome

  • Initial load fetches first 50 albums quickly
  • As user scrolls down, more albums load automatically
  • Loading indicat...

This pull request was created as a result of the following prompt from Copilot chat.

Summary

Implement infinite scroll pagination for albums to replace the current hard-coded limit of 50 albums.

Current Problem

In JellyFinApiService.cs, the GetAlbumsAsync method has a hard-coded Limit=50 which only loads 50 albums at startup, even if the user has hundreds or thousands of albums in their library.

Implementation Requirements

1. API Service Changes (JellyFinApiService.cs)

Add a new paginated method to the API service:

public async Task<(List<Album> Albums, int TotalCount)> GetAlbumsPagedAsync(
    string libraryId, int startIndex = 0, int limit = 50)
{
    if (!IsAuthenticated || _httpClient is null)
    {
        return ([], 0);
    }

    var response = await _httpClient.GetAsync(
        $"Items?IncludeItemTypes=MusicAlbum&ParentId={libraryId}" +
        $"&Recursive=true&StartIndex={startIndex}&Limit={limit}");
    
    response.EnsureSuccessStatusCode();
    var stream = await response.Content.ReadAsStreamAsync();
    var result = await JsonSerializer.DeserializeAsync(stream, 
        AppJsonSerializerContext.Default.JellyfinResponseAlbum);
    
    return (result?.Items ?? [], result?.TotalRecordCount ?? 0);
}

Don't forget to add this to the IJellyfinApiService interface as well.

2. ViewModel Changes (LibraryViewModel.cs)

Add pagination state and commands:

private int _albumsStartIndex = 0;
private int _totalAlbumCount = 0;
private const int AlbumsPageSize = 50;
private bool _isLoadingMoreAlbums = false;

public bool HasMoreAlbums => Albums.Count < _totalAlbumCount;
public bool IsLoadingMoreAlbums { get => _isLoadingMoreAlbums; set => this.RaiseAndSetIfChanged(ref _isLoadingMoreAlbums, value); }

public ReactiveCommand<Unit, Unit> LoadMoreAlbumsCommand { get; }

Modify LoadAlbumsAsync to:

  • Reset pagination state when loading fresh (artist selection changed, sort changed)
  • Use the new paginated API method
  • Update the total count

Add LoadMoreAlbumsAsync method:

  • Increment _albumsStartIndex by page size
  • Fetch next page
  • Append to existing Albums collection (don't replace)
  • Handle sorting properly (may need to fetch all and sort client-side, OR request sorted from server using Jellyfin's SortBy parameter)

3. UI Changes (AlbumsListView.axaml and AlbumsListView.axaml.cs)

Add scroll detection to trigger loading more albums when user scrolls near the bottom:

In the XAML, give the ListBox a name and add a loading indicator:

<ListBox
    x:Name="AlbumsListBox"
    ItemsSource="{Binding Albums}"
    ...>
</ListBox>

<!-- Loading indicator at bottom -->
<StackPanel 
    Grid.Row="1"
    VerticalAlignment="Bottom"
    HorizontalAlignment="Center"
    IsVisible="{Binding IsLoadingMoreAlbums}">
    <TextBlock Text="Loading more albums..." />
</StackPanel>

In the code-behind, add scroll detection:

public AlbumsListView()
{
    InitializeComponent();
    
    // Find the ScrollViewer inside the ListBox and subscribe to scroll events
    this.AttachedToVisualTree += (_, _) =>
    {
        var listBox = this.FindControl<ListBox>("AlbumsListBox");
        var scrollViewer = listBox?.GetVisualDescendants().OfType<ScrollViewer>().FirstOrDefault();
        
        if (scrollViewer != null)
        {
            scrollViewer.ScrollChanged += OnScrollChanged;
        }
    };
}

private void OnScrollChanged(object? sender, ScrollChangedEventArgs e)
{
    if (sender is not ScrollViewer sv) return;
    
    // Check if we're near the bottom (within 200 pixels)
    var distanceFromBottom = sv.Extent.Height - sv.Offset.Y - sv.Viewport.Height;
    
    if (distanceFromBottom < 200)
    {
        if (DataContext is LibraryViewModel vm && vm.HasMoreAlbums && !vm.IsLoadingMoreAlbums)
        {
            vm.LoadMoreAlbumsCommand?.Execute().Subscribe();
        }
    }
}

4. Sorting Considerations

When sorting is applied, you have two options:

  1. Server-side sorting: Use Jellyfin's SortBy and SortOrder query parameters (e.g., SortBy=Name&SortOrder=Ascending)
  2. Client-side sorting: Fetch all albums first, then sort locally

For simplicity and consistency, consider using server-side sorting by adding SortBy parameters to the API call:

  • SortBy=Name for A-Z
  • SortBy=ProductionYear&SortOrder=Descending for BY RELEASE YEAR
  • SortBy=IsFavorite&SortOrder=Descending for BY RATING (if supported)

5. Edge Cases to Handle

  • Reset pagination when:
    • Artist selection changes
    • Sort order changes
    • Reset button is clicked
  • Prevent duplicate loading (use IsLoadingMoreAlbums flag)
  • Handle the case where GetAlbumsByArtistAsync is called (artist selected) - this probably doesn't need pagination as artists typically have fewer albums
  • Update AlbumCount display to show "X of Y ALBUMS" or similar to indicate more are available

Expected Outcome

  • Initial load fetches first 50 albums quickly
  • As user scrolls down, more albums load automatically
  • Loading indicator shows when fetching more
  • All albums are eventually accessible through scrolling
  • Sorting works correctly with pagination

💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Copilot AI and others added 2 commits December 3, 2025 01:04
Co-authored-by: adrianstevens <5965865+adrianstevens@users.noreply.github.com>
Co-authored-by: adrianstevens <5965865+adrianstevens@users.noreply.github.com>
Copilot AI changed the title [WIP] Add infinite scroll pagination for albums Implement infinite scroll pagination for albums Dec 3, 2025
Copilot AI requested a review from adrianstevens December 3, 2025 01:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants