mirror of
https://github.com/clangen/musikcube.git
synced 2025-01-30 06:32:36 +00:00
Updated HttpServer to allow querying audio data by either id or
external_id.
This commit is contained in:
parent
59946d3cea
commit
8b2752abe5
@ -141,6 +141,8 @@ namespace request {
|
||||
|
||||
namespace fragment {
|
||||
static const std::string audio = "audio";
|
||||
static const std::string id = "id";
|
||||
static const std::string external_id = "external_id";
|
||||
}
|
||||
|
||||
namespace broadcast {
|
||||
|
@ -100,6 +100,74 @@ static std::string contentType(const std::string& fn) {
|
||||
return "application/octet-stream";
|
||||
}
|
||||
|
||||
/* toHex, urlEncode, fromHex, urlDecode are stilen from here:
|
||||
http://dlib.net/dlib/server/server_http.cpp.html */
|
||||
static inline unsigned char toHex(unsigned char x) {
|
||||
return x + (x > 9 ? ('A' - 10) : '0');
|
||||
}
|
||||
|
||||
std::string urlEncode(const std::string& s) {
|
||||
std::ostringstream os;
|
||||
|
||||
for (std::string::const_iterator ci = s.begin(); ci != s.end(); ++ci) {
|
||||
if ((*ci >= 'a' && *ci <= 'z') ||
|
||||
(*ci >= 'A' && *ci <= 'Z') ||
|
||||
(*ci >= '0' && *ci <= '9'))
|
||||
{ // allowed
|
||||
os << *ci;
|
||||
}
|
||||
else if (*ci == ' ') {
|
||||
os << '+';
|
||||
}
|
||||
else {
|
||||
os << '%' << toHex(*ci >> 4) << toHex(*ci % 16);
|
||||
}
|
||||
}
|
||||
|
||||
return os.str();
|
||||
}
|
||||
|
||||
inline unsigned char from_hex(unsigned char ch) {
|
||||
if (ch <= '9' && ch >= '0') {
|
||||
ch -= '0';
|
||||
}
|
||||
else if (ch <= 'f' && ch >= 'a') {
|
||||
ch -= 'a' - 10;
|
||||
}
|
||||
else if (ch <= 'F' && ch >= 'A') {
|
||||
ch -= 'A' - 10;
|
||||
}
|
||||
else {
|
||||
ch = 0;
|
||||
}
|
||||
|
||||
return ch;
|
||||
}
|
||||
|
||||
std::string urlDecode(const std::string& str) {
|
||||
using namespace std;
|
||||
string result;
|
||||
string::size_type i;
|
||||
|
||||
for (i = 0; i < str.size(); ++i) {
|
||||
if (str[i] == '+') {
|
||||
result += ' ';
|
||||
}
|
||||
else if (str[i] == '%' && str.size() > i + 2) {
|
||||
const unsigned char ch1 = from_hex(str[i + 1]);
|
||||
const unsigned char ch2 = from_hex(str[i + 2]);
|
||||
const unsigned char ch = (ch1 << 4) | ch2;
|
||||
result += ch;
|
||||
i += 2;
|
||||
}
|
||||
else {
|
||||
result += str[i];
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static ssize_t fileReadCallback(void *cls, uint64_t pos, char *buf, size_t max) {
|
||||
Range* range = static_cast<Range*>(cls);
|
||||
|
||||
@ -192,6 +260,9 @@ bool HttpServer::Start() {
|
||||
nullptr,
|
||||
&HttpServer::HandleRequest,
|
||||
this,
|
||||
MHD_OPTION_UNESCAPE_CALLBACK,
|
||||
&HttpServer::HandleUnescape,
|
||||
this,
|
||||
MHD_OPTION_END);
|
||||
|
||||
this->running = (httpServer != nullptr);
|
||||
@ -213,6 +284,12 @@ bool HttpServer::Stop() {
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t HttpServer::HandleUnescape(void * cls, struct MHD_Connection *c, char *s) {
|
||||
/* don't do anything. the default implementation will decode the
|
||||
entire path, which breaks if we have individually decoded segments. */
|
||||
return strlen(s);
|
||||
}
|
||||
|
||||
int HttpServer::HandleRequest(
|
||||
void *cls,
|
||||
struct MHD_Connection *connection,
|
||||
@ -237,9 +314,18 @@ int HttpServer::HandleRequest(
|
||||
std::vector<std::string> parts;
|
||||
boost::split(parts, urlStr, boost::is_any_of("/"));
|
||||
if (parts.size() > 0) {
|
||||
if (parts.at(0) == fragment::audio && parts.size() == 2) {
|
||||
unsigned long long id = std::stoull(parts.at(1));
|
||||
IRetainedTrack* track = server->context.dataProvider->QueryTrack(id);
|
||||
if (parts.at(0) == fragment::audio && parts.size() == 3) {
|
||||
IRetainedTrack* track = nullptr;
|
||||
|
||||
if (parts.at(1) == fragment::id) {
|
||||
unsigned long long id = std::stoull(urlDecode(parts.at(2)));
|
||||
track = server->context.dataProvider->QueryTrackById(id);
|
||||
}
|
||||
else if (parts.at(1) == fragment::external_id) {
|
||||
std::string externalId = urlDecode(parts.at(2));
|
||||
track = server->context.dataProvider->QueryTrackByExternalId(externalId.c_str());
|
||||
}
|
||||
|
||||
if (track) {
|
||||
std::string filename = GetMetadataString(track, key::filename);
|
||||
track->Release();
|
||||
@ -267,6 +353,7 @@ int HttpServer::HandleRequest(
|
||||
fclose(file);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -59,6 +59,11 @@ class HttpServer {
|
||||
size_t *upload_data_size,
|
||||
void **con_cls);
|
||||
|
||||
static size_t HandleUnescape(
|
||||
void * cls,
|
||||
struct MHD_Connection *c,
|
||||
char *s);
|
||||
|
||||
struct MHD_Daemon *httpServer;
|
||||
Context& context;
|
||||
volatile bool running;
|
||||
|
@ -87,7 +87,7 @@ ITrackList* LocalSimpleDataProvider::QueryTracks(const char* query, int limit, i
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
IRetainedTrack* LocalSimpleDataProvider::QueryTrack(unsigned long long trackId) {
|
||||
IRetainedTrack* LocalSimpleDataProvider::QueryTrackById(unsigned long long trackId) {
|
||||
try {
|
||||
TrackPtr target(new LibraryTrack(trackId, this->library));
|
||||
|
||||
@ -101,7 +101,30 @@ IRetainedTrack* LocalSimpleDataProvider::QueryTrack(unsigned long long trackId)
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
musik::debug::err(TAG, "QueryTrack failed");
|
||||
musik::debug::err(TAG, "QueryTrackById failed");
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
IRetainedTrack* LocalSimpleDataProvider::QueryTrackByExternalId(const char* externalId) {
|
||||
if (strlen(externalId)) {
|
||||
try {
|
||||
TrackPtr target(new LibraryTrack(0, this->library));
|
||||
target->SetValue("external_id", externalId);
|
||||
|
||||
std::shared_ptr<TrackMetadataQuery> search(
|
||||
new TrackMetadataQuery(target, this->library));
|
||||
|
||||
this->library->Enqueue(search, ILibrary::QuerySynchronous);
|
||||
|
||||
if (search->GetStatus() == IQuery::Finished) {
|
||||
return new RetainedTrack(target);
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
musik::debug::err(TAG, "QueryTrackByExternalId failed");
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
|
@ -51,7 +51,9 @@ namespace musik { namespace core { namespace db { namespace local {
|
||||
int limit = -1,
|
||||
int offset = 0);
|
||||
|
||||
virtual musik::core::sdk::IRetainedTrack* QueryTrack(unsigned long long trackId);
|
||||
virtual musik::core::sdk::IRetainedTrack* QueryTrackById(unsigned long long trackId);
|
||||
|
||||
virtual musik::core::sdk::IRetainedTrack* QueryTrackByExternalId(const char* externalId);
|
||||
|
||||
virtual musik::core::sdk::ITrackList*
|
||||
QueryTracksByCategory(
|
||||
|
@ -41,59 +41,68 @@ using namespace musik::core::db::local;
|
||||
using namespace musik::core;
|
||||
using namespace musik::core::library;
|
||||
|
||||
static const std::string ALL_METADATA_QUERY =
|
||||
"SELECT DISTINCT t.track, t.disc, t.bpm, t.duration, t.filesize, t.year, t.title, t.filename, t.thumbnail_id, al.name AS album, alar.name AS album_artist, gn.name AS genre, ar.name AS artist, t.filetime, t.visual_genre_id, t.visual_artist_id, t.album_artist_id, t.album_id, t.source_id, t.external_id "
|
||||
"FROM tracks t, albums al, artists alar, artists ar, genres gn "
|
||||
"WHERE t.id=? AND t.album_id=al.id AND t.album_artist_id=alar.id AND t.visual_genre_id=gn.id AND t.visual_artist_id=ar.id ";
|
||||
static const std::string COLUMNS = "t.track, t.disc, t.bpm, t.duration, t.filesize, t.year, t.title, t.filename, t.thumbnail_id, al.name AS album, alar.name AS album_artist, gn.name AS genre, ar.name AS artist, t.filetime, t.visual_genre_id, t.visual_artist_id, t.album_artist_id, t.album_id, t.source_id, t.external_id";
|
||||
static const std::string TABLES = "tracks t, albums al, artists alar, artists ar, genres gn";
|
||||
static const std::string PREDICATE = "t.album_id=al.id AND t.album_artist_id=alar.id AND t.visual_genre_id=gn.id AND t.visual_artist_id=ar.id";
|
||||
|
||||
static const std::string URI_ONLY_QUERY =
|
||||
"SELECT DISTINCT filename "
|
||||
"FROM tracks "
|
||||
"WHERE t.id=? ";
|
||||
static const std::string ALL_METADATA_QUERY_BY_ID =
|
||||
"SELECT DISTINCT " + COLUMNS + " " +
|
||||
"FROM " + TABLES + " " +
|
||||
"WHERE t.id=? AND " + PREDICATE;
|
||||
|
||||
TrackMetadataQuery::TrackMetadataQuery(TrackPtr target, ILibraryPtr library, Type type) {
|
||||
static const std::string ALL_METADATA_QUERY_BY_EXTERNAL_ID =
|
||||
"SELECT DISTINCT " + COLUMNS + " " +
|
||||
"FROM " + TABLES + " " +
|
||||
"WHERE t.external_id=? AND " + PREDICATE;
|
||||
|
||||
TrackMetadataQuery::TrackMetadataQuery(TrackPtr target, ILibraryPtr library) {
|
||||
this->result = target;
|
||||
this->library = library;
|
||||
this->type = type;
|
||||
}
|
||||
|
||||
bool TrackMetadataQuery::OnRun(Connection& db) {
|
||||
if (this->type == Type::AllMetadata) {
|
||||
Statement trackQuery(ALL_METADATA_QUERY.c_str(), db);
|
||||
trackQuery.BindInt(0, (uint64) this->result->GetId());
|
||||
bool queryById = this->result->GetId() != 0;
|
||||
|
||||
if (trackQuery.Step() == Row) {
|
||||
result->SetValue(constants::Track::TRACK_NUM, trackQuery.ColumnText(0));
|
||||
result->SetValue(constants::Track::DISC_NUM, trackQuery.ColumnText(1));
|
||||
result->SetValue(constants::Track::BPM, trackQuery.ColumnText(2));
|
||||
result->SetValue(constants::Track::DURATION, trackQuery.ColumnText(3));
|
||||
result->SetValue(constants::Track::FILESIZE, trackQuery.ColumnText(4));
|
||||
result->SetValue(constants::Track::YEAR, trackQuery.ColumnText(5));
|
||||
result->SetValue(constants::Track::TITLE, trackQuery.ColumnText(6));
|
||||
result->SetValue(constants::Track::FILENAME, trackQuery.ColumnText(7));
|
||||
result->SetValue(constants::Track::THUMBNAIL_ID, trackQuery.ColumnText(8));
|
||||
result->SetValue(constants::Track::ALBUM, trackQuery.ColumnText(9));
|
||||
result->SetValue(constants::Track::ALBUM_ARTIST, trackQuery.ColumnText(10));
|
||||
result->SetValue(constants::Track::GENRE, trackQuery.ColumnText(11));
|
||||
result->SetValue(constants::Track::ARTIST, trackQuery.ColumnText(12));
|
||||
result->SetValue(constants::Track::FILETIME, trackQuery.ColumnText(13));
|
||||
result->SetValue(constants::Track::GENRE_ID, trackQuery.ColumnText(14));
|
||||
result->SetValue(constants::Track::ARTIST_ID, trackQuery.ColumnText(15));
|
||||
result->SetValue(constants::Track::ALBUM_ARTIST_ID, trackQuery.ColumnText(16));
|
||||
result->SetValue(constants::Track::ALBUM_ID, trackQuery.ColumnText(17));
|
||||
result->SetValue(constants::Track::SOURCE_ID, trackQuery.ColumnText(18));
|
||||
result->SetValue(constants::Track::EXTERNAL_ID, trackQuery.ColumnText(19));
|
||||
return true;
|
||||
}
|
||||
const std::string& query = queryById
|
||||
? ALL_METADATA_QUERY_BY_ID
|
||||
: ALL_METADATA_QUERY_BY_EXTERNAL_ID;
|
||||
|
||||
Statement trackQuery(query.c_str(), db);
|
||||
|
||||
if (queryById) {
|
||||
trackQuery.BindInt(0, (uint64) this->result->GetId());
|
||||
}
|
||||
else {
|
||||
Statement trackQuery(URI_ONLY_QUERY.c_str(), db);
|
||||
trackQuery.BindInt(0, (uint64) this->result->GetId());
|
||||
|
||||
if (trackQuery.Step() == Row) {
|
||||
result->SetValue(constants::Track::FILENAME, trackQuery.ColumnText(0));
|
||||
return true;
|
||||
const std::string& externalId = this->result->GetValue("external_id");
|
||||
if (!externalId.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
trackQuery.BindText(0, externalId);
|
||||
}
|
||||
|
||||
if (trackQuery.Step() == Row) {
|
||||
result->SetValue(constants::Track::TRACK_NUM, trackQuery.ColumnText(0));
|
||||
result->SetValue(constants::Track::DISC_NUM, trackQuery.ColumnText(1));
|
||||
result->SetValue(constants::Track::BPM, trackQuery.ColumnText(2));
|
||||
result->SetValue(constants::Track::DURATION, trackQuery.ColumnText(3));
|
||||
result->SetValue(constants::Track::FILESIZE, trackQuery.ColumnText(4));
|
||||
result->SetValue(constants::Track::YEAR, trackQuery.ColumnText(5));
|
||||
result->SetValue(constants::Track::TITLE, trackQuery.ColumnText(6));
|
||||
result->SetValue(constants::Track::FILENAME, trackQuery.ColumnText(7));
|
||||
result->SetValue(constants::Track::THUMBNAIL_ID, trackQuery.ColumnText(8));
|
||||
result->SetValue(constants::Track::ALBUM, trackQuery.ColumnText(9));
|
||||
result->SetValue(constants::Track::ALBUM_ARTIST, trackQuery.ColumnText(10));
|
||||
result->SetValue(constants::Track::GENRE, trackQuery.ColumnText(11));
|
||||
result->SetValue(constants::Track::ARTIST, trackQuery.ColumnText(12));
|
||||
result->SetValue(constants::Track::FILETIME, trackQuery.ColumnText(13));
|
||||
result->SetValue(constants::Track::GENRE_ID, trackQuery.ColumnText(14));
|
||||
result->SetValue(constants::Track::ARTIST_ID, trackQuery.ColumnText(15));
|
||||
result->SetValue(constants::Track::ALBUM_ARTIST_ID, trackQuery.ColumnText(16));
|
||||
result->SetValue(constants::Track::ALBUM_ID, trackQuery.ColumnText(17));
|
||||
result->SetValue(constants::Track::SOURCE_ID, trackQuery.ColumnText(18));
|
||||
result->SetValue(constants::Track::EXTERNAL_ID, trackQuery.ColumnText(19));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -41,15 +41,9 @@ namespace musik { namespace core { namespace db { namespace local {
|
||||
|
||||
class TrackMetadataQuery : public LocalQueryBase {
|
||||
public:
|
||||
enum class Type : int {
|
||||
AllMetadata,
|
||||
UriOnly
|
||||
};
|
||||
|
||||
TrackMetadataQuery(
|
||||
musik::core::TrackPtr target,
|
||||
musik::core::ILibraryPtr library,
|
||||
Type type = Type::AllMetadata);
|
||||
musik::core::ILibraryPtr library);
|
||||
|
||||
virtual ~TrackMetadataQuery() { }
|
||||
|
||||
@ -64,7 +58,6 @@ class TrackMetadataQuery : public LocalQueryBase {
|
||||
private:
|
||||
ILibraryPtr library;
|
||||
TrackPtr result;
|
||||
Type type;
|
||||
};
|
||||
|
||||
} } } }
|
@ -48,7 +48,9 @@ namespace musik { namespace core { namespace sdk {
|
||||
int limit = -1,
|
||||
int offset = 0) = 0;
|
||||
|
||||
virtual IRetainedTrack* QueryTrack(unsigned long long trackId) = 0;
|
||||
virtual IRetainedTrack* QueryTrackById(unsigned long long trackId) = 0;
|
||||
|
||||
virtual IRetainedTrack* QueryTrackByExternalId(const char* externalId) = 0;
|
||||
|
||||
virtual ITrackList* QueryTracksByCategory(
|
||||
const char* categoryType,
|
||||
|
@ -120,7 +120,9 @@ namespace musik {
|
||||
static const char* ArtistId = "visual_artist_id";
|
||||
static const char* AlbumArtistId = "album_artist_id";
|
||||
static const char* AlbumId = "album_id";
|
||||
static const char* SourceId = "source_id";
|
||||
static const char* ExternalId = "external_id";
|
||||
}
|
||||
|
||||
static const int SdkVersion = 5;
|
||||
static const int SdkVersion = 6;
|
||||
} } }
|
Loading…
x
Reference in New Issue
Block a user