summaryrefslogtreecommitdiffstats
path: root/amarok/src/collectiondb.h
blob: 16ab29a6a953b798113a4474d2ff471b5ed7b72c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
// (c) 2004 Mark Kretschmann <markey@web.de>
// (c) 2004 Christian Muehlhaeuser <chris@chris.de>
// (c) 2004 Sami Nieminen <sami.nieminen@iki.fi>
// (c) 2005 Ian Monroe <ian@monroe.nu>
// (c) 2005 Jeff Mitchell <kde-dev@emailgoeshere.com>
// (c) 2005 Isaiah Damron <xepo@trifault.net>
// (c) 2005 Alexandre Pereira de Oliveira <aleprj@gmail.com>
// (c) 2006 Jonas Hurrelmann <j@outpo.st>
// (c) 2006 Shane King <kde@dontletsstart.com>
// (c) 2006 Peter C. Ndikuwera <pndiku@gmail.com>
// See COPYING file for licensing information.

#ifndef AMAROK_COLLECTIONDB_H
#define AMAROK_COLLECTIONDB_H

#include "engineobserver.h"
#include "threadmanager.h" //baseclass
#include "amarok_export.h"

#include <kurl.h>
#include <tqdir.h>            //stack allocated
#include <tqdatetime.h>
#include <tqimage.h>
#include <tqmutex.h>
#include <tqobject.h>         //baseclass
#include <tqptrqueue.h>       //baseclass
#include <tqsemaphore.h>      //stack allocated
#include <tqstringlist.h>     //stack allocated
#include <tqptrvector.h>
#include <tqthread.h>
#include <tqvaluestack.h>

namespace TDEIO { class Job; }

class DbConnection;
class CoverFetcher;
class MetaBundle;
class OrganizeCollectionDialog;
class PodcastChannelBundle;
class PodcastEpisodeBundle;
class TQListViewItem;
class Scrobbler;

class DbConfig
{};


class SqliteConfig : public DbConfig
{
    public:
        SqliteConfig( const TQString& /* dbfile */ );

        TQString dbFile() const { return m_dbfile; }

    private:
        TQString m_dbfile;
};


class MySqlConfig : public DbConfig
{
    public:
        MySqlConfig(
            const TQString& /* host */,
            const int /* port */,
            const TQString& /* database */,
            const TQString& /* username */,
            const TQString& /* password */);

        TQString host() const { return m_host; }
        int port() const { return m_port; }
        TQString database() const { return m_database; }
        TQString username() const { return m_username; }
        TQString password() const { return m_password; }

    private:
        TQString m_host;
        int m_port;
        TQString m_database;
        TQString m_username;
        TQString m_password;
};


class PostgresqlConfig : public DbConfig
{
    public:
        PostgresqlConfig(
            const TQString& /* host */,
            const int /* port */,
            const TQString& /* database */,
            const TQString& /* username */,
            const TQString& /* password */);

        TQString host() const { return m_host; }
        int port() const { return m_port; }
        TQString database() const { return m_database; }
        TQString username() const { return m_username; }
        TQString password() const { return m_password; }

    private:
        TQString m_host;
        int m_port;
        TQString m_database;
        TQString m_username;
        TQString m_password;
};


class DbConnection
{
    public:
        enum DbConnectionType { sqlite = 0, mysql = 1, postgresql = 2 };

        DbConnection();
        virtual ~DbConnection() {}

        virtual TQStringList query( const TQString& /* statement */, bool suppressDebug ) = 0;
        virtual int insert( const TQString& /* statement */, const TQString& /* table */ ) = 0;
        bool isInitialized() const { return m_initialized; }
        virtual bool isConnected() const = 0;
        virtual TQString lastError() const { return "None"; }

    protected:
        bool m_initialized;
};


typedef struct sqlite3 sqlite3;
typedef struct sqlite3_context sqlite3_context;
typedef struct Mem sqlite3_value;

class SqliteConnection : public DbConnection
{
    public:
        SqliteConnection( const SqliteConfig* /* config */ );
       ~SqliteConnection();

        TQStringList query( const TQString& /* statement */, bool suppressDebug = false );
        int insert( const TQString& /* statement */, const TQString& /* table */ );
        bool isConnected()const { return true; }
    private:
        static void sqlite_rand( sqlite3_context *context, int /*argc*/, sqlite3_value ** /*argv*/ );
        static void sqlite_power( sqlite3_context *context, int argc, sqlite3_value **argv );
        static void sqlite_like_new( sqlite3_context *context, int argc, sqlite3_value **argv );

        sqlite3* m_db;
};


