Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Batch scrobbling and Scrobbler classes #63

Merged
merged 24 commits into from
Apr 25, 2015
Merged

Batch scrobbling and Scrobbler classes #63

merged 24 commits into from
Apr 25, 2015

Conversation

rikkit
Copy link
Member

@rikkit rikkit commented Apr 5, 2015

Tracking issue for the new auto-caching + batching scrobbler

TODO

  • Base Scrobbler classes
  • SQLite Scrobbler class
  • Tests
  • Support multiple scrobbles on ScrobbleCommand
  • Batch up scrobbles in ScrobblerBase
  • Test batching and caching in unit tests
  • Remove extra nugets from SQLite lib
  • SQLite nuget package

@rikkit rikkit added the sdk label Apr 5, 2015
@rikkit rikkit self-assigned this Apr 5, 2015
@rikkit rikkit added this to the v0.2.0 milestone Apr 5, 2015
@rikkit
Copy link
Member Author

rikkit commented Apr 7, 2015

@Zumicts yo. Do you think this pattern makes sense for you? The intention is that this feature (batching + caching) can be integrated in pretty much one line of code. The core includes a basic non-caching scrobbler (Scrobbler.cs), then scrobblers that use database packages can be distributed separately (the SQLiteScrobbler class in this PR will be distributed in Inflatable.Lastfm.SQLite for example).

Theoretical code:

_scrobbler = new SQLiteScrobbler(auth, databaseFile);
var response = _scrobbler.ScrobbleAsync(track.AsScrobble(DateTimeOffset.Now));

// if _scrobbler.CacheEnabled == true then response.Status will be LastResponseStatus.Cached or LastResponseStatus.CacheFailed, otherwise will be the status of the internal scrobble HTTP request

Related issue; I notice in Audiotica you have wrapped calls to the SDK in extra service classes, I'd like for that to be unnecessary if possible to reduce lines of code. So you'd DI ITrackApi directly. Would like your thoughts on that too.

@haroldma-zz
Copy link
Contributor

I think it makes sense, follows the SOLID pattern and in the future it makes it easier to implement other cache scrobblers. All though maybe we can break the cache functionality away from the Scrobbler class and just have something like Scrobbler.SetCache(IScrobblerCache:cache) or pass it in the constructor (more DI friendly).

Yeah, in Audiotica all the related class I use for last.fm are injected.

@rikkit
Copy link
Member Author

rikkit commented Apr 7, 2015

Good to hear. Feature should be in master in the next couple days. 👍

Yeah, in Audiotica all the related class I use for last.fm are injected.

I was talking about IScrobblerService specifically. Having a single class to DI is definitely better than seven. Does this class do anything special? If not we can make something like it in Core.

@haroldma-zz
Copy link
Contributor

Oh, yeah it is definitely better to have one class that interacts with the api. The class does not implement anything special. Actually, I think it is using my credential helper class for storing username and password but that can easily be abstracted.

EDIT:

These rely on the cred helper and should be remove if you plan on distributing it:

 event EventHandler<BoolEventArgs> AuthStateChanged;
 bool HasCredentials { get; }
 bool IsAuthenticated { get; }
 void Logout();

@rikkit
Copy link
Member Author

rikkit commented Apr 7, 2015

So I'm thinking of abusing properties a bit and making a super-api class which composes the existing ones.

So the README example

var auth = new LastAuth("apikey", "apisecret");

// wait for authentication
var response = await auth.GetSessionTokenAsync("username", "pass");

if (response.Success && auth.HasAuthenticated) {
    var trackApi = new TrackApi(auth);
    var loved = await trackApi.LoveTrackAsync("CIRCLONT6A [141.98][Syrobonkus mix]", "Aphex Twin");
}

becomes

var lastApi = new SuperLastApi(apikey, apisecret, httpclient, scrobbler);

var response = lastApi.Auth.GetSessionTokenAsync();
if (response.Success && lastApi.Auth.HasAuthenticated)
{
var loved = lastApi.Track.LoveAsync("CIRCLONT6A [141.98][Syrobonkus mix]", "Aphex Twin");
}

This'd be flexible to both approaches whilst keeping the codebase tidy. It mirrors the api method names nicely too.

@haroldma-zz
Copy link
Contributor

That seems like a nice way of doing it. Would you use DI, or do some lazy loading like:

public Auth Auth
{
      get
      {
          return _auth ?? (_auth = new Auth());
      }
}    

@rikkit
Copy link
Member Author

rikkit commented Apr 9, 2015

I don't think it's worth implementing lazy loading since the Super API class will only ever hold around 10 object references. The API classes themselves don't store any references, apart from LastAuth (strings and UserSession), and HttpClient if that is provided.

public SuperLastApi(string key, string secret, HttpClient httpClient = null, IScrobbler scrobbler = null)
{
    var client = httpClient ?? new HttpClient(); // TODO copy options from command base

    Auth = new LastAuth(key, secret, client);
    Scrobbler = scrobbler ?? new Scrobbler(client);
    Track = new TrackApi(client);
    Album = new AlbumApi(client);
    Artist = new ArtistApi(client);
    Library = new LibraryApi(client);
    // etc.
}

@rikkit
Copy link
Member Author

rikkit commented Apr 9, 2015

All though maybe we can break the cache functionality away from the Scrobbler class and just have something like Scrobbler.SetCache(IScrobblerCache:cache) or pass it in the constructor (more DI friendly).

Didn't see this edit earlier.

This depends on whether there are other kinds of things we can do with a database in the SDK; ILastDbProvider and LastSQLiteDbProvider will definitely be the way to go if so; right now though all I can think of is error logging, which I don't want to implement in this release (0.2 is already way overdue lol). I think the current derived class approach is friendly enough to both DI and non-DI apps for now and can always revisit the issue in the future.

@haroldma-zz
Copy link
Contributor

I guess you're right but should it implement IDisposable because of the HttpClient (don't want a memory leak)?

Ok, let's keep what we have and improve as we go, after all this is just 0.2 and we have a long way to 1.0 😆

@haroldma-zz
Copy link
Contributor

Now that I think of it, all commands should be disposable since each one is using an HttpClient but in the SuperLastApi we will only disposed of the client that was passed to each command.

@rikkit
Copy link
Member Author

rikkit commented Apr 9, 2015

I guess you're right but should it implement IDisposable because of the HttpClient (don't want a memory leak)?

Yuuuuuuup. >_>

Now that I think of it, all commands should be disposable since each one is using an HttpClient but in the SuperLastApi we will only disposed of the client that was passed to each command.

#41 covers this, commands created inside a *Api class will use the same HttpClient. *Api classes should be disposable though. I'll do that as part of this PR

@rikkit rikkit mentioned this pull request Apr 10, 2015
… marking ITrackApi.ScrobbleAsync as obsolete
Adjusted generate method signature to sort by ASCII order
rikkit added a commit that referenced this pull request Apr 25, 2015
Batching and caching scrobbler classes.
@rikkit rikkit merged commit 9a698cc into master Apr 25, 2015
@rikkit rikkit deleted the scrobbler branch April 26, 2015 17:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants