Added a bunch of boring parsing and indexing logic.

This commit is contained in:
casey langen 2019-01-03 20:47:44 -08:00
parent 3a1f5d073c
commit e4c2a5b085
10 changed files with 347 additions and 80 deletions

View File

@ -303,25 +303,12 @@ static int64_t writeToTracksTable(
see if we can find the corresponding ID. this can happen when
IInputSource plugins are reading/writing track data. */
if (id == 0) {
if (sourceId == 0) {
db::Statement stmt("SELECT id FROM tracks WHERE source_id=? AND external_id=?", dbConnection);
stmt.BindInt32(0, sourceId);
stmt.BindText(1, externalId);
if (stmt.Step() == db::Row) {
track.SetId(stmt.ColumnInt64(0));
}
}
else {
std::string fn = track.GetString("filename");
if (fn.size()) {
db::Statement stmt("SELECT id, external_id FROM tracks WHERE filename=?", dbConnection);
stmt.BindText(0, track.GetString("filename"));
if (stmt.Step() == db::Row) {
id = stmt.ColumnInt64(0);
track.SetId(id);
track.SetValue("external_id", stmt.ColumnText(1));
}
}
db::Statement stmt("SELECT id FROM tracks WHERE source_id=? AND external_id=?", dbConnection);
stmt.BindInt32(0, sourceId);
stmt.BindText(1, externalId);
if (stmt.Step() == db::Row) {
id = stmt.ColumnInt64(0);
track.SetId(id);
}
}

View File

@ -1,5 +1,6 @@
set (gmedecoder_SOURCES
plugin.cpp
GmeDataStream.cpp
GmeDecoder.cpp
GmeIndexerSource.cpp
gme/c140.c

View File

@ -48,6 +48,37 @@
static const std::string PLUGIN_NAME = "GME IDecoder";
static const std::set<std::string> FORMATS = {
"vgm", "gym", "spc", "sap", "nsfe",
"nsf", "ay", "gbs", "hes", "kss"
};
".vgm", ".gym", ".spc", ".sap", ".nsfe",
".nsf", ".ay", ".gbs", ".hes", ".kss"
};
static inline bool canHandle(const std::string& fn) {
for (auto& ext : FORMATS) {
if (fn.rfind(ext) == fn.size() - ext.size()) {
return true;
}
}
return false;
}
static inline bool parseExternalId(const std::string& externalId, std::string& fn, int& track) {
if (externalId.find("gme://") == 0) {
std::string trimmed = externalId.substr(6);
auto slash = trimmed.find("/");
if (slash != std::string::npos) {
try {
track = std::stoi(trimmed.substr(0, slash));
fn = trimmed.substr(slash + 1);
return true;
}
catch (...) {
return false;
}
}
}
return false;
}
static inline std::string createExternalId(const std::string& fn, int track) {
return "gme://" + std::to_string(track) + "/" + fn;
}

View File

@ -0,0 +1,104 @@
//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2004-2019 musikcube team
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// * Neither the name of the author nor the names of other contributors may
// be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////////
#include "Constants.h"
#include "GmeDataStream.h"
#include <core/sdk/IEnvironment.h>
using namespace musik::core::sdk;
extern IEnvironment* environment;
bool GmeDataStream::Open(const char *uri, unsigned int options) {
std::string fn;
if (parseExternalId(uri, fn, this->trackNumber)) {
this->stream = environment->GetDataStream(fn.c_str());
if (this->stream) {
return true;
}
}
return false;
}
bool GmeDataStream::Close() {
return this->stream->Close();
}
void GmeDataStream::Interrupt() {
this->stream->Interrupt();
}
void GmeDataStream::Release() {
if (stream) {
stream->Release();
stream = nullptr;
}
delete this;
}
PositionType GmeDataStream::Read(void *buffer, PositionType readBytes) {
return this->stream->Read(buffer, readBytes);
}
bool GmeDataStream::SetPosition(PositionType position) {
return this->stream->SetPosition(position);
}
PositionType GmeDataStream::Position() {
return this->stream->Position();
}
bool GmeDataStream::Seekable() {
return this->stream->Seekable();
}
bool GmeDataStream::Eof() {
return this->stream->Eof();
}
long GmeDataStream::Length() {
return this->stream->Length();
}
const char* GmeDataStream::Type() {
return this->stream->Type();
}
const char* GmeDataStream::Uri() {
return this->stream->Uri();
}
bool GmeDataStream::CanPrefetch() {
return this->stream->CanPrefetch();
}

View File

@ -0,0 +1,63 @@
//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2004-2019 musikcube team
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// * Neither the name of the author nor the names of other contributors may
// be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////////
#pragma once
#include <core/sdk/IDataStream.h>
#include <string>
class GmeDataStream: public musik::core::sdk::IDataStream {
public:
using PositionType = musik::core::sdk::PositionType;
virtual bool Open(const char *uri, unsigned int options = 0) override;
virtual bool Close() override;
virtual void Interrupt() override;
virtual void Release() override;
virtual PositionType Read(void *buffer, PositionType readBytes) override;
virtual bool SetPosition(PositionType position) override;
virtual PositionType Position() override;
virtual bool Seekable() override;
virtual bool Eof() override;
virtual long Length() override;
virtual const char* Type() override;
virtual const char* Uri() override;
virtual bool CanPrefetch() override;
int GetTrackNumber() { return this->trackNumber; }
private:
int trackNumber { 0 };
musik::core::sdk::IDataStream* stream { nullptr };
};

View File

@ -34,6 +34,7 @@
#include "Constants.h"
#include "GmeDecoder.h"
#include <cassert>
GmeDecoder::GmeDecoder() {
}
@ -41,8 +42,10 @@ GmeDecoder::GmeDecoder() {
GmeDecoder::~GmeDecoder() {
}
bool GmeDecoder::Open(musik::core::sdk::IDataStream *stream){
return false;
bool GmeDecoder::Open(musik::core::sdk::IDataStream *stream) {
this->stream = dynamic_cast<GmeDataStream*>(stream);
assert(this->stream);
return true;
}
void GmeDecoder::Release() {

View File

@ -37,6 +37,7 @@
#include <core/sdk/constants.h>
#include <core/sdk/IDecoder.h>
#include <core/sdk/IDataStream.h>
#include "GmeDataStream.h"
#include <stddef.h>
using namespace musik::core::sdk;
@ -52,4 +53,9 @@ class GmeDecoder: public musik::core::sdk::IDecoder {
virtual double GetDuration() override;
virtual bool Open(musik::core::sdk::IDataStream *stream) override;
virtual bool Exhausted() override;
private:
GmeDataStream* stream { nullptr };
};

View File

@ -39,44 +39,89 @@
#include <set>
#include <map>
#include <gme.h>
#include <unistd.h>
#include <string.h>
using namespace musik::core::sdk;
static std::vector<std::string> tokenize(const std::string& str, char delim = '/') {
std::vector<std::string> result;
std::istringstream iss(str);
std::string token;
while (std::getline(iss, token, delim)) {
result.push_back(token);
static std::string getM3uFor(const std::string& fn) {
size_t lastDot = fn.find_last_of(".");
if (lastDot != std::string::npos) {
std::string m3u = fn.substr(0, lastDot) + ".m3u";
if (access(m3u.c_str(), R_OK) != -1) {
return m3u;
}
}
return result;
}
static std::string createExternalId(const std::string& fn, int track) {
return "gme/" + std::to_string(track) + "/" + fn;
return "";
}
static bool exists(const std::string& externalId) {
return false;
std::string fn;
int trackNum;
if (!parseExternalId(externalId, fn, trackNum)) {
return false;
}
return access(fn.c_str(), R_OK) != -1;
}
static void updateMetadata(const std::string& fn, IIndexerWriter* indexer) {
// gme_t* data = nullptr;
// gme_err_t err = gme_open_file("/tmp/mario.nsf", &data, 44100);
// if (err) {
// std::cerr << "error: " << err << "\n";
// }
// else {
// gme_info_t* info = nullptr;
// err = gme_track_info(data, &info, 0);
// if (err) {
// std::cerr << "error: " << err << "\n";
// }
// gme_free_info(info);
// }
// gme_delete(data);
static void updateMetadata(IIndexerSource* source, const std::string& fn, IIndexerWriter* indexer) {
gme_t* data = nullptr;
gme_err_t err = gme_open_file(fn.c_str(), &data, 44100);
if (err) {
std::cerr << "error opening file: " << err << "\n";
}
else {
std::string m3u = getM3uFor(fn);
if (m3u.size()) {
err = gme_load_m3u(data, m3u.c_str());
if (err) {
std::cerr << "m3u found, but load failed: " << err << "\n";
}
}
for (int i = 0; i < gme_track_count(data); i++) {
gme_info_t* info = nullptr;
err = gme_track_info(data, &info, i);
if (err) {
std::cerr << "error getting track: " << err << "\n";
}
else if (info) {
auto track = indexer->CreateWriter();
const std::string trackNum = std::to_string(i + 1).c_str();
const std::string defaultTitle = "Track " + std::to_string(1 + i);
const std::string duration = std::to_string((float) info->play_length / 1000.0f);
const std::string externalId = createExternalId(fn, i);
track->SetValue("album", info->game);
track->SetValue("album_artist", info->system);
track->SetValue("genre", info->system);
track->SetValue("track", trackNum.c_str());
track->SetValue("duration", duration.c_str());
track->SetValue("filename", externalId.c_str());
if (strlen(info->author)) {
track->SetValue("artist", info->author);
}
else {
track->SetValue("artist", info->system);
}
if (strlen(info->song)) {
track->SetValue("title", info->song);
}
else {
track->SetValue("title", defaultTitle.c_str());
}
indexer->Save(source, track, externalId.c_str());
track->Release();
}
gme_free_info(info);
}
}
gme_delete(data);
}
static void scanDirectory(const std::string& path, IIndexerWriter* indexer) {
@ -107,6 +152,9 @@ ScanResult GmeIndexerSource::Scan(
for (size_t i = 0; i < indexerPathsCount; i++) {
scanDirectory(std::string(indexerPaths[i]), indexer);
}
updateMetadata(this, "/tmp/mario.nsf", indexer);
return ScanCommit;
}
@ -119,9 +167,9 @@ void GmeIndexerSource::ScanTrack(
ITagStore* tagStore,
const char* externalId)
{
// if (!exists(this->discIds, this->model, externalId)) {
// indexer->RemoveByExternalId(this, externalId);
// }
if (!exists(externalId)) {
indexer->RemoveByExternalId(this, externalId);
}
}
int GmeIndexerSource::SourceId() {

View File

@ -30,7 +30,10 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////////
#pragma once
#include <core/sdk/IIndexerSource.h>
#include <functional>

View File

@ -36,25 +36,23 @@
#include <core/sdk/constants.h>
#include <core/sdk/IPlugin.h>
#include <core/sdk/IDecoderFactory.h>
#include <core/sdk/IDataStreamFactory.h>
#include <core/sdk/IEnvironment.h>
#include "GmeDecoder.h"
#include "GmeIndexerSource.h"
#include "GmeDataStream.h"
#ifdef WIN32
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
return true;
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
return true;
}
#endif
static inline bool supported(const std::string& s) {
for (auto& ext : FORMATS) {
if (s.rfind(ext) == s.size() - ext.size()) {
return true;
}
}
return false;
}
using namespace musik::core::sdk;
class GmePlugin : public musik::core::sdk::IPlugin {
IEnvironment* environment = nullptr;
class GmePlugin: public IPlugin {
public:
virtual void Release() { delete this; };
virtual const char* Name() { return PLUGIN_NAME.c_str(); }
@ -67,15 +65,9 @@ class GmePlugin : public musik::core::sdk::IPlugin {
virtual int SdkVersion() { return musik::core::sdk::SdkVersion; }
};
class GmeDecoderFactory : public musik::core::sdk::IDecoderFactory {
class GmeDecoderFactory: public IDecoderFactory {
public:
GmeDecoderFactory() {
}
~GmeDecoderFactory() {
}
virtual musik::core::sdk::IDecoder* CreateDecoder() override {
virtual IDecoder* CreateDecoder() override {
return new GmeDecoder();
}
@ -84,18 +76,47 @@ class GmeDecoderFactory : public musik::core::sdk::IDecoderFactory {
}
virtual bool CanHandle(const char* type) const override {
return supported(std::string(type));
return canHandle(std::string(type));
}
};
extern "C" DLLEXPORT musik::core::sdk::IPlugin* GetPlugin() {
class GmeDataStreamFactory: public IDataStreamFactory{
public:
virtual bool CanRead(const char *uri) override {
std::string str = uri;
return str.find("gme://") == 0 && canHandle(str);
}
virtual IDataStream* Open(const char *uri, unsigned int options = 0) {
auto result = new GmeDataStream();
if (result->Open(uri, options)) {
return result;
}
result->Release();
return nullptr;
}
virtual void Release() {
delete this;
}
};
extern "C" DLLEXPORT IPlugin* GetPlugin() {
return new GmePlugin();
}
extern "C" DLLEXPORT musik::core::sdk::IDecoderFactory* GetDecoderFactory() {
extern "C" DLLEXPORT IDecoderFactory* GetDecoderFactory() {
return new GmeDecoderFactory();
}
extern "C" DLLEXPORT IDataStreamFactory* GetDataStreamFactory() {
return new GmeDataStreamFactory();
}
extern "C" DLLEXPORT IIndexerSource* GetIndexerSource() {
return new GmeIndexerSource();
}
extern "C" DLLEXPORT void SetEnvironment(IEnvironment* environment) {
::environment = environment;
}