Merge uri_implementation branch to trunk.

This commit is contained in:
Daniel Önnerby 2008-12-07 21:47:32 +00:00
parent 6b45841056
commit 99de95c93d
88 changed files with 4168 additions and 1719 deletions

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,114 @@
/*
index: frame index data structure and functions
copyright 2007-8 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Thomas Orgis
*/
#include "index.h"
#include "debug.h"
/* The next expected frame offset, one step ahead. */
static off_t fi_next(struct frame_index *fi)
{
return (off_t)fi->fill*fi->step;
}
/* Shrink down the used index to the half.
Be careful with size = 1 ... there's no shrinking possible there. */
static void fi_shrink(struct frame_index *fi)
{
if(fi->fill < 2) return; /* Won't shrink below 1. */
else
{ /* Double the step, half the fill. Should work as well for fill%2 = 1 */
size_t c;
debug2("shrink index with fill %lu and step %lu", (unsigned long)fi->fill, (unsigned long)fi->step);
fi->step *= 2;
fi->fill /= 2;
/* Move the data down. */
for(c = 0; c < fi->fill; ++c)
fi->data[c] = fi->data[2*c];
}
fi->next = fi_next(fi);
}
void fi_init(struct frame_index *fi)
{
fi->data = NULL;
fi->step = 1;
fi->fill = 0;
fi->size = 0;
fi->grow_size = 0;
fi->next = fi_next(fi);
}
void fi_exit(struct frame_index *fi)
{
debug2("fi_exit: %p and %lu", (void*)fi->data, (unsigned long)fi->size);
if(fi->size && fi->data != NULL) free(fi->data);
fi_init(fi); /* Be prepared for further fun, still. */
}
int fi_resize(struct frame_index *fi, size_t newsize)
{
off_t *newdata = NULL;
if(newsize == fi->size) return 0;
if(newsize > 0 && newsize < fi->size)
{ /* When we reduce buffer size a bit, shrink stuff. */
while(fi->fill > newsize){ fi_shrink(fi); }
}
newdata = safe_realloc(fi->data, newsize*sizeof(off_t));
if(newsize == 0 || newdata != NULL)
{
fi->data = newdata;
fi->size = newsize;
if(fi->fill > fi->size) fi->fill = fi->size;
fi->next = fi_next(fi);
debug2("new index of size %lu at %p", (unsigned long)fi->size, (void*)fi->data);
return 0;
}
else
{
error("failed to resize index!");
return -1;
}
}
void fi_add(struct frame_index *fi, off_t pos)
{
debug3("wanting to add to fill %lu, step %lu, size %lu", (unsigned long)fi->fill, (unsigned long)fi->step, (unsigned long)fi->size);
if(fi->fill == fi->size)
{ /* Index is full, we need to shrink... or grow. */
/* Store the current frame number to check later if we still want it. */
off_t framenum = fi->fill*fi->step;
/* If we want not / cannot grow, we shrink. */
if( !(fi->grow_size && fi_resize(fi, fi->size+fi->grow_size)==0) )
fi_shrink(fi);
/* Now check if we still want to add this frame (could be that not, because of changed step). */
if(fi->next != framenum) return;
}
/* When we are here, we want that frame. */
if(fi->fill < fi->size) /* safeguard for size=1, or just generally */
{
debug1("adding to index at %p", (void*)(fi->data+fi->fill));
fi->data[fi->fill] = pos;
++fi->fill;
fi->next = fi_next(fi);
debug3("added pos %li to index with fill %lu and step %lu", (long) pos, (unsigned long)fi->fill, (unsigned long)fi->step);
}
}
void fi_reset(struct frame_index *fi)
{
debug1("reset with size %zu", fi->size);
fi->fill = 0;
fi->step = 1;
fi->next = fi_next(fi);
}

View File

@ -0,0 +1,56 @@
#ifndef MPG123_H_INDEX
#define MPG123_H_INDEX
/*
index: frame index data structure and functions
This is for keeping track of frame positions for accurate seeking.
Now in it's own file, with initial code from frame.c and parse.c .
The idea of the index with a certain amount of entries is to cover
all yet-encountered frame positions with minimal coarseness.
Meaning: At first every frame position is recorded, then, when
the index is full, every second position is trown away to make
space. Next time it is full, the same happens. And so on.
In this manner we maintain a good resolution with the given
maximum index size while covering the whole stream.
copyright 2007-8 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Thomas Orgis
*/
#include "config.h"
#include "compat.h"
struct frame_index
{
off_t *data; /* actual data, the frame positions */
off_t step; /* advancement in frame number per index point */
off_t next; /* frame offset supposed to come next into the index */
size_t size; /* total number of possible entries */
size_t fill; /* number of used entries */
size_t grow_size; /* if > 0: index allowed to grow on need with these steps, instead of lowering resolution */
};
/* The condition for a framenum to be appended to the index.
if(FI_NEXT(fr->index, fr->num)) fi_add(offset); */
#define FI_NEXT(fi, framenum) ((fi).size && framenum == (fi).next)
/* Initialize stuff, set things to zero and NULL... */
void fi_init(struct frame_index *fi);
/* Deallocate/zero things. */
void fi_exit(struct frame_index *fi);
/* Prepare a given size, preserving current fill, if possible.
If the new size is smaller than fill, the entry density is reduced.
Return 0 on success. */
int fi_resize(struct frame_index *fi, size_t newsize);
/* Append a frame position, reducing index density if needed. */
void fi_add(struct frame_index *fi, off_t pos);
/* Empty the index (setting fill=0 and step=1), but keep current size. */
void fi_reset(struct frame_index *fi);
#endif

159
src/core/GenericTrack.cpp Normal file
View File

@ -0,0 +1,159 @@
//////////////////////////////////////////////////////////////////////////////
//
// License Agreement:
//
// The following are Copyright © 2008, Daniel Önnerby
//
// 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 "pch.hpp"
#include <core/GenericTrack.h>
#include <core/NonLibraryTrackHelper.h>
//////////////////////////////////////////////////////////////////////////////
using namespace musik::core;
//////////////////////////////////////////////////////////////////////////////
TrackPtr GenericTrack::Create(const utfchar *uri){
GenericTrack *newTrack = new GenericTrack(uri);
TrackPtr track( newTrack );
if(newTrack){
newTrack->selfPtr = track;
}
// Add this to the NonLibraryTrackHelper to read the metadata
NonLibraryTrackHelper::Instance().ReadTrack(track);
return track;
}
GenericTrack::GenericTrack(void)
{
}
GenericTrack::GenericTrack(const utfchar *uri)
{
if(uri){
this->uri = uri;
}
}
GenericTrack::~GenericTrack(void){
}
const utfchar* GenericTrack::GetValue(const char* metakey){
if(metakey){
std::string metaKey(metakey);
{
boost::mutex::scoped_lock lock(NonLibraryTrackHelper::TrackMutex());
MetadataMap::iterator metavalue = this->metadata.find(metaKey);
if(metavalue!=this->metadata.end()){
return metavalue->second.c_str();
}
}
if(metaKey=="title"){
// In case there is no title
utfstring::size_type lastSlash = this->uri.find_last_of(UTF("/\\"));
if(lastSlash!=utfstring::npos){
static utfstring tempString;
tempString = this->uri.substr(lastSlash+1);
return tempString.c_str();
}else{
return this->uri.c_str();
}
}
// Lets try prepend "visual_"
if(metaKey.substr(0,7)=="visual_"){
metaKey = metaKey.substr(7);
return this->GetValue(metaKey.c_str());
}
}
return NULL;
}
void GenericTrack::SetValue(const char* metakey,const utfchar* value){
if(metakey && value){
boost::mutex::scoped_lock lock(NonLibraryTrackHelper::TrackMutex());
this->metadata.insert(std::pair<std::string,utfstring>(metakey,value));
}
}
void GenericTrack::ClearValue(const char* metakey){
boost::mutex::scoped_lock lock(NonLibraryTrackHelper::TrackMutex());
this->metadata.erase(metakey);
}
void GenericTrack::SetThumbnail(const char *data,long size){
}
const utfchar* GenericTrack::URI(){
if(!this->uri.empty()){
return this->uri.c_str();
}
return NULL;
}
const utfchar* GenericTrack::URL(){
if(!this->uri.empty()){
return this->uri.c_str();
}
return NULL;
}
Track::MetadataIteratorRange GenericTrack::GetValues(const char* metakey){
boost::mutex::scoped_lock lock(NonLibraryTrackHelper::TrackMutex());
return this->metadata.equal_range(metakey);
}
Track::MetadataIteratorRange GenericTrack::GetAllValues(){
return Track::MetadataIteratorRange(this->metadata.begin(),this->metadata.end());
}
TrackPtr GenericTrack::Copy(){
// Do not copy generic tracks, try to return a selfPtr instead
TrackPtr trackCopy;
if(trackCopy = this->selfPtr.lock()){
return trackCopy;
}
return GenericTrack::Create(this->uri.c_str());
}

86
src/core/GenericTrack.h Normal file
View File

@ -0,0 +1,86 @@
//////////////////////////////////////////////////////////////////////////////
//
// License Agreement:
//
// The following are Copyright © 2008, Daniel Önnerby
//
// 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/config_filesystem.h>
#include <core/Track.h>
#include <core/Library/Base.h>
#include <boost/weak_ptr.hpp>
//////////////////////////////////////////////////////////////////////////////
namespace musik{ namespace core{
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////
///\brief
///A GenericTrack is not related to any library. It must contain a URI
//////////////////////////////////////////
class GenericTrack : public Track {
public:
static TrackPtr Create(const utfchar *uri);
protected:
GenericTrack(void);
GenericTrack(const utfchar *uri);
public:
virtual ~GenericTrack(void);
virtual const utfchar* GetValue(const char* metakey);
virtual void SetValue(const char* metakey,const utfchar* value);
virtual void ClearValue(const char* metakey);
virtual void SetThumbnail(const char *data,long size);
virtual const utfchar* URI();
virtual const utfchar* URL();
virtual MetadataIteratorRange GetValues(const char* metakey);
virtual MetadataIteratorRange GetAllValues();
virtual TrackPtr Copy();
private:
// The variables
utfstring uri;
Track::MetadataMap metadata;
typedef boost::weak_ptr<Track> SelfWeakPtr;
SelfWeakPtr selfPtr;
};
//////////////////////////////////////////////////////////////////////////////
} } // musik::core
//////////////////////////////////////////////////////////////////////////////

View File

