- Added support for album-level album art (previously only worked at the

track-level). Updated queries accordingly.
- Added support for vending album art ids in the web socket server
- Added support for returning album art data in the http serfver
- Fixed debug build against the latest version of visual studio 2017
  (the wrong platform sdk version was selected in the project files)
This commit is contained in:
casey langen 2017-11-01 21:31:30 -07:00
parent c02d824235
commit fd7fa4d6a9
28 changed files with 308 additions and 197 deletions

View File

@ -13,7 +13,7 @@
<PropertyGroup Label="Globals">
<ProjectGuid>{B2165720-B4B2-4F4B-8888-8C390C3CB4DB}</ProjectGuid>
<RootNamespace>doe</RootNamespace>
<WindowsTargetPlatformVersion>10.0.15063.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">

View File

@ -13,7 +13,7 @@
<PropertyGroup Label="Globals">
<ProjectGuid>{B2165720-B4B2-4F4B-9634-8C390C3CB4DB}</ProjectGuid>
<RootNamespace>core</RootNamespace>
<WindowsTargetPlatformVersion>10.0.15063.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">

View File

@ -50,7 +50,7 @@ using namespace musik::core;
using namespace musik::core::library;
using namespace musik::core::runtime;
#define DATABASE_VERSION 4
#define DATABASE_VERSION 5
#define VERBOSE_LOGGING 0
#define MESSAGE_QUERY_COMPLETED 5000
@ -315,6 +315,11 @@ static void upgradeV3ToV4(db::Connection& db) {
scheduleSyncDueToDbUpgrade = true;
}
static void upgradeV4ToV5(db::Connection& db) {
db.Execute("DELETE from tracks");
scheduleSyncDueToDbUpgrade = true;
}
static void setVersion(db::Connection& db, int version) {
db.Execute("DELETE FROM version");
@ -514,6 +519,10 @@ void LocalLibrary::CreateDatabase(db::Connection &db){
upgradeV3ToV4(db);
}
if (lastVersion >= 1 && lastVersion < 5) {
upgradeV4ToV5(db);
}
/* ensure our version is set correctly */
setVersion(db, DATABASE_VERSION);

View File

@ -60,11 +60,8 @@ static const std::string COLUMNS =
"albums.id, "
"albums.name as album, "
"tracks.album_artist_id, "
"artists.name as album_artist ";
/* thumbnails may have different images even though they are part
of the same album. need to figure out how to work around this
efficiently. */
// "tracks.thumbnail_id ";
"artists.name as album_artist, "
"albums.thumbnail_id ";
static const std::string TABLES =
"albums, tracks, artists ";

View File

@ -499,7 +499,7 @@ static std::string createTrackExternalId(IndexerTrack& track) {
return std::string("local-") + std::to_string(hash1) + "-" + std::to_string(hash2);
}
int64_t IndexerTrack::SaveAlbum(db::Connection& dbConnection) {
int64_t IndexerTrack::SaveAlbum(db::Connection& dbConnection, int64_t thumbnailId) {
std::string album = this->GetString("album");
std::string value = album + "-" + this->GetString("album_artist");
@ -522,6 +522,15 @@ int64_t IndexerTrack::SaveAlbum(db::Connection& dbConnection) {
}
}
if (thumbnailId != 0) {
db::Statement updateStatement(
"UPDATE albums SET thumbnail_id=? WHERE id=?", dbConnection);
updateStatement.BindInt64(0, thumbnailId);
updateStatement.BindInt64(1, id);
updateStatement.Step();
}
return id;
}
@ -656,11 +665,11 @@ bool IndexerTrack::Save(db::Connection &dbConnection, std::string libraryDirecto
this->id = writeToTracksTable(dbConnection, *this);
int64_t albumId = this->SaveAlbum(dbConnection);
int64_t thumbnailId = this->SaveThumbnail(dbConnection, libraryDirectory);
int64_t albumId = this->SaveAlbum(dbConnection, thumbnailId);
int64_t genreId = this->SaveGenre(dbConnection);
int64_t artistId = this->SaveArtist(dbConnection);
int64_t albumArtistId = this->SaveSingleValueField(dbConnection, "album_artist", "artists");
int64_t thumbnailId = this->SaveThumbnail(dbConnection, libraryDirectory);
/* ensure we have a correct source id */
int sourceId = 0;

View File

@ -105,7 +105,7 @@ namespace musik { namespace core {
int64_t SaveArtist(db::Connection& connection);
int64_t SaveAlbum(db::Connection& connection);
int64_t SaveAlbum(db::Connection& connection, int64_t thumbnailId);
int64_t SaveSingleValueField(
db::Connection& connection,

View File

@ -54,15 +54,40 @@ using namespace musik::core::db::local;
using namespace musik::core::io;
using namespace musik::core::sdk;
typedef void(*SetEnvironment)(IEnvironment*);
typedef void(*SetSimpleDataProvider)(ISimpleDataProvider*);
typedef void(*SetIndexerNotifier)(IIndexerNotifier*);
static ILibraryPtr library;
static LocalSimpleDataProvider* dataProvider = nullptr;
static class Environment : public IEnvironment {
public:
virtual size_t GetPath(PathType type, char* dst, int size) override {
std::string path;
switch (type) {
case PathUserHome: path = GetHomeDirectory(); break;
case PathData: path = GetDataDirectory(); break;
case PathApplication: path = GetApplicationDirectory(); break;
case PathPlugins: path = GetPluginDirectory(); break;
case PathUserHome: path =
GetHomeDirectory();
break;
case PathData:
path = GetDataDirectory();
break;
case PathApplication:
path = GetApplicationDirectory();
break;
case PathPlugins:
path = GetPluginDirectory();
break;
case PathLibrary: {
if (library) {
path = GetDataDirectory() + std::to_string(library->Id()) + "/";
}
break;
}
}
return CopyString(path, dst, size);
}
@ -84,13 +109,6 @@ static class Environment : public IEnvironment {
}
} environment;
typedef void(*SetEnvironment)(IEnvironment*);
typedef void(*SetSimpleDataProvider)(ISimpleDataProvider*);
LocalSimpleDataProvider* dataProvider = nullptr;
typedef void(*SetIndexerNotifier)(IIndexerNotifier*);
namespace musik { namespace core { namespace plugin {
void InstallDependencies(ILibraryPtr library) {
@ -99,7 +117,8 @@ namespace musik { namespace core { namespace plugin {
/* data providers */
delete dataProvider;
dataProvider = new LocalSimpleDataProvider(library);
::library = library;
::dataProvider = new LocalSimpleDataProvider(library);
PluginFactory::Instance().QueryFunction<SetSimpleDataProvider>(
"SetSimpleDataProvider",
@ -137,7 +156,8 @@ namespace musik { namespace core { namespace plugin {
});
delete dataProvider;
dataProvider = nullptr;
::dataProvider = nullptr;
::library.reset();
/* indexer */
PluginFactory::Instance().QueryFunction<SetIndexerNotifier>(

View File

@ -89,7 +89,8 @@ namespace musik {
PathUserHome,
PathData,
PathApplication,
PathPlugins
PathPlugins,
PathLibrary
};
enum class Capability : int {

View File

@ -13,7 +13,7 @@
<PropertyGroup Label="Globals">
<ProjectGuid>{01869283-4cc3-4da4-a06c-3df6a0de98cc}</ProjectGuid>
<RootNamespace>glue</RootNamespace>
<WindowsTargetPlatformVersion>10.0.15063.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">

View File

@ -15,7 +15,7 @@
<ProjectGuid>{C7102EB1-7311-4B36-A7FF-89DD7F077FF9}</ProjectGuid>
<RootNamespace>musikcube</RootNamespace>
<Keyword>Win32Proj</Keyword>
<WindowsTargetPlatformVersion>10.0.15063.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">

View File

@ -14,7 +14,7 @@
<ProjectGuid>{54764854-5A73-4329-9BAD-9AF22C72D9E2}</ProjectGuid>
<RootNamespace>cddadecoder</RootNamespace>
<Keyword>Win32Proj</Keyword>
<WindowsTargetPlatformVersion>10.0.15063.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">

View File

@ -13,7 +13,7 @@
<PropertyGroup Label="Globals">
<ProjectGuid>{51c18730-dc48-411a-829d-f2b3b7ac4c97}</ProjectGuid>
<RootNamespace>directsoundout</RootNamespace>
<WindowsTargetPlatformVersion>10.0.15063.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">

View File

@ -14,7 +14,7 @@
<ProjectGuid>{465EF178-91C1-4068-BE1D-F9616ECCB6DE}</ProjectGuid>
<RootNamespace>flacdecoder</RootNamespace>
<Keyword>Win32Proj</Keyword>
<WindowsTargetPlatformVersion>10.0.15063.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">

View File

@ -14,7 +14,7 @@
<ProjectGuid>{fa74d37c-8184-4596-bfe9-766c159045e1}</ProjectGuid>
<RootNamespace>httpdatastream</RootNamespace>
<Keyword>Win32Proj</Keyword>
<WindowsTargetPlatformVersion>10.0.15063.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">

View File

@ -14,7 +14,7 @@
<ProjectGuid>{4993E68D-E97A-4CD2-AC8E-168AE315BAC5}</ProjectGuid>
<RootNamespace>m4adecoder</RootNamespace>
<Keyword>Win32Proj</Keyword>
<WindowsTargetPlatformVersion>10.0.15063.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
<ProjectName>m4adecoder</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />

View File

@ -14,7 +14,7 @@
<ProjectGuid>{ca56a398-7f9a-493a-a7fc-c6b4d550b674}</ProjectGuid>
<RootNamespace>nomaddecoder</RootNamespace>
<Keyword>Win32Proj</Keyword>
<WindowsTargetPlatformVersion>10.0.15063.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">

View File

@ -13,7 +13,7 @@
<PropertyGroup Label="Globals">
<ProjectGuid>{ba3902e0-8915-4e59-ad3b-f9960507f234}</ProjectGuid>
<RootNamespace>nullout</RootNamespace>
<WindowsTargetPlatformVersion>10.0.15063.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">

View File

@ -14,7 +14,7 @@
<ProjectGuid>{292974B0-C8B7-41EF-B603-554A2B25CB90}</ProjectGuid>
<RootNamespace>oggdecoder</RootNamespace>
<Keyword>Win32Proj</Keyword>
<WindowsTargetPlatformVersion>10.0.15063.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">

View File

@ -14,7 +14,7 @@
<ProjectGuid>{7CD00EC4-D090-48BE-9388-FA4857AC332C}</ProjectGuid>
<RootNamespace>taglib_plugin</RootNamespace>
<Keyword>Win32Proj</Keyword>
<WindowsTargetPlatformVersion>10.0.15063.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">

View File

@ -13,7 +13,7 @@
<PropertyGroup Label="Globals">
<ProjectGuid>{ebd2e652-aa1b-4b8b-8d03-ccecb9bf3304}</ProjectGuid>
<RootNamespace>wasapiout</RootNamespace>
<WindowsTargetPlatformVersion>10.0.15063.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">

View File

@ -13,7 +13,7 @@
<PropertyGroup Label="Globals">
<ProjectGuid>{4F10C17A-8AF7-4FAC-A4E2-087AE6E8F9D8}</ProjectGuid>
<RootNamespace>waveout</RootNamespace>
<WindowsTargetPlatformVersion>10.0.15063.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">

View File

@ -164,6 +164,7 @@ namespace fragment {
static const std::string audio = "audio";
static const std::string id = "id";
static const std::string external_id = "external_id";
static const std::string thumbnail = "thumbnail";
}
namespace broadcast {

View File

@ -76,7 +76,8 @@ std::unordered_map<std::string, std::string> CONTENT_TYPE_MAP = {
{ ".mp+", "audio/x-musepack" },
{ ".mpp", "audio/x-musepack" },
{ ".ape", "audio/monkeys-audio" },
{ ".wma", "audio/x-ms-wma" }
{ ".wma", "audio/x-ms-wma" },
{ ".jpg", "image/jpeg" }
};
struct Range {
@ -94,6 +95,7 @@ static std::string contentType(const std::string& fn) {
try {
boost::filesystem::path p(fn);
std::string ext = boost::trim_copy(p.extension().string());
boost::to_lower(ext);
auto it = CONTENT_TYPE_MAP.find(ext);
if (it != CONTENT_TYPE_MAP.end()) {
@ -313,7 +315,7 @@ int HttpServer::HandleRequest(
struct MHD_Response* response = nullptr;
int ret = MHD_NO;
int status = MHD_HTTP_OK;
int status = MHD_HTTP_NOT_FOUND;
try {
if (!isAuthenticated(connection, server->context)) {
@ -336,159 +338,13 @@ int HttpServer::HandleRequest(
std::vector<std::string> parts;
boost::split(parts, urlStr, boost::is_any_of("/"));
if (parts.size() > 0) {
/* /audio/id/<id> OR /audio/external_id/<external_id> */
if (parts.at(0) == fragment::audio && parts.size() == 3) {
ITrack* track = nullptr;
bool byExternalId = (parts.at(1) == fragment::external_id);
if (byExternalId) {
std::string externalId = urlDecode(parts.at(2));
track = server->context.dataProvider->QueryTrackByExternalId(externalId.c_str());
#ifdef ENABLE_DEBUG
std::cerr << "externalId: " << externalId<< "\n";
std::cerr << "title: " << GetMetadataString(track, "title") << std::endl;
#endif
}
else if (parts.at(1) == fragment::id) {
uint64_t id = std::stoull(urlDecode(parts.at(2)));
track = server->context.dataProvider->QueryTrackById(id);
}
if (track) {
std::string duration = GetMetadataString(track, key::duration);
std::string filename = GetMetadataString(track, key::filename);
track->Release();
size_t bitrate = getUnsignedUrlParam(connection, "bitrate", 0);
IDataStream* file = (bitrate == 0)
? server->context.environment->GetDataStream(filename.c_str())
: Transcoder::Transcode(server->context, filename, bitrate);
const char* rangeVal = MHD_lookup_connection_value(
connection, MHD_HEADER_KIND, "Range");
#ifdef ENABLE_DEBUG
if (rangeVal) {
std::cerr << "range header: " << rangeVal << "\n";
}
#endif
Range* range = parseRange(file, rangeVal);
#ifdef ENABLE_DEBUG
std::cerr << "potential response header : " << range->HeaderValue() << std::endl;
#endif
/* ehh... */
bool isOnDemandTranscoder = !!dynamic_cast<TranscodingDataStream*>(file);
#ifdef ENABLE_DEBUG
std::cerr << "on demand? " << isOnDemandTranscoder << std::endl;
#endif
/* gotta be careful with request ranges if we're transcoding. don't
allow any custom ranges other than from 0 to end. */
if (isOnDemandTranscoder && rangeVal && strlen(rangeVal)) {
if (range->from != 0 || range->to != range->total - 1) {
delete range;
#ifdef ENABLE_DEBUG
std::cerr << "removing range header, seek requested with ondemand transcoder\n";
#endif
if (HTTP_416_DISABLED) {
rangeVal = nullptr; /* ignore the header from here on out. */
/* lots of clients don't seem to be to deal with 416 properly;
instead, ignore the range header and return the whole file,
and a 200 (not 206) */
if (file) {
range = parseRange(file, nullptr);
}
}
else {
if (file) {
file->Release();
file = nullptr;
}
if (false && server->context.prefs->GetBool(
prefs::transcoder_synchronous_fallback.c_str(),
defaults::transcoder_synchronous_fallback))
{
/* if we're allowed, fall back to synchronous transcoding. we'll block
here until the entire file has been converted and cached */
file = Transcoder::TranscodeAndWait(server->context, filename, bitrate);
range = parseRange(file, rangeVal);
}
else {
/* otherwise fail with a "range not satisfiable" status */
status = 416;
char empty[1];
response = MHD_create_response_from_buffer(0, empty, MHD_RESPMEM_PERSISTENT);
}
}
}
}
if (file) {
size_t length = (range->to - range->from);
response = MHD_create_response_from_callback(
length == 0 ? MHD_SIZE_UNKNOWN : length + 1,
4096,
&fileReadCallback,
range,
&fileFreeCallback);
#ifdef ENABLE_DEBUG
std::cerr << "response length: " << ((length == 0) ? 0 : length + 1) << "\n";
std::cerr << "id: " << file << "\n";
#endif
if (response) {
if (!isOnDemandTranscoder) {
MHD_add_response_header(response, "Accept-Ranges", "bytes");
}
else {
MHD_add_response_header(response, "X-musikcube-Estimated-Content-Length", "true");
}
if (duration.size()) {
MHD_add_response_header(response, "X-Content-Duration", duration.c_str());
MHD_add_response_header(response, "Content-Duration", duration.c_str());
}
if (byExternalId) {
/* if we're using an on-demand transcoder, ensure the client does not cache the
result because we have to guess the content length. */
std::string value = isOnDemandTranscoder ? "no-cache" : "public, max-age=31536000";
MHD_add_response_header(response, "Cache-Control", value.c_str());
}
MHD_add_response_header(response, "Content-Type", contentType(filename).c_str());
MHD_add_response_header(response, "Server", "musikcube websocket_remote");
if ((rangeVal && strlen(rangeVal)) || range->from > 0) {
if (range->total > 0) {
MHD_add_response_header(response, "Content-Range", range->HeaderValue().c_str());
status = MHD_HTTP_PARTIAL_CONTENT;
#ifdef ENABLE_DEBUG
if (rangeVal) {
std::cerr << "actual range header: " << range->HeaderValue() << "\n";
}
#endif
}
}
}
else {
file->Release();
file = nullptr;
}
}
}
status = HandleAudioTrackRequest(server, response, connection, parts);
}
/* /thumbnail/<id> */
else if (parts.at(0) == fragment::thumbnail && parts.size() == 2) {
status = HandleThumbnailRequest(server, response, connection, parts);
}
}
}
@ -511,3 +367,208 @@ int HttpServer::HandleRequest(
return ret;
}
int HttpServer::HandleAudioTrackRequest(
HttpServer* server,
MHD_Response*& response,
MHD_Connection *connection,
std::vector<std::string>& pathParts)
{
int status = MHD_HTTP_OK;
ITrack* track = nullptr;
bool byExternalId = (pathParts.at(1) == fragment::external_id);
if (byExternalId) {
std::string externalId = urlDecode(pathParts.at(2));
track = server->context.dataProvider->QueryTrackByExternalId(externalId.c_str());
#ifdef ENABLE_DEBUG
std::cerr << "externalId: " << externalId << "\n";
std::cerr << "title: " << GetMetadataString(track, "title") << std::endl;
#endif
}
else if (pathParts.at(1) == fragment::id) {
uint64_t id = std::stoull(urlDecode(pathParts.at(2)));
track = server->context.dataProvider->QueryTrackById(id);
}
if (track) {
std::string duration = GetMetadataString(track, key::duration);
std::string filename = GetMetadataString(track, key::filename);
track->Release();
size_t bitrate = getUnsignedUrlParam(connection, "bitrate", 0);
IDataStream* file = (bitrate == 0)
? server->context.environment->GetDataStream(filename.c_str())
: Transcoder::Transcode(server->context, filename, bitrate);
const char* rangeVal = MHD_lookup_connection_value(
connection, MHD_HEADER_KIND, "Range");
#ifdef ENABLE_DEBUG
if (rangeVal) {
std::cerr << "range header: " << rangeVal << "\n";
}
#endif
Range* range = parseRange(file, rangeVal);
#ifdef ENABLE_DEBUG
std::cerr << "potential response header : " << range->HeaderValue() << std::endl;
#endif
/* ehh... */
bool isOnDemandTranscoder = !!dynamic_cast<TranscodingDataStream*>(file);
#ifdef ENABLE_DEBUG
std::cerr << "on demand? " << isOnDemandTranscoder << std::endl;
#endif
/* gotta be careful with request ranges if we're transcoding. don't
allow any custom ranges other than from 0 to end. */
if (isOnDemandTranscoder && rangeVal && strlen(rangeVal)) {
if (range->from != 0 || range->to != range->total - 1) {
delete range;
#ifdef ENABLE_DEBUG
std::cerr << "removing range header, seek requested with ondemand transcoder\n";
#endif
if (HTTP_416_DISABLED) {
rangeVal = nullptr; /* ignore the header from here on out. */
/* lots of clients don't seem to be to deal with 416 properly;
instead, ignore the range header and return the whole file,
and a 200 (not 206) */
if (file) {
range = parseRange(file, nullptr);
}
}
else {
if (file) {
file->Release();
file = nullptr;
}
if (false && server->context.prefs->GetBool(
prefs::transcoder_synchronous_fallback.c_str(),
defaults::transcoder_synchronous_fallback))
{
/* if we're allowed, fall back to synchronous transcoding. we'll block
here until the entire file has been converted and cached */
file = Transcoder::TranscodeAndWait(server->context, filename, bitrate);
range = parseRange(file, rangeVal);
}
else {
/* otherwise fail with a "range not satisfiable" status */
status = 416;
char empty[1];
response = MHD_create_response_from_buffer(0, empty, MHD_RESPMEM_PERSISTENT);
}
}
}
}
if (file) {
size_t length = (range->to - range->from);
response = MHD_create_response_from_callback(
length == 0 ? MHD_SIZE_UNKNOWN : length + 1,
4096,
&fileReadCallback,
range,
&fileFreeCallback);
#ifdef ENABLE_DEBUG
std::cerr << "response length: " << ((length == 0) ? 0 : length + 1) << "\n";
std::cerr << "id: " << file << "\n";
#endif
if (response) {
if (!isOnDemandTranscoder) {
MHD_add_response_header(response, "Accept-Ranges", "bytes");
}
else {
MHD_add_response_header(response, "X-musikcube-Estimated-Content-Length", "true");
}
if (duration.size()) {
MHD_add_response_header(response, "X-Content-Duration", duration.c_str());
MHD_add_response_header(response, "Content-Duration", duration.c_str());
}
if (byExternalId) {
/* if we're using an on-demand transcoder, ensure the client does not cache the
result because we have to guess the content length. */
std::string value = isOnDemandTranscoder ? "no-cache" : "public, max-age=31536000";
MHD_add_response_header(response, "Cache-Control", value.c_str());
}
MHD_add_response_header(response, "Content-Type", contentType(filename).c_str());
MHD_add_response_header(response, "Server", "musikcube websocket_remote");
if ((rangeVal && strlen(rangeVal)) || range->from > 0) {
if (range->total > 0) {
MHD_add_response_header(response, "Content-Range", range->HeaderValue().c_str());
status = MHD_HTTP_PARTIAL_CONTENT;
#ifdef ENABLE_DEBUG
if (rangeVal) {
std::cerr << "actual range header: " << range->HeaderValue() << "\n";
}
#endif
}
}
}
else {
file->Release();
file = nullptr;
}
}
}
return status;
}
int HttpServer::HandleThumbnailRequest(
HttpServer* server,
MHD_Response*& response,
MHD_Connection* connection,
std::vector<std::string>& pathParts)
{
int status = MHD_HTTP_NOT_FOUND;
char pathBuffer[4096];
server->context.environment->GetPath(
PathType::PathLibrary, pathBuffer, sizeof(pathBuffer));
if (strlen(pathBuffer)) {
std::string path = std::string(pathBuffer) + "thumbs/" + pathParts.at(1) + ".jpg";
IDataStream* file = server->context.environment->GetDataStream(path.c_str());
if (file) {
long length = file->Length();
response = MHD_create_response_from_callback(
length == 0 ? MHD_SIZE_UNKNOWN : length + 1,
4096,
&fileReadCallback,
parseRange(file, nullptr),
&fileFreeCallback);
if (response) {
MHD_add_response_header(response, "Cache-Control", "public, max-age=31536000");
MHD_add_response_header(response, "Content-Type", contentType(path).c_str());
MHD_add_response_header(response, "Server", "musikcube websocket_remote");
int status = MHD_HTTP_OK;
}
else {
file->Release();
}
}
}
return status;
}

View File

@ -38,6 +38,7 @@
#include "Context.h"
#include <condition_variable>
#include <mutex>
#include <vector>
class HttpServer {
public:
@ -64,6 +65,18 @@ class HttpServer {
struct MHD_Connection *c,
char *s);
static int HandleAudioTrackRequest(
HttpServer* server,
MHD_Response*& response,
MHD_Connection* connection,
std::vector<std::string>& pathParts);
static int HandleThumbnailRequest(
HttpServer* server,
MHD_Response*& response,
MHD_Connection* connection,
std::vector<std::string>& pathParts);
struct MHD_Daemon *httpServer;
Context& context;
volatile bool running;

View File

@ -620,7 +620,7 @@ void WebSocketServer::RespondWithQueryAlbums(connection_hdl connection, json& re
result.push_back({
{ key::title, GetValueString(album) },
{ key::id, album->GetId() },
{ key::thumbnail_id, 0 }, /* note: thumbnails aren't supported at the album level yet */
{ key::thumbnail_id, album->GetInt64(key::thumbnail_id.c_str()) },
{ key::album_artist_id, album->GetInt64(key::album_artist_id.c_str()) },
{ key::album_artist, GetMetadataString(album, key::album_artist) }
});

View File

@ -21,7 +21,7 @@
<PropertyGroup Label="Globals">
<ProjectGuid>{43A78C57-C9A3-4852-B0BE-05335C5C077D}</ProjectGuid>
<RootNamespace>musikcubewebsockets</RootNamespace>
<WindowsTargetPlatformVersion>10.0.15063.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">

View File

@ -21,7 +21,7 @@
<SccProjectName />
<SccLocalPath />
<ProjectGuid>{68AA481E-3CCE-440F-8CCE-69F1B371C89D}</ProjectGuid>
<WindowsTargetPlatformVersion>10.0.15063.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
<ProjectName>win32gdivis</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />

View File

@ -13,7 +13,7 @@
<PropertyGroup Label="Globals">
<ProjectGuid>{3E30064E-B9C4-4690-8AC2-2C694176A319}</ProjectGuid>
<RootNamespace>win32globalhotkeys</RootNamespace>
<WindowsTargetPlatformVersion>10.0.15063.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">