Skip to content

Commit

Permalink
first commit, rewrite in c++
Browse files Browse the repository at this point in the history
  • Loading branch information
Henrik Friedrichsen committed Aug 25, 2008
1 parent b6a1c0b commit dfbc2bb
Show file tree
Hide file tree
Showing 13 changed files with 1,060 additions and 0 deletions.
18 changes: 18 additions & 0 deletions Makefile
@@ -0,0 +1,18 @@
CXX ?= g++
OBJ = main.o md5.o utils.o mpd.o audioscrobbler.o cache.o
OUT = mpdas

CFLAGS = `pkg-config --cflags libmpd libcurl` -s -O2 -pipe -g
LDFLAGS = `pkg-config --libs libmpd libcurl` -g

all: $(OUT)

%.o: %.cpp
$(CXX) -c -o $@ $<

$(OUT): $(OBJ)
$(CXX) $(LDFLAGS) $(OBJ) -o $(OUT)

clean:
rm -rf $(OBJ) $(OUT)

122 changes: 122 additions & 0 deletions audioscrobbler.cpp
@@ -0,0 +1,122 @@
#include "mpdas.h"

#define HOST "http://post.audioscrobbler.com"
#define VERSION "1.2.1"
#define CLIENT "mp5"
#define CVERSION "0.1"

CAudioScrobbler* AudioScrobbler = 0;

#define CLEANUP() _response.clear()

size_t
writecb(void* ptr, size_t size, size_t nmemb, void *stream)
{
AudioScrobbler->ReportResponse((char*)ptr, size*nmemb);
}

CAudioScrobbler::CAudioScrobbler()
{
_response = "";
_handle = curl_easy_init();
if(!_handle) {
eprintf("%s", "Could not initialize CURL.");
exit(EXIT_FAILURE);
}
}

void
CAudioScrobbler::OpenURL(std::string url, const char* postfields = 0, char* errbuf = 0)
{
curl_easy_setopt(_handle, CURLOPT_DNS_CACHE_TIMEOUT, 0);
curl_easy_setopt(_handle, CURLOPT_NOPROGRESS, 1);
curl_easy_setopt(_handle, CURLOPT_WRITEFUNCTION, writecb);

if(postfields) {
curl_easy_setopt(_handle, CURLOPT_POST, 1);
curl_easy_setopt(_handle, CURLOPT_POSTFIELDS, postfields);
}
else
curl_easy_setopt(_handle, CURLOPT_POST, 0);
if(errbuf)
curl_easy_setopt(_handle, CURLOPT_ERRORBUFFER, errbuf);

curl_easy_setopt(_handle, CURLOPT_URL, url.c_str());
curl_easy_perform(_handle);
}


void
CAudioScrobbler::ReportResponse(char* buf, size_t size)
{
_response.append(buf);
}

bool
CAudioScrobbler::Scrobble(centry_t* entry)
{
bool retval = false;
std::ostringstream post;
iprintf("Scrobbling: %s - %s", entry->artist, entry->title);
post << "s=" << _sessionid << "&a[0]=" << entry->artist << "&t[0]=" << entry->title << "&i[0]=" << entry->starttime << "&o[0]=P&r[0]=&l[0]=" << entry->time << "&b[0]=";
if(entry->album)
post << entry->album;
post << "&n[0]=&m[0]=";

OpenURL(_scroburl, post.str().c_str());
if(_response.find("OK") == 0)
retval = true;
CLEANUP();
return retval;
}

bool
CAudioScrobbler::SendNowPlaying(mpd_Song* song)
{
bool retval = false;
if(!song->artist || !song->title) return retval;
char* artist = curl_easy_escape(_handle, song->artist, 0);
char* title = curl_easy_escape(_handle, song->title, 0);
char* album = 0;
if(song->album)
album = curl_easy_escape(_handle, song->album, 0);

std::ostringstream post;
post << "s=" << _sessionid << "&a=" << artist << "&t=" << title << "&b=";
if(album)
post << album;
post << "&l=" << song->time << "&n=&m=";

OpenURL(_npurl, post.str().c_str());

if(_response.find("OK") == 0)
retval = true;
CLEANUP();
return retval;
}

void
CAudioScrobbler::Handshake()
{
time_t timestamp = time(NULL);
_password = md5sum((char*)"%s", (char*)LPASSWORD);
std::string authtoken(md5sum((char*)"%s%i", _password.c_str(), timestamp));

std::ostringstream query;
query << HOST << "/?hs=true&p=" << VERSION << "&c=" << CLIENT << "&v=" << CVERSION << "&u=" << LUSER << "&t=" << timestamp << "&a=" << authtoken;

OpenURL(query.str());
if(_response.find("OK") == 0) {
iprintf("%s", "AudioScrobbler handshake successful.");
_sessionid = _response.substr(3, 32);
int found = _response.find("\n", 36);
_npurl = _response.substr(36, found-36);
_scroburl = _response.substr(found+1, _response.length()-found-2);
}
else if(_response.find("BADAUTH") == 0) {
eprintf("%s", "Bad username/password.");
exit(EXIT_FAILURE);
}

CLEANUP();
}
27 changes: 27 additions & 0 deletions audioscrobbler.h
@@ -0,0 +1,27 @@
#ifndef _AUDIOSCROBBLER_H
#define _AUDIOSCROBBLER_H

