lastfm_edit/discovery/
album_tracks.rs

1use super::common::filter_by_original_album_artist;
2use crate::{
3    AsyncDiscoveryIterator, AsyncPaginatedIterator, ExactScrobbleEdit, LastFmEditClientImpl,
4    Result, ScrobbleEdit,
5};
6use async_trait::async_trait;
7
8/// Case 3: Album tracks discovery (album specified, track not specified)
9///
10/// This discovers all tracks in a specific album by iterating through the album's tracks
11/// and for each track, loading its scrobbles incrementally. This is now truly incremental
12/// like the artist tracks discovery.
13pub struct AlbumTracksDiscovery {
14    client: LastFmEditClientImpl,
15    edit: ScrobbleEdit,
16    album_name: String,
17    tracks_iterator: crate::AlbumTracksIterator,
18    current_track_results: Vec<ExactScrobbleEdit>,
19    current_track_index: usize,
20}
21
22impl AlbumTracksDiscovery {
23    pub fn new(client: LastFmEditClientImpl, edit: ScrobbleEdit, album_name: String) -> Self {
24        let tracks_iterator = crate::AlbumTracksIterator::new(
25            client.clone(),
26            album_name.clone(),
27            edit.artist_name_original.clone(),
28        );
29
30        Self {
31            client,
32            edit,
33            album_name,
34            tracks_iterator,
35            current_track_results: Vec::new(),
36            current_track_index: 0,
37        }
38    }
39}
40
41#[async_trait(?Send)]
42impl AsyncDiscoveryIterator<ExactScrobbleEdit> for AlbumTracksDiscovery {
43    async fn next(&mut self) -> Result<Option<ExactScrobbleEdit>> {
44        // If we have results from the current track, return the next one
45        if self.current_track_index < self.current_track_results.len() {
46            let result = self.current_track_results[self.current_track_index].clone();
47            self.current_track_index += 1;
48            return Ok(Some(result));
49        }
50
51        // Get the next track from the iterator
52        while let Some(track) = self.tracks_iterator.next().await? {
53            log::debug!(
54                "Getting scrobble data for track '{}' from album '{}' by '{}'",
55                track.name,
56                self.album_name,
57                self.edit.artist_name_original
58            );
59
60            // Get scrobble data for this track
61            match self
62                .client
63                .load_edit_form_values_internal(&track.name, &self.edit.artist_name_original)
64                .await
65            {
66                Ok(track_scrobbles) => {
67                    // Apply user's changes and filtering
68                    let mut modified_edits = Vec::new();
69                    for scrobble in track_scrobbles {
70                        let mut modified_edit = scrobble.clone();
71                        if let Some(new_track_name) = &self.edit.track_name {
72                            modified_edit.track_name = new_track_name.clone();
73                        }
74                        if let Some(new_album_name) = &self.edit.album_name {
75                            modified_edit.album_name = new_album_name.clone();
76                        }
77                        modified_edit.artist_name = self.edit.artist_name.clone();
78                        if let Some(new_album_artist_name) = &self.edit.album_artist_name {
79                            modified_edit.album_artist_name = new_album_artist_name.clone();
80                        }
81                        modified_edit.edit_all = self.edit.edit_all;
82                        modified_edits.push(modified_edit);
83                    }
84
85                    let filtered_edits =
86                        filter_by_original_album_artist(modified_edits, &self.edit);
87
88                    if !filtered_edits.is_empty() {
89                        // Store results and return the first one
90                        self.current_track_results = filtered_edits;
91                        self.current_track_index = 1; // We'll return the first result below
92                        return Ok(Some(self.current_track_results[0].clone()));
93                    }
94                }
95                Err(e) => {
96                    log::debug!(
97                        "Failed to get scrobble data for track '{}': {}",
98                        track.name,
99                        e
100                    );
101                    // Continue with next track
102                }
103            }
104        }
105
106        // No more tracks
107        Ok(None)
108    }
109}