mirror of
https://github.com/clangen/musikcube.git
synced 2025-03-29 19:20:28 +00:00
- 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:
parent
c02d824235
commit
fd7fa4d6a9
2
src/3rdparty/3rdparty.vcxproj
vendored
2
src/3rdparty/3rdparty.vcxproj
vendored
@ -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">
|
||||
|
@ -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">
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 ";
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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>(
|
||||
|
@ -89,7 +89,8 @@ namespace musik {
|
||||
PathUserHome,
|
||||
PathData,
|
||||
PathApplication,
|
||||
PathPlugins
|
||||
PathPlugins,
|
||||
PathLibrary
|
||||
};
|
||||
|
||||
enum class Capability : int {
|
||||
|
@ -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">
|
||||
|
@ -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">
|
||||
|
@ -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">
|
||||
|
@ -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">
|
||||
|
@ -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">
|
||||
|
@ -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">
|
||||
|
@ -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" />
|
||||
|
@ -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">
|
||||
|
@ -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">
|
||||
|
@ -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">
|
||||
|
@ -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">
|
||||
|
@ -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">
|
||||
|
@ -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">
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
@ -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;
|
||||
|
@ -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) }
|
||||
});
|
||||
|
@ -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">
|
||||
|
@ -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" />
|
||||
|
@ -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">
|
||||
|
Loading…
x
Reference in New Issue
Block a user