#ifdef USE_MYSQL
typedef struct MYSQL_STRUCT MYSQL;

class MySqlConnection : public DbConnection
{
    public:
        MySqlConnection( const MySqlConfig* /* config */ );
       ~MySqlConnection();

        TQStringList query( const TQString& /* statement */, bool suppressDebug = false );
        int insert( const TQString& /* statement */, const TQString& /* table */ );
        bool isConnected()const { return m_connected; }
        TQString lastError() const { return m_error; }
    private:
        void setMysqlError();
        MYSQL* m_db;
        bool m_connected;
        TQString m_error;
};
#endif


#ifdef USE_POSTGRESQL
typedef struct pg_conn PGconn;

class PostgresqlConnection : public DbConnection
{
    public:
        PostgresqlConnection( const PostgresqlConfig* /* config */ );
       ~PostgresqlConnection();

        TQStringList query( const TQString& /* statement */, bool suppressDebug = false );
        int insert( const TQString& /* statement */, const TQString& /* table */ );
        bool isConnected()const { return m_connected; }
        TQString lastError() const { return m_error; }
    private:
        void setPostgresqlError();
        PGconn* m_db;
        bool m_connected;
        TQString m_error;
};
#endif


class LIBAMAROK_EXPORT CollectionDB : public TQObject, public EngineObserver
{
    Q_OBJECT
    

    friend class SimilarArtistsInsertionJob;

    signals:
        void scanStarted();
        void scanDone( bool changed );
        void databaseEngineChanged();

        void databaseUpdateDone();

        void scoreChanged( const TQString &url, float score );
        void ratingChanged( const TQString &url, int rating );
        void labelsChanged( const TQString &url );
        void fileMoved( const TQString &srcUrl, const TQString &dstUrl );
        void fileMoved( const TQString &srcUrl, const TQString &dstUrl, const TQString &uniqueid );
        void fileDeleted( const TQString &absPath );
        void fileDeleted( const TQString &absPath, const TQString &uniqueid );
        void fileAdded( const TQString &absPath );
        void fileAdded( const TQString &absPath, const TQString &uniqueid );
        void filesAdded( const TQMap<TQString,TQString> &map );
        void uniqueIdChanged( const TQString &url, const TQString &originalid, const TQString &newid );
        void coverChanged( const TQString &artist, const TQString &album ); //whenever a cover changes
        void coverFetched( const TQString &artist, const TQString &album ); //only when fetching from amazon
        void coverRemoved( const TQString &artist, const TQString &album );
        void coverFetcherError( const TQString &error );

        void similarArtistsFetched( const TQString &artist );
        void tagsChanged( const MetaBundle &bundle );
        void tagsChanged( const TQString &oldArtist, const TQString &oldAlbum );
        void imageFetched( const TQString &remoteURL ); //for fetching remote podcast images

        // Cross-thread communication
        void startScrobblerSimilarArtistsFetch(const TQString &);

    public:
        CollectionDB();
        ~CollectionDB();

        static CollectionDB *instance();

        /**
         * performs all initializations which require directory or URL data stored in the
         * database.
         */
        void initDirOperations();

        enum labelTypes { typeUser = 1 };           //add new types add the end!

        TQString escapeString(TQString string ) const
        {
            return
            #ifdef USE_MYSQL
                // We have to escape "\" for mysql, but can't do so for sqlite
                ( m_dbConnType == DbConnection::mysql )
                ? string.replace("\\", "\\\\").replace( '\'', "''" ) :
            #endif
                  string.replace( '\'', "''" );
        }

        TQString boolT() const { if (getDbConnectionType() == DbConnection::postgresql) return "true"; else return "1"; }
        TQString boolF() const { if (getDbConnectionType() == DbConnection::postgresql) return "false"; else return "0"; }
        inline bool boolFromSql( const TQString &b ) { return ( b == boolT() || b == "t" ); }
        //textColumnType should be used for normal strings, which need to be compared
        //either case-sensitively or -insensitively
        TQString textColumnType( int length=255 ) const { if ( getDbConnectionType() == DbConnection::postgresql ) return "TEXT"; else return TQString("VARCHAR(%1)").arg(length); }
        //exactTextColumnType should be used for strings that must be stored exactly, such
        //as URLs (necessary for holding control chars etc. if present in URL), except for
        //trailing spaces. Comparisions should always be done case-sensitively.
        //As we create indices on these columns, we have to restrict them to
        //<= 255 chars for mysql < 5.0.3
        TQString exactTextColumnType( int length=1024 ) const { if ( getDbConnectionType() == DbConnection::mysql ) return TQString( "VARBINARY(%1)" ).arg( length>255 ? 255 : length ); else return textColumnType( length ); }
        // We might consider using LONGTEXT type, as some lyrics could be VERY long..???
        TQString longTextColumnType() const { if ( getDbConnectionType() == DbConnection::postgresql ) return "TEXT"; else return "TEXT"; }
        TQString randomFunc() const { if ( getDbConnectionType() == DbConnection::postgresql ) return "random()"; else return "RAND()"; }