class CAudioScrobbler
{
public:
CAudioScrobbler();
void Handshake();
bool Scrobble(centry_t* entry);
void ReportResponse(char* buf, size_t size);
bool SendNowPlaying(mpd_Song* song);
private:
void OpenURL(std::string url, const char* postfields, char* errbuf);

CURL* _handle;

std::string _password;
std::string _response;

std::string _sessionid;
std::string _npurl;
std::string _scroburl;
};

extern CAudioScrobbler* AudioScrobbler;

#endif
124 changes: 124 additions & 0 deletions cache.cpp
@@ -0,0 +1,124 @@
#include "mpdas.h"

CCache* Cache = 0;

void
CCache::SaveCache()
{
std::string path = getenv("HOME");
path.append("/.mpdascache");
remove(path.c_str());
std::ofstream ofs(path.c_str());
if(!_entries.size()) {
remove(path.c_str());
return;
}

for(unsigned int i = 0; i < _entries.size(); i++) {
centry_t* entry = _entries[i];
if(entry->album)
ofs << true << "\n";
ofs << entry->artist << "\n";
ofs << entry->title << "\n";
ofs << entry->time << "\n";
ofs << entry->starttime;
if(entry->album)
ofs << "\n" << entry->album;
if(i+1 == _entries.size())
ofs.flush();
else
ofs << std::endl;
}
ofs.close();
}

void
CCache::LoadCache()
{
int length;
std::string path = getenv("HOME");
path.append("/.mpdascache");
std::ifstream ifs(path.c_str(), std::ios::in|std::ios::binary);

ifs.seekg (0, std::ios::end);
length = ifs.tellg();
ifs.seekg (0, std::ios::beg);


while(ifs.good()) {
if(length == ifs.tellg())
break;
std::string artist, album, title;
bool gotalbum = false;
int time;
time_t starttime;

ifs >> gotalbum;
ifs.ignore(1);
ifs >> artist;
ifs >> title;
ifs >> time;
ifs.ignore(1);
ifs >> starttime;
ifs.ignore(1);
if(gotalbum)
getline(ifs, album);
AddToCache(time, artist, title, album, starttime, true);
}

ifs.close();
remove(path.c_str());
}

void
CCache::WorkCache()
{
if(_failtime && time(NULL) - _failtime < 300) {
return;
}
_failtime = 0;
while(_entries.size()) {
if(AudioScrobbler->Scrobble(_entries.front())) {
curl_free((void*)_entries.front()->artist);
curl_free((void*)_entries.front()->title);
if(_entries.front()->album)
curl_free((void*)_entries.front()->album);
delete _entries.front();
_entries.erase(_entries.begin());
}
else {
eprintf("%s", "Error scrobbling. Trying again in 5 minutes.");
_failtime = time(NULL);
break;
}
sleep(1);
}
SaveCache();
}

void
CCache::AddToCache(int time, std::string artist, std::string title, std::string album, time_t starttime, bool escaped = false)
{
centry_t* entry = new centry_t;
bzero(entry, sizeof(centry_t));

entry->time = time;
if(!escaped) {
entry->artist = (char*)curl_escape(artist.c_str(), 0);
entry->title = (char*)curl_escape(title.c_str(), 0);
if(album.size())
entry->album = (char*)curl_escape(album.c_str(), 0);
} else {
entry->artist = new char[artist.size()+1];
strcpy(entry->artist, artist.c_str());
entry->title = new char[title.size()+1];
strcpy(entry->title, title.c_str());
if(album.size()) {
entry->album = new char[album.size()+1];
strcpy(entry->album, album.c_str());
}
}
entry->starttime = starttime;
_entries.push_back(entry);
SaveCache();
}
29 changes: 29 additions & 0 deletions cache.h
@@ -0,0 +1,29 @@
#ifndef _CACHE_H
#define _CACHE_H

typedef struct
centry_s
{
time_t starttime;

char* artist;
char* title;
char* album;
int time;
} centry_t;

class CCache
{
public:
void AddToCache(int time, std::string artist, std::string title, std::string album, time_t starttime, bool escaped);
void WorkCache();
void SaveCache();
void LoadCache();
private:
time_t _failtime;
std::vector<centry_s*> _entries;
};

extern CCache* Cache;

#endif
35 changes: 35 additions & 0 deletions main.cpp
@@ -0,0 +1,35 @@
#include "mpdas.h"

void
onclose()
{
iprintf("%s", "Closing mpdas.");
delete MPD;
delete AudioScrobbler;
delete Cache;
}

int
main(int argc, char* argv[])
{
atexit(onclose);

MPD = new CMPD();
if(!MPD->isConnected()) {
delete MPD;
return EXIT_FAILURE;
}

AudioScrobbler = new CAudioScrobbler();
AudioScrobbler->Handshake();
Cache = new CCache();
Cache->LoadCache();

scan:
MPD->Update();
Cache->WorkCache();
usleep(500000);
goto scan;

return EXIT_SUCCESS;
}

0 comments on commit dfbc2bb

Please sign in to comment.