Struct LastFmEditClientImpl

Source
pub struct LastFmEditClientImpl { /* private fields */ }
Expand description

Main implementation for interacting with Last.fm’s web interface.

This implementation handles authentication, session management, and provides methods for browsing user libraries and editing scrobble data through web scraping.

§Examples

use lastfm_edit::{LastFmEditClient, LastFmEditClientImpl, Result};

#[tokio::main]
async fn main() -> Result<()> {
    // Create client with any HTTP implementation
    let http_client = http_client::native::NativeClient::new();
    let mut client = LastFmEditClientImpl::new(Box::new(http_client));

    // Login to Last.fm
    client.login("username", "password").await?;

    // Check if authenticated
    assert!(client.is_logged_in());

    Ok(())
}

Implementations§

Source§

impl LastFmEditClientImpl

Source

pub fn new(client: Box<dyn HttpClient + Send + Sync>) -> Self

Create a new LastFmEditClient with the default Last.fm URL.

Note: This creates an unauthenticated client. You must call login or restore_session before using most functionality.

§Arguments
  • client - Any HTTP client implementation that implements HttpClient
§Examples
use lastfm_edit::{LastFmEditClient, LastFmEditClientImpl, Result};

#[tokio::main]
async fn main() -> Result<()> {
    let http_client = http_client::native::NativeClient::new();
    let mut client = LastFmEditClientImpl::new(Box::new(http_client));
    client.login("username", "password").await?;
    Ok(())
}
Source

pub fn with_base_url( client: Box<dyn HttpClient + Send + Sync>, base_url: String, ) -> Self

Create a new LastFmEditClient with a custom base URL.

Note: This creates an unauthenticated client. You must call login or restore_session before using most functionality.

This is useful for testing or if Last.fm changes their domain.

§Arguments
  • client - Any HTTP client implementation
  • base_url - The base URL for Last.fm (e.g., https://www.last.fm)
Source

pub fn with_rate_limit_patterns( client: Box<dyn HttpClient + Send + Sync>, base_url: String, rate_limit_patterns: Vec<String>, ) -> Self

Create a new LastFmEditClient with custom rate limit detection patterns.

§Arguments
  • client - Any HTTP client implementation
  • base_url - The base URL for Last.fm
  • rate_limit_patterns - Text patterns that indicate rate limiting in responses
Source

pub async fn login_with_credentials( client: Box<dyn HttpClient + Send + Sync>, username: &str, password: &str, ) -> Result<Self>

Create a new authenticated LastFmEditClient by logging in with username and password.

This is a convenience method that combines client creation and login into one step.

§Arguments
  • client - Any HTTP client implementation
  • username - Last.fm username or email
  • password - Last.fm password
§Returns

Returns an authenticated client on success, or LastFmError::Auth on failure.

§Examples
use lastfm_edit::{LastFmEditClient, LastFmEditClientImpl, Result};

#[tokio::main]
async fn main() -> Result<()> {
    let client = LastFmEditClientImpl::login_with_credentials(
        Box::new(http_client::native::NativeClient::new()),
        "username",
        "password"
    ).await?;
    assert!(client.is_logged_in());
    Ok(())
}
Source

pub fn from_session( client: Box<dyn HttpClient + Send + Sync>, session: LastFmEditSession, ) -> Self

Create a new LastFmEditClient by restoring a previously saved session.

This allows you to resume a Last.fm session without requiring the user to log in again.

§Arguments
  • client - Any HTTP client implementation
  • session - Previously saved LastFmEditSession
§Returns

Returns a client with the restored session.

§Examples
use lastfm_edit::{LastFmEditClient, LastFmEditClientImpl, LastFmEditSession};

fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
    // Assume we have a saved session
    let session_json = std::fs::read_to_string("session.json")?;
    let session = LastFmEditSession::from_json(&session_json)?;

    let client = LastFmEditClientImpl::from_session(
        Box::new(http_client::native::NativeClient::new()),
        session
    );
    assert!(client.is_logged_in());
    Ok(())
}
Source

pub fn get_session(&self) -> LastFmEditSession

Extract the current session state for persistence.

This allows you to save the authentication state and restore it later without requiring the user to log in again.

§Returns

Returns a LastFmEditSession that can be serialized and saved.

§Examples
use lastfm_edit::{LastFmEditClient, LastFmEditClientImpl, Result};

#[tokio::main]
async fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
    let mut client = LastFmEditClientImpl::new(Box::new(http_client::native::NativeClient::new()));
    client.login("username", "password").await?;

    // Save session for later use
    let session = client.get_session();
    let session_json = session.to_json()?;
    std::fs::write("session.json", session_json)?;
    Ok(())
}
Source

pub fn restore_session(&self, session: LastFmEditSession)

Restore session state from a previously saved LastFmEditSession.

This allows you to restore authentication state without logging in again.

§Arguments
  • session - Previously saved session state
§Examples
use lastfm_edit::{LastFmEditClient, LastFmEditClientImpl, LastFmEditSession};

fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
    let mut client = LastFmEditClientImpl::new(Box::new(http_client::native::NativeClient::new()));

    // Restore from saved session
    let session_json = std::fs::read_to_string("session.json")?;
    let session = LastFmEditSession::from_json(&session_json)?;
    client.restore_session(session);

    assert!(client.is_logged_in());
    Ok(())
}
Source

pub async fn login(&self, username: &str, password: &str) -> Result<()>

Authenticate with Last.fm using username and password.

This method:

  1. Fetches the login page to extract CSRF tokens
  2. Submits the login form with credentials
  3. Validates the authentication by checking for session cookies
  4. Stores session data for subsequent requests
§Arguments
  • username - Last.fm username or email
  • password - Last.fm password
§Returns

Returns [Ok(())] on successful authentication, or LastFmError::Auth on failure.

§Examples
let mut client = LastFmEditClientImpl::new(Box::new(http_client::native::NativeClient::new()));
client.login("username", "password").await?;
assert!(client.is_logged_in());
Source

pub fn username(&self) -> String

Get the currently authenticated username.

Returns an empty string if not logged in.

Source

pub fn is_logged_in(&self) -> bool

Check if the client is currently authenticated.

Returns true if login was successful and session is active.

Source

pub async fn get_recent_scrobbles(&self, page: u32) -> Result<Vec<Track>>

Fetch recent scrobbles from the user’s listening history This gives us real scrobble data with timestamps for editing

Source

pub async fn find_recent_scrobble_for_track( &self, track_name: &str, artist_name: &str, max_pages: u32, ) -> Result<Option<Track>>

Find the most recent scrobble for a specific track This searches through recent listening history to find real scrobble data

Source

pub async fn edit_scrobble(&self, edit: &ScrobbleEdit) -> Result<EditResponse>

Source

pub async fn find_scrobble_by_timestamp(&self, timestamp: u64) -> Result<Track>

Find a scrobble by its timestamp in recent scrobbles

Source

pub async fn edit_scrobble_with_retry( &self, edit: &ScrobbleEdit, max_retries: u32, ) -> Result<EditResponse>

Source

pub async fn load_edit_form_values( &self, track_name: &str, artist_name: &str, ) -> Result<ScrobbleEdit>

Load prepopulated form values for editing a specific track This extracts scrobble data directly from the track page forms

Source

pub async fn get_album_tracks( &self, album_name: &str, artist_name: &str, ) -> Result<Vec<Track>>

Get tracks from a specific album page This makes a single request to the album page and extracts track data

Source

pub async fn edit_album( &self, old_album_name: &str, new_album_name: &str, artist_name: &str, ) -> Result<EditResponse>

Edit album metadata by updating scrobbles with new album name This edits ALL tracks from the album that are found in recent scrobbles

Source

pub async fn edit_artist( &self, old_artist_name: &str, new_artist_name: &str, ) -> Result<EditResponse>

Edit artist metadata by updating scrobbles with new artist name This edits ALL tracks from the artist that are found in recent scrobbles

Source

pub async fn edit_artist_for_track( &self, track_name: &str, old_artist_name: &str, new_artist_name: &str, ) -> Result<EditResponse>

Edit artist metadata for a specific track only This edits only the specified track if found in recent scrobbles

Source

pub async fn edit_artist_for_album( &self, album_name: &str, old_artist_name: &str, new_artist_name: &str, ) -> Result<EditResponse>

Edit artist metadata for all tracks in a specific album This edits ALL tracks from the specified album that are found in recent scrobbles

Source

pub async fn get_artist_tracks_page( &self, artist: &str, page: u32, ) -> Result<TrackPage>

Source

pub fn extract_tracks_from_document( &self, document: &Html, artist: &str, album: Option<&str>, ) -> Result<Vec<Track>>

Extract tracks from HTML document (delegates to parser)

Source

pub fn parse_tracks_page( &self, document: &Html, page_number: u32, artist: &str, album: Option<&str>, ) -> Result<TrackPage>

Parse tracks page (delegates to parser)

Source

pub fn parse_recent_scrobbles(&self, document: &Html) -> Result<Vec<Track>>

Parse recent scrobbles from HTML document (for testing)

Source

pub async fn get(&self, url: &str) -> Result<Response>

Make an HTTP GET request with authentication and retry logic

Source

pub async fn get_artist_albums_page( &self, artist: &str, page: u32, ) -> Result<AlbumPage>

Source§

impl LastFmEditClientImpl

Source

pub fn artist_tracks(&self, artist: &str) -> ArtistTracksIterator

Create an iterator for browsing an artist’s tracks from the user’s library.

Source

pub fn artist_albums(&self, artist: &str) -> ArtistAlbumsIterator