        inline static TQString exactCondition( const TQString &right );
        static TQString likeCondition( const TQString &right, bool anyBegin=false, bool anyEnd=false );
        int getType() { return getDbConnectionType(); }

        //sql helper methods
        TQStringList query( const TQString& statement, bool suppressDebug = false );
        int insert( const TQString& statement, const TQString& table );

        /**
         * TODO: write doc
         * @param showAll
         * @return a string which can be appended to an existing sql where statement
         */
        TQString deviceidSelection( const bool showAll = false );

        /**
         * converts the result of a query which contains a deviceid and a relative path
         * to a list of absolute paths. the order of entries in each result row must be
         * deviceid first, relative path second.
         * @param result the result of the sql query, deviceid first, relative path second
         * @return a list of urls
         */
        TQStringList URLsFromQuery( const TQStringList &result ) const;

        /**
         * converts the result list of a amarok-sql query to a list of urls
         */
        KURL::List URLsFromSqlDrag( const TQStringList &values ) const;

        //table management methods
        bool isEmpty();
        bool isValid();
        TQString adminValue( TQString noption );
        void setAdminValue( TQString noption, TQString value );
        void createTables( const bool temporary = false );
        void createIndices(  );
        void createPermanentIndices();
        void dropTables( const bool temporary = false);
        void clearTables( const bool temporary = false);
        void copyTempTables(  );
        void prepareTempTables();

        uint artistID( TQString value, bool autocreate = true, const bool temporary = false, bool exact = true );
        uint composerID( TQString value, bool autocreate = true, const bool temporary = false, bool exact = true );
        uint albumID( TQString value, bool autocreate = true, const bool temporary = false, bool exact = true );
        uint genreID( TQString value, bool autocreate = true, const bool temporary = false, bool exact = true );
        uint yearID( TQString value, bool autocreate = true, const bool temporary = false, bool exact = true );

        bool isDirInCollection( TQString path );
        bool isFileInCollection( const TQString &url );
        TQString getURL( const MetaBundle &bundle );
        void removeDirFromCollection( TQString path );
        void removeSongsInDir( TQString path, TQMap<TQString,TQString> *tagsRemoved = 0 );
        void removeSongs( const KURL::List& urls );
        void updateDirStats( TQString path, const long datetime, const bool temporary = false );

        //song methods
        bool addSong( MetaBundle* bundle, const bool incremental = false );
        void aftCheckPermanentTables( const TQString &currdeviceid, const TQString &currid, const TQString &currurl );
        void doAFTStuff( MetaBundle *bundle, const bool tempTables = true );
        void emitFileAdded( const TQString &absPath,
                            const TQString &uniqueid = TQString() );
        void emitFilesAdded( const TQMap<TQString,TQString> &map ) { emit filesAdded( map ); }
        void emitFileDeleted( const TQString &absPath,
                              const TQString &uniqueid = TQString() );
        bool newUniqueIdForFile( const TQString &path );
        bool removeUniqueIdFromFile( const TQString &path );
        TQString urlFromUniqueId( const TQString &id );
        TQString uniqueIdFromUrl( const KURL &url );

        //podcast methods
        /// Insert a podcast channel into the database.  If @param replace is true, replace the row
        /// use updatePodcastChannel() always in preference
        bool addPodcastChannel( const PodcastChannelBundle &pcb, const bool &replace=false );
        /// Insert a podcast episode into the database.  If @param idToUpdate is provided, replace the row
        /// use updatePodcastEpisode() always in preference
        int  addPodcastEpisode( const PodcastEpisodeBundle &episode, const int idToUpdate=0 );
        int  addPodcastFolder( const TQString &name, const int parent_id=0, const bool isOpen=false );
        TQValueList<PodcastChannelBundle> getPodcastChannels();
        PodcastEpisodeBundle getPodcastEpisodeById( int id );
        TQValueList<PodcastEpisodeBundle> getPodcastEpisodes( const KURL &parent, bool newOnly=false, int limit=-1 );
        void removePodcastChannel( const KURL &url ); // will remove all episodes too
        void removePodcastEpisode( const int id );
        void removePodcastFolder( const int id );
        void updatePodcastChannel( const PodcastChannelBundle &b );
        void updatePodcastEpisode( const int id, const PodcastEpisodeBundle &b );
        void updatePodcastFolder( const int folder_id, const TQString &name, const int parent_id=0, const bool isOpen=false );
        // these return false when no bundle was available
        bool getPodcastChannelBundle( const KURL &url, PodcastChannelBundle *channel );
        bool getPodcastEpisodeBundle( const KURL &url, PodcastEpisodeBundle *channel );

        MetaBundle bundleFromQuery( TQStringList::const_iterator *iter );
        /**
         * The @p bundle parameter's url() will be looked up in the Collection
         * @param bundle this will be filled in with tags for you
         * @return true if in the collection
         */
        bool bundleForUrl( MetaBundle* bundle );
        TQValueList<MetaBundle> bundlesByUrls( const KURL::List& urls );
        void addAudioproperties( const MetaBundle& bundle );

        //Helper function for updateTags
        void deleteRedundantName( const TQString &table, const TQString &id );

        void deleteAllRedundant( const TQString &table );

        void updateTags( const TQString &url, const MetaBundle &bundle, const bool updateView = true);
        void updateURL( const TQString &url, const bool updateView = true );
        TQString getUniqueId( const TQString &url );

        //statistics methods
        void addSongPercentage( const TQString &url, float percentage,
                const TQString &reason, const TQDateTime *playtime = 0 );
        float getSongPercentage( const TQString &url );
        int getSongRating( const TQString &url );
        void setSongPercentage( const TQString &url, float percentage );
        void setSongRating( const TQString &url, int percentage, bool toggleHalf = false );
        int getPlayCount( const TQString &url );
        TQDateTime getFirstPlay( const TQString &url );
        TQDateTime getLastPlay( const TQString &url );
        void migrateFile( const TQString &oldURL, const TQString &newURL );
        bool moveFile( const TQString &src, const TQString &dest, bool overwrite, bool copy = false );
        bool organizeFile( const KURL &src, OrganizeCollectionDialog &dialog, bool copy );

        //artist methods
        TQStringList similarArtists( const TQString &artist, uint count );

        //album methods
        void checkCompilations( const TQString &path, const bool temporary = false );
        void setCompilation( const KURL::List &urls, bool enabled, bool updateView );
        TQString albumSongCount( const TQString &artist_id, const TQString &album_id );
        bool albumIsCompilation( const TQString &album_id );
        void sanitizeCompilations();

        //label methods
        TQStringList getLabels( const TQString &url, const uint type );
        void removeLabels( const TQString &url, const TQStringList &labels, const uint type );
        bool addLabel( const TQString &url, const TQString &label, const TQString &uid, const uint type );
        void setLabels( const TQString &url, const TQStringList &labels, const TQString &uid, const uint type );

        void cleanLabels();

        TQStringList favoriteLabels( int type = CollectionDB::typeUser, int count = 10 );

        //list methods
        TQStringList artistList( bool withUnknowns = true, bool withCompilations = true );
        TQStringList composerList( bool withUnknowns = true, bool withCompilations = true );
        TQStringList albumList( bool withUnknowns = true, bool withCompilations = true );
        TQStringList genreList( bool withUnknowns = true, bool withCompilations = true );
        TQStringList yearList( bool withUnknowns = true, bool withCompilations = true );
        TQStringList labelList();

        TQStringList albumListOfArtist( const TQString &artist, bool withUnknown = true, bool withCompilations = true );
        TQStringList artistAlbumList( bool withUnknown = true, bool withCompilations = true );

        TQStringList albumTracks( const TQString &artist_id, const TQString &album_id );
        TQStringList albumDiscTracks( const TQString &artist_id, const TQString &album_id, const TQString &discNumber );
        TQStringList artistTracks( const TQString &artist_id );

        //cover management methods
        /** Returns the image from a given URL, network-transparently.
         * You must run TDEIO::NetAccess::removeTempFile( tmpFile ) when you are finished using the image;
         **/
        static TQImage fetchImage( const KURL& url, TQString &tmpFile );
        /** Saves images located on the user's filesystem */
        bool setAlbumImage( const TQString& artist, const TQString& album, const KURL& url );
        /** Saves images obtained from CoverFetcher */
        bool setAlbumImage( const TQString& artist, const TQString& album, TQImage img, const TQString& amazonUrl = TQString(), const TQString& asin = TQString() );

        TQString findAmazonImage( const TQString &artist, const TQString &album, const uint width = 1 );
        TQString findDirectoryImage( const TQString& artist, const TQString& album, uint width = 0 );
        TQString findEmbeddedImage( const TQString& artist, const TQString& album, uint width = 1 );
        TQString findMetaBundleImage( const MetaBundle &trackInformation, const uint = 1 );

        /// ensure the sql only return urls to tracks for efficiency
        static TQPixmap createDragPixmapFromSQL( const TQString &sql, TQString textOverRide=TQString() );
        static TQPixmap createDragPixmap( const KURL::List &urls, TQString textOverRide=TQString() );
        static const int DRAGPIXMAP_OFFSET_X = -12;
        static const int DRAGPIXMAP_OFFSET_Y = -28;

        /*
         * Retrieves the path to the local copy of the image pointed to by url,
         * initiates fetching of the remote image if necessary.
         * @param width the size of the image. 0 == full size, 1 == preview size
         */
        TQString podcastImage( const MetaBundle &bundle, const bool withShadow = false, uint width = 1 );
        TQString podcastImage( const TQString &remoteURL, const bool withShadow = false, uint width = 1 );

        /**
         * Retrieves the path to the image for the album of the requested item
         * @param width the size of the image. 0 == full size, 1 == preview size
         * @param embedded if not NULL, sets a bool indicating whether the path is an embedded image
         */
        TQString albumImage( const MetaBundle &trackInformation, const bool withShadow = false, uint width = 1, bool* embedded = 0 );
        TQString albumImage( const uint artist_id, const uint album_id, const bool withShadow = false, uint width = 1, bool* embedded = 0 );
        TQString albumImage( const TQString &artist, const TQString &album, const bool withShadow = false, uint width = 1, bool* embedded = 0 );
        TQMap<TQListViewItem*, CoverFetcher*> * getItemCoverMap() { return itemCoverMap; }
        TQMutex * getItemCoverMapMutex() { return itemCoverMapMutex; }

        bool removeAlbumImage( const uint artist_id, const uint album_id );
        bool removeAlbumImage( const TQString &artist, const TQString &album );

        static TQString makeShadowedImage( const TQString& albumImage, bool cache = true );

        //local cover methods
        void addImageToAlbum( const TQString& image, TQValueList< TQPair<TQString, TQString> > info, const bool temporary );
        TQString notAvailCover( const bool withShadow = false, int width = 1 );

        //embedded cover methods
        void addEmbeddedImage( const TQString& path, const TQString& hash, const TQString& description );
        void removeOrphanedEmbeddedImages();

        void applySettings();

        void setLyrics( const TQString& url, const TQString& lyrics, const TQString &uniqueid = TQString() );
        TQString getLyrics( const TQString& url );

        /** Remove from the amazon table the item with the specified md5sum **/
        void removeInvalidAmazonInfo( const TQString& md5sum );
        void newAmazonReloadDate( const TQString& asin, const TQString& locale, const TQString& md5sum );
        TQStringList staleImages();

        DbConnection::DbConnectionType getDbConnectionType() const { return m_dbConnType; }
        bool isConnected();
        void releasePreviousConnection(TQThread *currThread);

        void invalidateArtistAlbumCache() { m_validArtistCache=false; m_validComposerCache=false; m_validAlbumCache=false; };

        void vacuum();

        /**
        * Cancel the underlying move/copy file action
        */
        void cancelMovingFileJob();

    protected:
        TQCString md5sum( const TQString& artist, const TQString& album, const TQString& file = TQString() );
        void engineTrackEnded( int finalPosition, int trackLength, const TQString &reason );
        /** Manages regular folder monitoring scan */
        void timerEvent( TQTimerEvent* e );

    public slots:
        void fetchCover( TQWidget* parent, const TQString& artist, const TQString& album, bool noedit, TQListViewItem* item = 0 );
        void scanMonitor();
        void startScan();
        void stopScan();
        void scanModifiedDirs();
        void disableAutoScoring( bool disable = true ) { m_autoScoring = !disable; }

        void checkDatabase();

    private slots:
        void dirDirty( const TQString& path );
        void coverFetcherResult( CoverFetcher* );
        void similarArtistsFetched( const TQString& artist, const TQStringList& suggestions );
        void fileOperationResult( TDEIO::Job *job ); // moveFile depends on it
        void podcastImageResult( TDEIO::Job *job ); //for fetching remote podcast images
        void aftMigratePermanentTablesUrl( const TQString& oldUrl, const TQString& newUrl, const TQString& uniqueid ); //AFT-enable stats
        void aftMigratePermanentTablesUniqueId( const TQString& url, const TQString& oldid, const TQString& newid );

