do not run the update code in collectionDB anymore + some fixes for SQL statements
[amarok.git] / src / collection / sqlcollection / ScanResultProcessor.cpp
blobe7a4ea5db2845742a586d406bd6715a5a09384ef
1 /*
2 * Copyright (c) 2007 Maximilian Kossick <maximilian.kossick@googlemail.com>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 #include "ScanResultProcessor.h"
21 #include "debug.h"
22 #include "meta/MetaConstants.h"
23 #include "meta/MetaUtility.h"
24 #include "mountpointmanager.h"
25 #include "sqlcollection.h"
27 #include <KUrl>
29 using namespace Meta;
31 ScanResultProcessor::ScanResultProcessor( SqlCollection *collection )
32 : m_collection( collection )
34 //nothing to do
37 ScanResultProcessor::~ScanResultProcessor()
39 //nothing to do
42 void
43 ScanResultProcessor::processScanResult( const QMap<QString, QHash<QString, QString> > &scanResult )
45 DEBUG_BLOCK
46 m_collection->dbUpdater()->createTemporaryTables();
47 m_collection->dbUpdater()->prepareTemporaryTables();
48 QList<QHash<QString, QString> > dirData;
49 bool firstTrack = true;
50 QString dir;
51 QHash<QString,QString> track;
52 debug() << "Processing " << scanResult.size() << " tracks";
53 foreach( track, scanResult )
55 if( firstTrack )
57 KUrl url( track.value( Field::URL ) );
58 dir = url.directory();
59 firstTrack = false;
60 debug() << "first dir " << url.directory();
63 KUrl url( track.value( Field::URL ) );
64 debug() << "current dir" << url.directory();
65 if( url.directory() == dir )
67 dirData.append( track );
69 else
71 processDirectory( dirData );
72 dirData.clear();
73 dir = url.directory();
76 m_collection->dbUpdater()->copyToPermanentTables();
77 m_collection->dbUpdater()->removeTemporaryTables();
80 void
81 ScanResultProcessor::processDirectory( const QList<QHash<QString, QString> > &data )
83 DEBUG_BLOCK
84 //using the following heuristics:
85 //if more than one album is in the dir, use the artist of each track as albumartist
86 //if more than 60 files are in the dir, use the artist of each track as albumartist
87 //if all tracks have the same artist, use it as albumartist
88 //try to find the albumartist A: tracks must have the artist A or A feat. B (and variants)
89 //if no albumartist could be found, it's a compilation
90 QSet<QString> artists;
91 QString album;
92 bool multipleAlbums = false;
93 if( !data.isEmpty() )
94 album = data[0].value( Field::ALBUM );
95 QHash<QString,QString> row;
96 foreach( row, data )
98 artists.insert( row.value( Field::ARTIST ) );
99 if( row.value( Field::ALBUM ) != album )
100 multipleAlbums = true;
102 if( multipleAlbums || data.count() > 60 || artists.size() == 1 )
104 QHash<QString, QString> row;
105 foreach( row, data )
107 int artist = artistId( row.value( Field::ARTIST ) );
108 addTrack( row, artist );
111 else
113 QString albumArtist = findAlbumArtist( artists );
114 //an empty string means that no albumartist was found
115 int artist = albumArtist.isEmpty() ? 0 : artistId( albumArtist );
116 QHash<QString, QString> row;
117 foreach( row, data )
119 addTrack( row, artist );
124 QString
125 ScanResultProcessor::findAlbumArtist( const QSet<QString> &artists ) const
127 QMap<QString, int> artistCount;
128 foreach( QString artist, artists )
130 //this needs to be improved
131 if( artist.contains( "featuring" ) )
133 QStringList trackArtists = artist.split( "featuring" );
134 //always use the first artist
135 QString tmp = trackArtists[0].simplified();
136 if( tmp.isEmpty() )
138 //TODO error handling
140 else
142 if( artistCount.contains( tmp ) )
144 artistCount.insert( tmp, artistCount.value( tmp ) + 1 );
146 else
148 artistCount.insert( tmp, 1 );
152 else if( artist.contains( "feat." ) )
154 //FIXME code duplication, refactor!
155 QStringList trackArtists = artist.split( "feat." );
156 //always use the first artist
157 QString tmp = trackArtists[0].simplified();
158 if( tmp.isEmpty() )
160 //TODO error handling
162 else
164 if( artistCount.contains( tmp ) )
166 artistCount.insert( tmp, artistCount.value( tmp ) + 1 );
168 else
170 artistCount.insert( tmp, 1 );
174 else
176 if( artistCount.contains( artist ) )
178 artistCount.insert( artist, artistCount.value( artist ) + 1 );
180 else
182 artistCount.insert( artist, 1 );
186 QString albumArtist;
187 int count = 0;
188 foreach( QString key, artistCount.keys() )
190 if( artistCount.value( key ) > count )
192 albumArtist = key;
193 count = artistCount.value( key );
196 return albumArtist;
199 void
200 ScanResultProcessor::addTrack( const QHash<QString, QString> &trackData, int albumArtistId )
202 DEBUG_BLOCK
203 int album = albumId( trackData.value( Field::ALBUM ), albumArtistId );
204 int artist = artistId( trackData.value( Field::ARTIST ) );
205 int genre = genreId( trackData.value( Field::GENRE ) );
206 int composer = composerId( trackData.value( Field::COMPOSER ) );
207 int year = yearId( trackData.value( Field::YEAR ) );
208 int url = urlId( trackData.value( Field::URL ) );
210 QString insert = "INSERT INTO tracks_temp(url,artist,album,genre,composer,year,title,comment,"
211 "tracknumber,discnumber,bitrate,length,samplerate,filesize,filetype,bpm,"
212 "createdate,modifydate) VALUES ( %1,%2,%3,%4,%5,%6,'%7','%8',%9"; //goes up to tracknumber
213 insert = insert.arg( url ).arg( artist ).arg( album ).arg( genre ).arg( composer ).arg( year );
214 insert = insert.arg( m_collection->escape( trackData[ Field::TITLE ] ), m_collection->escape( trackData[ Field::COMMENT ] ) );
215 insert = insert.arg( trackData[Field::TRACKNUMBER].toInt() );
217 QString insert2 = ",%1,%2,%3,%4,%5,%6,%7,%8,%9);";
218 insert2 = insert2.arg( trackData[Field::BITRATE].toInt() ).arg( trackData[Field::LENGTH].toInt() );
219 insert2 = insert2.arg( trackData[Field::SAMPLERATE].toInt() ).arg( trackData[Field::FILESIZE].toInt() );
220 insert2 = insert2.arg( "0", "0", "0", "0" ); //filetype,bpm, createdate, modifydate not implemented yet
221 insert += insert2;
223 m_collection->insert( insert, "tracks_temp" );
227 ScanResultProcessor::artistId( const QString &artist )
229 if( m_artists.contains( artist ) )
230 return m_artists.value( artist );
231 QString query = QString( "SELECT id FROM artists_temp WHERE name = '%1';" ).arg( m_collection->escape( artist ) );
232 QStringList res = m_collection->query( query );
233 if( res.isEmpty() )
235 QString insert = QString( "INSERT INTO artists_temp( name ) VALUES ('%1');" ).arg( m_collection->escape( artist ) );
236 int id = m_collection->insert( insert, "artists_temp" );
237 m_artists.insert( artist, id );
238 return id;
240 else
242 int id = res[0].toInt();
243 m_artists.insert( artist, id );
244 return id;
249 ScanResultProcessor::genreId( const QString &genre )
251 if( m_genre.contains( genre ) )
252 return m_genre.value( genre );
253 QString query = QString( "SELECT id FROM genres_temp WHERE name = '%1';" ).arg( m_collection->escape( genre ) );
254 QStringList res = m_collection->query( query );
255 if( res.isEmpty() )
257 QString insert = QString( "INSERT INTO genres_temp( name ) VALUES ('%1');" ).arg( m_collection->escape( genre ) );
258 int id = m_collection->insert( insert, "genres_temp" );
259 m_genre.insert( genre, id );
260 return id;
262 else
264 int id = res[0].toInt();
265 m_genre.insert( genre, id );
266 return id;
271 ScanResultProcessor::composerId( const QString &composer )
273 if( m_composer.contains( composer ) )
274 return m_composer.value( composer );
275 QString query = QString( "SELECT id FROM composers_temp WHERE name = '%1';" ).arg( m_collection->escape( composer ) );
276 QStringList res = m_collection->query( query );
277 if( res.isEmpty() )
279 QString insert = QString( "INSERT INTO composers_temp( name ) VALUES ('%1');" ).arg( m_collection->escape( composer ) );
280 int id = m_collection->insert( insert, "composers_temp" );
281 m_composer.insert( composer, id );
282 return id;
284 else
286 int id = res[0].toInt();
287 m_composer.insert( composer, id );
288 return id;
293 ScanResultProcessor::yearId( const QString &year )
295 if( m_year.contains( year ) )
296 return m_year.value( year );
297 QString query = QString( "SELECT id FROM years_temp WHERE name = '%1';" ).arg( m_collection->escape( year ) );
298 QStringList res = m_collection->query( query );
299 if( res.isEmpty() )
301 QString insert = QString( "INSERT INTO years_temp( name ) VALUES ('%1');" ).arg( m_collection->escape( year ) );
302 int id = m_collection->insert( insert, "years_temp" );
303 m_year.insert( year, id );
304 return id;
306 else
308 int id = res[0].toInt();
309 m_year.insert( year, id );
310 return id;
314 int
315 ScanResultProcessor::albumId( const QString &album, int artistId )
317 //artistId == 0 means no albumartist
318 QPair<QString, int> key( album, artistId );
319 if( m_albums.contains( key ) )
320 return m_albums.value( key );
322 QString query;
323 if( artistId == 0 )
325 query = QString( "SELECT id FROM albums_temp WHERE artist IS NULL AND name = '%1';" )
326 .arg( m_collection->escape( album ) );
328 else
330 query = QString( "SELECT id FROM albums_temp WHERE artist = %1 AND name = '%2';" )
331 .arg( QString::number( artistId ), m_collection->escape( album ) );
333 QStringList res = m_collection->query( query );
334 if( res.isEmpty() )
336 QString insert = QString( "INSERT INTO albums_temp(artist, name) VALUES( %1, '%2' );" )
337 .arg( artistId ? QString::number( artistId ) : "NULL" )
338 .arg( m_collection->escape( album ) );
339 int id = m_collection->insert( insert, "albums_temp" );
340 m_albums.insert( key, id );
341 return id;
343 else
345 int id = res[0].toInt();
346 m_albums.insert( key, id );
347 return id;
352 ScanResultProcessor::urlId( const QString &url )
354 int deviceId = MountPointManager::instance()->getIdForUrl( url );
355 QString rpath = MountPointManager::instance()->getRelativePath( deviceId, url );
356 //don't bother caching the data, we only call this method for each url once
357 QString query = QString( "SELECT id FROM urls_temp WHERE deviceid = %1 AND rpath = '%2';" )
358 .arg( QString::number( deviceId ), m_collection->escape( rpath ) );
359 QStringList res = m_collection->query( query );
360 if( res.isEmpty() )
362 QString insert = QString( "INSERT INTO urls_temp(deviceid, rpath) VALUES ( %1, '%2' );" )
363 .arg( QString::number( deviceId ), m_collection->escape( rpath ) );
364 return m_collection->insert( insert, "urls_temp" );
366 else
368 return res[0].toInt();