Create an iterator for browsing an artist’s albums from the user’s library.

Source

pub fn recent_tracks(&self) -> RecentTracksIterator

Create an iterator for browsing the user’s recent tracks/scrobbles.

Source

pub fn recent_tracks_from_page( &self, starting_page: u32, ) -> RecentTracksIterator

Create an iterator for browsing the user’s recent tracks starting from a specific page.

Trait Implementations§

Source§

impl Clone for LastFmEditClientImpl

Source§

fn clone(&self) -> LastFmEditClientImpl

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl LastFmEditClient for LastFmEditClientImpl

Source§

fn login<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, username: &'life1 str, password: &'life2 str, ) -> Pin<Box<dyn Future<Output = Result<()>> + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Authenticate with Last.fm using username and password.
Source§

fn username(&self) -> String

Get the currently authenticated username.
Source§

fn is_logged_in(&self) -> bool

Check if the client is currently authenticated.
Source§

fn get_recent_scrobbles<'life0, 'async_trait>( &'life0 self, page: u32, ) -> Pin<Box<dyn Future<Output = Result<Vec<Track>>> + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Fetch recent scrobbles from the user’s listening history.
Source§

fn find_scrobble_by_timestamp<'life0, 'async_trait>( &'life0 self, timestamp: u64, ) -> Pin<Box<dyn Future<Output = Result<Track>> + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Find a scrobble by its timestamp in recent scrobbles.
Source§

fn find_recent_scrobble_for_track<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, track_name: &'life1 str, artist_name: &'life2 str, max_pages: u32, ) -> Pin<Box<dyn Future<Output = Result<Option<Track>>> + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Find the most recent scrobble for a specific track.
Source§

fn edit_scrobble<'life0, 'life1, 'async_trait>( &'life0 self, edit: &'life1 ScrobbleEdit, ) -> Pin<Box<dyn Future<Output = Result<EditResponse>> + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Edit a scrobble with the given edit parameters.
Source§

fn load_edit_form_values<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, track_name: &'life1 str, artist_name: &'life2 str, ) -> Pin<Box<dyn Future<Output = Result<ScrobbleEdit>> + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Load prepopulated form values for editing a specific track.
Source§

fn get_album_tracks<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, album_name: &'life1 str, artist_name: &'life2 str, ) -> Pin<Box<dyn Future<Output = Result<Vec<Track>>> + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Get tracks from a specific album page.
Source§

fn edit_album<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, old_album_name: &'life1 str, new_album_name: &'life2 str, artist_name: &'life3 str, ) -> Pin<Box<dyn Future<Output = Result<EditResponse>> + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait,

Edit album metadata by updating scrobbles with new album name.
Source§

fn edit_artist<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, old_artist_name: &'life1 str, new_artist_name: &'life2 str, ) -> Pin<Box<dyn Future<Output = Result<EditResponse>> + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Edit artist metadata by updating scrobbles with new artist name.
Source§

fn edit_artist_for_track<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, track_name: &'life1 str, old_artist_name: &'life2 str, new_artist_name: &'life3 str, ) -> Pin<Box<dyn Future<Output = Result<EditResponse>> + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait,

Edit artist metadata for a specific track only.
Source§

fn edit_artist_for_album<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, album_name: &'life1 str, old_artist_name: &'life2 str, new_artist_name: &'life3 str, ) -> Pin<Box<dyn Future<Output = Result<EditResponse>> + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait,

Edit artist metadata for all tracks in a specific album.
Source§

fn get_artist_tracks_page<'life0, 'life1, 'async_trait>( &'life0 self, artist: &'life1 str, page: u32, ) -> Pin<Box<dyn Future<Output = Result<TrackPage>> + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Get a page of tracks for a specific artist.
Source§

fn get_artist_albums_page<'life0, 'life1, 'async_trait>( &'life0 self, artist: &'life1 str, page: u32, ) -> Pin<Box<dyn Future<Output = Result<AlbumPage>> + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Get a page of albums for a specific artist.
Source§

fn get_session(&self) -> LastFmEditSession

Extract the current session state for persistence.
Source§

fn restore_session(&self, session: LastFmEditSession)

Restore session state from a previously saved session.
Source§

fn artist_tracks(&self, artist: &str) -> ArtistTracksIterator

Create an iterator for browsing an artist’s tracks from the user’s library.
Source§

fn artist_albums(&self, artist: &str) -> ArtistAlbumsIterator

Create an iterator for browsing an artist’s albums from the user’s library.
Source§

fn recent_tracks(&self) -> RecentTracksIterator

Create an iterator for browsing the user’s recent tracks/scrobbles.
Source§

fn recent_tracks_from_page(&self, starting_page: u32) -> RecentTracksIterator

Create an iterator for browsing the user’s recent tracks starting from a specific page.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

impl<T> ErasedDestructor for T
where T: 'static,