mirror of
https://github.com/tomahawk-player/tomahawk.git
synced 2025-04-19 07:22:32 +02:00
Merge branch 'master' into dynamic
Conflicts: src/libtomahawk/collection.h src/libtomahawk/database/databasecommand_loadplaylistentries.cpp src/libtomahawk/database/databasecommand_setplaylistrevision.cpp src/libtomahawk/database/databasecommand_setplaylistrevision.h src/libtomahawk/playlist.cpp src/libtomahawk/playlist.h
This commit is contained in:
commit
34799e36e0
54
README
54
README
@ -8,25 +8,25 @@ Quickstart on Ubuntu
|
||||
|
||||
Gloox 1.0 (XMPP library)
|
||||
------------------------
|
||||
On Ubuntu 10.10:
|
||||
On Ubuntu 10.10 (and higher):
|
||||
$ sudo apt-get install libgloox-dev
|
||||
|
||||
Otherwise see: http://camaya.net/glooxdownload
|
||||
You need to build gloox 1.0 from source, Ubuntu 10.04 only packages v0.9.
|
||||
You need to build gloox 1.0 from source, Ubuntu 10.04 only packages version 0.9.
|
||||
|
||||
$ # Download and unpack tarball
|
||||
Download and unpack tarball:
|
||||
$ ./configure --without-openssl --with-gnutls --without-libidn --with-zlib --without-examples --without-tests
|
||||
$ CXXFLAGS=-fPIC make
|
||||
$ sudo make install
|
||||
|
||||
QJson (Qt JSON library)
|
||||
-----------------------
|
||||
On Ubuntu 10.04:
|
||||
On Ubuntu 10.04 (and higher):
|
||||
$ sudo apt-get install libqjson-dev
|
||||
|
||||
Otherwise see: http://sourceforge.net/projects/qjson/files/ (developed using 0.7.1)
|
||||
Otherwise see: http://sourceforge.net/projects/qjson/files/ (developed using version 0.7.1)
|
||||
|
||||
$ # Download and unpack tarball
|
||||
Download and unpack tarball:
|
||||
$ ./configure && make
|
||||
$ sudo make install
|
||||
|
||||
@ -34,47 +34,38 @@ libEchonest 0.1
|
||||
---------------
|
||||
See: http://projects.kde.org/projects/playground/libs/libechonest/
|
||||
|
||||
$ git clone git://git.kde.org/libechonest.git
|
||||
$ cd libechonest
|
||||
Download and unpack tarball:
|
||||
$ mkdir build && cd build
|
||||
$ cmake ..
|
||||
$ make
|
||||
$ sudo make install
|
||||
|
||||
Now compile Tomahawk
|
||||
--------------------
|
||||
$ sudo ldconfig -v | grep -Ei 'qjson|gloox|echonest'
|
||||
$ mkdir build && cd build
|
||||
$ cmake ..
|
||||
$ make
|
||||
$ ./tomahawk
|
||||
|
||||
|
||||
Quickstart on OS X
|
||||
------------------
|
||||
|
||||
# Install homebrew
|
||||
Install homebrew
|
||||
$ ruby -e "$(curl -fsSL https://gist.github.com/raw/323731/install_homebrew.rb)"
|
||||
$ brew install qt qjson gloox libmad libvorbis flac taglib boost liblastfm
|
||||
|
||||
# Install libEchnoest as per the above instructions
|
||||
Install libEchnoest as per the above instructions.
|
||||
|
||||
# If liblastfm gives problems, do the below:
|
||||
If liblastfm gives problems, do the below:
|
||||
$ brew edit liblastfm
|
||||
# change url to https://github.com/davidsansome/liblastfm/tarball/0.3.1
|
||||
Change the url to https://github.com/davidsansome/liblastfm/tarball/0.3.1
|
||||
$ brew install liblastfm
|
||||
# copy the md5 hash it gives
|
||||
Copy the md5 hash it returns.
|
||||
$ brew edit liblastfm
|
||||
# replace the md5 hash with the new one you copied
|
||||
Replace the md5 hash with the new one you copied.
|
||||
$ brew install liblastfm
|
||||
|
||||
# Build Tomahawk
|
||||
$ git clone git://github.com/tomahawk-player/tomahawk.git
|
||||
$ cd tomahawk
|
||||
|
||||
Now compile Tomahawk
|
||||
--------------------
|
||||
$ mkdir build && cd build
|
||||
$ cmake ..
|
||||
$ make
|
||||
$ open tomahawk.app
|
||||
$ ./tomahawk
|
||||
|
||||
|
||||
Dependencies
|
||||
@ -102,10 +93,9 @@ Dependencies
|
||||
|
||||
To build the app:
|
||||
-----------------
|
||||
$ mkdir build
|
||||
$ cd build
|
||||
$ mkdir build && cd build
|
||||
|
||||
(Pick one of the following two choices. If unsure pick the second one, you probably want a GUI)
|
||||
Pick one of the following two choices. If uncertain pick the second one, you probably want a GUI.
|
||||
$ cmake -Dgui=no .. # enables headless mode, build without GUI
|
||||
$ cmake .. # normal build including GUI
|
||||
|
||||
@ -113,10 +103,14 @@ To build the app:
|
||||
|
||||
To run the app:
|
||||
---------------
|
||||
(Only run the next two commands if you installed any of the dependencies from source on Linux)
|
||||
Only run the next two commands if you installed any of the dependencies from source on Linux.
|
||||
$ export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
|
||||
$ sudo ldconfig -v
|
||||
|
||||
Start the application on Linux:
|
||||
$ ./tomahawk
|
||||
|
||||
Start the application on OS X:
|
||||
$ open tomahawk.app
|
||||
|
||||
Enjoy!
|
||||
|
@ -2,13 +2,13 @@
|
||||
|
||||
|
||||
FLACTranscode::FLACTranscode()
|
||||
: m_FLACInit( false )
|
||||
, m_FLACRunning( false )
|
||||
: m_FLACRunning( false )
|
||||
, m_finished( false )
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
|
||||
init();
|
||||
set_metadata_respond_all();
|
||||
}
|
||||
|
||||
|
||||
@ -33,7 +33,6 @@ FLACTranscode::clearBuffers()
|
||||
{
|
||||
QMutexLocker locker( &m_mutex );
|
||||
|
||||
m_FLACInit = false;
|
||||
m_FLACRunning = false;
|
||||
m_finished = false;
|
||||
|
||||
@ -52,14 +51,7 @@ FLACTranscode::processData( const QByteArray& data, bool finish )
|
||||
m_buffer.append( data );
|
||||
m_mutex.unlock();
|
||||
|
||||
if ( !m_FLACInit && m_buffer.size() > FLAC_BUFFER )
|
||||
{
|
||||
m_FLACInit = true;
|
||||
set_metadata_respond_all();
|
||||
process_single();
|
||||
}
|
||||
|
||||
while ( m_buffer.size() > FLAC_BUFFER / 2 )
|
||||
while ( m_buffer.size() >= FLAC_BUFFER )
|
||||
{
|
||||
process_single();
|
||||
}
|
||||
@ -79,10 +71,7 @@ FLACTranscode::read_callback( FLAC__byte buffer[], size_t *bytes )
|
||||
memcpy( buffer, (char*)m_buffer.data(), *bytes );
|
||||
m_buffer.remove( 0, *bytes );
|
||||
|
||||
// if ( !*bytes )
|
||||
// return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
|
||||
// else
|
||||
return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
|
||||
return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
|
||||
}
|
||||
|
||||
|
||||
@ -110,10 +99,17 @@ FLACTranscode::write_callback( const ::FLAC__Frame *frame, const FLAC__int32 *co
|
||||
}
|
||||
|
||||
|
||||
::FLAC__StreamDecoderSeekStatus
|
||||
FLACTranscode::seek_callback(FLAC__uint64 absolute_byte_offset)
|
||||
{
|
||||
return FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
FLACTranscode::metadata_callback( const ::FLAC__StreamMetadata *metadata )
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
qDebug() << Q_FUNC_INFO << metadata->is_last;
|
||||
|
||||
switch ( metadata->type )
|
||||
{
|
||||
|
@ -15,7 +15,7 @@
|
||||
#include <QMutex>
|
||||
#include <QDebug>
|
||||
|
||||
#define FLAC_BUFFER 32768
|
||||
#define FLAC_BUFFER 32768 * 36
|
||||
#define FLAC_BUFFER_PREFERRED 32768
|
||||
|
||||
class FLACTranscode : public TranscodeInterface , protected FLAC::Decoder::Stream
|
||||
@ -49,21 +49,17 @@ class FLACTranscode : public TranscodeInterface , protected FLAC::Decoder::Strea
|
||||
protected:
|
||||
virtual ::FLAC__StreamDecoderReadStatus read_callback( FLAC__byte buffer[], size_t *bytes );
|
||||
virtual ::FLAC__StreamDecoderWriteStatus write_callback( const ::FLAC__Frame *frame, const FLAC__int32 *const buffer[] );
|
||||
virtual ::FLAC__StreamDecoderSeekStatus seek_callback( FLAC__uint64 absolute_byte_offset );
|
||||
virtual bool eof_callback();
|
||||
void metadata_callback( const ::FLAC__StreamMetadata *metadata );
|
||||
virtual void metadata_callback( const ::FLAC__StreamMetadata *metadata );
|
||||
void error_callback( ::FLAC__StreamDecoderErrorStatus status );
|
||||
|
||||
/* ::FLAC__StreamDecoderSeekStatus seek_callback( FLAC__uint64 absolute_byte_offset );
|
||||
::FLAC__StreamDecoderTellStatus tell_callback( FLAC__uint64 *absolute_byte_offset );
|
||||
::FLAC__StreamDecoderLengthStatus length_callback( FLAC__uint64 *stream_length );*/
|
||||
|
||||
private:
|
||||
QByteArray m_outBuffer;
|
||||
|
||||
QMutex m_mutex;
|
||||
QByteArray m_buffer;
|
||||
|
||||
bool m_FLACInit;
|
||||
bool m_FLACRunning;
|
||||
bool m_finished;
|
||||
};
|
||||
|
@ -93,7 +93,7 @@ DatabaseCommand::factory( const QVariant& op, const source_ptr& source )
|
||||
return cmd;
|
||||
}
|
||||
|
||||
qDebug() << "ERRROR in" << Q_FUNC_INFO << name;
|
||||
qDebug() << "ERROR in" << Q_FUNC_INFO << name;
|
||||
// Q_ASSERT( false );
|
||||
return NULL;
|
||||
}
|
||||
|
@ -55,6 +55,7 @@ public:
|
||||
|
||||
virtual bool loggable() const { return false; }
|
||||
virtual bool singletonCmd() const { return false; }
|
||||
virtual bool localOnly() const { return false; }
|
||||
|
||||
QString guid() const
|
||||
{
|
||||
|
@ -14,33 +14,25 @@ DatabaseCommand_CollectionStats::DatabaseCommand_CollectionStats( const source_p
|
||||
void
|
||||
DatabaseCommand_CollectionStats::exec( DatabaseImpl* dbi )
|
||||
{
|
||||
//qDebug() << Q_FUNC_INFO;
|
||||
Q_ASSERT( !source().isNull() );
|
||||
TomahawkSqlQuery query = dbi->newquery();
|
||||
|
||||
Q_ASSERT( source()->isLocal() || source()->id() >= 1 );
|
||||
TomahawkSqlQuery query = dbi->newquery();
|
||||
|
||||
if( source()->isLocal() )
|
||||
{
|
||||
query.exec("SELECT count(*), max(mtime), (SELECT guid FROM oplog WHERE source IS NULL ORDER BY id DESC LIMIT 1) "
|
||||
"FROM file "
|
||||
"WHERE source IS NULL");
|
||||
query.exec( "SELECT count(*), max(mtime), (SELECT guid FROM oplog WHERE source IS NULL ORDER BY id DESC LIMIT 1) "
|
||||
"FROM file "
|
||||
"WHERE source IS NULL" );
|
||||
}
|
||||
else
|
||||
{
|
||||
query.prepare("SELECT count(*), max(mtime), "
|
||||
" (SELECT lastop FROM source WHERE id = ?) "
|
||||
"FROM file "
|
||||
"WHERE source = ?"
|
||||
);
|
||||
query.prepare( "SELECT count(*), max(mtime), (SELECT lastop FROM source WHERE id = ?) "
|
||||
"FROM file "
|
||||
"WHERE source = ?" );
|
||||
query.addBindValue( source()->id() );
|
||||
query.addBindValue( source()->id() );
|
||||
}
|
||||
if( !query.exec() )
|
||||
{
|
||||
qDebug() << "Failed to get collection stats:" << query.boundValues();
|
||||
throw "failed to get collection stats";
|
||||
}
|
||||
|
||||
query.exec();
|
||||
|
||||
QVariantMap m;
|
||||
if( query.next() )
|
||||
@ -50,8 +42,5 @@ DatabaseCommand_CollectionStats::exec( DatabaseImpl* dbi )
|
||||
m.insert( "lastop", query.value( 2 ).toString() );
|
||||
}
|
||||
|
||||
//qDebug() << "Loaded collection stats for"
|
||||
// << (source()->isLocal() ? "LOCAL" : source()->username())
|
||||
// << m;
|
||||
emit done( m );
|
||||
}
|
||||
|
@ -35,6 +35,6 @@ DatabaseCommand_loadOps::exec( DatabaseImpl* dbi )
|
||||
ops << op;
|
||||
}
|
||||
|
||||
qDebug() << "Loaded" << ops.length() << "ops from db";
|
||||
// qDebug() << "Loaded" << ops.length() << "ops from db";
|
||||
emit done( m_since, lastguid, ops );
|
||||
}
|
||||
|
@ -43,31 +43,33 @@ void DatabaseCommand_LoadPlaylistEntries::generateEntries( DatabaseImpl* dbi )
|
||||
"duration, addedon, addedby, result_hint "
|
||||
"FROM playlist_item "
|
||||
"WHERE guid IN %1").arg( inclause );
|
||||
//qDebug() << sql;
|
||||
|
||||
query.exec( sql );
|
||||
while( query.next() )
|
||||
{
|
||||
plentry_ptr e( new PlaylistEntry );
|
||||
e->setGuid( query.value( 0 ).toString() );
|
||||
e->setAnnotation( query.value( 4 ).toString() );
|
||||
e->setDuration( query.value( 5 ).toUInt() );
|
||||
e->setLastmodified( 0 ); // TODO e->lastmodified = query.value(6).toInt();
|
||||
e->setResulthint( query.value( 8 ).toString() );
|
||||
|
||||
QVariantMap m;
|
||||
m.insert( "artist", query.value( 2 ).toString() );
|
||||
m.insert( "album", query.value( 3 ).toString() );
|
||||
m.insert( "track", query.value( 1 ).toString() );
|
||||
m.insert( "qid", uuid() );
|
||||
|
||||
Tomahawk::query_ptr q( new Tomahawk::Query( m ) );
|
||||
e->setQuery( q );
|
||||
|
||||
m_entrymap.insert( e->guid(), e );
|
||||
}
|
||||
|
||||
prevrev = query_entries.value( 4 ).toString();
|
||||
//qDebug() << sql;
|
||||
|
||||
query.exec( sql );
|
||||
while( query.next() )
|
||||
{
|
||||
plentry_ptr e( new PlaylistEntry );
|
||||
e->setGuid( query.value( 0 ).toString() );
|
||||
e->setAnnotation( query.value( 4 ).toString() );
|
||||
e->setDuration( query.value( 5 ).toUInt() );
|
||||
e->setLastmodified( 0 ); // TODO e->lastmodified = query.value(6).toInt();
|
||||
e->setResultHint( query.value( 8 ).toString() );
|
||||
|
||||
QVariantMap m;
|
||||
m.insert( "artist", query.value( 2 ).toString() );
|
||||
m.insert( "album", query.value( 3 ).toString() );
|
||||
m.insert( "track", query.value( 1 ).toString() );
|
||||
m.insert( "resulthint", query.value( 8 ).toString() );
|
||||
m.insert( "qid", uuid() );
|
||||
|
||||
Tomahawk::query_ptr q( new Tomahawk::Query( m ) );
|
||||
e->setQuery( q );
|
||||
|
||||
m_entrymap.insert( e->guid(), e );
|
||||
}
|
||||
|
||||
prevrev = query_entries.value( 4 ).toString();
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -35,7 +35,9 @@ DatabaseCommand_PlaybackHistory::exec( DatabaseImpl* dbi )
|
||||
"FROM track, artist "
|
||||
"WHERE artist.id = track.artist "
|
||||
"AND track.id = %1 "
|
||||
).arg( query.value( 0 ).toUInt() );
|
||||
"%2"
|
||||
).arg( query.value( 0 ).toUInt() )
|
||||
.arg( m_amount > 0 ? QString( "LIMIT 0, %1" ).arg( m_amount ) : QString() );
|
||||
|
||||
query_track.prepare( sql );
|
||||
query_track.exec();
|
||||
|
@ -16,6 +16,7 @@ Q_OBJECT
|
||||
public:
|
||||
explicit DatabaseCommand_PlaybackHistory( const Tomahawk::source_ptr& source, QObject* parent = 0 )
|
||||
: DatabaseCommand( parent )
|
||||
, m_amount( 0 )
|
||||
{
|
||||
setSource( source );
|
||||
}
|
||||
@ -25,10 +26,13 @@ public:
|
||||
virtual bool doesMutates() const { return false; }
|
||||
virtual QString commandname() const { return "playbackhistory"; }
|
||||
|
||||
void setLimit( unsigned int amount ) { m_amount = amount; }
|
||||
|
||||
signals:
|
||||
void tracks( const QList<Tomahawk::query_ptr>& queries );
|
||||
|
||||
private:
|
||||
unsigned int m_amount;
|
||||
};
|
||||
|
||||
#endif // DATABASECOMMAND_PLAYBACKHISTORY_H
|
||||
|
@ -19,12 +19,34 @@ DatabaseCommand_Resolve::DatabaseCommand_Resolve( const QVariant& v, bool search
|
||||
void
|
||||
DatabaseCommand_Resolve::exec( DatabaseImpl* lib )
|
||||
{
|
||||
const Tomahawk::QID qid = m_v.toMap().value("qid").toString();
|
||||
const QString artistname = m_v.toMap().value("artist").toString();
|
||||
const QString albumname = m_v.toMap().value("album").toString();
|
||||
const QString trackname = m_v.toMap().value("track").toString();
|
||||
const QMap<QString, QVariant> map = m_v.toMap();
|
||||
|
||||
//qDebug() << Q_FUNC_INFO << artistname << trackname;
|
||||
const Tomahawk::QID qid = map.value( "qid" ).toString();
|
||||
const QString artistname = map.value( "artist" ).toString();
|
||||
const QString albumname = map.value( "album" ).toString();
|
||||
const QString trackname = map.value( "track" ).toString();
|
||||
const QString resulthint = map.value( "resulthint" ).toString();
|
||||
|
||||
collection_ptr coll;
|
||||
QList<Tomahawk::result_ptr> res;
|
||||
if ( !resulthint.isEmpty() )
|
||||
{
|
||||
qDebug() << "Using result-hint to speed up resolving:" << resulthint;
|
||||
|
||||
QVariantMap m = lib->result( resulthint );
|
||||
if ( !m.isEmpty() )
|
||||
{
|
||||
if ( m.value( "srcid" ).toUInt() > 0 )
|
||||
coll = SourceList::instance()->get( m.value( "srcid" ).toUInt() )->collection();
|
||||
else
|
||||
coll = SourceList::instance()->getLocal()->collection();
|
||||
|
||||
res << Tomahawk::result_ptr( new Tomahawk::Result( m, coll ) );
|
||||
emit results( qid, res );
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Resolving is a 2 stage process.
|
||||
@ -33,15 +55,14 @@ DatabaseCommand_Resolve::exec( DatabaseImpl* lib )
|
||||
results that are less than MINSCORE
|
||||
*/
|
||||
|
||||
typedef QPair<int,float> scorepair_t;
|
||||
typedef QPair<int, float> scorepair_t;
|
||||
|
||||
// STEP 1
|
||||
QList< int > artists = lib->searchTable( "artist", artistname, 10 );
|
||||
QList< int > tracks = lib->searchTable( "track", trackname, 10 );
|
||||
QList< int > albums = lib->searchTable( "album", albumname, 10 );
|
||||
QList< int > tracks = lib->searchTable( "track", trackname, 10 );
|
||||
QList< int > albums = lib->searchTable( "album", albumname, 10 );
|
||||
|
||||
//qDebug() << "art" << artists.size() << "trk" << tracks.size();
|
||||
//qDebug() << "searchTable calls duration:" << timer.elapsed();
|
||||
//qDebug() << "searchTable calls duration:" << timer.elapsed() << "ms";
|
||||
|
||||
if( artists.length() == 0 || tracks.length() == 0 )
|
||||
{
|
||||
@ -53,8 +74,10 @@ DatabaseCommand_Resolve::exec( DatabaseImpl* lib )
|
||||
TomahawkSqlQuery files_query = lib->newquery();
|
||||
|
||||
QStringList artsl, trksl;
|
||||
foreach( int i, artists ) artsl.append( QString::number(i) );
|
||||
foreach( int i, tracks ) trksl.append( QString::number(i) );
|
||||
foreach( int i, artists )
|
||||
artsl.append( QString::number( i ) );
|
||||
foreach( int i, tracks )
|
||||
trksl.append( QString::number( i ) );
|
||||
|
||||
QString sql = QString( "SELECT "
|
||||
"url, mtime, size, md5, mimetype, duration, bitrate, file_join.artist, file_join.album, file_join.track, "
|
||||
@ -73,81 +96,63 @@ DatabaseCommand_Resolve::exec( DatabaseImpl* lib )
|
||||
"file.source %1 AND "
|
||||
"file.id = file_join.file AND "
|
||||
"file_join.artist IN (%2) AND "
|
||||
"file_join.track IN (%3) "
|
||||
"ORDER by file_join.artist,file_join.track"
|
||||
).arg( m_searchlocal ? "IS NULL" : " IN (SELECT id FROM source WHERE isonline = 'true') " )
|
||||
.arg( artsl.join(",") )
|
||||
.arg( trksl.join(",") );
|
||||
"file_join.track IN (%3)"
|
||||
).arg( m_searchlocal ? "IS NULL" : " > 0 " )
|
||||
.arg( artsl.join( "," ) )
|
||||
.arg( trksl.join( "," ) );
|
||||
|
||||
files_query.prepare( sql );
|
||||
bool ok = files_query.exec();
|
||||
if(!ok)
|
||||
throw "Error";
|
||||
|
||||
//qDebug() << "SQL exec() duration, ms, " << timer.elapsed()
|
||||
// << "numresults" << files_query.numRowsAffected();
|
||||
//qDebug() << sql;
|
||||
|
||||
QList<Tomahawk::result_ptr> res;
|
||||
files_query.exec();
|
||||
|
||||
while( files_query.next() )
|
||||
{
|
||||
QVariantMap m;
|
||||
|
||||
m["mtime"] = files_query.value(1).toString();
|
||||
m["size"] = files_query.value(2).toInt();
|
||||
m["hash"] = files_query.value(3).toString();
|
||||
m["mimetype"] = files_query.value(4).toString();
|
||||
m["duration"] = files_query.value(5).toInt();
|
||||
m["bitrate"] = files_query.value(6).toInt();
|
||||
m["artist"] = files_query.value(10).toString();
|
||||
m["artistid"] = files_query.value(15).toUInt();
|
||||
m["album"] = files_query.value(11).toString();
|
||||
m["albumid"] = files_query.value(16).toUInt();
|
||||
m["track"] = files_query.value(12).toString();
|
||||
m["srcid"] = files_query.value(13).toInt();
|
||||
m["albumpos"] = files_query.value(14).toUInt();
|
||||
m["mtime"] = files_query.value( 1 ).toString();
|
||||
m["size"] = files_query.value( 2 ).toInt();
|
||||
m["hash"] = files_query.value( 3 ).toString();
|
||||
m["mimetype"] = files_query.value( 4 ).toString();
|
||||
m["duration"] = files_query.value( 5 ).toInt();
|
||||
m["bitrate"] = files_query.value( 6 ).toInt();
|
||||
m["artist"] = files_query.value( 10 ).toString();
|
||||
m["artistid"] = files_query.value( 15 ).toUInt();
|
||||
m["album"] = files_query.value( 11 ).toString();
|
||||
m["albumid"] = files_query.value( 16 ).toUInt();
|
||||
m["track"] = files_query.value( 12 ).toString();
|
||||
m["srcid"] = files_query.value( 13 ).toInt();
|
||||
m["albumpos"] = files_query.value( 14 ).toUInt();
|
||||
m["sid"] = uuid();
|
||||
|
||||
collection_ptr coll;
|
||||
|
||||
source_ptr s;
|
||||
const QString url_str = files_query.value( 0 ).toString();
|
||||
if( m_searchlocal )
|
||||
{
|
||||
coll = SourceList::instance()->getLocal()->collection();
|
||||
s = SourceList::instance()->getLocal();
|
||||
m["url"] = url_str;
|
||||
m["source"] = "Local Database"; // TODO
|
||||
}
|
||||
else
|
||||
{
|
||||
source_ptr s = SourceList::instance()->get( files_query.value( 13 ).toUInt() );
|
||||
s = SourceList::instance()->get( files_query.value( 13 ).toUInt() );
|
||||
if( s.isNull() )
|
||||
{
|
||||
//qDebug() << "Skipping result for offline sourceid:" << files_query.value(13).toUInt();
|
||||
//qDebug() << "Skipping result for offline sourceid:" << files_query.value( 13 ).toUInt();
|
||||
// will happen for valid sources which are offline (and thus not in the sourcelist)
|
||||
return;
|
||||
}
|
||||
|
||||
coll = s->collection();
|
||||
m.insert( "url", QString( "servent://%1\t%2" )
|
||||
.arg( s->userName() )
|
||||
.arg( url_str ) );
|
||||
m.insert( "url", QString( "servent://%1\t%2" ).arg( s->userName() ).arg( url_str ) );
|
||||
m.insert( "source", s->friendlyName() );
|
||||
}
|
||||
|
||||
//int artid = files_query.value( 7 ).toInt();
|
||||
//int albid = files_query.value( 8 ).toInt();
|
||||
//int trkid = files_query.value( 9 ).toInt();
|
||||
|
||||
float score = how_similar( m_v.toMap(), m );
|
||||
//qDebug() << "Score calc:" << timer.elapsed();
|
||||
//qDebug() << "Score calc:" << timer.elapsed() << "ms";
|
||||
|
||||
m["score"] = score;
|
||||
//qDebug() << "RESULT" << score << m;
|
||||
|
||||
if( score < MINSCORE )
|
||||
continue;
|
||||
|
||||
coll = s->collection();
|
||||
res << Tomahawk::result_ptr( new Tomahawk::Result( m, coll ) );
|
||||
}
|
||||
|
||||
@ -160,24 +165,24 @@ float
|
||||
DatabaseCommand_Resolve::how_similar( const QVariantMap& q, const QVariantMap& r )
|
||||
{
|
||||
// query values
|
||||
const QString qArtistname = DatabaseImpl::sortname( q.value("artist").toString() );
|
||||
const QString qAlbumname = DatabaseImpl::sortname( q.value("album").toString() );
|
||||
const QString qTrackname = DatabaseImpl::sortname( q.value("track").toString() );
|
||||
const QString qArtistname = DatabaseImpl::sortname( q.value( "artist" ).toString() );
|
||||
const QString qAlbumname = DatabaseImpl::sortname( q.value( "album" ).toString() );
|
||||
const QString qTrackname = DatabaseImpl::sortname( q.value( "track" ).toString() );
|
||||
|
||||
// result values
|
||||
const QString rArtistname = DatabaseImpl::sortname( r.value("artist").toString() );
|
||||
const QString rAlbumname = DatabaseImpl::sortname( r.value("album").toString() );
|
||||
const QString rTrackname = DatabaseImpl::sortname( r.value("track").toString() );
|
||||
const QString rArtistname = DatabaseImpl::sortname( r.value( "artist" ).toString() );
|
||||
const QString rAlbumname = DatabaseImpl::sortname( r.value( "album" ).toString() );
|
||||
const QString rTrackname = DatabaseImpl::sortname( r.value( "track" ).toString() );
|
||||
|
||||
// normal edit distance
|
||||
int artdist = levenshtein( qArtistname, rArtistname );
|
||||
int albdist = levenshtein( qAlbumname, rAlbumname );
|
||||
int trkdist = levenshtein( qTrackname, rTrackname );
|
||||
int albdist = levenshtein( qAlbumname, rAlbumname );
|
||||
int trkdist = levenshtein( qTrackname, rTrackname );
|
||||
|
||||
// max length of name
|
||||
int mlart = qMax( qArtistname.length(), rArtistname.length() );
|
||||
int mlalb = qMax( qAlbumname.length(), rAlbumname.length() );
|
||||
int mltrk = qMax( qTrackname.length(), rTrackname.length() );
|
||||
int mlalb = qMax( qAlbumname.length(), rAlbumname.length() );
|
||||
int mltrk = qMax( qTrackname.length(), rTrackname.length() );
|
||||
|
||||
// distance scores
|
||||
float dcart = (float)( mlart - artdist ) / mlart;
|
||||
@ -185,10 +190,90 @@ DatabaseCommand_Resolve::how_similar( const QVariantMap& q, const QVariantMap& r
|
||||
float dctrk = (float)( mltrk - trkdist ) / mltrk;
|
||||
|
||||
// don't penalize for missing album name
|
||||
if( qAlbumname.length() == 0 ) dcalb = 1.0;
|
||||
if( qAlbumname.length() == 0 )
|
||||
dcalb = 1.0;
|
||||
|
||||
// weighted, so album match is worth less than track title
|
||||
float combined = ( dcart*4 + dcalb + dctrk*5 ) / 10;
|
||||
|
||||
return combined;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
DatabaseCommand_Resolve::levenshtein( const QString& source, const QString& target )
|
||||
{
|
||||
// Step 1
|
||||
const int n = source.length();
|
||||
const int m = target.length();
|
||||
|
||||
if ( n == 0 )
|
||||
return m;
|
||||
if ( m == 0 )
|
||||
return n;
|
||||
|
||||
// Good form to declare a TYPEDEF
|
||||
typedef QVector< QVector<int> > Tmatrix;
|
||||
Tmatrix matrix;
|
||||
matrix.resize( n + 1 );
|
||||
|
||||
// Size the vectors in the 2.nd dimension. Unfortunately C++ doesn't
|
||||
// allow for allocation on declaration of 2.nd dimension of vec of vec
|
||||
for ( int i = 0; i <= n; i++ )
|
||||
{
|
||||
QVector<int> tmp;
|
||||
tmp.resize( m + 1 );
|
||||
matrix.insert( i, tmp );
|
||||
}
|
||||
|
||||
// Step 2
|
||||
for ( int i = 0; i <= n; i++ )
|
||||
matrix[i][0] = i;
|
||||
for ( int j = 0; j <= m; j++ )
|
||||
matrix[0][j] = j;
|
||||
|
||||
// Step 3
|
||||
for ( int i = 1; i <= n; i++ )
|
||||
{
|
||||
const QChar s_i = source[i - 1];
|
||||
|
||||
// Step 4
|
||||
for ( int j = 1; j <= m; j++ )
|
||||
{
|
||||
const QChar t_j = target[j - 1];
|
||||
|
||||
// Step 5
|
||||
int cost;
|
||||
if ( s_i == t_j )
|
||||
cost = 0;
|
||||
else
|
||||
cost = 1;
|
||||
|
||||
// Step 6
|
||||
const int above = matrix[i - 1][j];
|
||||
const int left = matrix[i][j - 1];
|
||||
const int diag = matrix[i - 1][j - 1];
|
||||
|
||||
int cell = ( ((left + 1) > (diag + cost)) ? diag + cost : left + 1 );
|
||||
if( above + 1 < cell )
|
||||
cell = above + 1;
|
||||
|
||||
// Step 6A: Cover transposition, in addition to deletion,
|
||||
// insertion and substitution. This step is taken from:
|
||||
// Berghel, Hal ; Roach, David : "An Extension of Ukkonen's
|
||||
// Enhanced Dynamic Programming ASM Algorithm"
|
||||
// (http://www.acm.org/~hlb/publications/asm/asm.html)
|
||||
if ( i > 2 && j > 2 )
|
||||
{
|
||||
int trans = matrix[i - 2][j - 2] + 1;
|
||||
|
||||
if ( source[ i - 2 ] != t_j ) trans++;
|
||||
if ( s_i != target[ j - 2 ] ) trans++;
|
||||
if ( cell > trans) cell = trans;
|
||||
}
|
||||
matrix[i][j] = cell;
|
||||
}
|
||||
}
|
||||
|
||||
// Step 7
|
||||
return matrix[n][m];
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ public:
|
||||
virtual QString commandname() const { return "dbresolve"; }
|
||||
virtual bool doesMutates() const { return false; }
|
||||
|
||||
virtual void exec(DatabaseImpl *lib);
|
||||
virtual void exec( DatabaseImpl *lib );
|
||||
|
||||
signals:
|
||||
void results( Tomahawk::QID qid, QList<Tomahawk::result_ptr> results );
|
||||
@ -30,75 +30,7 @@ private:
|
||||
bool m_searchlocal;
|
||||
|
||||
float how_similar( const QVariantMap& q, const QVariantMap& r );
|
||||
|
||||
static int levenshtein(const QString& source, const QString& target)
|
||||
{
|
||||
// Step 1
|
||||
const int n = source.length();
|
||||
const int m = target.length();
|
||||
if (n == 0) {
|
||||
return m;
|
||||
}
|
||||
if (m == 0) {
|
||||
return n;
|
||||
}
|
||||
// Good form to declare a TYPEDEF
|
||||
typedef QVector< QVector<int> > Tmatrix;
|
||||
Tmatrix matrix;
|
||||
matrix.resize( n+1 );
|
||||
|
||||
// Size the vectors in the 2.nd dimension. Unfortunately C++ doesn't
|
||||
// allow for allocation on declaration of 2.nd dimension of vec of vec
|
||||
for (int i = 0; i <= n; i++) {
|
||||
QVector<int> tmp;
|
||||
tmp.resize( m+1 );
|
||||
matrix.insert( i, tmp );
|
||||
}
|
||||
// Step 2
|
||||
for (int i = 0; i <= n; i++) {
|
||||
matrix[i][0]=i;
|
||||
}
|
||||
for (int j = 0; j <= m; j++) {
|
||||
matrix[0][j]=j;
|
||||
}
|
||||
// Step 3
|
||||
for (int i = 1; i <= n; i++) {
|
||||
const QChar s_i = source[i-1];
|
||||
// Step 4
|
||||
for (int j = 1; j <= m; j++) {
|
||||
const QChar t_j = target[j-1];
|
||||
// Step 5
|
||||
int cost;
|
||||
if (s_i == t_j) {
|
||||
cost = 0;
|
||||
}
|
||||
else {
|
||||
cost = 1;
|
||||
}
|
||||
// Step 6
|
||||
const int above = matrix[i-1][j];
|
||||
const int left = matrix[i][j-1];
|
||||
const int diag = matrix[i-1][j-1];
|
||||
//int cell = min( above + 1, min(left + 1, diag + cost));
|
||||
int cell = (((left+1)>(diag+cost))?diag+cost:left+1);
|
||||
if(above+1 < cell) cell = above+1;
|
||||
// Step 6A: Cover transposition, in addition to deletion,
|
||||
// insertion and substitution. This step is taken from:
|
||||
// Berghel, Hal ; Roach, David : "An Extension of Ukkonen's
|
||||
// Enhanced Dynamic Programming ASM Algorithm"
|
||||
// (http://www.acm.org/~hlb/publications/asm/asm.html)
|
||||
if (i>2 && j>2) {
|
||||
int trans=matrix[i-2][j-2]+1;
|
||||
if (source[i-2]!=t_j) trans++;
|
||||
if (s_i!=target[j-2]) trans++;
|
||||
if (cell>trans) cell=trans;
|
||||
}
|
||||
matrix[i][j]=cell;
|
||||
}
|
||||
}
|
||||
// Step 7
|
||||
return matrix[n][m];
|
||||
};
|
||||
static int levenshtein( const QString& source, const QString& target );
|
||||
};
|
||||
|
||||
#endif // DATABASECOMMAND_RESOLVE_H
|
||||
|
@ -12,10 +12,11 @@ DatabaseCommand_SetDynamicPlaylistRevision::DatabaseCommand_SetDynamicPlaylistRe
|
||||
const QString& oldrev,
|
||||
const QStringList& orderedguids,
|
||||
const QList< plentry_ptr >& addedentries,
|
||||
const QList<plentry_ptr>& entries,
|
||||
const QString& type,
|
||||
GeneratorMode mode,
|
||||
const QList< dyncontrol_ptr >& controls )
|
||||
: DatabaseCommand_SetPlaylistRevision( s, playlistguid, newrev, oldrev, orderedguids, addedentries )
|
||||
: DatabaseCommand_SetPlaylistRevision( s, playlistguid, newrev, oldrev, orderedguids, addedentries, entries )
|
||||
, m_type( type )
|
||||
, m_mode( mode )
|
||||
, m_controls( controls )
|
||||
@ -30,7 +31,7 @@ DatabaseCommand_SetDynamicPlaylistRevision::DatabaseCommand_SetDynamicPlaylistRe
|
||||
const QString& type,
|
||||
GeneratorMode mode,
|
||||
const QList< dyncontrol_ptr >& controls )
|
||||
: DatabaseCommand_SetPlaylistRevision( s, playlistguid, newrev, oldrev, QStringList(), QList< plentry_ptr >() )
|
||||
: DatabaseCommand_SetPlaylistRevision( s, playlistguid, newrev, oldrev, QStringList(), QList< plentry_ptr >(), QList< plentry_ptr >() )
|
||||
, m_type( type )
|
||||
, m_mode( mode )
|
||||
, m_controls( controls )
|
||||
|
@ -28,6 +28,7 @@ public:
|
||||
const QString& oldrev,
|
||||
const QStringList& orderedguids,
|
||||
const QList<Tomahawk::plentry_ptr>& addedentries,
|
||||
const QList<plentry_ptr>& entries,
|
||||
const QString& type,
|
||||
GeneratorMode mode,
|
||||
const QList< dyncontrol_ptr >& controls );
|
||||
|
@ -12,13 +12,17 @@ DatabaseCommand_SetPlaylistRevision::DatabaseCommand_SetPlaylistRevision(
|
||||
const QString& newrev,
|
||||
const QString& oldrev,
|
||||
const QStringList& orderedguids,
|
||||
const QList<plentry_ptr>& addedentries )
|
||||
const QList<plentry_ptr>& addedentries,
|
||||
const QList<plentry_ptr>& entries )
|
||||
: DatabaseCommandLoggable( s )
|
||||
, m_applied( false )
|
||||
, m_newrev( newrev )
|
||||
, m_oldrev( oldrev )
|
||||
, m_addedentries( addedentries )
|
||||
, m_entries( entries )
|
||||
{
|
||||
m_localOnly = ( newrev == oldrev );
|
||||
|
||||
setPlaylistguid( playlistguid );
|
||||
|
||||
QVariantList tmp;
|
||||
@ -34,6 +38,9 @@ DatabaseCommand_SetPlaylistRevision::postCommitHook()
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
|
||||
if ( m_localOnly )
|
||||
return;
|
||||
|
||||
QStringList orderedentriesguids;
|
||||
foreach( const QVariant& v, m_orderedguids )
|
||||
orderedentriesguids << v.toString();
|
||||
@ -68,7 +75,7 @@ DatabaseCommand_SetPlaylistRevision::exec( DatabaseImpl* lib )
|
||||
// get the current revision for this playlist
|
||||
// this also serves to check the playlist exists.
|
||||
TomahawkSqlQuery chkq = lib->newquery();
|
||||
chkq.prepare("SELECT currentrevision FROM playlist WHERE guid = ?");
|
||||
chkq.prepare( "SELECT currentrevision FROM playlist WHERE guid = ?" );
|
||||
chkq.addBindValue( m_playlistguid );
|
||||
if( chkq.exec() && chkq.next() )
|
||||
{
|
||||
@ -87,38 +94,59 @@ DatabaseCommand_SetPlaylistRevision::exec( DatabaseImpl* lib )
|
||||
|
||||
// add any new items:
|
||||
TomahawkSqlQuery adde = lib->newquery();
|
||||
|
||||
QString sql = "INSERT INTO playlist_item( guid, playlist, trackname, artistname, albumname, "
|
||||
"annotation, duration, addedon, addedby, result_hint ) "
|
||||
"VALUES( ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )";
|
||||
adde.prepare( sql );
|
||||
|
||||
qDebug() << "Num new playlist_items to add:" << m_addedentries.length();
|
||||
foreach( const plentry_ptr& e, m_addedentries )
|
||||
if ( m_localOnly )
|
||||
{
|
||||
m_addedmap.insert( e->guid(), e ); // needed in postcommithook
|
||||
QString sql = "UPDATE playlist_item SET result_hint = ? WHERE guid = ?";
|
||||
adde.prepare( sql );
|
||||
|
||||
adde.bindValue( 0, e->guid() );
|
||||
adde.bindValue( 1, m_playlistguid );
|
||||
adde.bindValue( 2, e->query()->track() );
|
||||
adde.bindValue( 3, e->query()->artist() );
|
||||
adde.bindValue( 4, e->query()->album() );
|
||||
adde.bindValue( 5, e->annotation() );
|
||||
adde.bindValue( 6, (int) e->duration() );
|
||||
adde.bindValue( 7, e->lastmodified() );
|
||||
adde.bindValue( 8, source()->isLocal() ? QVariant(QVariant::Int) : source()->id() );
|
||||
adde.bindValue( 9, "" );
|
||||
adde.exec();
|
||||
foreach( const plentry_ptr& e, m_entries )
|
||||
{
|
||||
if ( e->query()->results().isEmpty() )
|
||||
continue;
|
||||
|
||||
adde.bindValue( 0, e->query()->results().first()->url() );
|
||||
adde.bindValue( 1, e->guid() );
|
||||
adde.exec();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
QString sql = "INSERT INTO playlist_item( guid, playlist, trackname, artistname, albumname, "
|
||||
"annotation, duration, addedon, addedby, result_hint ) "
|
||||
"VALUES( ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )";
|
||||
adde.prepare( sql );
|
||||
|
||||
qDebug() << "Num new playlist_items to add:" << m_addedentries.length();
|
||||
foreach( const plentry_ptr& e, m_addedentries )
|
||||
{
|
||||
m_addedmap.insert( e->guid(), e ); // needed in postcommithook
|
||||
|
||||
QString resultHint;
|
||||
if ( !e->query()->results().isEmpty() )
|
||||
resultHint = e->query()->results().first()->url();
|
||||
|
||||
adde.bindValue( 0, e->guid() );
|
||||
adde.bindValue( 1, m_playlistguid );
|
||||
adde.bindValue( 2, e->query()->track() );
|
||||
adde.bindValue( 3, e->query()->artist() );
|
||||
adde.bindValue( 4, e->query()->album() );
|
||||
adde.bindValue( 5, e->annotation() );
|
||||
adde.bindValue( 6, (int) e->duration() );
|
||||
adde.bindValue( 7, e->lastmodified() );
|
||||
adde.bindValue( 8, source()->isLocal() ? QVariant(QVariant::Int) : source()->id() );
|
||||
adde.bindValue( 9, resultHint );
|
||||
adde.exec();
|
||||
}
|
||||
}
|
||||
|
||||
// add the new revision:
|
||||
//qDebug() << "Adding new playlist revision, guid:" << m_newrev
|
||||
// << entries;
|
||||
// add / update the revision:
|
||||
TomahawkSqlQuery query = lib->newquery();
|
||||
sql = "INSERT INTO playlist_revision(guid, playlist, entries, author, timestamp, previous_revision) "
|
||||
"VALUES(?, ?, ?, ?, ?, ?)";
|
||||
|
||||
QString sql = "INSERT INTO playlist_revision(guid, playlist, entries, author, timestamp, previous_revision) "
|
||||
"VALUES(?, ?, ?, ?, ?, ?)";
|
||||
query.prepare( sql );
|
||||
|
||||
query.addBindValue( m_newrev );
|
||||
query.addBindValue( m_playlistguid );
|
||||
query.addBindValue( entries );
|
||||
@ -131,9 +159,10 @@ DatabaseCommand_SetPlaylistRevision::exec( DatabaseImpl* lib )
|
||||
// if optimistic locking is ok, update current revision to this new one
|
||||
if( m_currentRevision == m_oldrev )
|
||||
{
|
||||
TomahawkSqlQuery query2 = lib->newquery();
|
||||
qDebug() << "updating current revision, optimistic locking ok";
|
||||
query2.prepare("UPDATE playlist SET currentrevision = ? WHERE guid = ?");
|
||||
|
||||
TomahawkSqlQuery query2 = lib->newquery();
|
||||
query2.prepare( "UPDATE playlist SET currentrevision = ? WHERE guid = ?" );
|
||||
query2.bindValue( 0, m_newrev );
|
||||
query2.bindValue( 1, m_playlistguid );
|
||||
query2.exec();
|
||||
@ -155,6 +184,7 @@ DatabaseCommand_SetPlaylistRevision::exec( DatabaseImpl* lib )
|
||||
QJson::Parser parser;
|
||||
QVariant v = parser.parse( query_entries.value(0).toByteArray(), &ok );
|
||||
Q_ASSERT( ok && v.type() == QVariant::List ); //TODO
|
||||
|
||||
m_previous_rev_orderedguids = v.toStringList();
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ public:
|
||||
explicit DatabaseCommand_SetPlaylistRevision( QObject* parent = 0 )
|
||||
: DatabaseCommandLoggable( parent )
|
||||
, m_applied( false )
|
||||
, m_localOnly( false )
|
||||
{}
|
||||
|
||||
explicit DatabaseCommand_SetPlaylistRevision( const source_ptr& s,
|
||||
@ -30,13 +31,16 @@ public:
|
||||
const QString& newrev,
|
||||
const QString& oldrev,
|
||||
const QStringList& orderedguids,
|
||||
const QList<Tomahawk::plentry_ptr>& addedentries );
|
||||
const QList<Tomahawk::plentry_ptr>& addedentries,
|
||||
const QList<Tomahawk::plentry_ptr>& entries );
|
||||
|
||||
QString commandname() const { return "setplaylistrevision"; }
|
||||
|
||||
virtual void exec( DatabaseImpl* lib );
|
||||
virtual void postCommitHook();
|
||||
|
||||
virtual bool doesMutates() const { return true; }
|
||||
virtual bool localOnly() const { return m_localOnly; }
|
||||
|
||||
void setAddedentriesV( const QVariantList& vlist )
|
||||
{
|
||||
@ -81,7 +85,9 @@ protected:
|
||||
QString m_currentRevision;
|
||||
private:
|
||||
QVariantList m_orderedguids;
|
||||
QList<Tomahawk::plentry_ptr> m_addedentries;
|
||||
QList<Tomahawk::plentry_ptr> m_addedentries, m_entries;
|
||||
|
||||
bool m_localOnly;
|
||||
};
|
||||
|
||||
#endif // DATABASECOMMAND_SETPLAYLISTREVISION_H
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include "database/database.h"
|
||||
#include "databasecommand_updatesearchindex.h"
|
||||
#include "sourcelist.h"
|
||||
|
||||
/* !!!! You need to manually generate schema.sql.h when the schema changes:
|
||||
cd src/libtomahawk/database
|
||||
@ -469,3 +470,93 @@ DatabaseImpl::album( int id )
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
QVariantMap
|
||||
DatabaseImpl::result( const QString& url )
|
||||
{
|
||||
TomahawkSqlQuery query = newquery();
|
||||
Tomahawk::source_ptr s;
|
||||
QString fileUrl;
|
||||
|
||||
if ( url.contains( "servent://" ) )
|
||||
{
|
||||
QStringList parts = url.mid( QString( "servent://" ).length() ).split( "\t" );
|
||||
s = SourceList::instance()->get( parts.at( 0 ) );
|
||||
fileUrl = parts.at( 1 );
|
||||
}
|
||||
else if ( url.contains( "file://" ) )
|
||||
{
|
||||
s = SourceList::instance()->getLocal();
|
||||
fileUrl = url;
|
||||
}
|
||||
else
|
||||
Q_ASSERT( false );
|
||||
|
||||
bool searchlocal = s->isLocal();
|
||||
|
||||
QString sql = QString( "SELECT "
|
||||
"url, mtime, size, md5, mimetype, duration, bitrate, file_join.artist, file_join.album, file_join.track, "
|
||||
"artist.name as artname, "
|
||||
"album.name as albname, "
|
||||
"track.name as trkname, "
|
||||
"file.source, "
|
||||
"file_join.albumpos, "
|
||||
"artist.id as artid, "
|
||||
"album.id as albid "
|
||||
"FROM file, file_join, artist, track "
|
||||
"LEFT JOIN album ON album.id = file_join.album "
|
||||
"WHERE "
|
||||
"artist.id = file_join.artist AND "
|
||||
"track.id = file_join.track AND "
|
||||
"file.source %1 AND "
|
||||
"file_join.file = file.id AND "
|
||||
"file.url = ?"
|
||||
).arg( searchlocal ? "IS NULL" : QString( "= %1" ).arg( s->id() ) );
|
||||
|
||||
query.prepare( sql );
|
||||
query.bindValue( 0, fileUrl );
|
||||
query.exec();
|
||||
|
||||
QVariantMap m;
|
||||
if( query.next() )
|
||||
{
|
||||
const QString url_str = query.value( 0 ).toString();
|
||||
if( searchlocal )
|
||||
{
|
||||
m["url"] = url_str;
|
||||
m["source"] = "Local Database"; // TODO
|
||||
}
|
||||
else
|
||||
{
|
||||
Tomahawk::source_ptr s;
|
||||
s = SourceList::instance()->get( query.value( 13 ).toUInt() );
|
||||
if( s.isNull() )
|
||||
{
|
||||
//qDebug() << "Skipping result for offline sourceid:" << files_query.value( 13 ).toUInt();
|
||||
// will happen for valid sources which are offline (and thus not in the sourcelist)
|
||||
return m;
|
||||
}
|
||||
|
||||
m.insert( "url", QString( "servent://%1\t%2" ).arg( s->userName() ).arg( url_str ) );
|
||||
m.insert( "source", s->friendlyName() );
|
||||
}
|
||||
|
||||
m["mtime"] = query.value( 1 ).toString();
|
||||
m["size"] = query.value( 2 ).toInt();
|
||||
m["hash"] = query.value( 3 ).toString();
|
||||
m["mimetype"] = query.value( 4 ).toString();
|
||||
m["duration"] = query.value( 5 ).toInt();
|
||||
m["bitrate"] = query.value( 6 ).toInt();
|
||||
m["artist"] = query.value( 10 ).toString();
|
||||
m["artistid"] = query.value( 15 ).toUInt();
|
||||
m["album"] = query.value( 11 ).toString();
|
||||
m["albumid"] = query.value( 16 ).toUInt();
|
||||
m["track"] = query.value( 12 ).toString();
|
||||
m["srcid"] = query.value( 13 ).toInt();
|
||||
m["albumpos"] = query.value( 14 ).toUInt();
|
||||
m["sid"] = uuid();
|
||||
m["score"] = 1.0;
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
@ -46,6 +46,7 @@ public:
|
||||
QVariantMap album( int id );
|
||||
QVariantMap track( int id );
|
||||
QVariantMap file( int fid );
|
||||
QVariantMap result( const QString& url );
|
||||
|
||||
static bool scorepairSorter( const QPair<int,float>& left, const QPair<int,float>& right )
|
||||
{
|
||||
|
@ -71,7 +71,7 @@ DatabaseWorker::doWork( QSharedPointer<DatabaseCommand> cmd )
|
||||
{
|
||||
cmd->_exec( m_dbimpl ); // runs actual SQL stuff
|
||||
|
||||
if( cmd->loggable() )
|
||||
if( cmd->loggable() && !cmd->localOnly() )
|
||||
{
|
||||
// We only save our own ops to the oplog, since incoming ops from peers
|
||||
// are applied immediately.
|
||||
@ -110,7 +110,7 @@ DatabaseWorker::doWork( QSharedPointer<DatabaseCommand> cmd )
|
||||
|
||||
if( cmd->doesMutates() )
|
||||
{
|
||||
qDebug() << "Comitting" << cmd->commandname();;
|
||||
qDebug() << "Committing" << cmd->commandname();;
|
||||
if( !m_dbimpl->database().commit() )
|
||||
{
|
||||
|
||||
@ -152,6 +152,7 @@ DatabaseWorker::doWork( QSharedPointer<DatabaseCommand> cmd )
|
||||
Q_ASSERT( false );
|
||||
throw;
|
||||
}
|
||||
|
||||
cmd->emitFinished();
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include <QTime>
|
||||
|
||||
|
||||
FuzzyIndex::FuzzyIndex( DatabaseImpl &db )
|
||||
: QObject()
|
||||
, m_db( db )
|
||||
@ -16,10 +17,10 @@ void
|
||||
FuzzyIndex::loadNgramIndex()
|
||||
{
|
||||
// this updates the index in the DB, if needed:
|
||||
qDebug() << "Checking catalogue is fully indexed..";
|
||||
m_db.updateSearchIndex("artist",0);
|
||||
m_db.updateSearchIndex("album",0);
|
||||
m_db.updateSearchIndex("track",0);
|
||||
qDebug() << "Checking catalogue is fully indexed...";
|
||||
m_db.updateSearchIndex( "artist", 0 );
|
||||
m_db.updateSearchIndex( "album", 0 );
|
||||
m_db.updateSearchIndex( "track", 0 );
|
||||
|
||||
// loads index from DB into memory:
|
||||
qDebug() << "Loading search index for catalogue metadata..." << thread();
|
||||
@ -36,6 +37,7 @@ FuzzyIndex::loadNgramIndex_helper( QHash< QString, QMap<quint32, quint16> >& idx
|
||||
{
|
||||
QTime t;
|
||||
t.start();
|
||||
|
||||
TomahawkSqlQuery query = m_db.newquery();
|
||||
query.exec( QString( "SELECT ngram, id, num "
|
||||
"FROM %1_search_index "
|
||||
@ -46,13 +48,14 @@ FuzzyIndex::loadNgramIndex_helper( QHash< QString, QMap<quint32, quint16> >& idx
|
||||
QString lastngram;
|
||||
while( query.next() )
|
||||
{
|
||||
const QString ng = query.value( 0 ).toString();
|
||||
if( lastngram.isEmpty() )
|
||||
lastngram = query.value(0).toString();
|
||||
lastngram = ng;
|
||||
|
||||
if( query.value( 0 ).toString() != lastngram )
|
||||
if( ng != lastngram )
|
||||
{
|
||||
idx.insert( lastngram, ngram_idx );
|
||||
lastngram = query.value( 0 ).toString();
|
||||
lastngram = ng;
|
||||
ngram_idx.clear();
|
||||
}
|
||||
|
||||
@ -63,11 +66,11 @@ FuzzyIndex::loadNgramIndex_helper( QHash< QString, QMap<quint32, quint16> >& idx
|
||||
idx.insert( lastngram, ngram_idx );
|
||||
qDebug() << "Loaded" << idx.size()
|
||||
<< "ngram entries for" << table
|
||||
<< "in" << t.elapsed();
|
||||
<< "in" << t.elapsed() << "ms";
|
||||
}
|
||||
|
||||
|
||||
void FuzzyIndex::mergeIndex( const QString& table, QHash< QString, QMap<quint32, quint16> > tomerge )
|
||||
void FuzzyIndex::mergeIndex( const QString& table, const QHash< QString, QMap<quint32, quint16> >& tomerge )
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << table << tomerge.keys().size();
|
||||
|
||||
@ -75,9 +78,10 @@ void FuzzyIndex::mergeIndex( const QString& table, QHash< QString, QMap<quint32,
|
||||
if ( table == "artist" ) idx = &m_artist_ngrams;
|
||||
else if( table == "album" ) idx = &m_album_ngrams;
|
||||
else if( table == "track" ) idx = &m_track_ngrams;
|
||||
else Q_ASSERT(false);
|
||||
else Q_ASSERT( false );
|
||||
|
||||
if( tomerge.size() == 0 ) return;
|
||||
if( tomerge.size() == 0 )
|
||||
return;
|
||||
|
||||
if( idx->size() == 0 )
|
||||
{
|
||||
@ -85,12 +89,13 @@ void FuzzyIndex::mergeIndex( const QString& table, QHash< QString, QMap<quint32,
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach( const QString& ngram, tomerge.keys() )
|
||||
QList<QString> tmk = tomerge.keys();
|
||||
foreach( const QString& ngram, tmk )
|
||||
{
|
||||
|
||||
if( idx->contains( ngram ) )
|
||||
{
|
||||
foreach( quint32 id, tomerge[ngram].keys() )
|
||||
QList<unsigned int> tmkn = tomerge[ngram].keys();
|
||||
foreach( quint32 id, tmkn )
|
||||
{
|
||||
(*idx)[ ngram ][ id ] += tomerge[ngram][id];
|
||||
}
|
||||
@ -101,6 +106,7 @@ void FuzzyIndex::mergeIndex( const QString& table, QHash< QString, QMap<quint32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
qDebug() << Q_FUNC_INFO << table << "merge complete, num items:" << tomerge.size();
|
||||
}
|
||||
|
||||
@ -120,6 +126,7 @@ FuzzyIndex::search( const QString& table, const QString& name )
|
||||
{
|
||||
if( !idx->contains( ngram ) )
|
||||
continue;
|
||||
|
||||
//qDebug() << name_orig << "NGRAM:" << ngram << "candidates:" << (*idx)[ngram].size();
|
||||
QMapIterator<quint32, quint16> iter( (*idx)[ngram] );
|
||||
while( iter.hasNext() )
|
||||
|
@ -12,6 +12,7 @@ class DatabaseImpl;
|
||||
class FuzzyIndex : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit FuzzyIndex( DatabaseImpl &db );
|
||||
|
||||
@ -21,7 +22,7 @@ signals:
|
||||
public slots:
|
||||
void loadNgramIndex();
|
||||
QMap< int, float > search( const QString& table, const QString& name );
|
||||
void mergeIndex( const QString& table, QHash< QString, QMap<quint32, quint16> > tomerge );
|
||||
void mergeIndex( const QString& table, const QHash< QString, QMap<quint32, quint16> >& tomerge );
|
||||
|
||||
private:
|
||||
void loadNgramIndex_helper( QHash< QString, QMap<quint32, quint16> >& idx, const QString& table, unsigned int fromkey = 0 );
|
||||
|
@ -165,6 +165,7 @@ DynamicPlaylist::createNewRevision( const QString& newrev,
|
||||
oldrev,
|
||||
orderedguids,
|
||||
added,
|
||||
entries,
|
||||
type,
|
||||
Static,
|
||||
controls );
|
||||
|
@ -204,7 +204,10 @@ DBSyncConnection::handleMsg( msg_ptr msg )
|
||||
DatabaseCommand *cmd = DatabaseCommand::factory( m, m_source );
|
||||
if ( !cmd )
|
||||
{
|
||||
qDebug() << "UNKNOWN DBOP CMD!";
|
||||
qDebug() << "UNKNOWN DBOP CMD";
|
||||
|
||||
if( !msg->is( Msg::FRAGMENT ) ) // last msg in this batch
|
||||
lastOpApplied();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -215,6 +218,7 @@ DBSyncConnection::handleMsg( msg_ptr msg )
|
||||
changeState( SAVING ); // just DB work left to complete
|
||||
connect( cmd, SIGNAL( finished() ), this, SLOT( lastOpApplied() ) );
|
||||
}
|
||||
|
||||
Database::instance()->enqueue( QSharedPointer<DatabaseCommand>( cmd ) );
|
||||
return;
|
||||
}
|
||||
@ -253,15 +257,11 @@ void
|
||||
DBSyncConnection::sendOps()
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
|
||||
if ( m_lastSentOp.isEmpty() )
|
||||
m_lastSentOp = m_uscache.value( "lastop" ).toString();
|
||||
|
||||
qDebug() << "Will send peer all ops since" << m_lastSentOp;
|
||||
qDebug() << "Will send peer all ops since" << m_uscache.value( "lastop" ).toString();
|
||||
|
||||
source_ptr src = SourceList::instance()->getLocal();
|
||||
|
||||
DatabaseCommand_loadOps* cmd = new DatabaseCommand_loadOps( src, m_lastSentOp );
|
||||
DatabaseCommand_loadOps* cmd = new DatabaseCommand_loadOps( src, m_uscache.value( "lastop" ).toString() );
|
||||
connect( cmd, SIGNAL( done( QString, QString, QList< dbop_ptr > ) ),
|
||||
this, SLOT( sendOpsData( QString, QString, QList< dbop_ptr > ) ) );
|
||||
|
||||
@ -273,8 +273,11 @@ void
|
||||
DBSyncConnection::sendOpsData( QString sinceguid, QString lastguid, QList< dbop_ptr > ops )
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << sinceguid << lastguid << "Num ops to send:" << ops.length();
|
||||
m_lastSentOp = lastguid;
|
||||
|
||||
if ( m_lastSentOp == lastguid )
|
||||
ops.clear();
|
||||
|
||||
m_lastSentOp = lastguid;
|
||||
if( ops.length() == 0 )
|
||||
{
|
||||
sendMsg( Msg::factory( "ok", Msg::DBOP ) );
|
||||
|
@ -100,7 +100,7 @@ Servent::startListening( QHostAddress ha, bool upnp, int port )
|
||||
// last.fm office firewall policy hack
|
||||
// (corp. firewall allows outgoing connections to this port,
|
||||
// so listen on this if you want lastfmers to connect to you)
|
||||
if( qApp->arguments().contains("--porthack") )
|
||||
if( qApp->arguments().contains( "--porthack" ) )
|
||||
{
|
||||
tryport = 3389;
|
||||
pf->remove( tryport );
|
||||
@ -603,13 +603,13 @@ Servent::claimOffer( ControlConnection* cc, const QString &key, const QHostAddre
|
||||
QSharedPointer<QIODevice>
|
||||
Servent::remoteIODeviceFactory( const result_ptr& result )
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << thread() ;
|
||||
qDebug() << Q_FUNC_INFO << thread();
|
||||
QSharedPointer<QIODevice> sp;
|
||||
|
||||
QStringList parts = result->url().mid( QString( "servent://" ).length()).split( "\t" );
|
||||
const QString& sourceName = parts.at( 0 );
|
||||
const QString& fileId = parts.at( 1 );
|
||||
const source_ptr& s = SourceList::instance()->get( sourceName );
|
||||
QStringList parts = result->url().mid( QString( "servent://" ).length() ).split( "\t" );
|
||||
const QString sourceName = parts.at( 0 );
|
||||
const QString fileId = parts.at( 1 );
|
||||
source_ptr s = SourceList::instance()->get( sourceName );
|
||||
if ( s.isNull() )
|
||||
return sp;
|
||||
|
||||
|
@ -91,7 +91,7 @@ public:
|
||||
void reverseOfferRequest( ControlConnection* orig_conn, const QString& key, const QString& theirkey );
|
||||
|
||||
void setExternalAddress( QHostAddress ha, int port );
|
||||
bool visibleExternally() const { return m_externalPort > 0; }
|
||||
bool visibleExternally() const { return m_externalPort > 0 && !m_externalAddress.isNull(); }
|
||||
QHostAddress externalAddress() const { return m_externalAddress; }
|
||||
int externalPort() const { return m_externalPort; }
|
||||
|
||||
|
@ -97,7 +97,7 @@ Pipeline::add( const QList<query_ptr>& qlist, bool prioritized )
|
||||
m_queries_pending.append( qlist );
|
||||
}
|
||||
|
||||
if ( m_index_ready )
|
||||
if ( m_index_ready && m_queries_pending.count() )
|
||||
shuntNext();
|
||||
}
|
||||
|
||||
@ -143,7 +143,10 @@ void
|
||||
Pipeline::shuntNext()
|
||||
{
|
||||
if ( m_queries_pending.isEmpty() )
|
||||
{
|
||||
emit idle();
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
Since resolvers are async, we now dispatch to the highest weighted ones
|
||||
|
@ -53,6 +53,9 @@ public slots:
|
||||
void add( const QList<query_ptr>& qlist, bool prioritized = true );
|
||||
void databaseReady();
|
||||
|
||||
signals:
|
||||
void idle();
|
||||
|
||||
private slots:
|
||||
void shunt( const query_ptr& q );
|
||||
void shuntNext();
|
||||
|
@ -20,14 +20,14 @@ PlaylistEntry::PlaylistEntry() {}
|
||||
PlaylistEntry::~PlaylistEntry() {}
|
||||
|
||||
void
|
||||
PlaylistEntry::setQueryvariant( const QVariant& v )
|
||||
PlaylistEntry::setQueryVariant( const QVariant& v )
|
||||
{
|
||||
m_query = query_ptr( new Query( v ) );
|
||||
}
|
||||
|
||||
|
||||
QVariant
|
||||
PlaylistEntry::queryvariant() const
|
||||
PlaylistEntry::queryVariant() const
|
||||
{
|
||||
return m_query->toVariant();
|
||||
}
|
||||
@ -46,13 +46,13 @@ PlaylistEntry::query() const
|
||||
|
||||
|
||||
source_ptr
|
||||
PlaylistEntry::lastsource() const
|
||||
PlaylistEntry::lastSource() const
|
||||
{
|
||||
return m_lastsource;
|
||||
}
|
||||
|
||||
void
|
||||
PlaylistEntry::setLastsource( source_ptr s )
|
||||
PlaylistEntry::setLastSource( source_ptr s )
|
||||
{
|
||||
m_lastsource = s;
|
||||
}
|
||||
@ -84,6 +84,7 @@ Playlist::Playlist( const source_ptr& src,
|
||||
, m_shared( shared )
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << "1";
|
||||
init();
|
||||
}
|
||||
|
||||
|
||||
@ -103,6 +104,15 @@ Playlist::Playlist( const source_ptr& author,
|
||||
, m_shared( shared )
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << "2";
|
||||
init();
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
Playlist::init()
|
||||
{
|
||||
m_locallyChanged = false;
|
||||
connect( Pipeline::instance(), SIGNAL( idle() ), SLOT( onResolvingFinished() ) );
|
||||
}
|
||||
|
||||
Playlist::~Playlist() {}
|
||||
@ -227,7 +237,7 @@ Playlist::createNewRevision( const QString& newrev, const QString& oldrev, const
|
||||
foreach( plentry_ptr p, entries )
|
||||
orderedguids << p->guid();
|
||||
|
||||
// source making the change (localy user in this case)
|
||||
// source making the change (local user in this case)
|
||||
source_ptr author = SourceList::instance()->getLocal();
|
||||
// command writes new rev to DB and calls setRevision, which emits our signal
|
||||
DatabaseCommand_SetPlaylistRevision* cmd =
|
||||
@ -236,7 +246,9 @@ Playlist::createNewRevision( const QString& newrev, const QString& oldrev, const
|
||||
newrev,
|
||||
oldrev,
|
||||
orderedguids,
|
||||
added );
|
||||
added,
|
||||
entries
|
||||
);
|
||||
Database::instance()->enqueue( QSharedPointer<DatabaseCommand>( cmd ) );
|
||||
}
|
||||
|
||||
@ -253,9 +265,6 @@ Playlist::setRevision( const QString& rev,
|
||||
{
|
||||
if( QThread::currentThread() != thread() )
|
||||
{
|
||||
//qDebug() << "Calling setRevision in correct thread, instead of"
|
||||
// << QThread::currentThread();
|
||||
|
||||
QMetaObject::invokeMethod( this,
|
||||
"setRevision",
|
||||
Qt::BlockingQueuedConnection,
|
||||
@ -263,7 +272,7 @@ Playlist::setRevision( const QString& rev,
|
||||
Q_ARG( QList<QString>, neworderedguids ),
|
||||
Q_ARG( QList<QString>, oldorderedguids ),
|
||||
Q_ARG( bool, is_newest_rev ),
|
||||
QGenericArgument( "QMap< QString,Tomahawk::plentry_ptr >" , (const void*)&addedmap ),
|
||||
QGenericArgument( "QMap< QString,Tomahawk::plentry_ptr >", (const void*)&addedmap ),
|
||||
Q_ARG( bool, applied )
|
||||
);
|
||||
return;
|
||||
@ -274,7 +283,13 @@ Playlist::setRevision( const QString& rev,
|
||||
if( applied )
|
||||
m_currentrevision = rev;
|
||||
pr.applied = applied;
|
||||
|
||||
|
||||
foreach( const plentry_ptr& entry, m_entries )
|
||||
{
|
||||
connect( entry->query().data(), SIGNAL( resultsAdded( QList<Tomahawk::result_ptr> ) ),
|
||||
SLOT( onResultsFound( QList<Tomahawk::result_ptr> ) ), Qt::UniqueConnection );
|
||||
|
||||
}
|
||||
emit revisionLoaded( pr );
|
||||
}
|
||||
|
||||
@ -285,22 +300,16 @@ Playlist::setNewRevision( const QString& rev,
|
||||
bool is_newest_rev,
|
||||
const QMap< QString, Tomahawk::plentry_ptr >& addedmap )
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << (qlonglong)this
|
||||
<< rev << neworderedguids << oldorderedguids
|
||||
<< "isnewest:" << is_newest_rev << addedmap << m_entries;
|
||||
|
||||
|
||||
// build up correctly ordered new list of plentry_ptrs from
|
||||
// existing ones, and the ones that have been added
|
||||
QMap<QString, plentry_ptr> entriesmap;
|
||||
foreach( const plentry_ptr& p, m_entries )
|
||||
entriesmap.insert( p->guid(), p );
|
||||
|
||||
//qDebug() << "Entries map:" << entriesmap;
|
||||
|
||||
QList<plentry_ptr> entries;
|
||||
//qDebug() << "m_entries:" << m_entries.count() << m_entries;
|
||||
|
||||
//qDebug() << "counters:" << neworderedguids.count() << entriesmap.count() << addedmap.count();
|
||||
foreach( const QString& id, neworderedguids )
|
||||
{
|
||||
//qDebug() << "id:" << id;
|
||||
@ -316,7 +325,8 @@ Playlist::setNewRevision( const QString& rev,
|
||||
else if( addedmap.contains( id ) )
|
||||
{
|
||||
entries.append( addedmap.value( id ) );
|
||||
if( is_newest_rev ) m_entries.append( addedmap.value( id ) );
|
||||
if( is_newest_rev )
|
||||
m_entries.append( addedmap.value( id ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -343,12 +353,12 @@ Playlist::setNewRevision( const QString& rev,
|
||||
if( is_newest_rev )
|
||||
{
|
||||
//qDebug() << "Removing from m_entries" << remid;
|
||||
for( int k = 0 ; k<m_entries.length(); ++k )
|
||||
for( int k = 0 ; k < m_entries.length(); ++k )
|
||||
{
|
||||
if( m_entries.at(k)->guid() == remid )
|
||||
if( m_entries.at( k )->guid() == remid )
|
||||
{
|
||||
//qDebug() << "removed at " << k;
|
||||
m_entries.removeAt(k);
|
||||
//qDebug() << "removed at" << k;
|
||||
m_entries.removeAt( k );
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -358,10 +368,6 @@ Playlist::setNewRevision( const QString& rev,
|
||||
|
||||
pr.added = addedmap.values();
|
||||
|
||||
//qDebug() << "Revision set:" << rev
|
||||
// << "added" << pr.added.size()
|
||||
// << "removed" << pr.removed.size()
|
||||
// << "total entries" << m_entries.size();
|
||||
|
||||
pr.newlist = entries;
|
||||
return pr;
|
||||
@ -373,7 +379,8 @@ Playlist::author()
|
||||
return m_source;
|
||||
}
|
||||
|
||||
void Playlist::resolve()
|
||||
void
|
||||
Playlist::resolve()
|
||||
{
|
||||
QList< query_ptr > qlist;
|
||||
foreach( const plentry_ptr& p, m_entries )
|
||||
@ -383,6 +390,24 @@ void Playlist::resolve()
|
||||
Pipeline::instance()->add( qlist );
|
||||
}
|
||||
|
||||
void
|
||||
Playlist::onResultsFound( const QList<Tomahawk::result_ptr>& results )
|
||||
{
|
||||
Query* query = qobject_cast<Query*>( sender() );
|
||||
m_locallyChanged = true;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Playlist::onResolvingFinished()
|
||||
{
|
||||
if ( m_locallyChanged )
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
m_locallyChanged = false;
|
||||
createNewRevision( currentrevision(), currentrevision(), m_entries );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Playlist::addEntry( const query_ptr& query, const QString& oldrev )
|
||||
@ -440,7 +465,7 @@ Playlist::newEntries( const QList< plentry_ptr >& entries )
|
||||
QList<plentry_ptr> added;
|
||||
foreach( plentry_ptr p, entries )
|
||||
{
|
||||
if( !currentguids.contains(p->guid()) )
|
||||
if( !currentguids.contains( p->guid() ) )
|
||||
added << p;
|
||||
}
|
||||
return added;
|
||||
|
@ -24,10 +24,10 @@ class DLLEXPORT PlaylistEntry : public QObject
|
||||
Q_OBJECT
|
||||
Q_PROPERTY( QString guid READ guid WRITE setGuid )
|
||||
Q_PROPERTY( QString annotation READ annotation WRITE setAnnotation )
|
||||
Q_PROPERTY( QString resulthint READ resulthint WRITE setResulthint )
|
||||
Q_PROPERTY( QString resulthint READ resultHint WRITE setResultHint )
|
||||
Q_PROPERTY( unsigned int duration READ duration WRITE setDuration )
|
||||
Q_PROPERTY( unsigned int lastmodified READ lastmodified WRITE setLastmodified )
|
||||
Q_PROPERTY( QVariant query READ queryvariant WRITE setQueryvariant )
|
||||
Q_PROPERTY( QVariant query READ queryVariant WRITE setQueryVariant )
|
||||
|
||||
public:
|
||||
PlaylistEntry();
|
||||
@ -37,8 +37,8 @@ public:
|
||||
const Tomahawk::query_ptr& query() const;
|
||||
|
||||
// I wish Qt did this for me once i specified the Q_PROPERTIES:
|
||||
void setQueryvariant( const QVariant& v );
|
||||
QVariant queryvariant() const;
|
||||
void setQueryVariant( const QVariant& v );
|
||||
QVariant queryVariant() const;
|
||||
|
||||
QString guid() const { return m_guid; }
|
||||
void setGuid( const QString& s ) { m_guid = s; }
|
||||
@ -46,8 +46,8 @@ public:
|
||||
QString annotation() const { return m_annotation; }
|
||||
void setAnnotation( const QString& s ) { m_annotation = s; }
|
||||
|
||||
QString resulthint() const { return m_resulthint; }
|
||||
void setResulthint( const QString& s ) { m_resulthint= s; }
|
||||
QString resultHint() const { return m_resulthint; }
|
||||
void setResultHint( const QString& s ) { m_resulthint= s; }
|
||||
|
||||
unsigned int duration() const { return m_duration; }
|
||||
void setDuration( unsigned int i ) { m_duration = i; }
|
||||
@ -55,8 +55,8 @@ public:
|
||||
unsigned int lastmodified() const { return m_lastmodified; }
|
||||
void setLastmodified( unsigned int i ) { m_lastmodified = i; }
|
||||
|
||||
source_ptr lastsource() const;
|
||||
void setLastsource( source_ptr s );
|
||||
source_ptr lastSource() const;
|
||||
void setLastSource( source_ptr s );
|
||||
|
||||
private:
|
||||
QString m_guid;
|
||||
@ -188,8 +188,14 @@ protected:
|
||||
const QMap< QString, Tomahawk::plentry_ptr >& addedmap );
|
||||
|
||||
QList<plentry_ptr> addEntriesInternal( const QList<Tomahawk::query_ptr>& queries );
|
||||
|
||||
private slots:
|
||||
void onResultsFound( const QList<Tomahawk::result_ptr>& results );
|
||||
void onResolvingFinished();
|
||||
|
||||
private:
|
||||
Playlist();
|
||||
void init();
|
||||
|
||||
source_ptr m_source;
|
||||
QString m_currentrevision;
|
||||
@ -198,6 +204,7 @@ private:
|
||||
bool m_shared;
|
||||
|
||||
QList< plentry_ptr > m_entries;
|
||||
bool m_locallyChanged;
|
||||
|
||||
};
|
||||
|
||||
|
@ -45,12 +45,13 @@ SourceList::add( const Tomahawk::source_ptr& s )
|
||||
Q_ASSERT( s->id() );
|
||||
m_sources_id2name.insert( s->id(), s->userName() );
|
||||
}
|
||||
qDebug() << "SourceList::add(" << s->userName() << "), total sources now:" << m_sources.size();
|
||||
if( s->isLocal() )
|
||||
{
|
||||
Q_ASSERT( m_local.isNull() );
|
||||
m_local = s;
|
||||
}
|
||||
|
||||
qDebug() << "SourceList::add(" << s->userName() << "), total sources now:" << m_sources.size();
|
||||
}
|
||||
|
||||
emit sourceAdded( s );
|
||||
@ -86,7 +87,7 @@ SourceList::remove( Tomahawk::Source* s )
|
||||
void
|
||||
SourceList::removeAllRemote()
|
||||
{
|
||||
foreach( source_ptr s, m_sources )
|
||||
foreach( const source_ptr& s, m_sources )
|
||||
{
|
||||
if( s != m_local )
|
||||
remove( s );
|
||||
|
@ -26,9 +26,10 @@ public:
|
||||
void removeAllRemote();
|
||||
|
||||
QList<Tomahawk::source_ptr> sources() const;
|
||||
unsigned int count() const;
|
||||
|
||||
Tomahawk::source_ptr get( const QString& username ) const;
|
||||
Tomahawk::source_ptr get( unsigned int id ) const;
|
||||
unsigned int count() const;
|
||||
|
||||
signals:
|
||||
void sourceAdded( const Tomahawk::source_ptr& );
|
||||
|
@ -73,7 +73,7 @@ PlaylistModel::loadPlaylist( const Tomahawk::playlist_ptr& playlist )
|
||||
{
|
||||
int c = rowCount( QModelIndex() );
|
||||
|
||||
qDebug() << "starting loading" << playlist->title();
|
||||
qDebug() << "Starting loading" << playlist->title();
|
||||
emit loadingStarts();
|
||||
emit beginInsertRows( QModelIndex(), c, c + entries.count() - 1 );
|
||||
|
||||
@ -133,6 +133,7 @@ PlaylistModel::loadHistory( const Tomahawk::source_ptr& source, unsigned int amo
|
||||
setReadOnly( true );
|
||||
|
||||
DatabaseCommand_PlaybackHistory* cmd = new DatabaseCommand_PlaybackHistory( source );
|
||||
cmd->setLimit( amount );
|
||||
|
||||
connect( cmd, SIGNAL( tracks( QList<Tomahawk::query_ptr> ) ),
|
||||
SLOT( onTracksAdded( QList<Tomahawk::query_ptr> ) ), Qt::QueuedConnection );
|
||||
|
@ -131,6 +131,7 @@ SourceTreeItemWidget::onLoadingStateChanged( DBSyncConnection::State newstate, D
|
||||
void
|
||||
SourceTreeItemWidget::onPlaybackStarted( const Tomahawk::query_ptr& query )
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << query->toString();
|
||||
ui->activityLabel->setText( tr( "Playing: %1 by %2" ).arg( query->track() ).arg( query->artist() ) );
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user