@ -42,11 +42,19 @@
namespace musik{ namespace core{
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////
///\brief
///The virtual base for all tracks
//////////////////////////////////////////
class ITrack {
public:
virtual const utfchar* GetValue(const char* metakey) const = 0;
virtual ~ITrack(){};
virtual const utfchar* GetValue(const char* metakey) = 0;
virtual void SetValue(const char* metakey,const utfchar* value) = 0;
virtual void SetThumbnail(const char *data,unsigned int size) = 0;
virtual void ClearValue(const char* metakey) = 0;
virtual void SetThumbnail(const char *data,long size) = 0;
virtual const utfchar* URI() = 0;
virtual const utfchar* URL() = 0;
};
//////////////////////////////////////////////////////////////////////////////

View File

@ -40,7 +40,7 @@
#include <core/config_filesystem.h>
#include <core/config_format.h>
#include <core/Track.h>
#include <core/LibraryTrack.h>
#include <core/db/Connection.h>
#include <core/db/Statement.h>
#include <core/PluginFactory.h>
@ -411,8 +411,8 @@ void Indexer::SyncDirectory(utfstring &sFolder,DBINT iParentFolderId,DBINT iPath
}
// This is a file, create a IndexerTrack object
//IndexerTrack oTrack(oFile->path());
musik::core::Track track;
track.InitMeta(NULL); // Not threadsafe, only used in this thread.
musik::core::LibraryTrack track;
// track.InitMeta(NULL); // Not threadsafe, only used in this thread.
// Get file-info from database
if(track.CompareDBAndFileInfo(oFile->path(),this->dbConnection,iFolderId)){

View File

@ -37,7 +37,6 @@
#include "pch.hpp"
#include <core/Library/Base.h>
#include <core/tracklist/Standard.h>
#include <core/config_filesystem.h>
#include <core/Query/Base.h>
@ -51,12 +50,14 @@ using namespace musik::core;
///\brief
///Constructor
//////////////////////////////////////////
Library::Base::Base(utfstring identifier)
:identifier(identifier)
Library::Base::Base(utfstring name,int id)
:name(name)
,id(id)
,queueCallbackStarted(false)
,exit(false)
,userId(1)
{
this->identifier = boost::lexical_cast<utfstring>(id);
}
//////////////////////////////////////////
@ -66,7 +67,7 @@ Library::Base::Base(utfstring identifier)
///\returns
///a tracklist::Ptr
//////////////////////////////////////////
musik::core::tracklist::Ptr Library::Base::NowPlaying(){
/*musik::core::tracklist::Ptr Library::Base::NowPlaying(){
if(tracklist::Ptr tracklist = this->nowPlaying.lock()){
return tracklist;
}
@ -78,7 +79,7 @@ musik::core::tracklist::Ptr Library::Base::NowPlaying(){
tracklist->SetLibrary(thisPtr);
}
return tracklist;
}
}*/
//////////////////////////////////////////
///\brief
@ -102,6 +103,18 @@ const utfstring& Library::Base::Identifier(){
return this->identifier;
}
int Library::Base::Id(){
return this->id;
}
//////////////////////////////////////////
///\brief
///Name of the library
//////////////////////////////////////////
const utfstring& Library::Base::Name(){
return this->name;
}
//////////////////////////////////////////
///\brief
///Get the directory-location of the library where you may store extra files.
@ -362,18 +375,35 @@ Query::Ptr Library::Base::GetNextQuery(){
///Returns true if all queues are finished
//////////////////////////////////////////
bool Library::Base::ClearFinishedQueries(){
// Remove old queries
boost::mutex::scoped_lock lock(this->libraryMutex);
std::vector<Query::Ptr> canceledQueries;
for(std::list<Query::Ptr>::iterator oCheckQuery=this->outgoingQueries.begin();oCheckQuery!=this->outgoingQueries.end();){
unsigned int status = (*oCheckQuery)->status;
if( (status & (Query::Base::Status::Finished | Query::Base::Status::Canceled)) ){
oCheckQuery = this->outgoingQueries.erase(oCheckQuery);
}else{
++oCheckQuery;
{
// Remove old queries
boost::mutex::scoped_lock lock(this->libraryMutex);
for(std::list<Query::Ptr>::iterator oCheckQuery=this->outgoingQueries.begin();oCheckQuery!=this->outgoingQueries.end();){
unsigned int status = (*oCheckQuery)->status;
if( (status & (Query::Base::Status::Finished | Query::Base::Status::Canceled)) ){
// If canceled
if( status & Query::Base::Status::Canceled ){
canceledQueries.push_back(*oCheckQuery);
}
oCheckQuery = this->outgoingQueries.erase(oCheckQuery);
}else{
++oCheckQuery;
}
}
}
return (this->incomingQueries.size()==0 && this->outgoingQueries.size()==0);
// Lets notify queries that they have been canceled
for(std::vector<Query::Ptr>::iterator query=canceledQueries.begin();query!=canceledQueries.end();++query){
(*query)->QueryFinished(query->get(),this,false);
}
{
boost::mutex::scoped_lock lock(this->libraryMutex);
return (this->incomingQueries.size()==0 && this->outgoingQueries.size()==0);
}
}
//////////////////////////////////////////
@ -411,10 +441,15 @@ bool Library::Base::RunCallbacks(){
if(oQuery){
if(oQuery->RunCallbacks(this)){
boost::mutex::scoped_lock lock(this->libraryMutex);
// Set to FINISHED if query returns true
oQuery->status |= Query::Base::Status::Finished;
{
boost::mutex::scoped_lock lock(this->libraryMutex);
// Set to FINISHED if query returns true
oQuery->status |= Query::Base::Status::Finished;
}
bAgain = true; // Continue to check results on the rest of the queue if this one is finished.
// Call the query signal that the query is finished
oQuery->QueryFinished(oQuery.get(),this,true);
}
}
this->ClearFinishedQueries();
@ -715,3 +750,4 @@ const std::string& Library::Base::AuthorizationKey(){
static std::string emptyAuthString;
return emptyAuthString;
}

View File

@ -57,7 +57,7 @@ namespace musik{ namespace core{
#include <core/config.h>
#include <core/db/Connection.h>
#include <core/tracklist/IRandomAccess.h>
//#include <core/tracklist/IRandomAccess.h>
#include <boost/thread/thread.hpp>
#include <boost/thread/condition.hpp>
@ -89,7 +89,7 @@ namespace musik{ namespace core{ namespace Library{
//////////////////////////////////////////
class Base : boost::noncopyable{
protected:
Base(utfstring identifier);
Base(utfstring name,int id);
public:
virtual ~Base(void);
@ -134,8 +134,10 @@ class Base : boost::noncopyable{
bool Exited();
const utfstring& Identifier();
int Id();
const utfstring& Name();
musik::core::tracklist::Ptr NowPlaying();
// musik::core::tracklist::Ptr NowPlaying();
virtual const std::string& AuthorizationKey();
@ -204,6 +206,15 @@ class Base : boost::noncopyable{
//////////////////////////////////////////
boost::mutex resultMutex;
//////////////////////////////////////////
///\brief
///This mutex is used by the LibraryTrack to protect the metadata map.
///
///\remarks
///This mutex needs to be public
//////////////////////////////////////////
boost::mutex trackMutex;
protected:
typedef std::list<Query::Ptr> QueryList;
@ -246,6 +257,13 @@ class Base : boost::noncopyable{
///GetLibraryDirectory
//////////////////////////////////////////
utfstring identifier;
int id;
//////////////////////////////////////////
///\brief
///Name of the library.
//////////////////////////////////////////
utfstring name;
//////////////////////////////////////////
///\brief
@ -265,7 +283,7 @@ class Base : boost::noncopyable{
bool exit;
boost::condition waitCondition;
musik::core::tracklist::WeakPtr nowPlaying;
// musik::core::tracklist::WeakPtr nowPlaying;
public:
boost::mutex libraryMutex;

View File

@ -53,8 +53,8 @@ using namespace musik::core;
///\see
///Startup
//////////////////////////////////////////
Library::LocalDB::LocalDB(utfstring identifier)
:Base(identifier)
Library::LocalDB::LocalDB(utfstring name,int id)
:Base(name,id)
{
}
@ -62,8 +62,8 @@ Library::LocalDB::LocalDB(utfstring identifier)
///\brief
///Create a LocalDB library
//////////////////////////////////////////
LibraryPtr Library::LocalDB::Create(utfstring identifier){
LibraryPtr lib(new Library::LocalDB(identifier));
LibraryPtr Library::LocalDB::Create(utfstring name,int id){
LibraryPtr lib(new Library::LocalDB(name,id));
lib->self = lib;
return lib;
}

View File

@ -70,10 +70,10 @@ namespace musik{ namespace core{ namespace Library{
//////////////////////////////////////////
class LocalDB : public Library::Base{
private:
LocalDB(utfstring identifier);
LocalDB(utfstring name,int id);
public:
// Methods:
static LibraryPtr Create(utfstring identifier);
static LibraryPtr Create(utfstring name,int id);
~LocalDB(void);
bool Startup();

View File

@ -58,8 +58,8 @@ using namespace musik::core;
///\see
///Startup
//////////////////////////////////////////
Library::Remote::Remote(utfstring identifier)
:Base(identifier)
Library::Remote::Remote(utfstring name,int id)
:Base(name,id)
,socket(ioService)
,httpPort("10544")
{
@ -69,8 +69,8 @@ Library::Remote::Remote(utfstring identifier)
///\brief
///Create a Remote library
//////////////////////////////////////////
LibraryPtr Library::Remote::Create(utfstring identifier){
LibraryPtr lib(new Library::Remote(identifier));
LibraryPtr Library::Remote::Create(utfstring name,int id){
LibraryPtr lib(new Library::Remote(name,id));
lib->self = lib;
return lib;
}
@ -125,7 +125,7 @@ void Library::Remote::ReadThread(){
std::string username,password;
{
Preferences prefs("Connection",this->Identifier().c_str());
Preferences prefs("Connection",this->Name().c_str());
this->address = UTF_TO_UTF8(prefs.GetString("address",UTF("localhost")));
this->port = UTF_TO_UTF8(prefs.GetString("port",UTF("10543")));

View File

@ -59,10 +59,10 @@ namespace musik{ namespace core{ namespace Library{
//////////////////////////////////////////
class Remote : public Library::Base{
private:
Remote(utfstring identifier);
Remote(utfstring name,int id);
public:
// Methods:
static LibraryPtr Create(utfstring identifier);
static LibraryPtr Create(utfstring name,int id);
~Remote(void);
bool Startup();

View File

@ -43,7 +43,14 @@
using namespace musik::core;
LibraryFactory LibraryFactory::sInstance;
//LibraryFactory LibraryFactory::sInstance;
LibraryFactory& LibraryFactory::Instance(){
typedef boost::shared_ptr<LibraryFactory> InstanceType;
static InstanceType sInstance(new LibraryFactory());
return *sInstance;
};
//////////////////////////////////////////
///\brief
@ -59,10 +66,10 @@ LibraryFactory::LibraryFactory(void){
Preferences::CreateDB(db);
// Get the libraries
db::Statement stmtGetLibs("SELECT name,type FROM libraries ORDER BY id",db);
db::Statement stmtGetLibs("SELECT id,name,type FROM libraries ORDER BY id",db);
while(stmtGetLibs.Step()==db::Row){
this->AddLibrary( stmtGetLibs.ColumnTextUTF(0),stmtGetLibs.ColumnInt(1) );
this->AddLibrary( stmtGetLibs.ColumnInt(0),stmtGetLibs.ColumnTextUTF(1),stmtGetLibs.ColumnInt(2) );
}
// If there are no libraries, add a LocalDB
@ -98,18 +105,19 @@ LibraryFactory::~LibraryFactory(void){
///\returns
///LibraryPtr of the added library. (NULL pointer on failure)
//////////////////////////////////////////
LibraryPtr LibraryFactory::AddLibrary(utfstring name,int type,bool sendEvent,bool startup){
LibraryPtr LibraryFactory::AddLibrary(int id,utfstring name,int type,bool sendEvent,bool startup){
LibraryPtr lib;
switch(type){
case Types::Remote:
lib = Library::Remote::Create(name);
lib = Library::Remote::Create(name,id);
break;
default:
lib = Library::LocalDB::Create(name);
lib = Library::LocalDB::Create(name,id);
}
if(lib){
this->libraries.push_back(lib);
this->libraryMap[id] = lib;
if(sendEvent){
this->LibrariesUpdated();
@ -156,7 +164,7 @@ LibraryPtr LibraryFactory::CreateLibrary(utfstring name,int type,bool startup){
stmtInsert.BindTextUTF(0,name);
stmtInsert.BindInt(1,type);
if(stmtInsert.Step()==db::Done){
return this->AddLibrary(name,type,true,startup);
return this->AddLibrary(db.LastInsertedId(),name,type,true,startup);
}
return LibraryPtr();
}
@ -169,6 +177,16 @@ void LibraryFactory::DeleteLibrary(utfstring name){
///Get the vector with all current libraries
//////////////////////////////////////////
LibraryFactory::LibraryVector& LibraryFactory::Libraries(){
return LibraryFactory::sInstance.libraries;
return LibraryFactory::Instance().libraries;
}
LibraryPtr LibraryFactory::GetLibrary(int identifier){
if(identifier){
LibraryMap::iterator lib = this->libraryMap.find(identifier);
if(lib!=this->libraryMap.end()){
return lib->second;
}
}
return LibraryPtr();
}

View File

@ -38,6 +38,7 @@
#include <core/config.h>
#include <core/Library/Base.h>
#include <sigslot/sigslot.h>
#include <map>
//////////////////////////////////////////////////////////////////////////////
@ -55,7 +56,7 @@ namespace musik{ namespace core{
//////////////////////////////////////////
class LibraryFactory{
private:
static LibraryFactory sInstance;
// static LibraryFactory sInstance;
public:
//////////////////////////////////////////
@ -68,18 +69,21 @@ class LibraryFactory{
};
typedef std::vector<LibraryPtr> LibraryVector;
typedef std::map<int,LibraryPtr> LibraryMap;
//////////////////////////////////////////
///\brief
///Get the LibraryFactory singleton
//////////////////////////////////////////
static LibraryFactory& Instance(){ return sInstance; };
static LibraryFactory& Instance();
static LibraryVector& Libraries();
LibraryPtr CreateLibrary(utfstring name,int type,bool startup=true);
void DeleteLibrary(utfstring name);
LibraryPtr GetLibrary(int identifier);
typedef sigslot::signal0<> LibrariesUpdatedEvent;
//////////////////////////////////////////
@ -88,14 +92,15 @@ class LibraryFactory{
//////////////////////////////////////////
LibrariesUpdatedEvent LibrariesUpdated;
~LibraryFactory(void);
private:
LibraryVector libraries;
LibraryMap libraryMap;
LibraryFactory(void);
~LibraryFactory(void);
LibraryPtr AddLibrary(utfstring name,int type,bool sendEvent=false,bool startup=true);
LibraryPtr AddLibrary(int id,utfstring name,int type,bool sendEvent=false,bool startup=true);
void RemoveLibrary(utfstring name);
};

635
src/core/LibraryTrack.cpp Normal file
View File

@ -0,0 +1,635 @@
//////////////////////////////////////////////////////////////////////////////
//
// License Agreement:
//
// The following are Copyright © 2008, Daniel Önnerby
//
// 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 "pch.hpp"
#include <core/LibraryTrack.h>
#include <core/LibraryFactory.h>
#include <core/Common.h>
#include <core/db/Connection.h>
#include <core/db/Statement.h>
#include <core/Library/Base.h>
#include <core/filestreams/Factory.h>
#include <boost/lexical_cast.hpp>
#include <boost/thread/mutex.hpp>
//////////////////////////////////////////////////////////////////////////////
using namespace musik::core;
//////////////////////////////////////////////////////////////////////////////
LibraryTrack::LibraryTrack(void)
:meta(NULL)
,id(0)
,libraryId(0)
{
}
LibraryTrack::LibraryTrack(DBINT id,int libraryId)
:meta(NULL)
,id(id)
,libraryId(libraryId)
{
}
LibraryTrack::LibraryTrack(DBINT id,musik::core::LibraryPtr library)
:meta(NULL)
,id(id)
,libraryId(library->Id())
{
}
LibraryTrack::~LibraryTrack(void){
if(this->meta){
delete this->meta;
this->meta = NULL;
}
}
const utfchar* LibraryTrack::GetValue(const char* metakey){
if(metakey && this->meta){
if(this->meta->library){
// Threadsafe
boost::mutex::scoped_lock lock(this->meta->library->trackMutex);
MetadataMap::iterator metavalue = this->meta->metadata.find(metakey);
if(metavalue!=this->meta->metadata.end()){
return metavalue->second.c_str();
}
}else{
MetadataMap::iterator metavalue = this->meta->metadata.find(metakey);
if(metavalue!=this->meta->metadata.end()){
return metavalue->second.c_str();
}
}
}
return NULL;
}
void LibraryTrack::SetValue(const char* metakey,const utfchar* value){
this->InitMeta();
if(metakey && value){
if(this->meta->library){
// Threadsafe
boost::mutex::scoped_lock lock(this->meta->library->trackMutex);
this->meta->metadata.insert(std::pair<std::string,utfstring>(metakey,value));
}else{
this->meta->metadata.insert(std::pair<std::string,utfstring>(metakey,value));
}
}
}
void LibraryTrack::ClearValue(const char* metakey){
if(this->meta){
if(this->meta->library){
boost::mutex::scoped_lock lock(this->meta->library->trackMutex);
this->meta->metadata.erase(metakey);
}else{
this->meta->metadata.erase(metakey);
}
}
}
void LibraryTrack::SetThumbnail(const char *data,long size){
this->InitMeta();
if(this->meta->thumbnailData)
delete this->meta->thumbnailData;
this->meta->thumbnailData = new char[size];
this->meta->thumbnailSize = size;
memcpy(this->meta->thumbnailData,data,size);
}
const utfchar* LibraryTrack::URI(){
static utfstring uri;
if(this->meta){
uri = UTF("mcdb://")+this->meta->library->Identifier()+UTF("/")+boost::lexical_cast<utfstring>(this->id);
return uri.c_str();
}else{
uri = UTF("mcdb://")+boost::lexical_cast<utfstring>(this->libraryId)+UTF("/")+boost::lexical_cast<utfstring>(this->id);
return uri.c_str();
}
return NULL;
}
const utfchar* LibraryTrack::URL(){
return this->GetValue("path");
}
Track::MetadataIteratorRange LibraryTrack::GetValues(const char* metakey){
if(this->meta){
if(this->meta->library){
boost::mutex::scoped_lock lock(this->meta->library->trackMutex);
return this->meta->metadata.equal_range(metakey);
}else{
return this->meta->metadata.equal_range(metakey);
}
}
return Track::MetadataIteratorRange();
}
Track::MetadataIteratorRange LibraryTrack::GetAllValues(){
if(this->meta){
return Track::MetadataIteratorRange(this->meta->metadata.begin(),this->meta->metadata.end());
}
return Track::MetadataIteratorRange();
}
DBINT LibraryTrack::Id(){
return this->id;
}
musik::core::LibraryPtr LibraryTrack::Library(){
if(this->meta){
return this->meta->library;
}
return LibraryFactory::Instance().GetLibrary(this->libraryId);
}
int LibraryTrack::LibraryId(){
return this->libraryId;
}
void LibraryTrack::InitMeta(){
if(!this->meta){
// Create the metadata
this->meta = new MetaData();
if(this->libraryId){
this->meta->library = LibraryFactory::Instance().GetLibrary(this->libraryId);
}
}
}
bool LibraryTrack::CompareDBAndFileInfo(const boost::filesystem::utfpath &file,db::Connection &dbConnection,DBINT currentFolderId){
this->SetValue("path",file.string().c_str());
this->SetValue("filename",file.leaf().c_str());
utfstring::size_type lastDot = file.leaf().find_last_of(UTF("."));
if(lastDot!=utfstring::npos){
this->SetValue("extension",file.leaf().substr(lastDot+1).c_str());
}
DBINT fileSize = (DBINT)boost::filesystem::file_size(file);
DBTIME fileTime = (DBTIME)boost::filesystem::last_write_time(file);
this->SetValue("filesize",boost::lexical_cast<utfstring>(fileSize).c_str());
this->SetValue("filetime",boost::lexical_cast<utfstring>(fileTime).c_str());
db::CachedStatement stmt("SELECT id,filename,filesize,filetime FROM tracks t WHERE folder_id=? AND filename=?",dbConnection);
stmt.BindInt(0,currentFolderId);
stmt.BindTextUTF(1,this->GetValue("filename"));
bool fileDifferent(true);
if(stmt.Step()==db::ReturnCode::Row){
// File found in database.
this->id = stmt.ColumnInt(0);
fileDifferent = false;
// Check if the track needs to be reread.
if(fileSize!=stmt.ColumnInt(2) || fileTime!=stmt.ColumnInt(3)){
fileDifferent = true;
}
}
return fileDifferent;
}
bool LibraryTrack::Save(db::Connection &dbConnection,utfstring libraryDirectory,DBINT folderId){
MetadataMap metadataCopy(this->meta->metadata);
unsigned int count;
db::ScopedTransaction transaction(dbConnection);
//////////////////////////////////////////////////////////////////////////////
// Start by removing existing relations
if(this->id!=0){
db::CachedStatement deleteGenre("DELETE FROM track_genres WHERE track_id=?",dbConnection);
deleteGenre.BindInt(0,this->id);
deleteGenre.Step();
db::CachedStatement deleteArtist("DELETE FROM track_artists WHERE track_id=?",dbConnection);
deleteArtist.BindInt(0,this->id);
deleteArtist.Step();
db::CachedStatement deleteMeta("DELETE FROM track_meta WHERE track_id=?",dbConnection);
deleteMeta.BindInt(0,this->id);
deleteMeta.Step();
}
//////////////////////////////////////////////////////////////////////////////
// 1. Start by inserting as many tags as possible into the tracks-table
{
db::CachedStatement stmt("INSERT OR REPLACE INTO tracks (id,track,bpm,duration,filesize,year,folder_id,title,filename,filetime) VALUES (?,?,?,?,?,?,?,?,?,?)",dbConnection);
// Bind all "static" tags (the once that are in the tracks tables)
stmt.BindTextUTF(1,this->GetValue("track"));
stmt.BindTextUTF(2,this->GetValue("bpm"));
stmt.BindTextUTF(3,this->GetValue("duration"));
stmt.BindTextUTF(4,this->GetValue("filesize"));
stmt.BindTextUTF(5,this->GetValue("year"));
stmt.BindInt(6,folderId);
stmt.BindTextUTF(7,this->GetValue("title"));
stmt.BindTextUTF(8,this->GetValue("filename"));
stmt.BindTextUTF(9,this->GetValue("filetime"));
if(this->id!=0){
stmt.BindInt(0,this->id);
}
if(stmt.Step()==db::ReturnCode::Done){
if(this->id==0){
this->id = dbConnection.LastInsertedId();
}
}
}
//////////////////////////////////////////////////////////////////////////////
// 2. Remove tags that has been used.
metadataCopy.erase("track");
metadataCopy.erase("bpm");
metadataCopy.erase("duration");
metadataCopy.erase("year");
metadataCopy.erase("title");
metadataCopy.erase("filename");
metadataCopy.erase("filetime");
metadataCopy.erase("filesize");
metadataCopy.erase("title");
metadataCopy.erase("path");
metadataCopy.erase("extension");
//////////////////////////////////////////////////////////////////////////////
// 3. Read genres
utfstring visualGenres;
DBINT genreId(0);
count = 0;
std::set<utfstring> alreadySetGenres; // Cache for not saving duplicates
MetadataIteratorRange genres = this->GetValues("genre");
while(genres.first!=genres.second){
if(alreadySetGenres.find(genres.first->second)==alreadySetGenres.end()){
alreadySetGenres.insert(genres.first->second);
// First save the genre
genreId = this->_GetGenre(dbConnection,genres.first->second,true);
// Second, add to the visual genre
if(count!=0){
visualGenres += UTF(", ");
}
visualGenres += genres.first->second;
++count;
}
++genres.first;
}
if(count>1 || genreId==0){
// Check for new genre, but do not insert the relation, this is the visual_genre_id field in the database
genreId = this->_GetGenre(dbConnection,visualGenres,false,true);
}
metadataCopy.erase("genre");
//////////////////////////////////////////////////////////////////////////////
// 4. Read artists
utfstring visualArtists;
DBINT artistId(0);
count = 0;
std::set<utfstring> alreadySetArtists; // Cache for not saving duplicates
MetadataIteratorRange artists = this->GetValues("artist");
while(artists.first!=artists.second){
if(alreadySetArtists.find(artists.first->second)==alreadySetArtists.end()){
alreadySetArtists.insert(artists.first->second);
// First save the artist
artistId = this->_GetArtist(dbConnection,artists.first->second,true);
// Second, add to the visual artist
if(count!=0){
visualArtists += UTF(", ");
}
visualArtists += artists.first->second;
++count;
}
++artists.first;
}
if(count>1 || artistId==0){
// Check for new artist, but do not insert the relation, this is the visual_artist_id field in the database
artistId = this->_GetArtist(dbConnection,visualArtists,false,true);
}
metadataCopy.erase("artist");
//////////////////////////////////////////////////////////////////////////////
// 5. Read album
DBINT albumId(0);
{
db::CachedStatement stmt("SELECT id FROM albums WHERE name=?",dbConnection);
const utfchar *album=this->GetValue("album");
if(album==NULL){
album=UTF("");
}
stmt.BindTextUTF(0,album);
if(stmt.Step()==db::ReturnCode::Row){
albumId = stmt.ColumnInt(0);
}else{
// INSERT a new album
db::Statement insertAlbum("INSERT INTO albums (name) VALUES (?)",dbConnection);
insertAlbum.BindTextUTF(0,album);
if(insertAlbum.Step()==db::ReturnCode::Done){
albumId = dbConnection.LastInsertedId();
}
}
}
metadataCopy.erase("album");
//////////////////////////////////////////////////////////////////////////////
// 6. Thumbnail
DBINT thumbnailId(0);
if(this->meta->thumbnailData){
UINT64 sum = Checksum(this->meta->thumbnailData,this->meta->thumbnailSize);
db::CachedStatement thumbs("SELECT id FROM thumbnails WHERE filesize=? AND checksum=?",dbConnection);
thumbs.BindInt(0,this->meta->thumbnailSize);
thumbs.BindInt64(1,sum);
if(thumbs.Step()==db::ReturnCode::Row){
thumbnailId = thumbs.ColumnInt(0);
}
if(thumbnailId==0){
// INSERT thumbnail
db::Statement insertThumb("INSERT INTO thumbnails (filesize,checksum) VALUES (?,?)",dbConnection);
insertThumb.BindInt(0,this->meta->thumbnailSize);
insertThumb.BindInt64(1,sum);
if(insertThumb.Step()==db::ReturnCode::Done){
thumbnailId = dbConnection.LastInsertedId();
}
// Save the file
utfstring filename = libraryDirectory+UTF("thumbs/")+boost::lexical_cast<utfstring>(thumbnailId)+UTF(".jpg");
// TODO, fix for UTF wide of not
FILE *thumbFile = _wfopen(filename.c_str(),UTF("wb"));
fwrite(this->meta->thumbnailData,sizeof(char),this->meta->thumbnailSize,thumbFile);
fclose(thumbFile);
}
}
//////////////////////////////////////////////////////////////////////////////
// 5. Update tracks table with relations
{
db::CachedStatement stmt("UPDATE tracks SET album_id=?,visual_genre_id=?,visual_artist_id=?,thumbnail_id=? WHERE id=?",dbConnection);
stmt.BindInt(0,albumId);
stmt.BindInt(1,genreId);
stmt.BindInt(2,artistId);
stmt.BindInt(3,thumbnailId);
stmt.BindInt(4,this->id);
stmt.Step();
}
//////////////////////////////////////////////////////////////////////////////
// 6. Fields that are left in tagsCopy should be meta-data (extra tags)
{
db::CachedStatement selectMetaKey("SELECT id FROM meta_keys WHERE name=?",dbConnection);
db::CachedStatement selectMetaValue("SELECT id FROM meta_values WHERE meta_key_id=? AND content=?",dbConnection);
db::CachedStatement insertMetaValue("INSERT INTO meta_values (meta_key_id,content) VALUES (?,?)",dbConnection);
db::CachedStatement insertTrackMeta("INSERT INTO track_meta (track_id,meta_value_id) VALUES (?,?)",dbConnection);
// Loop through the tags
for(MetadataMap::const_iterator metaData=metadataCopy.begin();metaData!=metadataCopy.end();++metaData){
// 1. Find the meta_key
DBINT metaKeyId(0);
std::string key;
selectMetaKey.BindText(0,metaData->first);
if(selectMetaKey.Step()==db::ReturnCode::Row){
// key found
metaKeyId = selectMetaKey.ColumnInt(0);
}else{
// key not found, INSERT
db::CachedStatement insertMetaKey("INSERT INTO meta_keys (name) VALUES (?)",dbConnection);
insertMetaKey.BindText(0,metaData->first);
if(insertMetaKey.Step()==db::ReturnCode::Done){
metaKeyId = dbConnection.LastInsertedId();
}
}
selectMetaKey.Reset();
// 2. Find meta value
if(metaKeyId!=0){
DBINT metaValueId(0);
// 2.1 Find meta_value
selectMetaValue.BindInt(0,metaKeyId);
selectMetaValue.BindTextUTF(1,metaData->second);
if(selectMetaValue.Step()==db::ReturnCode::Row){
// Value found
metaValueId = selectMetaValue.ColumnInt(0);
}else{
// Value not found, INSERT
insertMetaValue.BindInt(0,metaKeyId);
insertMetaValue.BindTextUTF(1,metaData->second);
if(insertMetaValue.Step()==db::ReturnCode::Done){
metaValueId = dbConnection.LastInsertedId();
}
insertMetaValue.Reset();
}
selectMetaValue.Reset();
// 3. INSERT into the track_meta table
if(metaValueId!=0){
insertTrackMeta.BindInt(0,this->id);
insertTrackMeta.BindInt(1,metaValueId);
insertTrackMeta.Step();
insertTrackMeta.Reset();
}
}
}
}
return true;
}
DBINT LibraryTrack::_GetGenre(db::Connection &dbConnection,utfstring genre,bool addRelation,bool aggregated){
DBINT genreId(0);
{
db::CachedStatement stmt("SELECT id FROM genres WHERE name=?",dbConnection);
stmt.BindTextUTF(0,genre);
if(stmt.Step()==db::ReturnCode::Row){
genreId = stmt.ColumnInt(0);
}
}
if(genreId==0){
// Insert the genre
DBINT aggregatedInt = (aggregated?1:0);
db::CachedStatement stmt("INSERT INTO genres (name,aggregated) VALUES (?,?)",dbConnection);
stmt.BindTextUTF(0,genre);
stmt.BindInt(1,aggregatedInt);
if(stmt.Step()==db::ReturnCode::Done){
genreId = dbConnection.LastInsertedId();
}
}
if(addRelation){
// Insert into track_genres
db::Statement stmt("INSERT INTO track_genres (track_id,genre_id) VALUES (?,?)",dbConnection);
stmt.BindInt(0,this->id);
stmt.BindInt(1,genreId);
stmt.Step();
}
return genreId;
}
DBINT LibraryTrack::_GetArtist(db::Connection &dbConnection,utfstring artist,bool addRelation,bool aggregated){
DBINT artistId(0);
db::CachedStatement stmt("SELECT id FROM artists WHERE name=?",dbConnection);
stmt.BindTextUTF(0,artist);
if(stmt.Step()==db::ReturnCode::Row){
artistId = stmt.ColumnInt(0);
}
if(artistId==0){
// Insert the genre
DBINT aggregatedInt = (aggregated?1:0);
db::Statement insertArtist("INSERT INTO artists (name,aggregated) VALUES (?,?)",dbConnection);
insertArtist.BindTextUTF(0,artist);
insertArtist.BindInt(1,aggregatedInt);
if(insertArtist.Step()==db::ReturnCode::Done){
artistId = dbConnection.LastInsertedId();
}
}
if(addRelation){
// Insert into track_genres
db::CachedStatement insertRel("INSERT INTO track_artists (track_id,artist_id) VALUES (?,?)",dbConnection);
insertRel.BindInt(0,this->id);
insertRel.BindInt(1,artistId);
insertRel.Step();
}
return artistId;
}
TrackPtr LibraryTrack::Copy(){
return TrackPtr(new LibraryTrack(this->id,this->libraryId));
}
bool LibraryTrack::GetFileData(DBINT id,db::Connection &db){
this->InitMeta();
this->id = id;
db::CachedStatement stmt("SELECT t.filename,t.filesize,t.filetime,p.path||f.relative_path||'/'||t.filename FROM tracks t,folders f,paths p WHERE t.folder_id=f.id AND f.path_id=p.id AND t.id=?",db);
stmt.BindInt(0,id);
if(stmt.Step()==db::Row){
this->SetValue("filename" ,stmt.ColumnTextUTF(0));
this->SetValue("filesize" ,stmt.ColumnTextUTF(1));
this->SetValue("filetime" ,stmt.ColumnTextUTF(2));
this->SetValue("path" ,stmt.ColumnTextUTF(3));
return true;
}
return false;
}
LibraryTrack::MetaData::MetaData()
:thumbnailData(NULL)
,thumbnailSize(0)
{
}
LibraryTrack::MetaData::~MetaData(){
if(this->thumbnailData)
delete this->thumbnailData;
}

122
src/core/LibraryTrack.h Normal file
View File

@ -0,0 +1,122 @@
//////////////////////////////////////////////////////////////////////////////
//
// License Agreement:
//
// The following are Copyright © 2008, Daniel Önnerby
//
// 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/config_filesystem.h>
#include <core/Track.h>
#include <core/Library/Base.h>
//////////////////////////////////////////////////////////////////////////////
// Forward declare
namespace musik{ namespace core{ namespace http{
class Responder;
} } }
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
namespace musik{ namespace core{
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////
///\brief
///A LibraryTrack is a track related to a Library.
//////////////////////////////////////////
class LibraryTrack : public Track {
public:
LibraryTrack(void);
LibraryTrack(DBINT id,int libraryId);
LibraryTrack(DBINT id,musik::core::LibraryPtr library);
virtual ~LibraryTrack(void);
virtual const utfchar* GetValue(const char* metakey);
virtual void SetValue(const char* metakey,const utfchar* value);
virtual void ClearValue(const char* metakey);
virtual void SetThumbnail(const char *data,long size);
virtual const utfchar* URI();
virtual const utfchar* URL();
virtual MetadataIteratorRange GetValues(const char* metakey);
virtual MetadataIteratorRange GetAllValues();
virtual TrackPtr Copy();
virtual DBINT Id();
virtual musik::core::LibraryPtr Library();
virtual int LibraryId();
private:
// The variables
DBINT id;
int libraryId;
private:
class MetaData{
public:
MetaData();
~MetaData();
Track::MetadataMap metadata;
char *thumbnailData;
long thumbnailSize;
musik::core::LibraryPtr library;
};
MetaData *meta;
private:
void InitMeta();
// Some special methods for the Indexer
friend class Indexer;
bool CompareDBAndFileInfo(const boost::filesystem::utfpath &file,db::Connection &dbConnection,DBINT currentFolderId);
bool Save(db::Connection &dbConnection,utfstring libraryDirectory,DBINT folderId);
DBINT _GetGenre(db::Connection &dbConnection,utfstring genre,bool addRelation,bool aggregated=false);
DBINT _GetArtist(db::Connection &dbConnection,utfstring artist,bool addRelation,bool aggregated=false);
private:
// Some special methods for the http::Responder
friend class http::Responder;
bool GetFileData(DBINT id,db::Connection &db);
};
//////////////////////////////////////////////////////////////////////////////
} } // musik::core
//////////////////////////////////////////////////////////////////////////////

View File

@ -0,0 +1,145 @@
//////////////////////////////////////////////////////////////////////////////
//
// License Agreement:
//
// The following are Copyright © 2008, Daniel Önnerby
//
// 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 "pch.hpp"
#include <core/NonLibraryTrackHelper.h>
#include <boost/bind.hpp>
#include <core/PluginFactory.h>
#include <core/Plugin/IMetaDataReader.h>
//////////////////////////////////////////////////////////////////////////////
using namespace musik::core;
//////////////////////////////////////////////////////////////////////////////
NonLibraryTrackHelper NonLibraryTrackHelper::sInstance;
NonLibraryTrackHelper::NonLibraryTrackHelper(void)
:threadIsRunning(false)
{
}
NonLibraryTrackHelper::~NonLibraryTrackHelper(void){
}
NonLibraryTrackHelper& NonLibraryTrackHelper::Instance(){
return NonLibraryTrackHelper::sInstance;
}
boost::mutex& NonLibraryTrackHelper::TrackMutex(){
return NonLibraryTrackHelper::sInstance.trackMutex;
}
void NonLibraryTrackHelper::ReadTrack(musik::core::TrackPtr track){
bool threadRunning(false);
{
boost::mutex::scoped_lock lock(this->mutex);
this->tracksToRead.push_back(TrackWeakPtr(track));
threadRunning = this->threadIsRunning;
}
if(!threadRunning){
if(this->helperThread){
this->helperThread->join();
}
this->helperThread.reset(new boost::thread(boost::bind(&NonLibraryTrackHelper::ThreadLoop,this)));
}
}
void NonLibraryTrackHelper::ThreadLoop(){
// Get the metadatareaders
typedef Plugin::IMetaDataReader PluginType;
typedef PluginFactory::DestroyDeleter<PluginType> Deleter;
typedef std::vector<boost::shared_ptr<Plugin::IMetaDataReader>> MetadataReaderList;
MetadataReaderList metadataReaders = PluginFactory::Instance().QueryInterface<PluginType, Deleter>("GetMetaDataReader");
// pop a track, read the metadata, notify and continue
bool moreTracks(true);
while(moreTracks){
moreTracks = false;
musik::core::TrackPtr track;
{
boost::mutex::scoped_lock lock(this->mutex);
if(!this->tracksToRead.empty()){
// is this a valid track
track = this->tracksToRead.front().lock();
this->tracksToRead.pop_front();
}
moreTracks = !this->tracksToRead.empty();
if(!moreTracks){
// Set to "not running". No locking beyond this point.
this->threadIsRunning = false;
}
}
if(track){
utfstring url(track->URL());
utfstring::size_type lastDot = url.find_last_of(UTF("."));
if(lastDot!=utfstring::npos){
track->SetValue("extension",url.substr(lastDot+1).c_str());
}
// Read track metadata
typedef MetadataReaderList::iterator Iterator;
Iterator it = metadataReaders.begin();
while (it != metadataReaders.end()) {
if((*it)->CanReadTag(track->GetValue("extension")) ){
// Should be able to read the tag
if( (*it)->ReadTag(track.get()) ){
// Successfully read the tag.
// tagRead=true;
}
}
it++;
}
// Lets notify that tracks has been read
this->TrackMetadataUpdated(track);
}
}
}

View File

@ -36,20 +36,14 @@
#pragma once
//////////////////////////////////////////////////////////////////////////////
// Forwar declare
namespace musik{ namespace core{
class Track;
namespace Library{
class Base;
}
} }
//////////////////////////////////////////////////////////////////////////////
#include <core/config.h>
#include <map>
#include <boost/utility.hpp>
#include <core/config_filesystem.h>
#include <core/Track.h>
#include <boost/scoped_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/thread.hpp>
#include <sigslot/sigslot.h>
#include <list>
//////////////////////////////////////////////////////////////////////////////
@ -57,41 +51,39 @@ namespace musik{ namespace core{
//////////////////////////////////////////////////////////////////////////////
class TrackMeta : boost::noncopyable{
class NonLibraryTrackHelper{
public:
TrackMeta(Library::Base *library);
~TrackMeta(void);
static NonLibraryTrackHelper& Instance();
static boost::mutex& TrackMutex();
void ReadTrack(musik::core::TrackPtr track);
typedef std::string Key;
typedef utfstring Value;
typedef std::multimap<Key,Value> TagMap;
typedef TagMap::iterator TagMapIterator;
typedef TagMap::const_iterator TagMapConstIterator;
typedef std::pair<TagMapConstIterator,TagMapConstIterator> TagMapIteratorPair;
typedef std::pair<Key,Value> TagMapPair;
typedef sigslot::signal1<musik::core::TrackPtr> TrackMetadataUpdatedEvent;
TrackMetadataUpdatedEvent TrackMetadataUpdated;
private:
friend class Track;
static NonLibraryTrackHelper sInstance;
const utfstring& GetValue(const Key &key) const;
TrackMeta::TagMapIteratorPair GetValues(const char* metakey) const;
void SetValue(const Key &key,const Value &value);
void ClearValue(const Key &key);
const utfchar* GetValue(const char* metakey) const;
NonLibraryTrackHelper(void);
~NonLibraryTrackHelper(void);
Library::Base *library;
TrackMeta::TagMap tags;
char *thumbnailData;
unsigned int thumbnailSize;
void SetThumbnail(const char *data,unsigned int size);
void ThreadLoop();
private:
const Value& _GetValue(const Key &key) const;
boost::mutex trackMutex;
boost::mutex mutex;
bool threadIsRunning;
typedef boost::weak_ptr<musik::core::Track> TrackWeakPtr;
std::list<TrackWeakPtr> tracksToRead;
typedef boost::scoped_ptr<boost::thread> ThreadPtr;
ThreadPtr helperThread;
};
//////////////////////////////////////////////////////////////////////////////
} } // musik::core
//////////////////////////////////////////////////////////////////////////////

View File

@ -33,6 +33,7 @@
#include "pch.hpp"
#include <core/PlaybackQueue.h>
#include <core/LibraryFactory.h>
#include <core/tracklist/MultiLibraryList.h>
#include <boost/algorithm/string.hpp>
@ -55,6 +56,7 @@ PlaybackQueue::PlaybackQueue(void)
,playing(false)
,paused(false)
{
this->nowPlaying.reset(new tracklist::MultiLibraryList());
this->transport.EventMixpointReached.connect(this,&PlaybackQueue::OnPlaybackEndOrFail);
}
@ -65,7 +67,6 @@ PlaybackQueue::PlaybackQueue(void)
PlaybackQueue::~PlaybackQueue(void)
{
this->transport.Stop();
this->nowPlaying.reset();
}
//////////////////////////////////////////
@ -94,7 +95,7 @@ tracklist::Ptr PlaybackQueue::NowPlayingTracklist(){
///Start playing the current track.
//////////////////////////////////////////
void PlaybackQueue::Play(){
if( !this->nowPlaying->Library()->Exited() ){
// if( !this->nowPlaying->Library()->Exited() ){
TrackPtr track(this->CurrentTrack());
if(track){
@ -105,7 +106,7 @@ void PlaybackQueue::Play(){
this->paused = false;
}
}
// }
}
//////////////////////////////////////////
@ -139,12 +140,10 @@ void PlaybackQueue::Resume()
///Start playing the next track.
//////////////////////////////////////////
void PlaybackQueue::Next(){
if(this->nowPlaying){
musik::core::TrackPtr track( this->nowPlaying->NextTrack() );
musik::core::TrackPtr track( this->nowPlaying->NextTrack() );
this->SetCurrentTrack(track);
this->Play();
}
this->SetCurrentTrack(track);
this->Play();
}
//////////////////////////////////////////
@ -152,12 +151,10 @@ void PlaybackQueue::Next(){
///Start playing the previous track.
//////////////////////////////////////////
void PlaybackQueue::Previous(){
if(this->nowPlaying){
musik::core::TrackPtr track( this->nowPlaying->PreviousTrack() );
musik::core::TrackPtr track( this->nowPlaying->PreviousTrack() );
this->SetCurrentTrack(track);
this->Play();
}
this->SetCurrentTrack(track);
this->Play();
}
//////////////////////////////////////////
@ -178,19 +175,16 @@ void PlaybackQueue::Stop(){
///Return the current running track
//////////////////////////////////////////
TrackPtr PlaybackQueue::CurrentTrack(){
if(this->nowPlaying){
if (this->nowPlaying->Size() <= 0)
{
return TrackPtr();
}
if(!this->currentTrack){
// If the current track is empty, get a track from the nowPlaying tracklist
this->SetCurrentTrack( this->nowPlaying->CurrentTrack() );
}
return this->currentTrack;
if (this->nowPlaying->Size() <= 0)
{
return TrackPtr();
}
return TrackPtr();
if(!this->currentTrack){
// If the current track is empty, get a track from the nowPlaying tracklist
this->SetCurrentTrack( this->nowPlaying->CurrentTrack() );
}
return this->currentTrack;
}
//////////////////////////////////////////
@ -213,7 +207,10 @@ void PlaybackQueue::SetCurrentTrack(TrackPtr track){
this->metadataQuery.Clear();
this->metadataQuery.RequestAllMetakeys();
this->metadataQuery.RequestTrack(this->currentTrack);
this->nowPlaying->Library()->AddQuery(this->metadataQuery,musik::core::Query::Wait|musik::core::Query::AutoCallback|musik::core::Query::UnCanceable|musik::core::Query::Prioritize);
LibraryPtr library = this->currentTrack->Library();
if(library){
library->AddQuery(this->metadataQuery,musik::core::Query::Wait|musik::core::Query::AutoCallback|musik::core::Query::UnCanceable|musik::core::Query::Prioritize);
}
}
// Call the signal if track updates
@ -228,18 +225,16 @@ void PlaybackQueue::SetCurrentTrack(TrackPtr track){
///\param tracklist
///Tracklist that should be copied to now playing
//////////////////////////////////////////
void PlaybackQueue::Play(tracklist::Ptr tracklist){
void PlaybackQueue::Play(tracklist::Base &tracklist){
// Set the "now playing" to libraries own playlist
this->nowPlaying = tracklist->Library()->NowPlaying();
(*this->nowPlaying) = tracklist;
this->currentTrack.reset();
this->nowPlaying->CopyTracks(tracklist);
this->Play();
}
void PlaybackQueue::Append(tracklist::Ptr tracklist){
void PlaybackQueue::Append(tracklist::Base &tracklist){
// Set the "now playing" to libraries own playlist
this->nowPlaying = tracklist->Library()->NowPlaying();
this->nowPlaying->AppendTracks(tracklist);
(*this->nowPlaying) += tracklist;
}

View File

@ -36,7 +36,7 @@
//////////////////////////////////////////////////////////////////////////////
#include <core/audio/Transport.h>
#include <core/tracklist/IRandomAccess.h>
#include <core/tracklist/Base.h>
#include <core/Query/TrackMetadata.h>
#include <sigslot/sigslot.h>
@ -95,8 +95,8 @@ class PlaybackQueue : public sigslot::has_slots<>{
// Now Playing control
tracklist::Ptr NowPlayingTracklist();
void Play(tracklist::Ptr tracklist);
void Append(tracklist::Ptr tracklist);
void Play(tracklist::Base &tracklist);
void Append(tracklist::Base &tracklist);
// Playback Control
void Play();

View File

@ -209,6 +209,17 @@ class Base : public sigslot::has_slots<> {
public:
void PostCopy();
//////////////////////////////////////////
typedef sigslot::signal3<Query::Base*,Library::Base*,bool> QueryFinishedEvent;
//////////////////////////////////////////
///\brief
///A signal called before the query is totaly removed from the library queue
///The bool will indicate if the query was finished successfully
//////////////////////////////////////////
QueryFinishedEvent QueryFinished;
};

View File

@ -38,6 +38,7 @@
#include <core/Query/ListSelection.h>
#include <core/Query/SortTracks.h>
#include <core/Query/SortTracksWithData.h>
#include <core/Query/TrackMetadata.h>
//////////////////////////////////////////////////////////////////////////////
@ -53,4 +54,5 @@ void Factory::GetQueries(QueryMap &queryMap){
queryMap["ListSelection"] = Ptr(new ListSelection());
queryMap["SortTracks"] = Ptr(new SortTracks());
queryMap["TrackMetadata"] = Ptr(new TrackMetadata());
queryMap["SortTracksWithData"] = Ptr(new SortTracksWithData());
}

View File

@ -40,6 +40,7 @@
#include <core/Common.h>
#include <core/xml/ParserNode.h>
#include <core/xml/WriterNode.h>
#include <core/LibraryTrack.h>
#include <boost/algorithm/string.hpp>
using namespace musik::core;
@ -149,7 +150,7 @@ bool Query::ListBase::ParseTracksSQL(std::string sql,Library::Base *library,db::
tempTrackResults.reserve(101);
int row(0);
while(selectTracks.Step()==db::ReturnCode::Row){
tempTrackResults.push_back(TrackPtr(new Track(selectTracks.ColumnInt(0))));
tempTrackResults.push_back(TrackPtr(new LibraryTrack(selectTracks.ColumnInt(0),library->Id())));
this->trackInfoDuration += selectTracks.ColumnInt64(1);
this->trackInfoSize += selectTracks.ColumnInt64(2);
this->trackInfoTracks++;
@ -253,7 +254,7 @@ bool Query::ListBase::SendResults(musik::core::xml::WriterNode &queryNode,Librar
if(trackCount!=0){
tracks.Content() += ",";
}
tracks.Content() += boost::lexical_cast<std::string>( (*track)->id );
tracks.Content() += boost::lexical_cast<std::string>( (*track)->Id() );
++track;
++trackCount;
}
@ -342,7 +343,7 @@ bool Query::ListBase::RecieveResults(musik::core::xml::ParserNode &queryNode,Lib
for(StringVector::iterator value=values.begin();value!=values.end();++value){
int trackId(boost::lexical_cast<DBINT>(*value));
tempTrackResults.push_back(TrackPtr(new Track(trackId)));
tempTrackResults.push_back(TrackPtr(new LibraryTrack(trackId,library->Id())));
}
{

View File

@ -38,7 +38,7 @@
#include <core/Query/Base.h>
#include <core/MetadataValue.h>
#include <core/Track.h>
#include <core/LibraryTrack.h>
#include <sigslot/sigslot.h>
#include <vector>

View File

@ -66,7 +66,7 @@ bool Query::PlaylistLoad::ParseQuery(Library::Base *library,db::Connection &db){
while(selectTracks.Step()==db::Row){
boost::mutex::scoped_lock lock(library->resultMutex);
this->trackResults.push_back(TrackPtr(new Track(selectTracks.ColumnInt(0))));
this->trackResults.push_back(TrackPtr(new LibraryTrack(selectTracks.ColumnInt(0),library->Id())));
}
return true;
}

View File

@ -37,6 +37,7 @@
#include "pch.hpp"
#include <core/Query/PlaylistSave.h>
#include <core/Library/Base.h>
#include <core/LibraryTrack.h>
using namespace musik::core;
@ -63,7 +64,8 @@ void Query::PlaylistSave::SavePlaylist(const utfstring playlistName,int playlist
if(tracklist){
for(int i(0);i<tracklist->Size();++i){
this->tracks.push_back((*tracklist)[i]->id);
// LibraryTrack *t = (LibraryTrack*)(*tracklist)[i].get();
this->tracks.push_back( (*tracklist)[i]->Id() );
}
}
}

View File

@ -44,7 +44,7 @@
#include <core/config.h>
#include <core/Query/Base.h>
#include <core/tracklist/IRandomAccess.h>
#include <core/tracklist/LibraryList.h>
//////////////////////////////////////////////////////////////
// Forward declarations
@ -64,7 +64,7 @@ namespace musik{ namespace core{
PlaylistSave(void);
~PlaylistSave(void);
void SavePlaylist(const utfstring playlistName,int playlistId=0,musik::core::tracklist::IRandomAccess *tracklist=NULL);
void SavePlaylist(const utfstring playlistName,int playlistId=0,musik::core::tracklist::Base *tracklist=NULL);
sigslot::signal1<int> PlaylistSaved;

View File

@ -49,7 +49,7 @@ namespace musik{ namespace core{
#include <core/config.h>
#include <core/Query/Base.h>
#include <core/tracklist/IRandomAccess.h>
#include <core/tracklist/LibraryList.h>
#include <boost/shared_ptr.hpp>
#include <boost/function.hpp>

View File

@ -144,7 +144,7 @@ bool Query::SortTracks::ParseQuery(Library::Base *library,db::Connection &db){
db::Statement selectMetaValue("SELECT mv.sort_order FROM meta_values mv,track_meta tm WHERE tm.meta_value_id=mv.id AND tm.track_id=? AND mv.meta_key_id=? LIMIT 1",db);
for(int i(0);i<this->tracksToSort.size();++i){
int track(this->tracksToSort[i]);
DBINT track(this->tracksToSort[i]);
insertTracks.BindInt(0,track);
// Lets find the meta values
@ -185,21 +185,21 @@ Query::Ptr Query::SortTracks::copy() const{
return queryCopy;
}
void Query::SortTracks::AddTrack(int trackId){
void Query::SortTracks::AddTrack(DBINT trackId){
this->tracksToSort.push_back(trackId);
}
void Query::SortTracks::AddTracks(std::vector<int> &tracks){
void Query::SortTracks::AddTracks(std::vector<DBINT> &tracks){
this->tracksToSort.reserve(this->tracksToSort.size()+tracks.size());
for(std::vector<int>::iterator track=tracks.begin();track!=tracks.end();++track){
for(std::vector<DBINT>::iterator track=tracks.begin();track!=tracks.end();++track){
this->tracksToSort.push_back(*track);
}
}
void Query::SortTracks::AddTracks(musik::core::tracklist::IRandomAccess &tracks){
void Query::SortTracks::AddTracks(musik::core::tracklist::LibraryList &tracks){
this->tracksToSort.reserve(this->tracksToSort.size()+tracks.Size());
for(int i(0);i<tracks.Size();++i){
this->tracksToSort.push_back(tracks[i]->id);
this->tracksToSort.push_back(tracks[i]->Id());
}
}

View File

@ -44,7 +44,7 @@
#include <core/config.h>
#include <core/Query/ListBase.h>
#include <core/tracklist/IRandomAccess.h>
#include <core/tracklist/LibraryList.h>
@ -57,9 +57,9 @@ namespace musik{ namespace core{
SortTracks(void);
~SortTracks(void);
void AddTrack(int trackId);
void AddTracks(std::vector<int> &tracks);
void AddTracks(musik::core::tracklist::IRandomAccess &tracks);
void AddTrack(DBINT trackId);
void AddTracks(std::vector<DBINT> &tracks);
void AddTracks(musik::core::tracklist::LibraryList &tracks);
void ClearTracks();
@ -67,7 +67,7 @@ namespace musik{ namespace core{
protected:
typedef std::vector<int> IntVector;
typedef std::vector<DBINT> IntVector;
IntVector tracksToSort;
typedef std::list<std::string> StringList;

View File

@ -0,0 +1,431 @@
//////////////////////////////////////////////////////////////////////////////
//
// License Agreement:
//
// The following are Copyright © 2008, Daniel Önnerby
//
// 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 "pch.hpp"
#include <core/Query/SortTracksWithData.h>
#include <core/Library/Base.h>
#include <core/config_format.h>
#include <boost/algorithm/string.hpp>
#include <core/xml/ParserNode.h>
#include <core/xml/WriterNode.h>
#include <core/LibraryTrack.h>
#include <core/Common.h>
using namespace musik::core;
//////////////////////////////////////////
///\brief
///Constructor
//////////////////////////////////////////
Query::SortTracksWithData::SortTracksWithData(void)
:clearedTrackResults(false)
{
}
//////////////////////////////////////////
///\brief
///Destructor
//////////////////////////////////////////
Query::SortTracksWithData::~SortTracksWithData(void){
}
//////////////////////////////////////////
///\brief
///Executes the query, meaning id does all the querying to the database.
//////////////////////////////////////////
bool Query::SortTracksWithData::ParseQuery(Library::Base *library,db::Connection &db){
// Create smart SQL statment
std::string selectSQL("SELECT temp_track_sort.track_id ");
std::string selectSQLTables("temp_track_sort JOIN tracks ON tracks.id=temp_track_sort.track_id ");
std::string selectSQLSort;
db::CachedStatement selectMetaKeyId("SELECT id FROM meta_keys WHERE name=?",db);
// Check if it's a fixed field
if(musik::core::Library::Base::IsStaticMetaKey(this->sortByMetaKey)){
selectSQL += ",tracks."+this->sortByMetaKey;
selectSQLSort += (selectSQLSort.empty()?" ORDER BY tracks.":",tracks.") + this->sortByMetaKey;
// Check if it's a special MTO (many to one relation) field
}else if(musik::core::Library::Base::IsSpecialMTOMetaKey(this->sortByMetaKey) || musik::core::Library::Base::IsSpecialMTMMetaKey(this->sortByMetaKey)){
if(this->sortByMetaKey=="album"){
selectSQLTables += " LEFT OUTER JOIN albums ON albums.id=tracks.album_id ";
selectSQL += ",albums.name";
selectSQLSort += (selectSQLSort.empty()?" ORDER BY albums.sort_order":",albums.sort_order");
}
if(this->sortByMetaKey=="visual_genre" || this->sortByMetaKey=="genre"){
selectSQLTables += " LEFT OUTER JOIN genres ON genres.id=tracks.visual_genre_id ";
selectSQL += ",genres.name";
selectSQLSort += (selectSQLSort.empty()?" ORDER BY genres.sort_order":",genres.sort_order");
}
if(this->sortByMetaKey=="visual_artist" || this->sortByMetaKey=="artist"){
selectSQLTables += " LEFT OUTER JOIN artists ON artists.id=tracks.visual_artist_id";
selectSQL += ",artists.name";
selectSQLSort += (selectSQLSort.empty()?" ORDER BY artists.sort_order":",artists.sort_order");
}
} else {
// Sort by metakeys table
selectMetaKeyId.BindText(0,this->sortByMetaKey);
if(selectMetaKeyId.Step()==db::Row){
selectSQLTables += " LEFT OUTER JOIN (SELECT track_meta.track_id,meta_values.content,meta_values.sort_order FROM track_meta,meta_values WHERE track_meta.meta_value_id=meta_values.id AND meta_values.meta_key_id="+boost::lexical_cast<std::string>(selectMetaKeyId.ColumnInt(0))+") the_meta ON the_meta.track_id=tracks.id ";
selectSQL += ",the_meta.content";
selectSQLSort += (selectSQLSort.empty()?" ORDER BY the_meta.sort_order":",the_meta.sort_order");
}
selectMetaKeyId.Reset();
}
// First lets start by inserting all tracks in a temporary table
db.Execute("DROP TABLE IF EXISTS temp_track_sort");
db.Execute("CREATE TEMPORARY TABLE temp_track_sort (id INTEGER PRIMARY KEY AUTOINCREMENT,track_id INTEGER NOT NULL default 0)");
{
db::Statement insertTracks("INSERT INTO temp_track_sort (track_id) VALUES (?)",db);
for(int i(0);i<this->tracksToSort.size();++i){
DBINT track(this->tracksToSort[i]);
insertTracks.BindInt(0,track);
insertTracks.Step();
insertTracks.Reset();
insertTracks.UnBindAll();
}
}
// Finaly keep sort order of inserted order.
selectSQLSort += ",temp_track_sort.id";
////////////////////////////////////////////////////////
// The main SQL query what this class is all about
std::string sql=selectSQL+" FROM "+selectSQLTables+selectSQLSort;
if(!library->QueryCanceled(this)){
db::Statement selectTracks(sql.c_str(),db);
TrackWithSortdataVector tempTrackResults;
int row(0);
while(selectTracks.Step()==db::ReturnCode::Row){
TrackWithSortdata newSortData;
newSortData.track.reset(new LibraryTrack(selectTracks.ColumnInt(0),library->Id()));
newSortData.sortData = selectTracks.ColumnTextUTF(1);
// Convert the content to lower if futher sorting need to be done
boost::algorithm::to_lower(newSortData.sortData);
tempTrackResults.push_back( newSortData );
// Each 100 result, lock the mutex and insert the results
if( (++row)%100==0 ){
boost::mutex::scoped_lock lock(library->resultMutex);
this->trackResults.insert(this->trackResults.end(),tempTrackResults.begin(),tempTrackResults.end());
tempTrackResults.clear();
}
}
// If there are any results not inserted, insert now
if(!tempTrackResults.empty()){
boost::mutex::scoped_lock lock(library->resultMutex);
this->trackResults.insert(this->trackResults.end(),tempTrackResults.begin(),tempTrackResults.end());
}
// All done succesfully, return true
return true;
}else{
return false;
}
}
//////////////////////////////////////////
///\brief
///Copy a query
///
///\returns
///A shared_ptr to the Query::Base
//////////////////////////////////////////
Query::Ptr Query::SortTracksWithData::copy() const{
Query::Ptr queryCopy(new Query::SortTracksWithData(*this));
queryCopy->PostCopy();
return queryCopy;
}
//////////////////////////////////////////
///\brief
///Add a track (trackId) in for sorting
//////////////////////////////////////////
void Query::SortTracksWithData::AddTrack(DBINT trackId){
this->tracksToSort.push_back(trackId);
}
//////////////////////////////////////////
///\brief
///What metakey to sort by
//////////////////////////////////////////
void Query::SortTracksWithData::SortByMetaKey(std::string metaKey){
this->sortByMetaKey = metaKey;
}
//////////////////////////////////////////
///\brief
///If you are reusing the query, clear what tracks to sort by
//////////////////////////////////////////
void Query::SortTracksWithData::ClearTracks(){
this->tracksToSort.clear();
}
//////////////////////////////////////////
///\brief
///Recieve the query from XML
///
///\param queryNode
///Reference to query XML node
///
///The excpeted input format is like this:
///\code
///<query type="SortTracksWithData">
/// <tracks>1,3,5,7</tracks>
///</query>
///\endcode
///
///\returns
///true when successfully recieved
//////////////////////////////////////////
bool Query::SortTracksWithData::RecieveQuery(musik::core::xml::ParserNode &queryNode){
while( musik::core::xml::ParserNode node = queryNode.ChildNode() ){
if(node.Name()=="sortby"){
node.WaitForContent();
this->sortByMetaKey = node.Content();
} else if(node.Name()=="tracks"){
node.WaitForContent();
typedef std::vector<std::string> StringVector;
StringVector values;
try{ // lexical_cast can throw
boost::algorithm::split(values,node.Content(),boost::algorithm::is_any_of(","));
for(StringVector::iterator value=values.begin();value!=values.end();++value){
this->tracksToSort.push_back( boost::lexical_cast<DBINT>(*value) );
}
}
catch(...){
return false;
}
}
}
return true;
}
//////////////////////////////////////////
///\brief
///The name ("SortTracksWithData") of the query.
//////////////////////////////////////////
std::string Query::SortTracksWithData::Name(){
return "SortTracksWithData";
}
//////////////////////////////////////////
///\brief
///Send the query to a musikServer
//////////////////////////////////////////
bool Query::SortTracksWithData::SendQuery(musik::core::xml::WriterNode &queryNode){
{
xml::WriterNode sortbyNode(queryNode,"sortby");
sortbyNode.Content() = this->sortByMetaKey;
}
{
xml::WriterNode tracksNode(queryNode,"tracks");
for(IntVector::iterator trackId=this->tracksToSort.begin();trackId!=this->tracksToSort.end();++trackId){
if(!tracksNode.Content().empty()){
tracksNode.Content().append(",");
}
tracksNode.Content().append(boost::lexical_cast<std::string>(*trackId));
}
}
return true;
}
//////////////////////////////////////////
///\brief
///Execute the callbacks. In this case the "TrackResults" signal
//////////////////////////////////////////
bool Query::SortTracksWithData::RunCallbacks(Library::Base *library){
bool bReturn(false);
TrackWithSortdataVector trackResultsCopy;
{ // Scope for swapping the results safely
boost::mutex::scoped_lock lock(library->resultMutex);
trackResultsCopy.swap(this->trackResults);
}
{
boost::mutex::scoped_lock lock(library->libraryMutex);
if( (this->status & Status::Ended)!=0){
// If the query is finished, this function should return true to report that it is finished.
bReturn = true;
}
}
// Check for Tracks
if( !trackResultsCopy.empty() ){
// Call the slots
this->TrackResults(&trackResultsCopy,!this->clearedTrackResults);
if(!this->clearedTrackResults){
this->clearedTrackResults = true;
}
}
return bReturn;
}
//////////////////////////////////////////
///\brief
///Send the results to the client
///
///The expected output format is like this:
///\code
/// <tracklist>
/// <t id="1">thesortdata</t>
/// <t id="2">thesortdata</t>
/// <t id="5">thesortdata</t>
/// </tracklist>
///\endcode
///
//////////////////////////////////////////
bool Query::SortTracksWithData::SendResults(musik::core::xml::WriterNode &queryNode,Library::Base *library){
bool continueSending(true);
while(continueSending && !library->QueryCanceled(this)){
TrackWithSortdataVector trackResultsCopy;
{ // Scope for swapping the results safely
boost::mutex::scoped_lock lock(library->resultMutex);
trackResultsCopy.swap(this->trackResults);
}
{
boost::mutex::scoped_lock lock(library->libraryMutex);
if( (this->status & Status::Ended)!=0){
// If the query is finished, stop sending
continueSending = false;
}
}
// Check for Tracks
if( !trackResultsCopy.empty() && !library->QueryCanceled(this) ){
musik::core::xml::WriterNode tracklist(queryNode,"tracklist");
if(!this->clearedTrackResults){
tracklist.Attributes()["clear"] = "true";
this->clearedTrackResults = true;
}
for(TrackWithSortdataVector::iterator track=trackResultsCopy.begin();track!=trackResultsCopy.end();++track){
musik::core::xml::WriterNode trackNode(tracklist,"t");
trackNode.Attributes()["id"] = boost::lexical_cast<std::string>( track->track->Id() );
trackNode.Content() = UTF_TO_UTF8(track->sortData);
}
}
if(continueSending){
if( trackResultsCopy.empty() ){
// Yield for more results
boost::thread::yield();
}
}
}
return true;
}
//////////////////////////////////////////
///\brief
///Recieve results from the musikServer.
//////////////////////////////////////////
bool Query::SortTracksWithData::RecieveResults(musik::core::xml::ParserNode &queryNode,Library::Base *library){
while( musik::core::xml::ParserNode node = queryNode.ChildNode() ){
typedef std::vector<std::string> StringVector;
// Recieve tracks
if( node.Name()=="tracklist"){
while( musik::core::xml::ParserNode trackNode = node.ChildNode("t") ){
trackNode.WaitForContent();
DBINT trackId(boost::lexical_cast<DBINT>(trackNode.Attributes()["id"]));
TrackWithSortdata newSortData;
newSortData.track.reset(new LibraryTrack(trackId,library->Id()));
newSortData.sortData = UTF8_TO_UTF(trackNode.Content());
{
boost::mutex::scoped_lock lock(library->resultMutex);
this->trackResults.push_back(newSortData);
}
}
}
}
return true;
}
//////////////////////////////////////////
///\brief
///Operator to be able to sort using the sortData
//////////////////////////////////////////
bool Query::SortTracksWithData::TrackWithSortdata::operator<(const TrackWithSortdata &trackWithSortData) const{
return this->sortData < trackWithSortData.sortData;
}

View File

@ -0,0 +1,119 @@
//////////////////////////////////////////////////////////////////////////////
//
// License Agreement:
//
// The following are Copyright © 2008, Daniel Önnerby
//
// 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/config.h>
#include <core/Query/Base.h>
#include <core/Track.h>
#include <boost/shared_ptr.hpp>
#include <boost/function.hpp>
#include <vector>
#include <list>
#include <string>
//////////////////////////////////////////////////////////////////////////////
namespace musik{ namespace core{ namespace Query{
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////
///\brief
///SortTracksWithData is a query used to recieve sorted tracklists along with the data the tracks are sorted by
///
///\remarks
///First concider to use the SortTracks query instead.
///
///\see
///musik::core::Query::SortTracks
//////////////////////////////////////////
class SortTracksWithData : public Query::Base{
public:
SortTracksWithData(void);
~SortTracksWithData(void);
void AddTrack(DBINT trackId);
void ClearTracks();
void SortByMetaKey(std::string metaKey);
//////////////////////////////////////////
///\brief
///The struct used to return both the track and the sorted data
//////////////////////////////////////////
struct TrackWithSortdata{
musik::core::TrackPtr track;
utfstring sortData;
bool operator<(const TrackWithSortdata &trackWithSortData) const;
};
typedef std::list<TrackWithSortdata> TrackWithSortdataVector;
typedef sigslot::signal2<TrackWithSortdataVector*,bool> TrackWithdataSignal;
TrackWithdataSignal TrackResults;
TrackWithSortdataVector trackResults;
protected:
typedef std::vector<DBINT> IntVector;
IntVector tracksToSort;
std::string sortByMetaKey;
bool clearedTrackResults;
friend class Library::Base;
friend class Library::LocalDB;
Ptr copy() const;
virtual std::string Name();
virtual bool ParseQuery(Library::Base *library,db::Connection &db);
virtual bool RecieveQuery(musik::core::xml::ParserNode &queryNode);
virtual bool SendQuery(musik::core::xml::WriterNode &queryNode);
virtual bool SendResults(musik::core::xml::WriterNode &queryNode,Library::Base *library);
virtual bool RecieveResults(musik::core::xml::ParserNode &queryNode,Library::Base *library);
bool RunCallbacks(Library::Base *library);
private:
};
//////////////////////////////////////////////////////////////////////////////
} } }
//////////////////////////////////////////////////////////////////////////////

View File

@ -39,6 +39,7 @@
#include <core/Common.h>
#include <core/Query/TrackMetadata.h>
#include <core/Library/Base.h>
#include <core/LibraryTrack.h>
#include <boost/algorithm/string.hpp>
using namespace musik::core;
@ -156,7 +157,7 @@ bool TrackMetadata::ParseQuery(Library::Base *library,db::Connection &db){
TrackPtr track(this->aRequestTracks.back());
this->aRequestTracks.pop_back();
trackData.BindInt(0,track->id);
trackData.BindInt(0,track->Id());
if(trackData.Step()==db::ReturnCode::Row){
@ -175,7 +176,7 @@ bool TrackMetadata::ParseQuery(Library::Base *library,db::Connection &db){
// Get the meta-value as well
if(this->requestAllFields){
// Get ALL meta
allMetadata.BindInt(0,track->id);
allMetadata.BindInt(0,track->Id());
while(allMetadata.Step()==db::ReturnCode::Row){
track->SetValue(allMetadata.ColumnText(1),allMetadata.ColumnTextUTF(0));
}
@ -184,7 +185,7 @@ bool TrackMetadata::ParseQuery(Library::Base *library,db::Connection &db){
}else{
for(std::set<std::string>::iterator metaKey=this->metaFields.begin();metaKey!=this->metaFields.end();++metaKey){
metadata.BindInt(0,track->id);
metadata.BindInt(0,track->Id());
metadata.BindText(1,metaKey->c_str());
while(metadata.Step()==db::ReturnCode::Row){
@ -197,7 +198,7 @@ bool TrackMetadata::ParseQuery(Library::Base *library,db::Connection &db){
// Find genres
if( this->categoryFields.find("genre")!=this->categoryFields.end() ){
genres.BindInt(0,track->id);
genres.BindInt(0,track->Id());
while(genres.Step()==db::ReturnCode::Row){
track->SetValue("genre",genres.ColumnTextUTF(0));
}
@ -206,7 +207,7 @@ bool TrackMetadata::ParseQuery(Library::Base *library,db::Connection &db){
// Find artists
if( this->categoryFields.find("artist")!=this->categoryFields.end() ){
artists.BindInt(0,track->id);
artists.BindInt(0,track->Id());
while(artists.Step()==db::ReturnCode::Row){
track->SetValue("artist",artists.ColumnTextUTF(0));
}
@ -293,9 +294,9 @@ Query::Ptr TrackMetadata::copy() const{
}
void TrackMetadata::PreAddQuery(Library::Base *library){
for(TrackVector::iterator track=this->aRequestTracks.begin();track!=this->aRequestTracks.end();++track){
/* for(TrackVector::iterator track=this->aRequestTracks.begin();track!=this->aRequestTracks.end();++track){
(*track)->InitMeta(library);
}
}*/
}
std::string TrackMetadata::Name(){
@ -330,7 +331,7 @@ bool TrackMetadata::RecieveQuery(musik::core::xml::ParserNode &queryNode){
try{ // lexical_cast can throw
boost::algorithm::split(values,metakeysNode.Content(),boost::algorithm::is_any_of(","));
for(StringVector::iterator value=values.begin();value!=values.end();++value){
this->RequestTrack(TrackPtr(new Track( boost::lexical_cast<DBINT>(*value) )));
this->RequestTrack(TrackPtr(new LibraryTrack( boost::lexical_cast<DBINT>(*value),0 )));
}
}
catch(...){
@ -365,7 +366,7 @@ bool TrackMetadata::SendQuery(musik::core::xml::WriterNode &queryNode){
if(!tracksNode.Content().empty()){
tracksNode.Content().append(",");
}
tracksNode.Content().append( boost::lexical_cast<std::string>( (*track)->id ) );
tracksNode.Content().append( boost::lexical_cast<std::string>( (*track)->Id()) );
}
}
@ -399,10 +400,10 @@ bool TrackMetadata::SendResults(musik::core::xml::WriterNode &queryNode,Library:
(*track)->ClearValue("path");
musik::core::xml::WriterNode trackNode(queryNode,"t");
trackNode.Attributes()["id"] = boost::lexical_cast<std::string>( (*track)->id );
trackNode.Attributes()["id"] = boost::lexical_cast<std::string>( (*track)->Id() );
TrackMeta::TagMapIteratorPair metaDatas( (*track)->GetAllValues() );
for(TrackMeta::TagMapConstIterator metaData=metaDatas.first;metaData!=metaDatas.second;++metaData){
Track::MetadataIteratorRange metaDatas( (*track)->GetAllValues() );
for(Track::MetadataMap::const_iterator metaData=metaDatas.first;metaData!=metaDatas.second;++metaData){
musik::core::xml::WriterNode metaDataNode(trackNode,"md");
metaDataNode.Attributes()["k"] = metaData->first;
metaDataNode.Content().append( ConvertUTF8(metaData->second) );
@ -441,7 +442,7 @@ bool TrackMetadata::RecieveResults(musik::core::xml::ParserNode &queryNode,Libra
TrackVector::iterator track=this->aRequestTracks.begin();
bool trackFound(false);
while(track!=this->aRequestTracks.end() && !trackFound){
if( (*track)->id==trackId ){
if( (*track)->Id()==trackId ){
// TrackPtr found
trackFound = true;
}else{
@ -463,7 +464,7 @@ bool TrackMetadata::RecieveResults(musik::core::xml::ParserNode &queryNode,Libra
// Special case for the "path" when connecting to a webserver
if(requestPath){
utfstring path(pathPrefix);
path += boost::lexical_cast<utfstring>(currentTrack->id);
path += boost::lexical_cast<utfstring>( currentTrack->Id() );
currentTrack->SetValue("path",path.c_str());
}

View File

@ -35,7 +35,7 @@
//////////////////////////////////////////////////////////////////////////////
#pragma once
#include <core/config.h>
#include <core/config_filesystem.h>
#include <core/server/Connection.h>
#include <core/db/Connection.h>

View File

@ -37,534 +37,27 @@
#include "pch.hpp"
#include <core/Track.h>
#include <core/config_filesystem.h>
#include <core/Common.h>
#include <core/db/Connection.h>
#include <core/db/Statement.h>
#include <core/Library/Base.h>
#include <boost/lexical_cast.hpp>
//////////////////////////////////////////////////////////////////////////////
using namespace musik::core;
Track::Track(void) : meta(NULL),id(0){
}
//////////////////////////////////////////////////////////////////////////////
Track::Track(DBINT newId) : meta(NULL),id(newId){
}
Track::~Track(void){
this->ClearMeta();
}
//////////////////////////////////////////
///\brief
///Get value of a (one) tag
///
///\param key
///String identifier of the required value.
///
///\param threadHelper
///If you pass this ThreadHelper object, the GetValue will be threadsafe.
///
///\returns
///A const reference to a wstring (UTF-16) with the value. Empty string if not found.
//////////////////////////////////////////
/*const TrackMeta::Value& Track::GetValue(const TrackMeta::Key &key) const{
if(this->meta){
return this->meta->GetValue(key);
}
static utfstring emptyString;
return emptyString;
}*/
const utfchar* Track::GetValue(const char* metakey) const{
if(this->meta){
return this->meta->GetValue(metakey);
}
return NULL;
DBINT Track::Id(){
return 0;
}
//////////////////////////////////////////
///\brief
///Get all values of one tag identifier.
///
///\param metakey
///String identifier of the required value.
///
///\returns
///A pair of iterators. The first is the start iterator and the second is the end.
///
///\see
///GetValue
//////////////////////////////////////////
TrackMeta::TagMapIteratorPair Track::GetValues(const char* metakey) const{
if(this->meta){
return this->meta->GetValues(metakey);
}
return TrackMeta::TagMapIteratorPair();
LibraryPtr Track::Library(){
static LibraryPtr nullLibrary;
return nullLibrary;
}
TrackMeta::TagMapIteratorPair Track::GetAllValues() const{
return TrackMeta::TagMapIteratorPair(this->meta->tags.begin(),this->meta->tags.end());
}
/*void Track::SetValue(const TrackMeta::Key &key,TrackMeta::Value &value){
if(this->meta){
this->meta->SetValue(key,value);
}
}
*/
void Track::SetValue(const char* metakey,const utfchar* value){
if(this->meta){
this->meta->SetValue(metakey,value);
}
}
void Track::ClearValue(const char* metakey){
if(this->meta){
this->meta->ClearValue(metakey);
}
}
void Track::ClearMeta(){
if(this->meta){
delete this->meta;
this->meta = NULL;
}
}
void Track::InitMeta(Library::Base *library){
if(!this->meta){
this->meta = new TrackMeta(library);
}
}
bool Track::HasMeta(){
return this->meta!=NULL;
}
bool Track::CompareDBAndFileInfo(const boost::filesystem::utfpath &file,db::Connection &dbConnection,DBINT currentFolderId){
this->SetValue("path",file.string().c_str());
this->SetValue("filename",file.leaf().c_str());
utfstring::size_type lastDot = file.leaf().find_last_of(UTF("."));
if(lastDot!=utfstring::npos){
this->SetValue("extension",file.leaf().substr(lastDot+1).c_str());
}
DBINT fileSize = (DBINT)boost::filesystem::file_size(file);
DBTIME fileTime = (DBTIME)boost::filesystem::last_write_time(file);
this->SetValue("filesize",boost::lexical_cast<utfstring>(fileSize).c_str());
this->SetValue("filetime",boost::lexical_cast<utfstring>(fileTime).c_str());
db::CachedStatement stmt("SELECT id,filename,filesize,filetime FROM tracks t WHERE folder_id=? AND filename=?",dbConnection);
stmt.BindInt(0,currentFolderId);
stmt.BindTextUTF(1,this->GetValue("filename"));
bool fileDifferent(true);
if(stmt.Step()==db::ReturnCode::Row){
// File found in database.
this->id = stmt.ColumnInt(0);
fileDifferent = false;
// Check if the track needs to be reread.
if(fileSize!=stmt.ColumnInt(2) || fileTime!=stmt.ColumnInt(3)){
fileDifferent = true;
}
}
return fileDifferent;
}
bool Track::Save(db::Connection &dbConnection,utfstring libraryDirectory,DBINT folderId){
TrackMeta::TagMap metadataCopy(this->meta->tags);
unsigned int count;
db::ScopedTransaction transaction(dbConnection);
//////////////////////////////////////////////////////////////////////////////
// Start by removing existing relations
if(this->id!=0){
db::CachedStatement deleteGenre("DELETE FROM track_genres WHERE track_id=?",dbConnection);
deleteGenre.BindInt(0,this->id);
deleteGenre.Step();
db::CachedStatement deleteArtist("DELETE FROM track_artists WHERE track_id=?",dbConnection);
deleteArtist.BindInt(0,this->id);
deleteArtist.Step();
db::CachedStatement deleteMeta("DELETE FROM track_meta WHERE track_id=?",dbConnection);
deleteMeta.BindInt(0,this->id);
deleteMeta.Step();
}
//////////////////////////////////////////////////////////////////////////////
// 1. Start by inserting as many tags as possible into the tracks-table
{
db::CachedStatement stmt("INSERT OR REPLACE INTO tracks (id,track,bpm,duration,filesize,year,folder_id,title,filename,filetime) VALUES (?,?,?,?,?,?,?,?,?,?)",dbConnection);
// Bind all "static" tags (the once that are in the tracks tables)
stmt.BindTextUTF(1,this->GetValue("track"));
stmt.BindTextUTF(2,this->GetValue("bpm"));
stmt.BindTextUTF(3,this->GetValue("duration"));
stmt.BindTextUTF(4,this->GetValue("filesize"));
stmt.BindTextUTF(5,this->GetValue("year"));
stmt.BindInt(6,folderId);
stmt.BindTextUTF(7,this->GetValue("title"));
stmt.BindTextUTF(8,this->GetValue("filename"));
stmt.BindTextUTF(9,this->GetValue("filetime"));
if(this->id!=0){
stmt.BindInt(0,this->id);
}
if(stmt.Step()==db::ReturnCode::Done){
if(this->id==0){
this->id = dbConnection.LastInsertedId();
}
}
}
//////////////////////////////////////////////////////////////////////////////
// 2. Remove tags that has been used.
metadataCopy.erase("track");
metadataCopy.erase("bpm");
metadataCopy.erase("duration");
metadataCopy.erase("year");
metadataCopy.erase("title");
metadataCopy.erase("filename");
metadataCopy.erase("filetime");
metadataCopy.erase("filesize");
metadataCopy.erase("title");
metadataCopy.erase("path");
metadataCopy.erase("extension");
//////////////////////////////////////////////////////////////////////////////
// 3. Read genres
utfstring visualGenres;
DBINT genreId(0);
count = 0;
std::set<utfstring> alreadySetGenres; // Cache for not saving duplicates
TrackMeta::TagMapIteratorPair genres = this->GetValues("genre");
while(genres.first!=genres.second){
if(alreadySetGenres.find(genres.first->second)==alreadySetGenres.end()){
alreadySetGenres.insert(genres.first->second);
// First save the genre
genreId = this->_GetGenre(dbConnection,genres.first->second,true);
// Second, add to the visual genre
if(count!=0){
visualGenres += UTF(", ");
}
visualGenres += genres.first->second;
++count;
}
++genres.first;
}
if(count>1 || genreId==0){
// Check for new genre, but do not insert the relation, this is the visual_genre_id field in the database
genreId = this->_GetGenre(dbConnection,visualGenres,false,true);
}
metadataCopy.erase("genre");
//////////////////////////////////////////////////////////////////////////////
// 4. Read artists
utfstring visualArtists;
DBINT artistId(0);
count = 0;
std::set<utfstring> alreadySetArtists; // Cache for not saving duplicates
TrackMeta::TagMapIteratorPair artists = this->GetValues("artist");
while(artists.first!=artists.second){
if(alreadySetArtists.find(artists.first->second)==alreadySetArtists.end()){
alreadySetArtists.insert(artists.first->second);
// First save the artist
artistId = this->_GetArtist(dbConnection,artists.first->second,true);
// Second, add to the visual artist
if(count!=0){
visualArtists += UTF(", ");
}
visualArtists += artists.first->second;
++count;
}
++artists.first;
}
if(count>1 || artistId==0){
// Check for new artist, but do not insert the relation, this is the visual_artist_id field in the database
artistId = this->_GetArtist(dbConnection,visualArtists,false,true);
}
metadataCopy.erase("artist");
//////////////////////////////////////////////////////////////////////////////
// 5. Read album
DBINT albumId(0);
{
db::CachedStatement stmt("SELECT id FROM albums WHERE name=?",dbConnection);
const utfchar *album=this->GetValue("album");
if(album==NULL){
album=UTF("");
}
stmt.BindTextUTF(0,album);
if(stmt.Step()==db::ReturnCode::Row){
albumId = stmt.ColumnInt(0);
}else{
// INSERT a new album
db::Statement insertAlbum("INSERT INTO albums (name) VALUES (?)",dbConnection);
insertAlbum.BindTextUTF(0,album);
if(insertAlbum.Step()==db::ReturnCode::Done){
albumId = dbConnection.LastInsertedId();
}
}
}
metadataCopy.erase("album");
//////////////////////////////////////////////////////////////////////////////
// 6. Thumbnail
DBINT thumbnailId(0);
if(this->meta->thumbnailData){
UINT64 sum = Checksum(this->meta->thumbnailData,this->meta->thumbnailSize);
db::CachedStatement thumbs("SELECT id FROM thumbnails WHERE filesize=? AND checksum=?",dbConnection);
thumbs.BindInt(0,this->meta->thumbnailSize);
thumbs.BindInt64(1,sum);
if(thumbs.Step()==db::ReturnCode::Row){
thumbnailId = thumbs.ColumnInt(0);
}
if(thumbnailId==0){
// INSERT thumbnail
db::Statement insertThumb("INSERT INTO thumbnails (filesize,checksum) VALUES (?,?)",dbConnection);
insertThumb.BindInt(0,this->meta->thumbnailSize);
insertThumb.BindInt64(1,sum);
if(insertThumb.Step()==db::ReturnCode::Done){
thumbnailId = dbConnection.LastInsertedId();
}
// Save the file
utfstring filename = libraryDirectory+UTF("thumbs/")+boost::lexical_cast<utfstring>(thumbnailId)+UTF(".jpg");
// TODO, fix for UTF wide of not
FILE *thumbFile = _wfopen(filename.c_str(),UTF("wb"));
fwrite(this->meta->thumbnailData,sizeof(char),this->meta->thumbnailSize,thumbFile);
fclose(thumbFile);
}
}
//////////////////////////////////////////////////////////////////////////////
// 5. Update tracks table with relations
{
db::CachedStatement stmt("UPDATE tracks SET album_id=?,visual_genre_id=?,visual_artist_id=?,thumbnail_id=? WHERE id=?",dbConnection);
stmt.BindInt(0,albumId);
stmt.BindInt(1,genreId);
stmt.BindInt(2,artistId);
stmt.BindInt(3,thumbnailId);
stmt.BindInt(4,this->id);
stmt.Step();
}
//////////////////////////////////////////////////////////////////////////////
// 6. Fields that are left in tagsCopy should be meta-data (extra tags)
{
db::CachedStatement selectMetaKey("SELECT id FROM meta_keys WHERE name=?",dbConnection);
db::CachedStatement selectMetaValue("SELECT id FROM meta_values WHERE meta_key_id=? AND content=?",dbConnection);
db::CachedStatement insertMetaValue("INSERT INTO meta_values (meta_key_id,content) VALUES (?,?)",dbConnection);
db::CachedStatement insertTrackMeta("INSERT INTO track_meta (track_id,meta_value_id) VALUES (?,?)",dbConnection);
// Loop through the tags
for(TrackMeta::TagMap::const_iterator metaData=metadataCopy.begin();metaData!=metadataCopy.end();++metaData){
// 1. Find the meta_key
DBINT metaKeyId(0);
std::string key;
selectMetaKey.BindText(0,metaData->first);
if(selectMetaKey.Step()==db::ReturnCode::Row){
// key found
metaKeyId = selectMetaKey.ColumnInt(0);
}else{
// key not found, INSERT
db::CachedStatement insertMetaKey("INSERT INTO meta_keys (name) VALUES (?)",dbConnection);
insertMetaKey.BindText(0,metaData->first);
if(insertMetaKey.Step()==db::ReturnCode::Done){
metaKeyId = dbConnection.LastInsertedId();
}
}
selectMetaKey.Reset();
// 2. Find meta value
if(metaKeyId!=0){
DBINT metaValueId(0);
// 2.1 Find meta_value
selectMetaValue.BindInt(0,metaKeyId);
selectMetaValue.BindTextUTF(1,metaData->second);
if(selectMetaValue.Step()==db::ReturnCode::Row){
// Value found
metaValueId = selectMetaValue.ColumnInt(0);
}else{
// Value not found, INSERT
insertMetaValue.BindInt(0,metaKeyId);
insertMetaValue.BindTextUTF(1,metaData->second);
if(insertMetaValue.Step()==db::ReturnCode::Done){
metaValueId = dbConnection.LastInsertedId();
}
insertMetaValue.Reset();
}
selectMetaValue.Reset();
// 3. INSERT into the track_meta table
if(metaValueId!=0){
insertTrackMeta.BindInt(0,this->id);
insertTrackMeta.BindInt(1,metaValueId);
insertTrackMeta.Step();
insertTrackMeta.Reset();
}
}
}
}
return true;
}
DBINT Track::_GetGenre(db::Connection &dbConnection,utfstring genre,bool addRelation,bool aggregated){
DBINT genreId(0);
{
db::CachedStatement stmt("SELECT id FROM genres WHERE name=?",dbConnection);
stmt.BindTextUTF(0,genre);
if(stmt.Step()==db::ReturnCode::Row){
genreId = stmt.ColumnInt(0);
}
}
if(genreId==0){
// Insert the genre
DBINT aggregatedInt = (aggregated?1:0);
db::CachedStatement stmt("INSERT INTO genres (name,aggregated) VALUES (?,?)",dbConnection);
stmt.BindTextUTF(0,genre);
stmt.BindInt(1,aggregatedInt);
if(stmt.Step()==db::ReturnCode::Done){
genreId = dbConnection.LastInsertedId();
}
}
if(addRelation){
// Insert into track_genres
db::Statement stmt("INSERT INTO track_genres (track_id,genre_id) VALUES (?,?)",dbConnection);
stmt.BindInt(0,this->id);
stmt.BindInt(1,genreId);
stmt.Step();
}
return genreId;
}
DBINT Track::_GetArtist(db::Connection &dbConnection,utfstring artist,bool addRelation,bool aggregated){
DBINT artistId(0);
db::CachedStatement stmt("SELECT id FROM artists WHERE name=?",dbConnection);
stmt.BindTextUTF(0,artist);
if(stmt.Step()==db::ReturnCode::Row){
artistId = stmt.ColumnInt(0);
}
if(artistId==0){
// Insert the genre
DBINT aggregatedInt = (aggregated?1:0);
db::Statement insertArtist("INSERT INTO artists (name,aggregated) VALUES (?,?)",dbConnection);
insertArtist.BindTextUTF(0,artist);
insertArtist.BindInt(1,aggregatedInt);
if(insertArtist.Step()==db::ReturnCode::Done){
artistId = dbConnection.LastInsertedId();
}
}
if(addRelation){
// Insert into track_genres
db::CachedStatement insertRel("INSERT INTO track_artists (track_id,artist_id) VALUES (?,?)",dbConnection);
insertRel.BindInt(0,this->id);
insertRel.BindInt(1,artistId);
insertRel.Step();
}
return artistId;
}
void Track::SetThumbnail(const char *data,unsigned int size){
if(this->meta){
this->meta->SetThumbnail(data,size);
}
}
TrackPtr Track::Copy(){
return TrackPtr(new Track(this->id));
}
bool Track::GetFileData(DBINT id,db::Connection &db){
this->InitMeta(NULL);
this->id = id;
db::CachedStatement stmt("SELECT t.filename,t.filesize,t.filetime,p.path||f.relative_path||'/'||t.filename FROM tracks t,folders f,paths p WHERE t.folder_id=f.id AND f.path_id=p.id AND t.id=?",db);
stmt.BindInt(0,id);
if(stmt.Step()==db::Row){
this->SetValue("filename" ,stmt.ColumnTextUTF(0));
this->SetValue("filesize" ,stmt.ColumnTextUTF(1));
this->SetValue("filetime" ,stmt.ColumnTextUTF(2));
this->SetValue("path" ,stmt.ColumnTextUTF(3));
return true;
}
return false;
int Track::LibraryId(){
return 0;
}

View File

@ -36,127 +36,50 @@
#pragma once
#include <core/config_filesystem.h>
#include <core/TrackMeta.h>
#include <core/ITrack.h>
#include <vector>
#include <boost/shared_ptr.hpp>
#include <boost/utility.hpp>
#include <vector>
#include <map>
//////////////////////////////////////////////////////////////////////////////
// Forward declare
namespace musik{ namespace core{
class Track;
namespace Library{
class Base;
}
namespace db{
class Connection;
}
namespace http{
class Responder;
}
typedef boost::shared_ptr<Library::Base> LibraryPtr;
class Track;
typedef boost::shared_ptr<Track> TrackPtr;
typedef std::vector<TrackPtr> TrackVector;
} }
//////////////////////////////////////////////////////////////////////////////
namespace musik{ namespace core{
//////////////////////////////////////////////////////////////////////////////
namespace musik{ namespace core{
//////////////////////////////////////////////////////////////////////////////
/*!
* \brief
* shared pointer to a track.
*
* The TrackPtr is used almost everywhere since the Tracks can be
* shared between different threads.
*
* \remarks
* For the Tracks to be thread-safe, you need to pass the ThreadHelper object when required.
*
* \see
* Track|TrackVector
*/
typedef boost::shared_ptr<Track> TrackPtr;
/*!
* \brief
* A vector of TrackPtr
*
* \see
* Track|TrackPtr
*/
typedef std::vector<TrackPtr> TrackVector;
/*!
* \brief
* Track is the main class for handling Tracks.
*
* \see
* TrackPtr|TrackVector
*/
//////////////////////////////////////////
///\brief
///The most basic implementation of a track
//////////////////////////////////////////
class Track : public ITrack {
public:
Track(void);
Track(DBINT newId);
~Track(void);
virtual ~Track();
virtual DBINT Id();
virtual musik::core::LibraryPtr Library();
virtual int LibraryId();
typedef std::multimap<std::string,utfstring> MetadataMap;
typedef std::pair<MetadataMap::iterator,MetadataMap::iterator> MetadataIteratorRange;
virtual MetadataIteratorRange GetValues(const char* metakey) = 0;
virtual MetadataIteratorRange GetAllValues() = 0;
virtual TrackPtr Copy() = 0;
TrackMeta::TagMapIteratorPair GetAllValues() const;
//void SetValue(const TrackMeta::Key &key,TrackMeta::Value &value);
TrackMeta::TagMapIteratorPair GetValues(const char* metakey) const;
const utfchar* GetValue(const char* metakey) const;
void SetValue(const char* metakey,const utfchar* value);
void SetThumbnail(const char *data,unsigned int size);
void ClearValue(const char* metakey);
void InitMeta(Library::Base *library);
bool HasMeta();
DBINT id;
void ClearMeta();
TrackPtr Copy();
private:
/*!
* \brief
* A pointer to a std::multimap of all required tags.
*
* \remarks
* The reason that this is a pointer and not a multimap on it's own
* is that we try to keep the Track as small as possible since there
* may be several thousand in use at the same time
*/
TrackMeta *meta;
inline const TrackMeta::Value& _GetValue(const TrackMeta::Key & key) const;
private:
friend class Indexer;
bool CompareDBAndFileInfo(const boost::filesystem::utfpath &file,db::Connection &dbConnection,DBINT currentFolderId);
bool Save(db::Connection &dbConnection,utfstring libraryDirectory,DBINT folderId);
DBINT _GetGenre(db::Connection &dbConnection,utfstring genre,bool addRelation,bool aggregated=false);
DBINT _GetArtist(db::Connection &dbConnection,utfstring artist,bool addRelation,bool aggregated=false);
private:
friend class http::Responder;
bool GetFileData(DBINT id,db::Connection &db);
};
//////////////////////////////////////////////////////////////////////////////
} } // musik::core
//////////////////////////////////////////////////////////////////////////////

56
src/core/TrackFactory.cpp Normal file
View File

@ -0,0 +1,56 @@
//////////////////////////////////////////////////////////////////////////////
//
// License Agreement:
//
// The following are Copyright © 2008, Daniel Önnerby
//
// 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 "pch.hpp"
#include <core/TrackFactory.h>
#include <core/LibraryTrack.h>
#include <core/GenericTrack.h>
#include <boost/regex.hpp>
#include <boost/lexical_cast.hpp>
using namespace musik::core;
TrackPtr TrackFactory::CreateTrack(utfstring uri){
if(uri.substr(0,7)==UTF("mcdb://")){
boost::wregex reg(UTF("mcdb://([0-9]+)/([0-9]+)"));
boost::wsmatch matches;
if(boost::regex_match(uri,matches,reg)){
return TrackPtr(new LibraryTrack(boost::lexical_cast<DBINT>(matches[1].str()),boost::lexical_cast<DBINT>(matches[0].str())));
}
}
return GenericTrack::Create(uri.c_str());
}

View File

@ -2,7 +2,7 @@
//
// License Agreement:
//
// The following are Copyright © 2008, mC2 team
// The following are Copyright © 2008, Daniel Önnerby
//
// All rights reserved.
//
@ -36,29 +36,22 @@
#pragma once
//////////////////////////////////////////////////////////////////////////////
#include <core/config.h>
#include <core/Track.h>
//////////////////////////////////////////////////////////////////////////////
#include <boost/utility.hpp>
namespace musik{ namespace core{
//////////////////////////////////////////////////////////////////////////////
namespace musik{ namespace core{
namespace tracklist {
class IBase : boost::noncopyable{
public:
virtual ~IBase(void){};
virtual musik::core::TrackPtr CurrentTrack()=0;
virtual musik::core::TrackPtr NextTrack()=0;
virtual musik::core::TrackPtr PreviousTrack()=0;
};
}
} } // musik::core
//////////////////////////////////////////
///\brief
///The TrackFactory helps to create tracks from a uri
//////////////////////////////////////////
class TrackFactory{
public:
static TrackPtr CreateTrack(utfstring uri);
};
//////////////////////////////////////////////////////////////////////////////
} }
//////////////////////////////////////////////////////////////////////////////

View File

@ -1,162 +0,0 @@
//////////////////////////////////////////////////////////////////////////////
//
// License Agreement:
//
// The following are Copyright © 2008, Daniel Önnerby
//
// 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 "pch.hpp"
#include <core/TrackMeta.h>
#include <core/ThreadHelper.h>
#include <core/Library/Base.h>
using namespace musik::core;
TrackMeta::TrackMeta(Library::Base *library) : library(library),thumbnailData(NULL),thumbnailSize(0){
}
TrackMeta::~TrackMeta(void){
if(this->thumbnailData)
delete this->thumbnailData;
}
//////////////////////////////////////////
///\brief
///Get value of a (one) tag
///
///\param key
///String identifier of the required value.
///
///\param threadHelper
///If you pass this ThreadHelper object, the GetValue will be threadsafe.
///
///\returns
///A const reference to a wstring (UTF-16) with the value. Empty string if not found.
//////////////////////////////////////////
const TrackMeta::Value& TrackMeta::GetValue(const TrackMeta::Key &key) const{
if(this->library){
boost::mutex::scoped_lock lock(this->library->libraryMutex);
return this->_GetValue(key);
}
return this->_GetValue(key);
}
const utfchar* TrackMeta::GetValue(const char* metakey) const{
if(this->library){
boost::mutex::scoped_lock lock(this->library->libraryMutex);
TrackMeta::TagMap::const_iterator oKeyValue = this->tags.find(metakey);
if(oKeyValue!=this->tags.end()){
return oKeyValue->second.c_str();
}
return NULL;
}
TrackMeta::TagMap::const_iterator oKeyValue = this->tags.find(metakey);
if(oKeyValue!=this->tags.end()){
return oKeyValue->second.c_str();
}
return NULL;
}
//////////////////////////////////////////
///\brief
///Get all values of one tag identifier.
///
///\param key
///String identifier of the required value.
///
///\param threadHelper
///If you pass this ThreadHelper object, the GetValue will be threadsafe.
///
///\returns
///A pair of iterators. The first is the start iterator and the second is the end.
///
///\throws <exception class>
///Description of criteria for throwing this exception.
///
///\see
///GetValue
//////////////////////////////////////////
TrackMeta::TagMapIteratorPair TrackMeta::GetValues(const char* metakey) const{
if(this->library){
boost::mutex::scoped_lock lock(library->libraryMutex);
return this->tags.equal_range(metakey);
}
return this->tags.equal_range(metakey);
}
void TrackMeta::SetValue(const TrackMeta::Key &key,const TrackMeta::Value &value){
if(!value.empty()){
if(this->library){
// Threadsafe
boost::mutex::scoped_lock lock(library->libraryMutex);
this->tags.insert( TrackMeta::TagMapPair(key,value) );
return;
}
// Non threadsafe
this->tags.insert( TrackMeta::TagMapPair(key,value) );
}
}
void TrackMeta::ClearValue(const TrackMeta::Key &key){
this->tags.erase(key);
}
const TrackMeta::Value& TrackMeta::_GetValue(const TrackMeta::Key &key) const{
TrackMeta::TagMap::const_iterator oKeyValue = this->tags.find(key);
if(oKeyValue!=this->tags.end()){
return oKeyValue->second;
}
static TrackMeta::Value emptyString;
return emptyString;
}
void TrackMeta::SetThumbnail(const char *data,unsigned int size){
if(this->thumbnailData)
delete this->thumbnailData;
this->thumbnailData = new char[size];
this->thumbnailSize = size;
memcpy(this->thumbnailData,data,size);
}

View File

@ -52,6 +52,7 @@ private:
IAudioOutput* output;
AudioPacketizer packetizer;
friend class Transport;
musik::core::TrackPtr track;
musik::core::filestreams::FileStreamPtr fileStream;

View File

@ -261,12 +261,15 @@ AudioStream* Transport::CreateStream(TrackPtr trackPtr)
this->registeredOutputSuppliers[0]->CreateAudioOutput();
// Get the path, filename and fileextension
if( trackPtr->GetValue("path")==NULL ){
if( trackPtr->URL()==NULL ){
return NULL;
}
utfstring fileName(trackPtr->GetValue("filename"));
utfstring filePath(trackPtr->GetValue("path"));
utfstring filePath(trackPtr->URL());
utfstring fileName(filePath);
if(trackPtr->GetValue("filename")){
fileName = trackPtr->GetValue("filename");
}
utfstring fileExtension(fileName);
// Extract fileextension
@ -320,4 +323,15 @@ void Transport::RemoveFinishedStreams()
--i;
}
}
}
}
TrackPtr Transport::CurrentTrack(){
if(this->activeStream){
if(this->activeStream->track){
return this->activeStream->track;
}
}
return TrackPtr();
}

View File

@ -97,6 +97,8 @@ public:
///\brief Get a list with descriptions of the open streams
AudioStreamOverview StreamsOverview() const;
TrackPtr CurrentTrack();
private:
typedef std::vector<boost::shared_ptr<IAudioSourceSupplier> > SourceSupplierList;
SourceSupplierList registeredSourceSuppliers;

View File

@ -242,27 +242,45 @@
Name="tracklist"
>
<File
RelativePath=".\tracklist\IBase.h"
RelativePath=".\tracklist\Base.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)\$(InputName)2.obj"
XMLDocumentationFileName="$(IntDir)\$(InputName)2.xdc"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)\$(InputName)2.obj"
XMLDocumentationFileName="$(IntDir)\$(InputName)2.xdc"
/>
</FileConfiguration>
</File>
<File
RelativePath=".\tracklist\Base.h"
>
</File>
<File
RelativePath=".\tracklist\IRandomAccess.h"
RelativePath=".\tracklist\LibraryList.cpp"
>
</File>
<File
RelativePath=".\tracklist\Playlist.cpp"
RelativePath=".\tracklist\LibraryList.h"
>
</File>
<File
RelativePath=".\tracklist\Playlist.h"
RelativePath=".\tracklist\MultiLibraryList.cpp"
>
</File>
<File
RelativePath=".\tracklist\Standard.cpp"
>
</File>
<File
RelativePath=".\tracklist\Standard.h"
RelativePath=".\tracklist\MultiLibraryList.h"
>
</File>
</Filter>
@ -338,30 +356,6 @@
RelativePath=".\Query\ListSelection.h"
>
</File>
<File
RelativePath=".\Query\PlaylistLoad.cpp"
>
</File>
<File
RelativePath=".\Query\PlaylistLoad.h"
>
</File>
<File
RelativePath=".\Query\Playlists.cpp"
>
</File>
<File
RelativePath=".\Query\Playlists.h"
>
</File>
<File
RelativePath=".\Query\PlaylistSave.cpp"
>
</File>
<File
RelativePath=".\Query\PlaylistSave.h"
>
</File>
<File
RelativePath=".\Query\SortTracks.cpp"
>
@ -370,6 +364,14 @@
RelativePath=".\Query\SortTracks.h"
>
</File>
<File
RelativePath=".\Query\SortTracksWithData.cpp"
>
</File>
<File
RelativePath=".\Query\SortTracksWithData.h"
>
</File>
<File
RelativePath=".\Query\TrackMetadata.cpp"
>
@ -451,10 +453,6 @@
<Filter
Name="metadata"
>
<File
RelativePath=".\ITrack.h"
>
</File>
<File
RelativePath=".\MetadataValue.cpp"
>
@ -472,19 +470,11 @@
>
</File>
<File
RelativePath=".\Track.cpp"
RelativePath=".\NonLibraryTrackHelper.cpp"
>
</File>
<File
RelativePath=".\Track.h"
>
</File>
<File
RelativePath=".\TrackMeta.cpp"
>
</File>
<File
RelativePath=".\TrackMeta.h"
RelativePath=".\NonLibraryTrackHelper.h"
>
</File>
</Filter>
@ -766,6 +756,46 @@
>
</File>
</Filter>
<Filter
Name="tracks"
>
<File
RelativePath=".\GenericTrack.cpp"
>
</File>
<File
RelativePath=".\GenericTrack.h"
>
</File>
<File
RelativePath=".\ITrack.h"
>
</File>
<File
RelativePath=".\LibraryTrack.cpp"
>
</File>
<File
RelativePath=".\LibraryTrack.h"
>
</File>
<File
RelativePath=".\Track.cpp"
>
</File>
<File
RelativePath=".\Track.h"
>
</File>
<File
RelativePath=".\TrackFactory.cpp"
>
</File>
<File
RelativePath=".\TrackFactory.h"
>
</File>
</Filter>
</Filter>
</Files>
<Globals>

View File

@ -37,6 +37,7 @@
#include "pch.hpp"
#include <core/db/Connection.h>
#include <boost/lexical_cast.hpp>
#include <boost/thread/thread.hpp>
#include <sqlite/sqlite3.h>
using namespace musik::core::db;
@ -180,7 +181,7 @@ int Connection::Execute(const char* sql){
}
// Execute the statement
int error = sqlite3_step(stmt);
int error = this->StepStatement(stmt);
if(error!=SQLITE_OK && error!=SQLITE_DONE){
sqlite3_finalize(stmt);
return ReturnCode::Error;
@ -217,7 +218,7 @@ int Connection::Execute(const wchar_t* sql){
}
// Execute the statement
int error = sqlite3_step(stmt);
int error = this->StepStatement(stmt);
if(error!=SQLITE_OK && error!=SQLITE_DONE){
sqlite3_finalize(stmt);
return ReturnCode::Error;
@ -259,6 +260,7 @@ int Connection::LastInsertedId(){
///This will set all the initial PRAGMAS
//////////////////////////////////////////
void Connection::Initialize(unsigned int cache){
sqlite3_enable_shared_cache(1);
sqlite3_busy_timeout(this->connection,10000);
sqlite3_exec(this->connection,"PRAGMA synchronous=OFF",NULL,NULL,NULL); // Not a critical DB. Sync set to OFF
@ -369,3 +371,17 @@ void Connection::Maintenance(bool init){
}
}
}
int Connection::StepStatement(sqlite3_stmt *stmt){
int waitCount(100);
int error(0);
do{
error = sqlite3_step(stmt);
if(error==SQLITE_LOCKED){
boost::thread::yield();
waitCount--;
}
}while(error==SQLITE_LOCKED && waitCount>0);
return error;
}

View File

@ -88,6 +88,8 @@ namespace musik{ namespace core{ namespace db{
sqlite3_stmt *GetCachedStatement(const char* sql);
void ReturnCachedStatement(const char* sql,sqlite3_stmt *stmt);
int StepStatement(sqlite3_stmt *stmt);
int transactionCounter;
sqlite3 *connection;

View File

@ -102,7 +102,7 @@ void Statement::UnBindAll(){
///musik::core::db::ReturnCode
//////////////////////////////////////////
int Statement::Step(){
return sqlite3_step(this->stmt);
return this->connection->StepStatement(this->stmt);
}

View File

@ -58,21 +58,23 @@ Factory::Factory(){
FileStreamPtr Factory::OpenFile(const utfchar *filename){
typedef musik::core::PluginFactory::DestroyDeleter<IFileStream> StreamDeleter;
for(FileStreamFactoryVector::iterator factory=sInstance.fileStreamFactories.begin();factory!=sInstance.fileStreamFactories.end();++factory){
if( (*factory)->CanReadFile(filename) ){
IFileStream* fileStream( (*factory)->OpenFile(filename) );
if(fileStream){
return FileStreamPtr(fileStream,StreamDeleter());
}else{
return FileStreamPtr();
if(filename){
for(FileStreamFactoryVector::iterator factory=sInstance.fileStreamFactories.begin();factory!=sInstance.fileStreamFactories.end();++factory){
if( (*factory)->CanReadFile(filename) ){
IFileStream* fileStream( (*factory)->OpenFile(filename) );
if(fileStream){
return FileStreamPtr(fileStream,StreamDeleter());
}else{
return FileStreamPtr();
}
}
}
}
// If non of the plugins match, lets create a regular file stream
FileStreamPtr regularFile( new LocalFileStream(),StreamDeleter() );
if(regularFile->Open(filename)){
return regularFile;
// If non of the plugins match, lets create a regular file stream
FileStreamPtr regularFile( new LocalFileStream(),StreamDeleter() );
if(regularFile->Open(filename)){
return regularFile;
}
}
return FileStreamPtr();
}

View File

@ -38,14 +38,12 @@
#include <core/config.h>
#include <core/filestreams/IFileStream.h>
#include <core/filestreams/IFileStreamFactory.h>
#include <boost/shared_ptr.hpp>
#include <vector>
//////////////////////////////////////////////////////////////////////////////
namespace musik{ namespace core{ namespace filestreams{
//////////////////////////////////////////////////////////////////////////////
typedef boost::shared_ptr<IFileStream> FileStreamPtr;
class Factory {
private:

View File

@ -37,6 +37,7 @@
#pragma once
#include <core/config.h>
#include <boost/shared_ptr.hpp>
//////////////////////////////////////////////////////////////////////////////
namespace musik{ namespace core{ namespace filestreams{
@ -132,6 +133,8 @@ class IFileStream{
};
typedef boost::shared_ptr<IFileStream> FileStreamPtr;
//////////////////////////////////////////////////////////////////////////////
} } }
//////////////////////////////////////////////////////////////////////////////

View File

@ -63,11 +63,16 @@ LocalFileStream::~LocalFileStream(){
}
bool LocalFileStream::Open(const utfchar *filename,unsigned int options){
boost::filesystem::utfpath file(filename);
this->filesize = (long)boost::filesystem::file_size(file);
try{
boost::filesystem::utfpath file(filename);
this->filesize = (long)boost::filesystem::file_size(file);
this->file = UTFFopen(filename,UTF("rb"));
return this->file!=NULL;
this->file = UTFFopen(filename,UTF("rb"));
return this->file!=NULL;
}
catch(...){
return false;
}
}
bool LocalFileStream::Close(){

View File

@ -51,7 +51,7 @@ class IRequestPlugin{
public:
virtual void Destroy()=0;
virtual const char* WatchPath()=0;
virtual void Execute(musik::core::http::IResponder* responder,musik::core::http::IRequestParser* request,const musik::core::ITrack* track)=0;
virtual void Execute(musik::core::http::IResponder* responder,musik::core::http::IRequestParser* request,musik::core::ITrack* track)=0;
};
//////////////////////////////////////////////////////////////////////////////

View File

@ -41,6 +41,7 @@
#include <core/http/RequestParser.h>
#include <core/Common.h>
#include <core/Server.h>
#include <core/LibraryTrack.h>
#include <iostream>
#include <stdlib.h>
@ -107,8 +108,9 @@ void Responder::ThreadLoop(){
const char* trackId = requester.Attribute("track_id");
if(trackId){
try{
track.reset(new musik::core::Track());
if(!track->GetFileData( boost::lexical_cast<DBINT>(trackId),this->db)){
musik::core::LibraryTrack *libraryTrack = new musik::core::LibraryTrack();
track.reset(libraryTrack);
if(!libraryTrack->GetFileData( boost::lexical_cast<DBINT>(trackId),this->db)){
track.reset();
}
}

View File

@ -37,6 +37,7 @@
#include "pch.hpp"
#include <core/http/TrackSender.h>
#include <core/Common.h>
#include <core/filestreams/Factory.h>
using namespace musik::core::http;
@ -51,21 +52,21 @@ const char* TrackSender::WatchPath(){
return this->watchPath.c_str();
}
void TrackSender::Execute(musik::core::http::IResponder* responder,musik::core::http::IRequestParser* request,const musik::core::ITrack* track){
void TrackSender::Execute(musik::core::http::IResponder* responder,musik::core::http::IRequestParser* request,musik::core::ITrack* track){
#define BUFFER_SIZE 4096
char buffer[BUFFER_SIZE];
std::size_t buffersize(0);
// TODO: Should rewrite this in a multiplatform manner
if(track && responder){
FILE *file = _wfopen(track->GetValue("path"),UTF("rb"));
musik::core::filestreams::FileStreamPtr file( musik::core::filestreams::Factory::OpenFile(track->URL()) );
if(file){
const char *seekOffset = request->Attribute("seek");
if(seekOffset){
try{
long seekTo = boost::lexical_cast<long>(seekOffset);
fseek(file,seekTo,SEEK_SET);
file->SetPosition(seekTo);
}
catch(...){
}
@ -78,17 +79,16 @@ void TrackSender::Execute(musik::core::http::IResponder* responder,musik::core::
responder->SendContent(header.c_str(),header.size());
while(!feof(file) && file && !responder->Exited()){
while(!file->Eof() && !responder->Exited()){
buffersize=0;
while(buffersize<BUFFER_SIZE && !feof(file)){
buffersize += fread(buffer,sizeof(char),BUFFER_SIZE-buffersize,file);
while(buffersize<BUFFER_SIZE && !file->Eof()){
buffersize += file->Read(buffer,BUFFER_SIZE-buffersize);
}
// send buffer
responder->SendContent(buffer,buffersize);
}
fclose(file);
}
}
}

View File

@ -38,6 +38,7 @@
#include <core/config.h>
#include <core/http/IRequestPlugin.h>
#include <core/ITrack.h>
#include <string>
//////////////////////////////////////////////////////////////////////////////
@ -51,7 +52,7 @@ class TrackSender : public IRequestPlugin{
virtual void Destroy();
virtual const char* WatchPath();
virtual void Execute(musik::core::http::IResponder* responder,musik::core::http::IRequestParser* request,const musik::core::ITrack* track);
virtual void Execute(musik::core::http::IResponder* responder,musik::core::http::IRequestParser* request,musik::core::ITrack* track);
private:
std::string watchPath;

View File

@ -50,15 +50,15 @@
#include <core/db/Statement.h>
#include <core/db/CachedStatement.h>
#include <core/Query/Base.h>
//#include <core/Query/Base.h>
#include <core/Track.h>
#include <core/TrackMeta.h>
//#include <core/Track.h>
//#include <core/TrackMeta.h>
#include <core/xml/Node.h>
#include <core/xml/WriterNode.h>
#include <core/xml/ParserNode.h>
#include <core/Library/Base.h>
//#include <core/Library/Base.h>
/*

View File

@ -56,10 +56,11 @@ using namespace musik::core::server;
Connection::Connection(boost::asio::io_service &ioService,musik::core::Server *server)
:socket(ioService)
,Base(UTF("Server"))
,Base(UTF("Server"),0)
,server(server)
,salt(musik::core::Crypt::GenerateSalt())
{
this->identifier = this->Name();
}
Connection::~Connection(void){

View File

@ -2,7 +2,7 @@
//
// License Agreement:
//
// The following are Copyright © 2008, mC2 team
// The following are Copyright © 2008, Daniel Önnerby
//
// All rights reserved.
//
@ -35,9 +35,7 @@
//////////////////////////////////////////////////////////////////////////////
#include "pch.hpp"
#include <core/tracklist/Playlist.h>
#include <core/Query/PlaylistLoad.h>
#include <core/tracklist/Base.h>
//////////////////////////////////////////////////////////////////////////////
@ -45,25 +43,30 @@ using namespace musik::core::tracklist;
//////////////////////////////////////////////////////////////////////////////
Playlist::Playlist(int id,utfstring name,musik::core::LibraryPtr library)
:Standard()
,id(0)
,name(name)
{
this->SetLibrary(library);
// Start by creating a query that loads the tracks
musik::core::LibraryPtr Base::Library(){
return musik::core::LibraryPtr();
}
Playlist::~Playlist(void){
Base::~Base(){
}
utfstring Playlist::Name(){
return this->name;
bool Base::SortTracks(std::string sortingMetakey){
return false;
}
int Playlist::Id(){
return this->id;
bool Base::ConnectToQuery(musik::core::Query::ListBase &query){
return false;
}
bool Base::AddRequestedMetakey(std::string metakey){
this->requestedMetakeys.insert(metakey);
return true;
}
void Base::HintVisibleRows(long rows){
this->hintedRows = rows;
}
void Base::ClearMetadata(){
}

279
src/core/tracklist/Base.h Normal file
View File

@ -0,0 +1,279 @@
//////////////////////////////////////////////////////////////////////////////
//
// License Agreement:
//
// The following are Copyright © 2008, Daniel Önnerby
//
// 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/config.h>
#include <core/Track.h>
#include <core/Library/Base.h>
#include <core/Query/ListBase.h>
//////////////////////////////////////////////////////////////////////////////
#include <boost/utility.hpp>
#include <boost/shared_ptr.hpp>
#include <set>
//////////////////////////////////////////////////////////////////////////////
namespace musik{ namespace core{ namespace tracklist {
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////
///\brief
///The virtual base that sets up the required methods for a tracklist
//////////////////////////////////////////
class Base : boost::noncopyable{
public:
virtual ~Base();
//////////////////////////////////////////
///\brief
///Get track from a specified position
///
///\param position
///Position in tracklist to get
///
///\returns
///shared pointer to track (could be a null pointer)
//////////////////////////////////////////
virtual musik::core::TrackPtr operator [](long position) = 0;
//////////////////////////////////////////
///\brief
///Get track with metadata from a specified position
///
///\param position
///Position in tracklist to get
///
///\returns
///shared pointer to track (could be a null pointer)
///
///This is similar to the operator[], but will also request the metadata.
///If the track does not currently have any metadata, it will be signaled
///using the TrackMetadataUpdated event when data arrives.
///
///\see
///TrackMetadataUpdated
//////////////////////////////////////////
virtual musik::core::TrackPtr TrackWithMetadata(long position)=0;
//////////////////////////////////////////
///\brief
///Get the current track
//////////////////////////////////////////
virtual musik::core::TrackPtr CurrentTrack()=0;
//////////////////////////////////////////
///\brief
///Get the next track and increase the position.
//////////////////////////////////////////
virtual musik::core::TrackPtr NextTrack()=0;
//////////////////////////////////////////
///\brief
///Get the previous track and decrease the position.
//////////////////////////////////////////
virtual musik::core::TrackPtr PreviousTrack()=0;
//////////////////////////////////////////
///\brief
///Set a new position in the tracklist.
///
///\param position
///Position to set.
///
///\returns
///True if position is a valid one and successfully set.
//////////////////////////////////////////
virtual bool SetPosition(long position)=0;
//////////////////////////////////////////
///\brief
///Get the current position. -1 if undefined.
//////////////////////////////////////////
virtual long CurrentPosition()=0;
//////////////////////////////////////////
///\brief
///Get current size of the tracklist. -1 if unknown.
//////////////////////////////////////////
virtual long Size() = 0;
//////////////////////////////////////////
///\brief
///Clear the tracklist
//////////////////////////////////////////
virtual void Clear() = 0;
//////////////////////////////////////////
///\brief
///Set (copy) tracklist from another tracklist
///
///\param tracklist
///tracklist to copy from
///
///\returns
///True if successfully copied.
//////////////////////////////////////////
virtual bool operator =(musik::core::tracklist::Base &tracklist) = 0;
//////////////////////////////////////////
///\brief
///Append a tracklist to this tracklist
///
///\param tracklist
///tracklist to append to this one
///
///\returns
///True if successfully appended
//////////////////////////////////////////
virtual bool operator +=(musik::core::tracklist::Base &tracklist) = 0;
//////////////////////////////////////////
///\brief
///Append a track to this tracklist
///
///\param track
///Track to add
///
///\returns
///True if successfully appended
//////////////////////////////////////////
virtual bool operator +=(musik::core::TrackPtr track) = 0;
//////////////////////////////////////////
///\brief
///Get related library. Null pointer if non.
//////////////////////////////////////////
virtual musik::core::LibraryPtr Library();
//////////////////////////////////////////
///\brief
///Sort tracks by a metakey
//////////////////////////////////////////
virtual bool SortTracks(std::string sortingMetakey);
//////////////////////////////////////////
///\brief
///Connect the tracklist to recieve tracks from a ListBase query.
//////////////////////////////////////////
virtual bool ConnectToQuery(musik::core::Query::ListBase &query);
//////////////////////////////////////////
///\brief
///Add a metakey to the list of metakeys to
///get when requesting TrackWithMetadata
//////////////////////////////////////////
virtual bool AddRequestedMetakey(std::string metakey);
//////////////////////////////////////////
///\brief
///Make a hint on how may rows that are visible
//////////////////////////////////////////
virtual void HintVisibleRows(long rows);
//////////////////////////////////////////
///\brief
///Clears the metadata cache
//////////////////////////////////////////
virtual void ClearMetadata();
/////////////////////////////////////////////////////////////////////////////
// EVENTS
/////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////
typedef sigslot::signal1<std::vector<long>> TrackMetadataEvent;
//////////////////////////////////////////
///\brief
///Event, called when metadata has been recieved.
///
///The event reciever will get a std::vector<long>& with
///a vector of positions of tracks that has been updated.
//////////////////////////////////////////
TrackMetadataEvent TrackMetadataUpdated;
//////////////////////////////////////////
typedef sigslot::signal3<UINT64,UINT64,UINT64> SummaryInfoEvent;
//////////////////////////////////////////
///\brief
///Event, called when summary has been updated
///
///The event reciever will get 3 UINT64 parameters
//////////////////////////////////////////
SummaryInfoEvent SummaryInfoUpdated;
//////////////////////////////////////////
typedef sigslot::signal1<bool> TracklistChangedEvent;
//////////////////////////////////////////
///\brief
///Event, called when tracks inside the tracklist has been added/changed
///
///The event reciever will get a bool set to true if the list has been cleared first
//////////////////////////////////////////
TracklistChangedEvent TracklistChanged;
//////////////////////////////////////////
typedef sigslot::signal2<long,long> PositionChangedEvent;
//////////////////////////////////////////
///\brief
///Event, called when position in tracklist has been updated
///
///The event reciever will get the nex position, and the old position
//////////////////////////////////////////
PositionChangedEvent PositionChanged;
protected:
std::set<std::string> requestedMetakeys;
long hintedRows;
};
typedef boost::shared_ptr<Base> Ptr;
//////////////////////////////////////////////////////////////////////////////
} } } // musik::core
//////////////////////////////////////////////////////////////////////////////

View File

@ -1,112 +0,0 @@
//////////////////////////////////////////////////////////////////////////////
//
// License Agreement:
//
// The following are Copyright © 2008, mC2 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
//////////////////////////////////////////////////////////////////////////////
// Forward declare
namespace musik{ namespace core{
namespace Query{
class ListBase;
}
namespace tracklist{
class IRandomAccess;
typedef boost::shared_ptr<IRandomAccess> Ptr;
typedef boost::weak_ptr<IRandomAccess> WeakPtr;
}
} }
//////////////////////////////////////////////////////////////////////////////
#include <core/tracklist/IBase.h>
#include <core/Library/Base.h>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <sigslot/sigslot.h>
namespace musik{ namespace core{
namespace tracklist {
class IRandomAccess : public IBase{
public:
~IRandomAccess(void){};
virtual void SetCurrentPosition(int position) = 0;
virtual int CurrentPosition() = 0;
virtual int Size() = 0;
virtual musik::core::TrackPtr operator [](int position) = 0;
virtual musik::core::TrackPtr TrackWithMetadata(int position)=0;
virtual void SetLibrary(musik::core::LibraryPtr setLibrary) = 0;
virtual musik::core::LibraryPtr Library() = 0;
virtual bool CopyTracks(musik::core::tracklist::Ptr tracklist) = 0;
virtual bool AppendTracks(musik::core::tracklist::Ptr tracklist) = 0;
virtual void AppendTrack(musik::core::TrackPtr track) = 0;
virtual void Clear() = 0;
virtual void AddRequestedMetakey(const char* metakey) = 0;
virtual void RemoveRequestedMetakey(const char* metakey) = 0;
virtual UINT64 Duration() = 0;
virtual UINT64 Filesize() = 0;
virtual void HintNumberOfRows(int rows) = 0;
virtual void ConnectToQuery(musik::core::Query::ListBase &listQuery)=0;
/////////////////////////////////////////////////////////////////////////
typedef sigslot::signal3<UINT64,UINT64,UINT64> TracklistInfoEvent;
TracklistInfoEvent TracklistInfoUpdated;
typedef sigslot::signal1<bool> TracksEvent;
TracksEvent TracksUpdated;
typedef sigslot::signal1<std::vector<int>&> TrackMetaEvent;
TrackMetaEvent TrackMetaUpdated;
typedef sigslot::signal2<int,int> PositionChangedEvent;
PositionChangedEvent PositionChanged;
};
}
} }

View File

@ -0,0 +1,485 @@
//////////////////////////////////////////////////////////////////////////////
//
// License Agreement:
//
// The following are Copyright © 2008, Daniel Önnerby
//
// 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 "pch.hpp"
#include <core/tracklist/LibraryList.h>
#include <core/LibraryTrack.h>
#include <core/Query/SortTracks.h>
//////////////////////////////////////////////////////////////////////////////
using namespace musik::core::tracklist;
//////////////////////////////////////////////////////////////////////////////
LibraryList::LibraryList(musik::core::LibraryPtr library)
:library(library)
,currentPosition(-1)
,maxCacheSize(100)
{
this->hintedRows = 10;
this->metadataQuery.OnTracksEvent.connect(this,&LibraryList::OnTracksMetaFromQuery);
}
//////////////////////////////////////////
///\brief
///Get track from a specified position
///
///\param position
///Position in tracklist to get
///
///\returns
///shared pointer to track (could be a null pointer)
//////////////////////////////////////////
musik::core::TrackPtr LibraryList::operator [](long position){
// Valid position?
if(position>=0 && position<this->Size()){
// If in cache?
PositionCacheMap::iterator trackPosition = this->positionCache.find(position);
if(trackPosition!=this->positionCache.end()){
return trackPosition->second;
}
return musik::core::TrackPtr(new LibraryTrack(this->tracklist[position],this->library));
}
return musik::core::TrackPtr();
}
//////////////////////////////////////////
///\brief
///Get track with metadata from a specified position
///
///\param position
///Position in tracklist to get
///
///\returns
///shared pointer to track (could be a null pointer)
///
///This is similar to the operator[], but will also request the metadata.
///If the track does not currently have any metadata, it will be signaled
///using the TrackMetadataUpdated event when data arrives.
///
///\see
///TrackMetadataUpdated
//////////////////////////////////////////
musik::core::TrackPtr LibraryList::TrackWithMetadata(long position){
// check the positionCache if the track is in the cache already
PositionCacheMap::iterator trackPosition = this->positionCache.find(position);
if(trackPosition!=this->positionCache.end()){
return trackPosition->second;
}
// Load and add to cache
this->LoadTrack(position);
return (*this)[position];
}
//////////////////////////////////////////
///\brief
///Get the current track
//////////////////////////////////////////
musik::core::TrackPtr LibraryList::CurrentTrack(){
if(this->currentPosition==-1 && this->Size()>0){
this->SetPosition(0);
}
return (*this)[this->currentPosition];
}
//////////////////////////////////////////
///\brief
///Get the next track and increase the position.
//////////////////////////////////////////
musik::core::TrackPtr LibraryList::NextTrack(){
long newPosition = this->currentPosition+1;
musik::core::TrackPtr nextTrack = (*this)[newPosition];
if(nextTrack){
this->SetPosition(newPosition);
}
return nextTrack;
}
//////////////////////////////////////////
///\brief
///Get the previous track and decrease the position.
//////////////////////////////////////////
musik::core::TrackPtr LibraryList::PreviousTrack(){
long newPosition = this->currentPosition-1;
musik::core::TrackPtr nextTrack = (*this)[newPosition];
if(nextTrack){
this->SetPosition(newPosition);
}
return nextTrack;
}
//////////////////////////////////////////
///\brief
///Set a new position in the tracklist.
///
///\param position
///Position to set.
///
///\returns
///True if position is a valid one and successfully set.
//////////////////////////////////////////
bool LibraryList::SetPosition(long position){
if(position>=0 && position<this->tracklist.size()){
this->PositionChanged(position,this->currentPosition);
this->currentPosition = position;
return true;
}
return false;
}
//////////////////////////////////////////
///\brief
///Get the current position. -1 if undefined.
//////////////////////////////////////////
long LibraryList::CurrentPosition(){
return this->currentPosition;
}
//////////////////////////////////////////
///\brief
///Get current size of the tracklist. -1 if unknown.
//////////////////////////////////////////
long LibraryList::Size(){
return (long)this->tracklist.size();
}
//////////////////////////////////////////
///\brief
///Clear the tracklist
//////////////////////////////////////////
void LibraryList::Clear(){
this->tracklist.clear();
this->ClearMetadata();
this->TracklistChanged(true);
this->PositionChanged(-1,this->currentPosition);
this->currentPosition = -1;
}
//////////////////////////////////////////
///\brief
///Clear the internal used cache
//////////////////////////////////////////
void LibraryList::ClearMetadata(){
this->positionCache.clear();
this->trackCache.clear();
}
//////////////////////////////////////////
///\brief
///Set (copy) tracklist from another tracklist
///
///\param tracklist
///tracklist to copy from
///
///\returns
///True if successfully copied.
//////////////////////////////////////////
bool LibraryList::operator =(musik::core::tracklist::Base &tracklist){
this->Clear();
// optimize if dynamic_cast works
LibraryList *fromLibraryList = dynamic_cast<LibraryList*>(&tracklist);
if(fromLibraryList){
if(fromLibraryList->library==this->library){
this->tracklist = fromLibraryList->tracklist;
this->TracklistChanged(true);
this->SetPosition(fromLibraryList->CurrentPosition());
return true;
}
this->TracklistChanged(true);
return false;
}
// The default way
this->tracklist.reserve(tracklist.Size());
int libraryId = this->library->Id();
bool success(false);
// Loop through the tracks and copy everything
for(long i(0);i<tracklist.Size();++i){
musik::core::TrackPtr currentTrack = tracklist[i];
if(currentTrack->Id()==libraryId){
// Same library, append
this->tracklist.push_back(currentTrack->Id());
success=true;
}
}
this->TracklistChanged(true);
this->SetPosition(tracklist.CurrentPosition());
return success;
}
//////////////////////////////////////////
///\brief
///Append tracks from another tracklist
///
///It will append all tracks that comes from
///the same library and ignore the rest.
//////////////////////////////////////////
bool LibraryList::operator +=(musik::core::tracklist::Base &tracklist){
this->tracklist.reserve(tracklist.Size()+this->Size());
int libraryId = this->library->Id();
bool success(false);
// Loop through the tracks and copy everything
for(long i(0);i<tracklist.Size();++i){
musik::core::TrackPtr currentTrack = tracklist[i];
if(currentTrack->Id()==libraryId){
// Same library, append
this->tracklist.push_back(currentTrack->Id());
success=true;
}
}
if(success){
this->TracklistChanged(false);
}
return success;
}
//////////////////////////////////////////
///\brief
///Append a track to this tracklist
///
///\param track
///Track to add
///
///\returns
///True if successfully appended
//////////////////////////////////////////
bool LibraryList::operator +=(musik::core::TrackPtr track){
if(this->library->Id()==track->LibraryId()){
this->tracklist.push_back(track->Id());
this->TracklistChanged(false);
return true;
}
return false;
}
//////////////////////////////////////////
///\brief
///Get related library. Null pointer if non.
//////////////////////////////////////////
musik::core::LibraryPtr LibraryList::Library(){
return this->library;
}
//////////////////////////////////////////
///\brief
///Load a tracks metadata (if not already loaded)
//////////////////////////////////////////
void LibraryList::LoadTrack(long position){
if(this->QueryForTrack(position)){
// If the track should load, then preload others as well
int trackCount(1);
// Preload hintedRows
for(long i(position+1);i<position+this->hintedRows;++i){
if(this->QueryForTrack(i)){
++trackCount;
}
}
// Send the query to the library
this->library->AddQuery(this->metadataQuery,musik::core::Query::Prioritize);
// Finally, clear the query for futher metadata
this->metadataQuery.Clear();
////////////////////////////////////////
// Lets see if the cache is too big
if(this->positionCache.size()>this->maxCacheSize){
// Cache too big, what end should be removed
PositionCacheMap::iterator front = this->positionCache.begin();
PositionCacheMap::iterator back = this->positionCache.end();
back--;
if(position-front->first < back->first-position){
// closer to beginning, lets erase in the end
while(this->positionCache.size()>this->maxCacheSize){
back = this->positionCache.end();
back--;
this->trackCache.erase(back->second);
this->positionCache.erase(back);
}
}else{
// closer to end, lets erase in the beginning
while(this->positionCache.size()>this->maxCacheSize){
front = this->positionCache.begin();
this->trackCache.erase(front->second);
this->positionCache.erase(front);
}
}
}
}
}
//////////////////////////////////////////
///\brief
///Request metadata for track
//////////////////////////////////////////
bool LibraryList::QueryForTrack(long position){
PositionCacheMap::iterator trackIt=this->positionCache.find(position);
if(trackIt==this->positionCache.end()){
// Not in cache, lets find the track
musik::core::TrackPtr track = (*this)[position];
if(track){
// Track is also a valid track, lets add it to the cache
this->positionCache[position] = track;
this->trackCache[track] = position;
// Finally, lets add it to the query
this->metadataQuery.RequestTrack(track);
return true;
}
}
return false;
}
//////////////////////////////////////////
///\brief
///Connect to recieve results from any query based on the musik::core::Query::ListBase
//////////////////////////////////////////
bool LibraryList::ConnectToQuery(musik::core::Query::ListBase &query){
query.OnTrackEvent().connect(this,&LibraryList::OnTracksFromQuery);
query.OnTrackInfoEvent().connect(this,&LibraryList::OnTracksSummaryFromQuery);
return true;
}
//////////////////////////////////////////
///\brief
///Recieve new tracks from a query
///
///\param newTracks
///The new tracks
///
///\param clear
///Should the tracklist be cleared before tracks are added.
//////////////////////////////////////////
void LibraryList::OnTracksFromQuery(musik::core::TrackVector *newTracks,bool clear){
if(clear){
this->positionCache.clear();
this->trackCache.clear();
this->tracklist.clear();
this->SetPosition(-1); // undefined
}
// Copy to tracklist
for(musik::core::TrackVector::iterator track=newTracks->begin();track!=newTracks->end();++track){
this->tracklist.push_back( (*track)->Id() );
}
this->TracklistChanged(true);
}
//////////////////////////////////////////
///\brief
///Called by connected queries to recieve summary from tracklist
//////////////////////////////////////////
void LibraryList::OnTracksSummaryFromQuery(UINT64 tracks,UINT64 duration,UINT64 filesize){
this->SummaryInfoUpdated(tracks,duration,filesize);
}
//////////////////////////////////////////
///\brief
///Recieves what tracks now have metadata, and pass them forward to the TrackMetadataUpdated signal
//////////////////////////////////////////
void LibraryList::OnTracksMetaFromQuery(musik::core::TrackVector *metaTracks){
std::vector<long> updateTrackPositions;
for(musik::core::TrackVector::iterator track=metaTracks->begin();track!=metaTracks->end();++track){
TrackCacheMap::iterator position = this->trackCache.find(*track);
if(position!=this->trackCache.end()){
updateTrackPositions.push_back(position->second);
}
}
this->TrackMetadataUpdated(updateTrackPositions);
}
//////////////////////////////////////////
///\brief
///Request another metakey
//////////////////////////////////////////
bool LibraryList::AddRequestedMetakey(std::string metakey){
this->requestedMetakeys.insert(metakey);
this->metadataQuery.RequestMetakeys(this->requestedMetakeys);
return true;
}
//////////////////////////////////////////
///\brief
///Sort the tracks in the tracklist
///
///The method will not wait for the tracklist to be sorted.
///Instead the query will be send to the library for parsing.
//////////////////////////////////////////
bool LibraryList::SortTracks(std::string sortingMetakey){
musik::core::Query::SortTracks sortQuery;
sortQuery.AddTracks(this->tracklist);
sortQuery.OnTrackEvent().connect(this,&LibraryList::OnTracksFromQuery);
std::list<std::string> sortBy;
sortBy.push_back(sortingMetakey);
sortQuery.SortByMetaKeys(sortBy);
this->library->AddQuery(sortQuery);
return true;
}

View File

@ -2,7 +2,7 @@
//
// License Agreement:
//
// The following are Copyright © 2008, mC2 team
// The following are Copyright © 2008, Daniel Önnerby
//
// All rights reserved.
//
@ -38,85 +38,92 @@
//////////////////////////////////////////////////////////////////////////////
#include <core/tracklist/IRandomAccess.h>
#include <core/tracklist/Base.h>
#include <core/Query/ListBase.h>
#include <core/Query/TrackMetadata.h>
#include <core/Library/Base.h>
#include <set>
#include <map>
#include <sigslot/sigslot.h>
#include <boost/shared_ptr.hpp>
#include <boost/bimap.hpp>
//////////////////////////////////////////////////////////////////////////////
namespace musik{ namespace core{ namespace tracklist {
//////////////////////////////////////////////////////////////////////////////
class Standard : public IRandomAccess, public sigslot::has_slots<> {
//////////////////////////////////////////
///\brief
///A LibraryList can only contain track from one single library
//////////////////////////////////////////
class LibraryList : public Base, public sigslot::has_slots<> {
public:
Standard(void);
~Standard(void);
LibraryList(musik::core::LibraryPtr library);
musik::core::TrackPtr CurrentTrack();
musik::core::TrackPtr NextTrack();
musik::core::TrackPtr PreviousTrack();
virtual musik::core::TrackPtr operator [](long position);
virtual musik::core::TrackPtr TrackWithMetadata(long position);
virtual musik::core::TrackPtr CurrentTrack();
virtual musik::core::TrackPtr NextTrack();
virtual musik::core::TrackPtr PreviousTrack();
virtual void SetCurrentPosition(int position);
virtual int CurrentPosition();
virtual int Size();
virtual musik::core::TrackPtr operator [](int position);
virtual musik::core::TrackPtr TrackWithMetadata(int position);
virtual void SetLibrary(musik::core::LibraryPtr setLibrary);
virtual musik::core::LibraryPtr Library();
virtual bool CopyTracks(musik::core::tracklist::Ptr tracklist);
virtual bool AppendTracks(musik::core::tracklist::Ptr tracklist);
virtual void AppendTrack(musik::core::TrackPtr track);
virtual bool SetPosition(long position);
virtual long CurrentPosition();
virtual long Size();
virtual void Clear();
virtual void AddRequestedMetakey(const char* metakey);
virtual void RemoveRequestedMetakey(const char* metakey);
virtual bool operator =(musik::core::tracklist::Base &tracklist);
virtual bool operator +=(musik::core::tracklist::Base &tracklist);
virtual bool operator +=(musik::core::TrackPtr track);
virtual musik::core::LibraryPtr Library();
virtual UINT64 Duration();
virtual UINT64 Filesize();
virtual bool ConnectToQuery(musik::core::Query::ListBase &query);
virtual void HintNumberOfRows(int rows);
virtual void ConnectToQuery(musik::core::Query::ListBase &listQuery);
/////////////////////////////////////////////////////////////////////
virtual void ClearMetadata();
virtual bool AddRequestedMetakey(std::string metakey);
virtual bool SortTracks(std::string sortingMetakey);
private:
void LoadTrack(long position);
bool QueryForTrack(long position);
void LoadTrack(int position);
bool QueryForTrack(int position);
void OnTracksFromQuery(musik::core::TrackVector *newTracks,bool clear);
void OnTracksSummaryFromQuery(UINT64 tracks,UINT64 duration,UINT64 filesize);
void OnTracksMetaFromQuery(musik::core::TrackVector *metaTracks);
void OnTracksInfoFromQuery(UINT64 tracks,UINT64 duration,UINT64 filesize);
std::set<std::string> requestedMetaKeys;
int currentPosition;
int hintedRows;
protected:
//////////////////////////////////////////
///\brief
///Internal representation of the tracklist.
///
///This is used instead of a std::vector<TrackPtr> because of
///speed and memory issues.
//////////////////////////////////////////
std::vector<DBINT> tracklist;
private:
//////////////////////////////////////////
// Cache
typedef long PositionType;
typedef std::map<PositionType,musik::core::TrackPtr> PositionCacheMap;
PositionCacheMap positionCache;
typedef std::map<musik::core::TrackPtr,PositionType> TrackCacheMap;
TrackCacheMap trackCache;
//////////////////////////////////////////
musik::core::LibraryPtr library;
musik::core::TrackVector tracks;
musik::core::Query::TrackMetadata trackQuery;
typedef boost::bimap<int,musik::core::TrackPtr> TrackCache;
TrackCache trackCache;
long currentPosition;
musik::core::Query::TrackMetadata metadataQuery;
long maxCacheSize;
UINT64 infoDuration;
UINT64 infoFilesize;
};
//////////////////////////////////////////////////////////////////////////////

View File

@ -0,0 +1,504 @@
//////////////////////////////////////////////////////////////////////////////
//
// License Agreement:
//
// The following are Copyright © 2008, Daniel Önnerby
//
// 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 "pch.hpp"
#include <core/tracklist/MultiLibraryList.h>
#include <core/LibraryTrack.h>
#include <core/LibraryFactory.h>
#include <core/NonLibraryTrackHelper.h>
#include <set>
//////////////////////////////////////////////////////////////////////////////
using namespace musik::core::tracklist;
//////////////////////////////////////////////////////////////////////////////
MultiLibraryList::MultiLibraryList()
:currentPosition(-1)
,inited(false)
,queryState(MultiLibraryList::Default)
{
}
//////////////////////////////////////////
///\brief
///Get track from a specified position
///
///\param position
///Position in tracklist to get
///
///\returns
///shared pointer to track (could be a null pointer)
//////////////////////////////////////////
musik::core::TrackPtr MultiLibraryList::operator [](long position){
// Valid position?
if(position>=0 && position<this->Size()){
return this->tracklist[position];
}
return musik::core::TrackPtr();
}
//////////////////////////////////////////
///\brief
///Get track with metadata from a specified position
///
///\param position
///Position in tracklist to get
///
///\returns
///shared pointer to track (could be a null pointer)
///
///This is similar to the operator[], but will also request the metadata.
///If the track does not currently have any metadata, it will be signaled
///using the TrackMetadataUpdated event when data arrives.
///
///\see
///TrackMetadataUpdated
//////////////////////////////////////////
musik::core::TrackPtr MultiLibraryList::TrackWithMetadata(long position){
// check the positionCache if the track is in the cache already
PositionCacheMap::iterator trackPosition = this->positionCache.find(position);
if(trackPosition!=this->positionCache.end()){
return trackPosition->second;
}
// Load and add to cache
this->LoadTrack(position);
return (*this)[position];
}
//////////////////////////////////////////
///\brief
///Get the current track
//////////////////////////////////////////
musik::core::TrackPtr MultiLibraryList::CurrentTrack(){
if(this->currentPosition==-1 && this->Size()>0){
this->SetPosition(0);
}
return (*this)[this->currentPosition];
}
//////////////////////////////////////////
///\brief
///Get the next track and increase the position.
//////////////////////////////////////////
musik::core::TrackPtr MultiLibraryList::NextTrack(){
long newPosition = this->currentPosition+1;
musik::core::TrackPtr nextTrack = (*this)[newPosition];
if(nextTrack){
this->SetPosition(newPosition);
}
return nextTrack;
}
//////////////////////////////////////////
///\brief
///Get the previous track and decrease the position.
//////////////////////////////////////////
musik::core::TrackPtr MultiLibraryList::PreviousTrack(){
long newPosition = this->currentPosition-1;
musik::core::TrackPtr nextTrack = (*this)[newPosition];
if(nextTrack){
this->SetPosition(newPosition);
}
return nextTrack;
}
//////////////////////////////////////////
///\brief
///Set a new position in the tracklist.
///
///\param position
///Position to set.
///
///\returns
///True if position is a valid one and successfully set.
//////////////////////////////////////////
bool MultiLibraryList::SetPosition(long position){
if(position>=0 && position<this->tracklist.size()){
this->PositionChanged(position,this->currentPosition);
this->currentPosition = position;
return true;
}
return false;
}
//////////////////////////////////////////
///\brief
///Get the current position. -1 if undefined.
//////////////////////////////////////////
long MultiLibraryList::CurrentPosition(){
return this->currentPosition;
}
//////////////////////////////////////////
///\brief
///Get current size of the tracklist. -1 if unknown.
//////////////////////////////////////////
long MultiLibraryList::Size(){
return (long)this->tracklist.size();
}
//////////////////////////////////////////
///\brief
///Clear the tracklist
//////////////////////////////////////////
void MultiLibraryList::Clear(){
this->tracklist.clear();
this->ClearMetadata();
this->TracklistChanged(true);
this->PositionChanged(-1,this->currentPosition);
this->currentPosition = -1;
}
void MultiLibraryList::ClearMetadata(){
this->positionCache.clear();
this->trackCache.clear();
}
//////////////////////////////////////////
///\brief
///Set (copy) tracklist from another tracklist
///
///\param tracklist
///tracklist to copy from
///
///\returns
///True if successfully copied.
//////////////////////////////////////////
bool MultiLibraryList::operator =(musik::core::tracklist::Base &tracklist){
if(!this->inited){
musik::core::NonLibraryTrackHelper::Instance().TrackMetadataUpdated.connect(this,&MultiLibraryList::OnTracksMetaFromNonLibrary);
this->inited = true;
}
if(&tracklist != this){
this->Clear();
this->tracklist.reserve(tracklist.Size());
// Loop through the tracks and copy everything
for(long i(0);i<tracklist.Size();++i){
this->tracklist.push_back(tracklist[i]->Copy());
}
this->TracklistChanged(true);
this->SetPosition(tracklist.CurrentPosition());
}
return true;
}
//////////////////////////////////////////
///\brief
///Append tracks from another tracklist
///
///It will append all tracks that comes from
///the same library and ignore the rest.
//////////////////////////////////////////
bool MultiLibraryList::operator +=(musik::core::tracklist::Base &tracklist){
if(!this->inited){
musik::core::NonLibraryTrackHelper::Instance().TrackMetadataUpdated.connect(this,&MultiLibraryList::OnTracksMetaFromNonLibrary);
this->inited = true;
}
this->tracklist.reserve(tracklist.Size()+this->Size());
// Loop through the tracks and copy everything
for(long i(0);i<tracklist.Size();++i){
this->tracklist.push_back(tracklist[i]->Copy());
}
this->TracklistChanged(false);
return true;
}
//////////////////////////////////////////
///\brief
///Append a track to this tracklist
///
///\param track
///Track to add
///
///\returns
///True if successfully appended
//////////////////////////////////////////
bool MultiLibraryList::operator +=(musik::core::TrackPtr track){
if(!this->inited){
musik::core::NonLibraryTrackHelper::Instance().TrackMetadataUpdated.connect(this,&MultiLibraryList::OnTracksMetaFromNonLibrary);
this->inited = true;
}
this->tracklist.push_back(track->Copy());
return true;
}
//////////////////////////////////////////
///\brief
///Load a tracks metadata (if not already loaded)
//////////////////////////////////////////
void MultiLibraryList::LoadTrack(long position){
if(this->QueryForTrack(position)){
// If the track should load, then preload others as well
int trackCount(1);
// Preload hintedRows
for(long i(position+1);i<position+this->hintedRows;++i){
if(this->QueryForTrack(i)){
++trackCount;
}
}
// Send the query to the library
for(MetadataQueryMap::iterator query=this->metadataQueries.begin();query!=this->metadataQueries.end();++query){
musik::core::LibraryPtr library = musik::core::LibraryFactory::Instance().GetLibrary(query->first);
if(library){
// Add the callbacks
query->second.OnTracksEvent.connect(this,&MultiLibraryList::OnTracksMetaFromQuery);
// What keys are requested
query->second.RequestMetakeys(this->requestedMetakeys);
// Execute the query
library->AddQuery(query->second,musik::core::Query::Prioritize);
}
}
// Finally, clear the query for futher metadata
this->metadataQueries.clear();
}
}
//////////////////////////////////////////
///\brief
///Request metadata for track
//////////////////////////////////////////
bool MultiLibraryList::QueryForTrack(long position){
PositionCacheMap::iterator trackIt=this->positionCache.find(position);
if(trackIt==this->positionCache.end()){
// Not in cache, lets find the track
musik::core::TrackPtr track = (*this)[position];
if(track){
// Track is also a valid track, lets add it to the cache
this->positionCache[position] = track;
this->trackCache[track] = position;
// Finally, lets add it to the query
this->metadataQueries[track->LibraryId()].RequestTrack(track);
return true;
}
}
return false;
}
void MultiLibraryList::OnTracksMetaFromQuery(musik::core::TrackVector *metaTracks){
std::vector<long> updateTrackPositions;
for(musik::core::TrackVector::iterator track=metaTracks->begin();track!=metaTracks->end();++track){
TrackCacheMap::iterator position = this->trackCache.find(*track);
if(position!=this->trackCache.end()){
updateTrackPositions.push_back(position->second);
}
}
this->TrackMetadataUpdated(updateTrackPositions);
}
void MultiLibraryList::OnTracksMetaFromNonLibrary(musik::core::TrackPtr track){
std::vector<long> updateTrackPositions;
TrackCacheMap::iterator position = this->trackCache.find(track);
if(position!=this->trackCache.end()){
updateTrackPositions.push_back(position->second);
}
if(!updateTrackPositions.empty()){
this->TrackMetadataUpdated(updateTrackPositions);
}
}
bool MultiLibraryList::SortTracks(std::string sortingMetakey){
this->queryState = MultiLibraryList::Sorting;
// Trick method. We need to sort al genericTracks by ourselfs
// and send all the other tracks for sorting to it's own libraries
SortTracksQueryMap queries;
// Lets clear the generic track sortvector
this->genericTrackSortVector.clear();
// Start by looping through all the tracks
for(TracklistVector::iterator track=this->tracklist.begin();track!=this->tracklist.end();++track){
int libraryId = (*track)->LibraryId();
if(libraryId){
// A library track, add to query
queries[libraryId].AddTrack((*track)->Id());
}else{
// A generic track
musik::core::Query::SortTracksWithData::TrackWithSortdata sortData;
sortData.track = *track;
const utfchar *metavalue = (*track)->GetValue(sortingMetakey.c_str());
if(metavalue){
sortData.sortData = metavalue;
boost::algorithm::to_lower(sortData.sortData);
}
this->genericTrackSortVector.push_back( sortData );
}
}
this->sortQueryCount = 0;
this->sortResultMap.clear();
// Check if there are any genericTracks
if(!this->genericTrackSortVector.empty()){
this->sortQueryCount++;
}
// Lets count the queries
for(SortTracksQueryMap::iterator query=queries.begin();query!=queries.end();++query){
musik::core::LibraryPtr lib = musik::core::LibraryFactory::Instance().GetLibrary(query->first);
if(lib){
this->sortQueryCount++;
}
}
// So, lets send the tracks to the libraries for sorting
for(SortTracksQueryMap::iterator query=queries.begin();query!=queries.end();++query){
// First, connect to callbacks for results
query->second.TrackResults.connect(this,&MultiLibraryList::OnTracksFromSortQuery);
query->second.QueryFinished.connect(this,&MultiLibraryList::OnSortQueryFinished);
query->second.SortByMetaKey(sortingMetakey);
// Then send the query
musik::core::LibraryPtr lib = musik::core::LibraryFactory::Instance().GetLibrary(query->first);
if(lib){
lib->AddQuery(query->second);
}
}
// Then sort the generic tracks by hand
if(!this->genericTrackSortVector.empty()){
this->genericTrackSortVector.sort();
this->sortQueryCount--;
this->SortTheLists();
}
return true;
}
void MultiLibraryList::OnTracksFromSortQuery(musik::core::Query::SortTracksWithData::TrackWithSortdataVector *newTracks,bool clear){
typedef musik::core::Query::SortTracksWithData::TrackWithSortdataVector SortDataVector;
if(newTracks){
if(!newTracks->empty()){
int libraryId( newTracks->front().track->LibraryId() );
if(libraryId){
for(SortDataVector::iterator track=newTracks->begin();track!=newTracks->end();++track){
this->sortResultMap[libraryId].push_back(*track);
}
}
}
}
}
void MultiLibraryList::OnSortQueryFinished(musik::core::Query::Base *query,musik::core::Library::Base *library,bool success){
if(success){
this->sortQueryCount--;
this->SortTheLists();
}
}
void MultiLibraryList::SortTheLists(){
if(this->sortQueryCount==0){
// All queries should be finished, lets do the real sorting
typedef std::multiset<SortHelper> SortHelperSet;
SortHelperSet sortSet;
// Insert all of the tracklists
for(SortTracksResults::iterator result=this->sortResultMap.begin();result!=this->sortResultMap.end();++result){
if(!result->second.empty()){
sortSet.insert(SortHelper(result->second));
}
}
// We need to insert the generic list as well
if(!this->genericTrackSortVector.empty()){
sortSet.insert(SortHelper(this->genericTrackSortVector));
}
// Clear the tracklist
this->Clear();
// while there is still tracks left, continue to feed
while(!sortSet.empty()){
SortHelperSet::iterator front = sortSet.begin();
this->tracklist.push_back(front->sortData.front().track);
// Remove the track from the tracklist
front->sortData.pop_front();
if(front->sortData.empty()){
// The list is empty, remove it
sortSet.erase(front);
}else{
// For indexing in sortSet, remove and the add again
SortHelper newSortHelper(*front);
sortSet.erase(front);
sortSet.insert(newSortHelper);
}
}
this->TracklistChanged(true);
this->PositionChanged(-1,this->currentPosition);
this->currentPosition = -1;
this->sortResultMap.clear();
this->genericTrackSortVector.clear();
}
}
bool MultiLibraryList::SortHelper::operator<(const MultiLibraryList::SortHelper &sortHelper) const{
return this->sortData.front().sortData < sortHelper.sortData.front().sortData;
}

View File

@ -0,0 +1,149 @@
//////////////////////////////////////////////////////////////////////////////
//
// License Agreement:
//
// The following are Copyright © 2008, Daniel Önnerby
//
// 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/tracklist/Base.h>
#include <core/Query/ListBase.h>
#include <core/Query/TrackMetadata.h>
#include <core/Library/Base.h>
#include <core/Query/SortTracksWithData.h>
#include <set>
#include <map>
#include <sigslot/sigslot.h>
//////////////////////////////////////////////////////////////////////////////
namespace musik{ namespace core{ namespace tracklist {
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////
///\brief
///The MultiLibraryList can contain tracks from multiple libraries and GenericTracks
//////////////////////////////////////////
class MultiLibraryList : public Base, public sigslot::has_slots<> {
public:
MultiLibraryList();
virtual musik::core::TrackPtr operator [](long position);
virtual musik::core::TrackPtr TrackWithMetadata(long position);
virtual musik::core::TrackPtr CurrentTrack();
virtual musik::core::TrackPtr NextTrack();
virtual musik::core::TrackPtr PreviousTrack();
virtual bool SetPosition(long position);
virtual long CurrentPosition();
virtual long Size();
virtual void Clear();
virtual bool operator =(musik::core::tracklist::Base &tracklist);
virtual bool operator +=(musik::core::tracklist::Base &tracklist);
virtual bool operator +=(musik::core::TrackPtr track);
virtual void ClearMetadata();
virtual bool SortTracks(std::string sortingMetakey);
private:
void LoadTrack(long position);
bool QueryForTrack(long position);
void OnTracksSummaryFromQuery(UINT64 tracks,UINT64 duration,UINT64 filesize);
void OnTracksMetaFromQuery(musik::core::TrackVector *metaTracks);
void OnTracksMetaFromNonLibrary(musik::core::TrackPtr track);
void OnTracksFromSortQuery(musik::core::Query::SortTracksWithData::TrackWithSortdataVector *newTracks,bool clear);
void OnSortQueryFinished(musik::core::Query::Base *query,musik::core::Library::Base *library,bool success);
void SortTheLists();
protected:
//////////////////////////////////////////
///\brief
///Internal representation of the tracklist.
//////////////////////////////////////////
typedef std::vector<musik::core::TrackPtr> TracklistVector;
TracklistVector tracklist;
private:
//////////////////////////////////////////
// Cache
typedef long PositionType;
typedef std::map<PositionType,musik::core::TrackPtr> PositionCacheMap;
PositionCacheMap positionCache;
typedef std::map<musik::core::TrackPtr,PositionType> TrackCacheMap;
TrackCacheMap trackCache;
//////////////////////////////////////////
long currentPosition;
bool inited;
// map with queries, to check for metadata
typedef std::map<int,musik::core::Query::TrackMetadata> MetadataQueryMap;
MetadataQueryMap metadataQueries;
//////////////////////////////////////////
// Sorting of tracks
enum QueryState:int{
Default=0,
Sorting=1
};
int queryState;
typedef std::map<int,musik::core::Query::SortTracksWithData> SortTracksQueryMap;
typedef std::map<int,musik::core::Query::SortTracksWithData::TrackWithSortdataVector> SortTracksResults;
SortTracksResults sortResultMap;
int sortQueryCount;
musik::core::Query::SortTracksWithData::TrackWithSortdataVector genericTrackSortVector;
// Helper class for sorting
struct SortHelper{
SortHelper(musik::core::Query::SortTracksWithData::TrackWithSortdataVector &sortData):sortData(sortData){}
musik::core::Query::SortTracksWithData::TrackWithSortdataVector &sortData;
bool operator<(const SortHelper &sortHelper) const;
};
};
//////////////////////////////////////////////////////////////////////////////
} } } // musik::core::tracklist
//////////////////////////////////////////////////////////////////////////////

View File

@ -1,274 +0,0 @@
//////////////////////////////////////////////////////////////////////////////
//
// License Agreement:
//
// The following are Copyright © 2008, mC2 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 "pch.hpp"
#include <core/tracklist/Standard.h>
using namespace musik::core::tracklist;
Standard::Standard(void)
:currentPosition(0)
,hintedRows(10)
,infoDuration(0)
,infoFilesize(0)
{
this->trackQuery.OnTracksEvent.connect(this,&Standard::OnTracksMetaFromQuery);
}
Standard::~Standard(void){
}
musik::core::TrackPtr Standard::CurrentTrack(){
return (*this)[this->currentPosition];
}
musik::core::TrackPtr Standard::NextTrack(){
this->SetCurrentPosition(this->currentPosition+1);
return this->CurrentTrack();
}
musik::core::TrackPtr Standard::PreviousTrack(){
this->SetCurrentPosition(this->currentPosition-1);
return this->CurrentTrack();
}
musik::core::TrackPtr Standard::TrackWithMetadata(int position){
this->LoadTrack(position);
return (*this)[position];
}
musik::core::TrackPtr Standard::operator [](int position){
if(position>=0 && position<this->tracks.size())
return this->tracks[position];
if(position==-1 && this->tracks.size()>0)
return this->tracks.front();
return musik::core::TrackPtr();
}
int Standard::Size(){
return (int)this->tracks.size();
}
void Standard::SetCurrentPosition(int position){
int lastPosition(this->currentPosition);
if(position<-1){
this->currentPosition = -1;
}else{
if(position >= (int)this->tracks.size()){
this->currentPosition = (int)this->tracks.size();
}else{
this->currentPosition = position;
}
}
if(this->currentPosition!=lastPosition){
this->PositionChanged(this->currentPosition,lastPosition);
}
}
int Standard::CurrentPosition(){
if(this->currentPosition<0)
return -1;
if(this->currentPosition >= (int)this->tracks.size())
return (int)this->tracks.size()-1;
return this->currentPosition;
}
void Standard::ConnectToQuery(musik::core::Query::ListBase &listQuery){
listQuery.OnTrackEvent().connect(this,&Standard::OnTracksFromQuery);
listQuery.OnTrackInfoEvent().connect(this,&Standard::OnTracksInfoFromQuery);
}
void Standard::SetLibrary(musik::core::LibraryPtr setLibrary){
this->library = setLibrary;
}
musik::core::LibraryPtr Standard::Library(){
return this->library;
}
void Standard::OnTracksFromQuery(musik::core::TrackVector *newTracks,bool clear){
if(clear){
this->trackCache.clear();
this->SetCurrentPosition(-1); // undefined
this->tracks = *newTracks;
this->TracksUpdated(true);
}else{
this->tracks.insert(this->tracks.end(),newTracks->begin(),newTracks->end());
this->TracksUpdated(false);
}
}
void Standard::LoadTrack(int position){
if(this->QueryForTrack(position)){
int trackCount(1);
for(int i(position+1);i<position+this->hintedRows;++i){
if(this->QueryForTrack(i)){
++trackCount;
}
}
if(trackCount && this->library){
this->library->AddQuery(this->trackQuery,musik::core::Query::Prioritize);
this->trackQuery.Clear();
}
}
}
bool Standard::QueryForTrack(int position){
TrackCache::left_map::iterator trackIterator = this->trackCache.left.find(position);
if(trackIterator==this->trackCache.left.end()){
// Not in cache, lets find the track
musik::core::TrackPtr track = (*this)[position];
if(track){
// Track is also a valid track, lets add it to the cache
this->trackCache.insert( TrackCache::value_type(position,track) );
// finally, lets add it to the query
this->trackQuery.RequestTrack(track);
return true;
}
}
return false;
}
void Standard::HintNumberOfRows(int rows){
this->hintedRows = rows;
}
void Standard::OnTracksMetaFromQuery(musik::core::TrackVector *metaTracks){
std::vector<int> updateTrackPositions;
for(musik::core::TrackVector::iterator track=metaTracks->begin();track!=metaTracks->end();++track){
TrackCache::right_map::iterator trackPosition = this->trackCache.right.find(*track);
if(trackPosition!=this->trackCache.right.end()){
updateTrackPositions.push_back(trackPosition->second);
}
}
this->TrackMetaUpdated(updateTrackPositions);
}
void Standard::AddRequestedMetakey(const char* metakey){
this->requestedMetaKeys.insert(metakey);
this->trackQuery.RequestMetakeys(this->requestedMetaKeys);
}
void Standard::RemoveRequestedMetakey(const char* metakey){
this->requestedMetaKeys.erase(metakey);
this->trackQuery.RequestMetakeys(this->requestedMetaKeys);
}
bool Standard::CopyTracks(musik::core::tracklist::Ptr tracklist){
if(tracklist.get()!=this){ // Do not copy to itself
this->trackCache.clear();
this->SetLibrary(tracklist->Library());
this->tracks.clear();
this->tracks.reserve(tracklist->Size());
for(int i(0);i<tracklist->Size();++i){
this->tracks.push_back( (*tracklist)[i]->Copy());
}
this->SetCurrentPosition(tracklist->CurrentPosition());
this->TracksUpdated(true);
this->infoDuration = tracklist->Duration();
this->infoFilesize = tracklist->Filesize();
this->TracklistInfoUpdated(this->tracks.size(),this->infoDuration,this->infoFilesize);
}
return true;
}
bool Standard::AppendTracks(musik::core::tracklist::Ptr tracklist){
if(this->library==tracklist->Library()){ // Only append to same library
if(!this->library){ // If library is not set, set it.
this->SetLibrary(tracklist->Library());
}
this->tracks.reserve(this->tracks.size()+tracklist->Size());
for(int i(0);i<tracklist->Size();++i){
this->tracks.push_back( (*tracklist)[i]->Copy());
}
this->TracksUpdated(false);
return true;
}
return false;
}
void Standard::OnTracksInfoFromQuery(UINT64 tracks,UINT64 duration,UINT64 filesize){
this->infoDuration = duration;
this->infoFilesize = filesize;
this->TracklistInfoUpdated(tracks,duration,filesize);
}
UINT64 Standard::Duration(){
return this->infoDuration;
}
UINT64 Standard::Filesize(){
return this->infoFilesize;
}
void Standard::AppendTrack(musik::core::TrackPtr track){
this->tracks.push_back(track);
}
void Standard::Clear(){
this->tracks.clear();
this->trackCache.clear();
}

View File

@ -41,7 +41,7 @@
#include <cube/BrowseView.hpp>
#include <cube/TracklistController.hpp>
#include <core/LibraryFactory.h>
#include <core/tracklist/Standard.h>
#include <core/tracklist/LibraryList.h>
#include <core/Indexer.h>
#include <win32cpp/ApplicationThread.hpp>
@ -71,8 +71,7 @@ void BrowseController::OnViewCreated(Window* window)
{
// Create a tracklist, connected to current library
musik::core::tracklist::Ptr browseTrackList(new musik::core::tracklist::Standard());
browseTrackList->SetLibrary(this->library);
musik::core::tracklist::Ptr browseTrackList(new musik::core::tracklist::LibraryList(this->library));
this->tracklistController = new TracklistController(*this->view.tracklistView,&this->selectionQuery,browseTrackList);

View File

@ -0,0 +1,81 @@
//////////////////////////////////////////////////////////////////////////////
//
// License Agreement:
//
// The following are Copyright © 2008, Daniel Önnerby
//
// 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 "pch.hpp"
#include <cube/CommandlineParser.h>
#include <core/PlaybackQueue.h>
#include <core/TrackFactory.h>
#include <boost/program_options.hpp>
#include <vector>
//////////////////////////////////////////////////////////////////////////////
using namespace musik::cube;
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////
///\brief
///Parses the commands send through commandline
//////////////////////////////////////////
void CommandlineParser::ParseCommands(utfchar *commandLine){
// arguments is a splitted list of files
std::vector<utfstring> arguments = boost::program_options::split_winmain(commandLine);
if(arguments.size()){
// Get the "now playing" tracklist
musik::core::tracklist::Ptr nowPlaying = musik::core::PlaybackQueue::Instance().NowPlayingTracklist();
if(nowPlaying){
bool tracksAdded(false);
// Loop through the tracks and add to the "now playing"
for(std::vector<utfstring>::iterator uri=arguments.begin();uri!=arguments.end();++uri){
musik::core::TrackPtr newTrack = musik::core::TrackFactory::CreateTrack(*uri);
if(newTrack){
(*nowPlaying) += newTrack;
tracksAdded = true;
}
}
if(tracksAdded){
// If tracks has been added through commandline, start playing them directly
musik::core::PlaybackQueue::Instance().Play();
}
}
}
}

View File

@ -2,64 +2,50 @@
//
// License Agreement:
//
// The following are Copyright © 2008, mC2 team
// The following are Copyright © 2008, Daniel Önnerby
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// 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
// * 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.
// * 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.
// 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/tracklist/Standard.h>
namespace musik{ namespace cube{
//////////////////////////////////////////////////////////////////////////////
namespace musik{ namespace core{ namespace tracklist {
//////////////////////////////////////////////////////////////////////////////
class Playlist : public Standard {
class CommandlineParser
{
public:
Playlist(int id,utfstring name,musik::core::LibraryPtr library);
~Playlist(void);
utfstring Name();
int Id();
private:
int id;
utfstring name;
static void ParseCommands(utfchar *commandLine);
};
//////////////////////////////////////////////////////////////////////////////
} } } // musik::core::tracklist
} }
//////////////////////////////////////////////////////////////////////////////

View File

@ -41,9 +41,12 @@
#include <cube/LibraryWindowView.hpp>
#include <cube/SourcesView.hpp>
#include <cube/SourcesController.hpp>
#include <cube/TracklistController.hpp>
#include <cube/TracklistView.hpp>
#include <core/LibraryFactory.h>
#include <core/Pluginfactory.h>
#include <core/PlaybackQueue.h>
#include <win32cpp/Types.hpp> // uichar, uistring
#include <win32cpp/TopLevelWindow.hpp>
@ -59,6 +62,7 @@ using namespace musik::cube;
/*ctor*/ LibraryWindowController::LibraryWindowController(LibraryWindowView& view)
: view(view)
, nowPlayingController(NULL)
{
musik::core::PluginFactory::Instance();
@ -70,12 +74,20 @@ using namespace musik::cube;
LibraryWindowController::~LibraryWindowController()
{
delete this->nowPlayingController;
}
void LibraryWindowController::OnViewCreated(Window* window)
{
using namespace musik::core;
// Start by adding the "now playing" tab
TracklistView *nowPlayingView = new TracklistView();
this->nowPlayingController = new TracklistController(*nowPlayingView,NULL,musik::core::PlaybackQueue::Instance().NowPlayingTracklist(),TracklistController::Deletable|TracklistController::HighlightActive);
this->view.AddTab( uistring(UTF("Now Playing")) ,nowPlayingView);
// Get libraries from LibraryFactory
this->UpdateLibraryTabs();
LibraryFactory::Instance().LibrariesUpdated.connect(this,&LibraryWindowController::UpdateLibraryTabs);
@ -107,7 +119,7 @@ void LibraryWindowController::UpdateLibraryTabs(){
if(!found){
SourcesView* sourcesView = new SourcesView();
this->libraries[sourcesView] = SourcesControllerPtr(new SourcesController(*sourcesView,*library));
this->view.AddTab( (*library)->Identifier() ,sourcesView);
this->view.AddTab( (*library)->Name() ,sourcesView);
}
}

View File

@ -50,6 +50,7 @@ namespace musik { namespace cube {
class LibraryWindowView;
class SourcesView;
class SourcesController;
class TracklistController;
} }
//////////////////////////////////////////////////////////////////////////////
@ -93,6 +94,8 @@ class LibraryWindowController : public EventHandler
SourcesController* CurrentSourceController();
private:
TracklistController *nowPlayingController;
};
//////////////////////////////////////////////////////////////////////////////

View File

@ -42,7 +42,6 @@
#include <cube/MainMenuController.hpp>
#include <cube/dialog/AddLibraryController.hpp>
#include <cube/dialog/HelpAboutController.hpp>
#include <cube/dialog/PreferencesController.hpp>
#include <win32cpp/Application.hpp>
#include <win32cpp/TopLevelWindow.hpp>
#include <boost/format.hpp>
@ -72,11 +71,10 @@ void MainMenuController::OnMainWindowCreated(Window* window)
void MainMenuController::ConnectMenuHandlers()
{
this->fileExit->Activated.connect(this, &MainMenuController::OnFileExit);
// this->filePreferences->Activated.connect(this, &MainMenuController::OnFilePreferences);
this->helpAbout->Activated.connect(this, &MainMenuController::OnHelpAbout);
this->fileAddLibraryLocal->Activated.connect(this,&MainMenuController::OnAddLibraryLocal);
this->fileAddLibraryRemote->Activated.connect(this,&MainMenuController::OnAddLibraryRemote);
// this->fileNewPlaylist->Activated.connect(this,&MainMenuController::OnNewPlaylist);
this->fileNewPlaylist->Activated.connect(this,&MainMenuController::OnNewPlaylist);
}
void MainMenuController::OnFileExit(MenuItemRef menuItem)
@ -84,16 +82,6 @@ void MainMenuController::OnFileExit(MenuItemRef menuItem)
Application::Instance().Terminate();
}
void MainMenuController::OnFilePreferences(MenuItemRef menuItem)
{
win32cpp::TopLevelWindow preferencesDialog(_(_T("mC2 Preferences")));
preferencesDialog.SetMinimumSize(Size(700, 500));
dialog::PreferencesController filePreferences(preferencesDialog);
preferencesDialog.ShowModal(&this->mainWindow);
}
void MainMenuController::OnAddLibraryLocal(MenuItemRef menuItem)
{
win32cpp::TopLevelWindow popupDialog(_(_T("Add local library")));
@ -123,6 +111,44 @@ void MainMenuController::OnHelpAbout(MenuItemRef menuItem)
dialog::HelpAboutController helpAbout(aboutDialog);
aboutDialog.ShowModal(&this->mainWindow);
return;
// randomize the contribuitors' names
std::vector<uistring> names;
names.push_back(_T(" - avatar\n"));
names.push_back(_T(" - bjorn\n"));
names.push_back(_T(" - doep\n"));
names.push_back(_T(" - naaina\n"));
names.push_back(_T(" - Jooles\n"));
std::random_shuffle(names.begin(), names.end());
uistring randomNames;
for (std::vector<uistring>::iterator it = names.begin(); it != names.end(); it++)
{
randomNames += *it;
}
uistring message =
_T("mC2 are copyright (c) mC2 Team 2007-2008\n")
_T("win32cpp are copyright (c) Casey Langen 2007-2008\n\n")
_T("Credits:\n")
_T("%1%\n")
_T("mC2 wouldn't be possible without these file projects:\n")
_T(" - tuniac (http://tuniac.sf.net)\n")
_T(" - boost (http://www.boost.org)\n")
_T(" - sqlite3 (http://www.sqlite.org)\n")
_T(" - taglib (http://developer.kde.org/~wheeler/taglib)\n\n")
_T("Version 2 developer milestone 1");
message = (boost::wformat(message.c_str()) % randomNames).str();
::MessageBox(
this->mainWindow.Handle(),
message.c_str(),
_T("mC2 - About"),
MB_ICONINFORMATION | MB_OK);
}
void MainMenuController::OnNewPlaylist(MenuItemRef menuItem){
@ -142,30 +168,28 @@ MenuRef MainMenuController::CreateMenu()
this->tags = mainItems.Append(MenuItem::Create(_(_T("&Tags"))));
this->help = mainItems.Append(MenuItem::Create(_(_T("&Help"))));
// file menu
this->fileMenu = Menu::Create();
MenuItemCollection& fileItems = this->fileMenu->Items();
//
this->file->SetSubMenu(this->fileMenu);
// file menu
this->fileMenu = Menu::Create();
MenuItemCollection& fileItems = this->fileMenu->Items();
//
this->file->SetSubMenu(this->fileMenu);
MenuItemRef addLibraryMenu = fileItems.Append(MenuItem::Create(_(_T("&New Library"))));
MenuRef addLibrarySubmenu = Menu::Create();
this->fileAddLibraryLocal = addLibrarySubmenu->Items().Append(MenuItem::Create(_(_T("&Local library"))));
this->fileAddLibraryRemote = addLibrarySubmenu->Items().Append(MenuItem::Create(_(_T("&Remote library"))));
addLibraryMenu->SetSubMenu(addLibrarySubmenu);
MenuItemRef addLibraryMenu = fileItems.Append(MenuItem::Create(_(_T("&New Library"))));
MenuRef addLibrarySubmenu = Menu::Create();
this->fileAddLibraryLocal = addLibrarySubmenu->Items().Append(MenuItem::Create(_(_T("&Local library"))));
this->fileAddLibraryRemote = addLibrarySubmenu->Items().Append(MenuItem::Create(_(_T("&Remote library"))));
addLibraryMenu->SetSubMenu(addLibrarySubmenu);
this->fileNewPlaylist = fileItems.Append(MenuItem::Create(_(_T("&New Playlist"))));
this->fileNewPlaylist = fileItems.Append(MenuItem::Create(_(_T("&New Playlist"))));
this->filePreferences = fileItems.Append(MenuItem::Create(_(_T("&Preferences"))));
this->fileExit = fileItems.Append(MenuItem::Create(_(_T("E&xit"))));
this->fileExit = fileItems.Append(MenuItem::Create(_(_T("E&xit"))));
// help menu
this->helpMenu = Menu::Create();
MenuItemCollection& helpItems = this->helpMenu->Items();
//
this->help->SetSubMenu(this->helpMenu);
this->helpAbout = helpItems.Append(MenuItem::Create(_(_T("&About"))));
// help menu
this->helpMenu = Menu::Create();
MenuItemCollection& helpItems = this->helpMenu->Items();
//
this->help->SetSubMenu(this->helpMenu);
this->helpAbout = helpItems.Append(MenuItem::Create(_(_T("&About"))));
this->ConnectMenuHandlers();

View File

@ -58,32 +58,27 @@ namespace musik { namespace cube {
class MainMenuController: public EventHandler
{
public:
MainMenuController(TopLevelWindow& mainWindow);
~MainMenuController();
public:
MainMenuController(TopLevelWindow& mainWindow);
~MainMenuController();
protected:
void OnMainWindowCreated(Window* window);
MenuRef CreateMenu();
void ConnectMenuHandlers();
protected:
void OnMainWindowCreated(Window* window);
MenuRef CreateMenu();
void ConnectMenuHandlers();
//
void OnFileExit(MenuItemRef menuItem);
void OnFilePreferences(MenuItemRef menuItem);
void OnHelpAbout(MenuItemRef menuItem);
void OnAddLibraryLocal(MenuItemRef menuItem);
void OnAddLibraryRemote(MenuItemRef menuItem);
void OnNewPlaylist(MenuItemRef menuItem);
//
void OnFileExit(MenuItemRef menuItem);
void OnHelpAbout(MenuItemRef menuItem);
void OnAddLibraryLocal(MenuItemRef menuItem);
void OnAddLibraryRemote(MenuItemRef menuItem);
void OnNewPlaylist(MenuItemRef menuItem);
private:
TopLevelWindow& mainWindow;
MenuRef mainMenu;
MenuRef fileMenu, helpMenu;
MenuItemRef file, view, audio, tags, help;
MenuItemRef fileExit, fileAddLibraryRemote, fileAddLibraryLocal, fileNewPlaylist, filePreferences;
MenuItemRef helpAbout;
private:
TopLevelWindow& mainWindow;
MenuRef mainMenu, fileMenu, helpMenu;
MenuItemRef file, view, audio, tags, help;
MenuItemRef fileExit, helpAbout, fileAddLibraryRemote, fileAddLibraryLocal, fileNewPlaylist;
};
//////////////////////////////////////////////////////////////////////////////

View File

@ -174,7 +174,7 @@ void SettingsView::OnCreated()
mainLayout->AddChild(new Frame(pathLayout,FramePadding(20,20,0,20)));
// test CheckBox
/* CheckBox* c = new CheckBox(_T("Test 1"));
CheckBox* c = new CheckBox(_T("Test 1"));
mainLayout->AddChild(c);
c->Check();
c->Pressed.connect(this, &SettingsView::OnPressTestCheckbox);
@ -233,7 +233,7 @@ void SettingsView::OnCreated()
mainLayout->AddChild(cb);
cb->SetModel(cb_testmodel);
cb->Select(2);
*/
this->AddChild(mainLayout);
}

View File

@ -39,10 +39,10 @@
#include <cube/SourcesController.hpp>
#include <cube/SourcesListModel.hpp>
#include <cube/SourcesView.hpp>
#include <cube/dialog/NewPlaylistController.hpp>
//#include <cube/dialog/NewPlaylistController.hpp>
#include <core/MessageQueue.h>
#include <core/Query/Playlists.h>
//#include <core/Query/Playlists.h>
//////////////////////////////////////////////////////////////////////////////
@ -72,7 +72,7 @@ using namespace musik::cube;
library->OnQueryQueueStart.connect(this,&SourcesController::QueryQueueStart);
library->OnQueryQueueEnd.connect(this,&SourcesController::QueryQueueEnd);
musik::core::MessageQueue::MessageEvent("NewPlaylist").connect(this,&SourcesController::OnNewPlaylist);
// musik::core::MessageQueue::MessageEvent("NewPlaylist").connect(this,&SourcesController::OnNewPlaylist);
}
@ -82,7 +82,7 @@ void SourcesController::OnViewCreated(Window* window)
this->model.CategoryRemoved.connect(this, &SourcesController::OnModelCategoryRemoved);
this->model.Load();
this->UpdatePlaylists();
// this->UpdatePlaylists();
}
@ -127,7 +127,7 @@ void SourcesController::QueryQueueLoop(){
this->library->RunCallbacks();
}
void SourcesController::OnNewPlaylist(void* data){
/*void SourcesController::OnNewPlaylist(void* data){
if(data==this->library.get()){
win32cpp::TopLevelWindow popupDialog(_(_T("New Playlist")));
popupDialog.SetMinimumSize(Size(300, 150));
@ -152,7 +152,7 @@ void SourcesController::OnPlaylists(std::vector<musik::core::tracklist::Ptr> tra
this->listController->sourcesListModel->Update();
// this->view.Redraw();
}
*/
//////////////////////////////////////////////////////////////////////////////
// SourcesController::ListController
//////////////////////////////////////////////////////////////////////////////

View File

@ -89,8 +89,8 @@ protected:
void QueryQueueEnd();
void QueryQueueLoop();
void UpdatePlaylists();
void OnPlaylists(std::vector<musik::core::tracklist::Ptr> trackLists);
// void UpdatePlaylists();
// void OnPlaylists(std::vector<musik::core::tracklist::Ptr> trackLists);
};
//////////////////////////////////////////////////////////////////////////////

View File

@ -45,7 +45,7 @@
#include <cube/TracklistView.hpp>
#include <cube/TracklistController.hpp>
#include <core/tracklist/Playlist.h>
//#include <core/tracklist/Playlist.h>
#include <win32cpp/Label.hpp>
@ -113,33 +113,7 @@ private: BrowseController controller;
//////////////////////////////////////////////////////////////////////////////
class NowPlayingItem: public SourcesItem
{
private:
/*ctor*/ NowPlayingItem(musik::core::LibraryPtr library)
: controller(view,NULL,library->NowPlaying(),TracklistController::HighlightActive|TracklistController::Deletable)
{
}
public: /*dtor*/ ~NowPlayingItem()
{
}
public: static SourcesItemRef Create(musik::core::LibraryPtr library)
{
return SourcesItemRef(new NowPlayingItem(library));
}
public: virtual uistring Caption() { return _(_T("Now Playing")); }
public: virtual Window* View()
{
return &this->view;
}
private: TracklistView view;
private: TracklistController controller;
};
//////////////////////////////////////////////////////////////////////////////
@ -162,8 +136,9 @@ public: static SourcesItemRef Create(musik::core::tracklist::Ptr playlist)
}
public: virtual uistring Caption() {
musik::core::tracklist::Playlist* playlist = (musik::core::tracklist::Playlist*)(this->controller.Tracklist().get());
return playlist->Name();
// musik::core::tracklist::Playlist* playlist = (musik::core::tracklist::Playlist*)(this->controller.Tracklist().get());
// return playlist->Name();
return uistring();
}
public: virtual Window* View()
{
@ -219,7 +194,6 @@ void SourcesModel::Load()
{
CategoryRef viewCategory(new Category(_(_T("View"))));
viewCategory->Add(BrowseItem::Create(this->library));
viewCategory->Add(NowPlayingItem::Create(this->library));
viewCategory->Add(SettingsItem::Create(this->library));
this->AddCategory(viewCategory);
@ -257,7 +231,7 @@ void SourcesModel::RemoveCategory(CategoryRef category)
this->CategoryRemoved(category);
}
void SourcesModel::OnPlaylists(SourcesModel::PlaylistVector &playlists){
/*void SourcesModel::OnPlaylists(SourcesModel::PlaylistVector &playlists){
// Start by removing the old ones
for(PlaylistItemMap::iterator it=this->playlistItemsMap.begin();it!=this->playlistItemsMap.end();++it){
// Remove the item from the category
@ -278,4 +252,4 @@ void SourcesModel::OnPlaylists(SourcesModel::PlaylistVector &playlists){
}
}
*/

View File

@ -81,11 +81,11 @@ class SourcesModel
void RemoveCategory(CategoryRef category);
void OnActiveItemChanged(ItemRef);
typedef std::vector<musik::core::tracklist::Ptr> PlaylistVector;
/* typedef std::vector<musik::core::tracklist::Ptr> PlaylistVector;
typedef std::map<musik::core::tracklist::Ptr,SourcesItemRef> PlaylistItemMap;
PlaylistItemMap playlistItemsMap;
*/
private:
ItemRef activeItem;
CategoryList categories;
@ -93,7 +93,7 @@ class SourcesModel
protected:
friend class SourcesController;
void OnPlaylists(PlaylistVector &playlists);
// void OnPlaylists(PlaylistVector &playlists);
};

View File

@ -69,7 +69,7 @@ using namespace musik::cube;
// Connect the tracklists TracklistInfoUpdated
TracklistModel* model = (TracklistModel*)this->model.get();
if(model){
model->tracklist->TracklistInfoUpdated.connect(this,&TracklistController::OnTracklistInfo);
model->tracklist->SummaryInfoUpdated.connect(this,&TracklistController::OnTracklistInfo);
}
}
@ -105,11 +105,6 @@ void TracklistController::OnViewCreated(Window* window)
listView->Resized.connect( this, &TracklistController::OnResized);
listView->ColumnClicked.connect( this, &TracklistController::OnColumnSort );
TracklistModel* model = (TracklistModel*)this->model.get();
if(model){
model->ConnectToQuery(&this->sortQuery);
}
// Add the context menu
this->contextMenu = win32cpp::Menu::CreatePopup();
@ -165,7 +160,7 @@ void TracklistController::OnResized(Window* window, Size size){
int rows = size.height/this->view.listView->RowHeight();
TracklistModel* model = (TracklistModel*)this->model.get();
if(model && rows>10 && rows<100){
model->tracklist->HintNumberOfRows(rows);
model->tracklist->HintVisibleRows(rows);
}
}
@ -175,19 +170,21 @@ void TracklistController::OnColumnSort(ListView *listView,ColumnRef column){
TracklistModel* model = (TracklistModel*)this->model.get();
if(tracklistColumn && model){
// what to sort by
std::list<std::string> sortList;
/* std::list<std::string> sortList;
sortList.push_back(tracklistColumn->metaKey);
this->sortQuery.SortByMetaKeys(sortList);
*/
model->tracklist->SortTracks(tracklistColumn->metaKey);
// Add the tracks to sort
this->sortQuery.AddTracks(*(model->tracklist));
/* this->sortQuery.AddTracks(*(model->tracklist));
musik::core::LibraryPtr library( model->tracklist->Library());
if(library){
library->AddQuery(this->sortQuery,musik::core::Query::CancelSimilar);
}
this->sortQuery.ClearTracks();
this->sortQuery.ClearTracks();*/
}
}

View File

@ -50,8 +50,7 @@ namespace musik { namespace cube {
#include <win32cpp/ListView.hpp>
#include <core/config.h>
#include <core/Query/ListBase.h>
#include <core/tracklist/IRandomAccess.h>
#include <core/Query/SortTracks.h>
#include <core/tracklist/Base.h>
//////////////////////////////////////////////////////////////////////////////
@ -98,7 +97,6 @@ private:
TracklistView& view;
ColumnList columns;
win32cpp::MenuRef contextMenu;
musik::core::Query::SortTracks sortQuery;
unsigned int options;
};

View File

@ -51,8 +51,7 @@
#include <core/LibraryFactory.h>
#include <core/PlaybackQueue.h>
#include <core/MetaKey.h>
#include <core/tracklist/IRandomAccess.h>
#include <core/tracklist/Standard.h>
#include <core/tracklist/MultiLibraryList.h>
using namespace musik::cube;
@ -68,11 +67,16 @@ using namespace musik::cube;
this->SetRowCount(0);
this->tracklist->TracksUpdated.connect(this,&TracklistModel::OnTracks);
this->tracklist->TrackMetaUpdated.connect(this,&TracklistModel::OnTrackMeta);
this->tracklist->TracklistChanged.connect(this,&TracklistModel::OnTracks);
this->tracklist->TrackMetadataUpdated.connect(this,&TracklistModel::OnTrackMeta);
this->tracklist->PositionChanged.connect(this,&TracklistModel::OnPositionChanged);
this->ConnectToQuery(connectedQuery);
// In case there are tracks in the list already.
if(this->tracklist->Size()){
this->OnTracks(true);
}
}
uistring TracklistModel::CellValueToString(int rowIndex, ColumnRef column)
@ -115,8 +119,13 @@ uistring TracklistModel::CellValueToString(int rowIndex, ColumnRef co
return _T("");
}
void TracklistModel::OnTrackMeta(std::vector<int> &trackPositions){
for(std::vector<int>::iterator row=trackPositions.begin();row!=trackPositions.end();++row){
void TracklistModel::OnTrackMeta(std::vector<long> trackPositions){
if(!win32cpp::ApplicationThread::InMainThread()){
std::vector<long> positionCopy(trackPositions);
win32cpp::ApplicationThread::Call1(this,&TracklistModel::OnTrackMeta,positionCopy);
return;
}
for(std::vector<long>::iterator row=trackPositions.begin();row!=trackPositions.end();++row){
this->InvalidateData(*row);
}
}
@ -129,36 +138,34 @@ void TracklistModel::OnTracks(bool cleared){
void TracklistModel::OnRowActivated(int row){
this->tracklist->SetCurrentPosition(row);
musik::core::PlaybackQueue::Instance().Play(this->tracklist);
this->tracklist->SetPosition(row);
musik::core::PlaybackQueue::Instance().Play(*this->tracklist);
}
void TracklistModel::OnPlayNow(win32cpp::ListView::RowIndexList& selectedRows){
// Create a temporary tracklist to put into the "now playing" tracklist
musik::core::tracklist::Ptr selectedTracklist(new musik::core::tracklist::Standard());
selectedTracklist->SetLibrary(this->tracklist->Library());
musik::core::tracklist::Ptr selectedTracklist(new musik::core::tracklist::MultiLibraryList());
for(win32cpp::ListView::RowIndexList::iterator row=selectedRows.begin();row!=selectedRows.end();++row){
musik::core::TrackPtr track( (*this->tracklist)[*row] );
if(track){
selectedTracklist->AppendTrack(track);
(*selectedTracklist) += track;
}
}
musik::core::PlaybackQueue::Instance().Play(selectedTracklist);
musik::core::PlaybackQueue::Instance().Play(*selectedTracklist);
}
void TracklistModel::OnEnqueue(win32cpp::ListView::RowIndexList& selectedRows){
// Create a temporary tracklist to put into the "now playing" tracklist
musik::core::tracklist::Ptr selectedTracklist(new musik::core::tracklist::Standard());
selectedTracklist->SetLibrary(this->tracklist->Library());
musik::core::tracklist::Ptr selectedTracklist(new musik::core::tracklist::MultiLibraryList());
for(win32cpp::ListView::RowIndexList::iterator row=selectedRows.begin();row!=selectedRows.end();++row){
musik::core::TrackPtr track( (*this->tracklist)[*row] );
if(track){
selectedTracklist->AppendTrack(track);
(*selectedTracklist) += track;
}
}
musik::core::PlaybackQueue::Instance().Append(selectedTracklist);
musik::core::PlaybackQueue::Instance().Append(*selectedTracklist);
}
void TracklistModel::ConnectToQuery(musik::core::Query::ListBase *connectedQuery){
@ -167,7 +174,7 @@ void TracklistModel::ConnectToQuery(musik::core::Query::ListBase *connectedQuery
}
}
void TracklistModel::OnPositionChanged(int activeRow,int oldActiveRow){
void TracklistModel::OnPositionChanged(long activeRow,long oldActiveRow){
if(!win32cpp::ApplicationThread::InMainThread()){
win32cpp::ApplicationThread::Call2(this,&TracklistModel::OnPositionChanged,activeRow,oldActiveRow);
return;

View File

@ -46,7 +46,7 @@ namespace musik{ namespace core{ namespace Query{
//////////////////////////////////////////////////////////////////////////////
#include <win32cpp/ListView.hpp>
#include <core/tracklist/IRandomAccess.h>
#include <core/tracklist/Base.h>
//////////////////////////////////////////////////////////////////////////////
@ -82,9 +82,9 @@ public:
// instance data
protected:
void OnTrackMeta(std::vector<int> &trackPositions);
void OnTrackMeta(std::vector<long> trackPositions);
void OnTracks(bool cleared);
void OnPositionChanged(int activeRow,int oldActiveRow);
void OnPositionChanged(long activeRow,long oldActiveRow);
int currentPosition;
unsigned int options;

View File

@ -107,6 +107,11 @@ void TransportController::OnViewCreated(Window* window)
this->playbackSliderTimer.ConnectToWindow(&this->transportView);
this->playbackSliderTimer.OnTimeout.connect(this, &TransportController::OnPlaybackSliderTimerTimedOut);
// In case playback has already started
if(musik::core::PlaybackQueue::Instance().Transport().CurrentTrack()){
this->OnPlaybackStarted(musik::core::PlaybackQueue::Instance().Transport().CurrentTrack());
}
}
void TransportController::OnViewResized(Window* window, Size size)
@ -227,18 +232,25 @@ void TransportController::OnPlaybackStopped(musik::core::TrackPtr track)
return;
}
if (this->displayedTrack->id == track->id) // For out of order signals
{
this->playing = false;
this->paused = false;
utfstring trackURI;
const utfchar* uri = track->URI();
if(uri)
trackURI = uri;
this->transportView.playButton->SetCaption(_T("Play"));
if(this->displayedTrack){
if (trackURI == this->displayedTrack->URI()) // For out of order signals
{
this->playing = false;
this->paused = false;
this->transportView.playbackSlider->SetPosition(0);
this->playbackSliderTimer.Stop();
this->transportView.timeElapsedLabel->SetCaption(_T("0:00"));
this->transportView.timeDurationLabel->SetCaption(_T("0:00"));
this->transportView.playButton->SetCaption(_T("Play"));
this->transportView.playbackSlider->SetPosition(0);
this->playbackSliderTimer.Stop();
this->transportView.timeElapsedLabel->SetCaption(_T("0:00"));
this->transportView.timeDurationLabel->SetCaption(_T("0:00"));
}
}
}

View File

@ -514,38 +514,18 @@
>
</File>
</Filter>
<Filter
Name="Preferences"
</Filter>
<Filter
Name="support"
>
<File
RelativePath=".\CommandlineParser.cpp"
>
<File
RelativePath=".\dialog\PreferencesCategoriesModel.cpp"
>
</File>
<File
RelativePath=".\dialog\PreferencesCategoriesModel.hpp"
>
</File>
<File
RelativePath=".\dialog\PreferencesCategory.hpp"
>
</File>
<File
RelativePath=".\dialog\PreferencesController.cpp"
>
</File>
<File
RelativePath=".\dialog\PreferencesController.hpp"
>
</File>
<File
RelativePath=".\dialog\PreferencesView.cpp"
>
</File>
<File
RelativePath=".\dialog\PreferencesView.hpp"
>
</File>
</Filter>
</File>
<File
RelativePath=".\CommandlineParser.h"
>
</File>
</Filter>
<File
RelativePath=".\main.cpp"

View File

@ -60,7 +60,7 @@ HelpAboutController::HelpAboutController(win32cpp::TopLevelWindow &mainWindow)
{
this->view = new HelpAboutView;
this->mainWindow.AddChild(this->view);
this->view->Created.connect(this, &HelpAboutController::OnViewCreated);
// Start drawing thread

View File

@ -38,7 +38,7 @@
#include <cube/dialog/NewPlaylistController.hpp>
#include <core/Library/Base.h>
#include <core/Query/PlaylistSave.h>
//#include <core/Query/PlaylistSave.h>
#include <core/MessageQueue.h>
#include <win32cpp/Window.hpp>
@ -79,11 +79,11 @@ void NewPlaylistController::OnCancel(win32cpp::Button* button){
}
void NewPlaylistController::OnOK(win32cpp::Button* button){
/*
musik::core::Query::PlaylistSave savePlaylistQuery;
savePlaylistQuery.SavePlaylist( this->view->name->Caption() );
this->library->AddQuery(savePlaylistQuery,musik::core::Query::UnCanceable);
*/
this->mainWindow.Close();
}

View File

@ -41,6 +41,8 @@
#include <win32cpp/Application.hpp>
#include <win32cpp/TopLevelWindow.hpp>
#include <core/Common.h>
#include <cube/CommandlineParser.h>
//////////////////////////////////////////////////////////////////////////////
@ -50,6 +52,9 @@ using namespace musik::cube;
int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prevInstance, LPTSTR commandLine, int showCommand)
{
// Lets parse the input arguments
CommandlineParser::ParseCommands(commandLine);
// Initialize locale
try {
@ -67,6 +72,7 @@ int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prevInstance, LPTSTR commandLi
TopLevelWindow mainWindow(_T("musikCube 2"));
MainWindowController mainController(mainWindow);
// Initialize and show the main window, and run the event loop.
Application::Instance().Run(mainWindow);

View File

@ -52,9 +52,7 @@
#include <core/config.h>
#include <core/LibraryFactory.h>
#include <core/ITrack.h>
#include <core/Track.h>
#include <core/TrackMeta.h>
#include <core/db/dbconfig.h>
#include <core/db/CachedStatement.h>
#include <core/db/Connection.h>