    private:
        //bump DATABASE_VERSION whenever changes to the table structure are made.
        // This erases tags, album, artist, composer, genre, year, images, embed, directory and related_artists tables.
        static const int DATABASE_VERSION = 35;
        // Persistent Tables hold data that is somehow valuable to the user, and can't be erased when rescaning.
        // When bumping this, write code to convert the data!
        static const int DATABASE_PERSISTENT_TABLES_VERSION = 19;
        // Bumping this erases stats table. If you ever need to, write code to convert the data!
        static const int DATABASE_STATS_VERSION = 12;
        // When bumping this, you should provide code to convert the data.
        static const int DATABASE_PODCAST_TABLES_VERSION = 2;
        static const int DATABASE_AFT_VERSION = 2;
        // persistent table. you should provide code to convert the data when bumping this
        static const int DATABASE_DEVICES_VERSION = 1;

        static const int MONITOR_INTERVAL = 60; //sec

        static TQDir largeCoverDir();
        static TQDir tagCoverDir();
        static TQDir cacheCoverDir();

        void initialize();
        void destroy();
        DbConnection* getMyConnection();

        //helper methods which perform updates of amarok's database
        void updateStatsTables();
        void updatePersistentTables();
        void updatePodcastTables();

        //A dirty hack to preserve Group By settings in Collection Browser after addition
        //of Composer table
        void updateGroupBy();

        void customEvent( TQCustomEvent * );

        // helpers for embedded images
        TQString loadHashFile( const TQCString& hash, uint width );
        bool extractEmbeddedImage( const MetaBundle &trackInformation, TQCString& hash );

        //general management methods
        void createStatsTable();
        void dropStatsTable();
        void createPersistentTables();
        void dropPersistentTables();
        void createPodcastTables();
        void dropPodcastTables();
        void createDevicesTable();
        void dropDevicesTable();

        //Archived forms of the above. useful for providing a linear upgrade routine that
        //stays the same
        void createStatsTableV8();
        void createStatsTableV10( bool temp );
        void dropStatsTableV1();
        void createPersistentTablesV12();
        void createPersistentTablesV14( bool temp );
        void dropPersistentTablesV14();
        void createPodcastTablesV2( bool temp );
        void dropPodcastTablesV2();


        TQCString makeWidthKey( uint width );
        TQString artistValue( uint id );
        TQString composerValue( uint id );
        TQString albumValue( uint id );
        TQString genreValue( uint id );
        TQString yearValue( uint id );

        //These should be avoided as they will be slow and potentially unsafe.
        //Use the Exact version where possible (faster and safer).
        //To convert output from Exact version from TQString to uint, use .toUInt()
        uint IDFromValue( TQString name, TQString value, bool autocreate = true, const bool temporary = false );
        TQString IDFromExactValue(  TQString table, TQString value, bool autocreate = true, bool temporary = false );
        TQString valueFromID( TQString table, uint id );

        //member variables
        TQString m_amazonLicense;
        bool    m_validArtistCache;
        bool    m_validComposerCache;
        bool    m_validAlbumCache;
        TQString m_cacheArtist[2];
        uint    m_cacheArtistID[2];
        TQString m_cacheComposer[2];
        uint    m_cacheComposerID[2];
        TQString m_cacheAlbum[2];
        uint    m_cacheAlbumID[2];

        bool m_monitor;
        bool m_autoScoring;

        static TQMap<TQListViewItem*, CoverFetcher*> *itemCoverMap;
        static TQMutex *itemCoverMapMutex;
        TQImage m_noCover, m_shadowImage;

        static TQMap<TQThread *, DbConnection *> *threadConnections;
        static TQMutex *connectionMutex;
        DbConnection::DbConnectionType m_dbConnType;
        DbConfig *m_dbConfig;

        //organize files stuff
        bool m_waitForFileOperation;
        bool m_fileOperationFailed;
        bool m_scanInProgress;
        bool m_rescanRequired;

        TQStringList m_aftEnabledPersistentTables;

        // Cancel move/copy job
        bool m_moveFileJobCancelled;

        // for handling podcast image url redirects
        TQMap<TDEIO::Job *, TQString> m_podcastImageJobs;

        // protect against multiple simultaneous queries/inserts
        TQMutex m_mutex;
};

#ifdef Q_MOC_RUN
// MOC_SKIP_BEGIN
class INotify : public JobBase
// MOC_SKIP_END
#else // Q_MOC_RUN
class INotify : public ThreadManager::DependentJob
#endif // Q_MOC_RUN
{
    Q_OBJECT
    

    public:
        INotify( CollectionDB *parent, int fd );
        ~INotify();

        static INotify *instance() { return s_instance; }

        bool watchDir( const TQString directory );
        int fd() { return m_fd; }

    private:
        virtual bool doJob();

        CollectionDB* m_parent;
        int m_fd;

        static INotify* s_instance;
};


