Make playlist items use the full width available to them and adjust correctly when...
[amarok.git] / src / metabundlesaver.cpp
blob9473df906779dbf86697d8cefccf6112c8a020ff
1 // Jeff Mitchell <kde-dev@emailgoeshere.com>, (C) 2006
2 // License: GNU General Public License V2
5 #define DEBUG_PREFIX "MetaBundleSaver"
7 #include <stdlib.h>
8 #include <unistd.h>
9 #include <stdio.h>
10 #include <time.h>
11 #include <sys/time.h>
12 #include <sys/types.h>
13 #include <fcntl.h>
15 #include "amarok.h"
16 #include "amarokconfig.h"
17 #include "collectiondb.h"
18 #include "debug.h"
19 #include "metabundlesaver.h"
20 #include "scancontroller.h"
21 #include <kapplication.h>
22 #include <kfilemetainfo.h>
23 #include <kio/global.h>
24 #include <kio/job.h>
25 #include <kio/jobclasses.h>
26 #include <kio/netaccess.h>
27 #include <kcodecs.h>
28 #include <kurl.h>
29 #include <QFile> //decodePath()
30 #include <QByteArray>
31 #include <QString>
32 #include <fileref.h>
34 #include "config-amarok.h"
36 #include "metabundle.h"
38 MetaBundleSaver::MetaBundleSaver( MetaBundle *bundle )
39 : QObject()
40 , m_bundle( bundle )
41 , m_tempSavePath( QString() )
42 , m_origRenamedSavePath( QString() )
43 , m_tempSaveDigest( 0 )
44 , m_saveFileref( 0 )
45 , m_maxlen( 8192 )
46 , m_cleanupNeeded( false )
49 DEBUG_BLOCK
52 MetaBundleSaver::~MetaBundleSaver()
54 DEBUG_BLOCK
55 if( m_cleanupNeeded )
56 cleanupSave();
59 TagLib::FileRef *
60 MetaBundleSaver::prepareToSave()
62 DEBUG_BLOCK
64 m_cleanupNeeded = true;
65 KMD5 md5sum( 0, 0 );
66 const KUrl origPath = m_bundle->url();
67 char hostbuf[32];
68 int hostname = gethostname( hostbuf, 32 );
69 hostbuf[31] = '\0';
70 if( hostname != 0 )
72 debug() << "Could not determine hostname!";
73 return 0;
75 QString pid;
76 QString randomString = m_bundle->getRandomString( 8, true );
77 m_tempSavePath = origPath.path() + ".amaroktemp.host-" + QString( hostbuf ) +
78 ".pid-" + pid.setNum( getpid() ) + ".random-" + randomString + '.' + m_bundle->type();
79 m_origRenamedSavePath = origPath.path() + ".amarokoriginal.host-" + QString( hostbuf ) +
80 ".pid-" + pid.setNum( getpid() ) + ".random-" + randomString + '.' + m_bundle->type();
83 //The next long step is to copy the file over. We can't use KIO because it's not thread save,
84 //and std and QFile only have provisions for renaming and removing, so manual it is
85 //doing it block-by-block results it not needing a huge amount of memory overhead
87 debug() << "Copying original file to copy and caluclating MD5";
89 if( QFile::exists( m_tempSavePath ) )
91 debug() << "Temp file already exists!";
92 return 0;
95 QFile orig( m_bundle->url().path() );
96 QFile copy( m_tempSavePath );
98 if( !orig.open( QIODevice::Unbuffered | QIODevice::ReadOnly ) )
100 debug() << "Could not open original file!";
101 return 0;
104 //Do this separately so as not to create a zero-length file if you can't read from input
105 if( !copy.open( QIODevice::Unbuffered | QIODevice::WriteOnly | QIODevice::Truncate ) )
107 debug() << "Could not create file copy";
108 return 0;
111 Q_LONG actualreadlen, actualwritelen;
113 while( ( actualreadlen = orig.read( m_databuf, m_maxlen ) ) > 0 )
115 md5sum.update( m_databuf, actualreadlen );
116 if( ( actualwritelen = copy.write( m_databuf, actualreadlen ) ) != actualreadlen )
118 debug() << "Error during copying of original file data to copy!";
119 return 0;
123 if( actualreadlen == -1 )
125 debug() << "Error during reading original file!";
126 return 0;
129 m_tempSaveDigest = md5sum.hexDigest();
131 //By this point, we have the following:
132 //The original file is copied at path m_tempSavePath
133 //We have generated what will be the filename to rename the original to in m_origRenamedSavePath
134 //We have successfully copied the original file to the temp location
135 //We've calculated the md5sum of the original file
137 debug() << "MD5 sum of temp file: " << m_tempSaveDigest.data();
139 //Now, we have a MD5 sum of the original file at the time of copying saved in m_tempSaveDigest
140 //Create a fileref on the copied file, for modification
142 m_saveFileref = new TagLib::FileRef( QFile::encodeName( m_tempSavePath ), false );
144 if( m_saveFileref && !m_saveFileref->isNull() )
145 return m_saveFileref;
147 debug() << "Error creating temp file's fileref!";
148 return 0;
151 bool
152 MetaBundleSaver::doSave()
154 //TODO: much commenting needed. For now this pretty much follows algorithm laid out in bug 131353,
155 //but isn't useable since I need to find a good way to switch the file path with taglib, or a good way
156 //to get all the metadata copied over.
158 DEBUG_BLOCK
159 m_cleanupNeeded = true;
160 bool revert = false;
162 QFile origRenamedFile( m_origRenamedSavePath );
163 KMD5 md5sum( 0, 0 );
164 Q_LONG actualreadlen;
166 int errcode;
168 QByteArray origRenamedDigest;
170 if( !m_saveFileref || m_tempSavePath.isEmpty() || m_tempSaveDigest.isEmpty() || m_origRenamedSavePath.isEmpty() )
172 debug() << "You must run prepareToSave() and it must return successfully before calling doSave()!";
173 return false;
176 debug() << "Saving tag changes to the temporary file...";
178 //We've made our changes to the fileref; save it first, then do the logic to move the correct file back
179 if( !m_saveFileref->save() )
181 debug() << "Could not save the new file!";
182 goto fail_remove_copy;
185 debug() << "Renaming original file to temporary name " << m_origRenamedSavePath;
187 errcode = std::rename( QFile::encodeName( m_bundle->url().path() ).data(),
188 QFile::encodeName( m_origRenamedSavePath ).data() );
189 if( errcode != 0 )
191 debug() << "Could not move original!";
192 perror( "Could not move original!" );
193 goto fail_remove_copy;
196 revert = true;
198 debug() << "Calculating MD5 of " << m_origRenamedSavePath;
200 if( !origRenamedFile.open( QIODevice::Unbuffered | QIODevice::ReadOnly ) )
202 debug() << "Could not open temporary file!";
203 goto fail_remove_copy;
206 while( ( actualreadlen = origRenamedFile.read( m_databuf, m_maxlen ) ) > 0 )
207 md5sum.update( m_databuf, actualreadlen );
209 if( actualreadlen == -1 )
211 debug() << "Error during checksumming temp file!";
212 goto fail_remove_copy;
215 origRenamedDigest = md5sum.hexDigest();
217 debug() << "md5sum of original file: " << origRenamedDigest.data();
219 if( origRenamedDigest != m_tempSaveDigest )
221 debug() << "Original checksum did not match current checksum!";
222 goto fail_remove_copy;
225 debug() << "Renaming temp file to original's filename";
227 errcode = std::rename( QFile::encodeName( m_tempSavePath ).data(),
228 QFile::encodeName( m_bundle->url().path() ).data() );
229 if( errcode != 0 )
231 debug() << "Could not rename newly-tagged file to original!";
232 perror( "Could not rename newly-tagged file to original!" );
233 goto fail_remove_copy;
236 debug() << "Deleting original";
238 errcode = std::remove( QFile::encodeName( m_origRenamedSavePath ) );
239 if( errcode != 0 )
241 debug() << "Could not delete the original file!";
242 perror( "Could not delete the original file!" );
243 return false;
246 debug() << "Save done, returning true!";
248 return true;
250 fail_remove_copy:
252 debug() << "Deleting temporary file...";
253 errcode = std::remove( QFile::encodeName( m_tempSavePath ).data() );
254 if( errcode != 0 )
256 debug() << "Could not delete the temporary file!";
257 perror( "Could not delete the temporary file!" );
260 if( !revert )
261 return false;
263 debug() << "Reverting original file to original filename!";
264 errcode = std::rename( QFile::encodeName( m_origRenamedSavePath ).data(),
265 QFile::encodeName( m_bundle->url().path() ).data() );
266 if( errcode != 0 )
268 debug() << "Could not revert file to original filename!";
269 perror( "Could not revert file to original filename!" );
272 return false;
275 bool
276 MetaBundleSaver::cleanupSave()
278 DEBUG_BLOCK
280 bool dirty = false;
282 if( !m_tempSavePath.isEmpty() && QFile::exists( m_tempSavePath ) )
284 int errcode;
285 errcode = std::remove( QFile::encodeName( m_tempSavePath ).data() );
286 if( errcode != 0 )
288 dirty = true;
289 debug() << "Could not delete the temporary file!";
293 m_tempSavePath.clear();
294 m_origRenamedSavePath.clear();
295 m_tempSaveDigest = QByteArray( 0 );
296 if( m_saveFileref )
298 delete m_saveFileref;
299 m_saveFileref = 0;
302 m_cleanupNeeded = false;
303 return !dirty;
306 #include "metabundlesaver.moc"