mirror of
https://github.com/clangen/musikcube.git
synced 2025-02-06 03:39:50 +00:00
Merge uri_implementation branch to trunk.
This commit is contained in:
parent
6b45841056
commit
99de95c93d
BIN
src/3rdparty/lib/libboost_program_options-vc80-mt-s-1_36.lib
vendored
Normal file
BIN
src/3rdparty/lib/libboost_program_options-vc80-mt-s-1_36.lib
vendored
Normal file
Binary file not shown.
BIN
src/3rdparty/lib/libboost_program_options-vc80-mt-sgd-1_36.lib
vendored
Normal file
BIN
src/3rdparty/lib/libboost_program_options-vc80-mt-sgd-1_36.lib
vendored
Normal file
Binary file not shown.
114
src/contrib/mpg123decoder/mpg123/src/libmpg123/index.c
Normal file
114
src/contrib/mpg123decoder/mpg123/src/libmpg123/index.c
Normal 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);
|
||||
}
|
56
src/contrib/mpg123decoder/mpg123/src/libmpg123/index.h
Normal file
56
src/contrib/mpg123decoder/mpg123/src/libmpg123/index.h
Normal 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
159
src/core/GenericTrack.cpp
Normal 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
86
src/core/GenericTrack.h
Normal 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
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -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)){
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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")));
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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
635
src/core/LibraryTrack.cpp
Normal 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
122
src/core/LibraryTrack.h
Normal 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
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
145
src/core/NonLibraryTrackHelper.cpp
Normal file
145
src/core/NonLibraryTrackHelper.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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())));
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
431
src/core/Query/SortTracksWithData.cpp
Normal file
431
src/core/Query/SortTracksWithData.cpp
Normal 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;
|
||||
}
|
||||
|
119
src/core/Query/SortTracksWithData.h
Normal file
119
src/core/Query/SortTracksWithData.h
Normal 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:
|
||||
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
} } }
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
123
src/core/Track.h
123
src/core/Track.h
@ -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
56
src/core/TrackFactory.cpp
Normal 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());
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
} }
|
||||
//////////////////////////////////////////////////////////////////////////////
|
@ -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);
|
||||
}
|
@ -52,6 +52,7 @@ private:
|
||||
IAudioOutput* output;
|
||||
AudioPacketizer packetizer;
|
||||
|
||||
friend class Transport;
|
||||
musik::core::TrackPtr track;
|
||||
musik::core::filestreams::FileStreamPtr fileStream;
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
} } }
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -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(){
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
|
||||
/*
|
||||
|
||||
|
@ -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){
|
||||
|
@ -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
279
src/core/tracklist/Base.h
Normal 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
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
} }
|
||||
|
||||
|
||||
|
485
src/core/tracklist/LibraryList.cpp
Normal file
485
src/core/tracklist/LibraryList.cpp
Normal 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;
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
504
src/core/tracklist/MultiLibraryList.cpp
Normal file
504
src/core/tracklist/MultiLibraryList.cpp
Normal 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;
|
||||
}
|
||||
|
149
src/core/tracklist/MultiLibraryList.h
Normal file
149
src/core/tracklist/MultiLibraryList.h
Normal 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
|
||||
//////////////////////////////////////////////////////////////////////////////
|
@ -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();
|
||||
}
|
@ -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);
|
||||
|
||||
|
81
src/cube/CommandlineParser.cpp
Normal file
81
src/cube/CommandlineParser.cpp
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
} }
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -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){
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
*/
|
||||
|
@ -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);
|
||||
|
||||
};
|
||||
|
||||
|
@ -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();*/
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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>
|
||||
|
Loading…
x
Reference in New Issue
Block a user