class QueryBuilder
{
    public:
        //attributes:
        enum qBuilderTables  { tabAlbum = 1, tabArtist = 2, tabComposer = 4, tabGenre = 8, tabYear = 16, tabSong = 64,
                               tabStats = 128, tabLyrics = 256, tabPodcastChannels = 512,
                               tabPodcastEpisodes = 1024, tabPodcastFolders = 2048,
                               tabDevices = 4096, tabLabels = 8192
                               /* dummy table for filtering */, tabDummy = 0 };
        enum qBuilderOptions { optNoCompilations = 1, optOnlyCompilations = 2, optRemoveDuplicates = 4,
                               optRandomize = 8,
                               optShowAll = 16 /* get all songs, not just mounted ones */ };
        /* This has been an enum in the past, but 32 bits wasn't enough anymore :-( */
        static const TQ_INT64 valDummy         = 0;
        static const TQ_INT64 valID            = 1LL << 0;
        static const TQ_INT64 valName          = 1LL << 1;
        static const TQ_INT64 valURL           = 1LL << 2;
        static const TQ_INT64 valTitle         = 1LL << 3;
        static const TQ_INT64 valTrack         = 1LL << 4;
        static const TQ_INT64 valScore         = 1LL << 5;
        static const TQ_INT64 valComment       = 1LL << 6;
        static const TQ_INT64 valBitrate       = 1LL << 7;
        static const TQ_INT64 valLength        = 1LL << 8;
        static const TQ_INT64 valSamplerate    = 1LL << 9;
        static const TQ_INT64 valPlayCounter   = 1LL << 10;
        static const TQ_INT64 valCreateDate    = 1LL << 11;
        static const TQ_INT64 valAccessDate    = 1LL << 12;
        //static const TQ_INT64 valPercentage    = 1LL << 13; // same as valScore
        static const TQ_INT64 valArtistID      = 1LL << 14;
        static const TQ_INT64 valAlbumID       = 1LL << 15;
        static const TQ_INT64 valYearID        = 1LL << 16;
        static const TQ_INT64 valGenreID       = 1LL << 17;
        static const TQ_INT64 valDirectory     = 1LL << 18;
        static const TQ_INT64 valLyrics        = 1LL << 19;
        static const TQ_INT64 valRating        = 1LL << 20;
        static const TQ_INT64 valComposerID    = 1LL << 21;
        static const TQ_INT64 valDiscNumber    = 1LL << 22;
        static const TQ_INT64 valFilesize      = 1LL << 23;
        static const TQ_INT64 valFileType      = 1LL << 24;
        static const TQ_INT64 valIsCompilation = 1LL << 25;
        static const TQ_INT64 valBPM           = 1LL << 26;
        // podcast relevant:
        static const TQ_INT64 valCopyright     = 1LL << 27;
        static const TQ_INT64 valParent        = 1LL << 28;
        static const TQ_INT64 valWeblink       = 1LL << 29;
        static const TQ_INT64 valAutoscan      = 1LL << 30;
        static const TQ_INT64 valFetchtype     = 1LL << 31;
        static const TQ_INT64 valAutotransfer  = 1LL << 32;
        static const TQ_INT64 valPurge         = 1LL << 33;
        static const TQ_INT64 valPurgeCount    = 1LL << 34;
        static const TQ_INT64 valIsNew         = 1LL << 35;
        // dynamic collection relevant:
        static const TQ_INT64 valDeviceId      = 1LL << 36;
        static const TQ_INT64 valRelativePath  = 1LL << 37;
        static const TQ_INT64 valDeviceLabel   = 1LL << 38;
        static const TQ_INT64 valMountPoint    = 1LL << 39;
        //label relevant
        static const TQ_INT64 valType         = 1LL << 40;

        static TQ_INT64 valForFavoriteSorting();
        void sortByFavorite();

        // sortByFavoriteAvg() add the average rating, if enabled, the average score, if enabled,
        // and the average playcounter as return values!
        void sortByFavoriteAvg();

        enum qBuilderFunctions  { funcNone = 0, funcCount = 1, funcMax = 2, funcMin = 4, funcAvg = 8, funcSum = 16 };

        // Note: modes beginMatch, endMatch are only supported for string filters
        // Likewise, modes between and notBetween are only supported for numeric filters
        enum qBuilderFilter  { modeNormal = 0, modeLess = 1, modeGreater = 2, modeEndMatch = 3, modeBeginMatch = 4, modeBetween = 5, modeNotBetween = 6};

        QueryBuilder();

        void addReturnValue( int table, TQ_INT64 value, bool caseSensitive = false /* unless value refers to a string */ );
        void addReturnFunctionValue( int function, int table, TQ_INT64 value);
        uint countReturnValues();

        // Note: the filter chain begins in AND mode
        void beginOR(); //filters will be ORed instead of ANDed
        void endOR();   //don't forget to end it!
        void beginAND(); // These do the opposite; for recursive and/or
        void endAND();

        void setGoogleFilter( int defaultTables, TQString query );

        void addURLFilters( const TQStringList& filter );

        void addFilter( int tables, const TQString& filter);
        void addFilter( int tables, TQ_INT64 value, const TQString& filter, int mode = modeNormal, bool exact = false );
        void addFilters( int tables, const TQStringList& filter );
        void excludeFilter( int tables, const TQString& filter );
        void excludeFilter( int tables, TQ_INT64 value, const TQString& filter, int mode = modeNormal, bool exact = false );

        void addMatch( int tables, const TQString& match, bool interpretUnknown = true, bool caseSensitive = true );
        void addMatch( int tables, TQ_INT64 value, const TQString& match, bool interpretUnknown = true, bool caseSensitive = true );
        void addMatches( int tables, const TQStringList& match, bool interpretUnknown = true, bool caseSensitive = true );
        void excludeMatch( int tables, const TQString& match );
        void having( int table, TQ_INT64 value, int function, int mode, const TQString& match );

        void exclusiveFilter( int tableMatching, int tableNotMatching, TQ_INT64 value );

        // For numeric filters:
        // modeNormal means strict equality; modeBeginMatch and modeEndMatch are not
        // allowed; modeBetween needs a second value endRange
        void addNumericFilter(int tables, TQ_INT64 value, const TQString &n,
                              int mode = modeNormal,
                              const TQString &endRange = TQString());

        void setOptions( int options );
        void sortBy( int table, TQ_INT64 value, bool descending = false );
        void sortByFunction( int function, int table, TQ_INT64 value, bool descending = false );
        void groupBy( int table, TQ_INT64 value );
        void setLimit( int startPos, int length );

        // Returns the results in random order.
        // If a \p table and \p value are specified, uses weighted random order on
        // that field.
        // The shuffle is cumulative with other sorts, but any sorts after this are
        // pointless because of the precision of the random function.
        void shuffle( int table = 0, TQ_INT64 value = 0 );

        static const int dragFieldCount;
        static TQString dragSQLFields();
        void initSQLDrag();

        void buildQuery( bool withDeviceidPlaceholder = false );
        TQString getQuery();
        //use withDeviceidPlaceholder = false if the query isn't run immediately (*CurrentTimeT*)
        //and replace (*MountedDeviceSelection*) with CollectionDB::instance()->deviceIdSelection()
        TQString query( bool withDeviceidPlaceholder = false ) { buildQuery( withDeviceidPlaceholder ); return m_query; };
        void clear();

        TQStringList run();

        // Transform a string table.value "field" into enum values
        // @return true if we succeeded
        bool getField(const TQString &tableValue, int *table, TQ_INT64 *value);

    private:
        TQString tableName( int table );
        const TQString &valueName( TQ_INT64 value );
        TQString functionName( int functions );
        bool coalesceField( int table, TQ_INT64 value );

        int getTableByName(const TQString &name);
        TQ_INT64 getValueByName(const TQString &field);

        TQStringList cleanURL( TQStringList result );

        void linkTables( int tables );

        TQValueStack<bool> m_OR;
        bool m_showAll;
        uint m_deviceidPos;

        TQString ANDslashOR() const;

        TQString m_query;
        TQString m_values;
        TQString m_tables;
        TQString m_join;
        TQString m_where;
        TQString m_sort;
        TQString m_group;
        TQString m_limit;
        TQString m_having;

        TQString m_url;      //url is used as primary key and linkTables needs to do some special stuff with it

        int m_linkTables;
        uint m_returnValues;
};

inline void QueryBuilder::beginOR()
{
    m_where += ANDslashOR() + " ( " + CollectionDB::instance()->boolF() + ' ';
    m_OR.push(true);
}
inline void QueryBuilder::endOR()
{
    m_where += " ) ";
    m_OR.pop();
}
inline void QueryBuilder::beginAND()
{
    m_where += ANDslashOR() + " ( " + CollectionDB::instance()->boolT() + ' ';
    m_OR.push(false);
}
inline void QueryBuilder::endAND()
{
    m_where += " ) ";
    m_OR.pop();
}
inline TQString QueryBuilder::ANDslashOR() const { return m_OR.top() ? "OR" : "AND"; }


#endif /* AMAROK_COLLECTIONDB_H */