File indexing completed on 2024-05-12 16:35:05
0001 /* This file is part of the KDE project 0002 * Copyright (C) 2007, 2009 Thomas Zander <zander@kde.org> 0003 * Copyright (C) 2007 Jan Hambrecht <jaham@gmx.net> 0004 * Copyright (C) 2008 C. Boemann <cbo@boemann.dk> 0005 * Copyright (C) 2008 Thorsten Zachmann <zachmann@kde.org> 0006 * Copyright (C) 2012 Gopalakrishna Bhat A <gopalakbhat@gmail.com> 0007 * 0008 * This library is free software; you can redistribute it and/or 0009 * modify it under the terms of the GNU Library General Public 0010 * License as published by the Free Software Foundation; either 0011 * version 2 of the License, or (at your option) any later version. 0012 * 0013 * This library is distributed in the hope that it will be useful, 0014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0016 * Library General Public License for more details. 0017 * 0018 * You should have received a copy of the GNU Library General Public License 0019 * along with this library; see the file COPYING.LIB. If not, write to 0020 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0021 * Boston, MA 02110-1301, USA. 0022 */ 0023 0024 #include "VideoData.h" 0025 0026 #include "VideoCollection.h" 0027 #include "VideoDebug.h" 0028 0029 #include <KoStore.h> 0030 #include <KoStoreDevice.h> 0031 0032 #include <QApplication> 0033 #include <QBuffer> 0034 #include <QCryptographicHash> 0035 #include <QFileInfo> 0036 #include <QTemporaryFile> 0037 #include <QPainter> 0038 #include <QAtomicInt> 0039 #include <QFile> 0040 #include <QUrl> 0041 0042 class VideoDataPrivate 0043 { 0044 public: 0045 VideoDataPrivate(); 0046 ~VideoDataPrivate(); 0047 0048 /// store the suffix based on the full filename. 0049 void setSuffix(const QString &fileName); 0050 0051 QAtomicInt refCount; 0052 QTemporaryFile *temporaryFile; 0053 /** 0054 * a unique key of the video data 0055 */ 0056 qint64 key; 0057 0058 QString suffix; // the suffix of the video e.g. avi TODO use a QByteArray ? 0059 0060 QString saveName; 0061 0062 QUrl videoLocation; 0063 0064 VideoData::ErrorCode errorCode; 0065 0066 VideoCollection *collection; 0067 0068 // video data store. 0069 VideoData::DataStoreState dataStoreState; 0070 0071 bool saveVideoInZip; 0072 }; 0073 0074 VideoDataPrivate::VideoDataPrivate() 0075 : refCount(0) 0076 , temporaryFile(0) 0077 , key(0) 0078 , errorCode(VideoData::Success) 0079 , collection(0) 0080 , dataStoreState(VideoData::StateEmpty) 0081 , saveVideoInZip(false) 0082 { 0083 0084 } 0085 0086 VideoDataPrivate::~VideoDataPrivate() 0087 { 0088 delete temporaryFile; 0089 } 0090 0091 void VideoDataPrivate::setSuffix(const QString &name) 0092 { 0093 QRegExp rx("\\.([^/]+$)"); // TODO does this work on windows or do we have to use \ instead of / for a path separator? 0094 if (rx.indexIn(name) != -1) { 0095 suffix = rx.cap(1); 0096 } 0097 } 0098 0099 VideoData::VideoData() 0100 : KoShapeUserData() 0101 , d(0) 0102 { 0103 } 0104 0105 VideoData::VideoData(const VideoData &videoData) 0106 : KoShapeUserData() 0107 , d(videoData.d) 0108 { 0109 Q_UNUSED(videoData); 0110 0111 if(d) { 0112 d->refCount.ref(); 0113 } 0114 } 0115 0116 VideoData::~VideoData() 0117 { 0118 if (d && d->collection) { 0119 d->collection->removeOnKey(d->key); 0120 } 0121 0122 if (d && !d->refCount.deref()) { 0123 delete d; 0124 } 0125 } 0126 0127 void VideoData::setExternalVideo(const QUrl &location, bool saveInternal, VideoCollection *collection) 0128 { 0129 if (collection) { 0130 // let the collection first check if it already has one. If it doesn't it'll call this method 0131 // again and we'll go to the other clause 0132 VideoData *other = collection->createExternalVideoData(location, saveInternal); 0133 this->operator=(*other); 0134 delete other; 0135 } else { 0136 delete d; 0137 d = new VideoDataPrivate(); 0138 d->refCount.ref(); 0139 0140 d->videoLocation = location; 0141 d->saveVideoInZip = saveInternal; 0142 if (d->saveVideoInZip) { 0143 QFileInfo fileInfo(location.toLocalFile()); 0144 d->setSuffix(fileInfo.fileName()); 0145 } else { 0146 d->setSuffix(location.toEncoded()); 0147 } 0148 0149 QCryptographicHash md5(QCryptographicHash::Md5); 0150 md5.addData(location.toEncoded().append(saveInternal ? "true" : "false")); 0151 d->key = VideoData::generateKey(md5.result()); 0152 } 0153 } 0154 0155 void VideoData::setVideo(const QString &url, KoStore *store, VideoCollection *collection) 0156 { 0157 if (collection) { 0158 // let the collection first check if it already has one. If it doesn't it'll call this method 0159 // again and we'll go to the other clause 0160 VideoData *other = collection->createVideoData(url, store); 0161 this->operator=(*other); 0162 delete other; 0163 } else { 0164 if (store->open(url)) { 0165 struct Finalizer { 0166 ~Finalizer() { store->close(); } 0167 KoStore *store; 0168 }; 0169 Finalizer closer; 0170 closer.store = store; 0171 KoStoreDevice device(store); 0172 //QByteArray data = device.readAll(); 0173 if (!device.open(QIODevice::ReadOnly)) { 0174 warnVideo << "open file from store " << url << "failed"; 0175 d->errorCode = OpenFailed; 0176 store->close(); 0177 return; 0178 } 0179 0180 copyToTemporary(device); 0181 0182 d->setSuffix(url); 0183 } else { 0184 warnVideo << "Find file in store " << url << "failed"; 0185 d->errorCode = OpenFailed; 0186 return; 0187 } 0188 } 0189 } 0190 0191 QUrl VideoData::playableUrl() const 0192 { 0193 if (d->dataStoreState == StateSpooled) { 0194 Q_ASSERT(d); 0195 return QUrl(d->temporaryFile->fileName()); 0196 } else { 0197 return d->videoLocation; 0198 } 0199 } 0200 0201 QString VideoData::tagForSaving(int &counter) 0202 { 0203 if (!d->saveName.isEmpty()) 0204 return d->saveName; 0205 0206 if (!d->videoLocation.isEmpty()) { 0207 if (d->saveVideoInZip) { 0208 d->saveName = QString("Videos/video%1.%2").arg(++counter).arg(d->suffix); 0209 return d->saveName; 0210 } else { 0211 return d->videoLocation.toString(); 0212 } 0213 } 0214 0215 if (d->suffix.isEmpty()) { 0216 return d->saveName = QString("Videos/video%1").arg(++counter); 0217 } else { 0218 return d->saveName = QString("Videos/video%1.%2").arg(++counter).arg(d->suffix); 0219 } 0220 } 0221 0222 bool VideoData::isValid() const 0223 { 0224 return d->dataStoreState != VideoData::StateEmpty 0225 && d->errorCode == Success; 0226 } 0227 0228 bool VideoData::operator==(const VideoData &other) const 0229 { 0230 Q_UNUSED(other); 0231 return false; 0232 } 0233 0234 VideoData &VideoData::operator=(const VideoData &other) 0235 { 0236 if (other.d) { 0237 other.d->refCount.ref(); 0238 } 0239 0240 if (d && !d->refCount.deref()) { 0241 delete d; 0242 } 0243 0244 d = other.d; 0245 return *this; 0246 } 0247 0248 bool VideoData::saveData(QIODevice &device) 0249 { 0250 if (d->dataStoreState == StateSpooled) { 0251 Q_ASSERT(d->temporaryFile); // otherwise the collection should not have called this 0252 if (d->temporaryFile) { 0253 if (!d->temporaryFile->open()) { 0254 warnVideo << "Read file from temporary store failed"; 0255 return false; 0256 } 0257 char buf[8192]; 0258 while (true) { 0259 d->temporaryFile->waitForReadyRead(-1); 0260 qint64 bytes = d->temporaryFile->read(buf, sizeof(buf)); 0261 if (bytes <= 0) 0262 break; // done! 0263 do { 0264 qint64 nWritten = device.write(buf, bytes); 0265 if (nWritten == -1) { 0266 d->temporaryFile->close(); 0267 return false; 0268 } 0269 bytes -= nWritten; 0270 } while (bytes > 0); 0271 } 0272 d->temporaryFile->close(); 0273 } 0274 return true; 0275 } else if (!d->videoLocation.isEmpty()) { 0276 if (d->saveVideoInZip) { 0277 // An external video have been specified 0278 QFile file(d->videoLocation.toLocalFile()); 0279 0280 if (!file.open(QIODevice::ReadOnly)) { 0281 warnVideo << "Read file failed"; 0282 return false; 0283 } 0284 char buf[8192]; 0285 while (true) { 0286 file.waitForReadyRead(-1); 0287 qint64 bytes = file.read(buf, sizeof(buf)); 0288 if (bytes <= 0) 0289 break; // done! 0290 do { 0291 qint64 nWritten = device.write(buf, bytes); 0292 if (nWritten == -1) { 0293 file.close(); 0294 return false; 0295 } 0296 bytes -= nWritten; 0297 } while (bytes > 0); 0298 } 0299 file.close(); 0300 } 0301 } 0302 return false; 0303 } 0304 0305 void VideoData::copyToTemporary(QIODevice &device) 0306 { 0307 delete d; 0308 d = new VideoDataPrivate(); 0309 d->temporaryFile = new QTemporaryFile(QLatin1String("KoVideoData/") + qAppName() + QLatin1String("_XXXXXX") ); 0310 d->refCount.ref(); 0311 if (!d->temporaryFile->open()) { 0312 warnVideo << "open temporary file for writing failed"; 0313 d->errorCode = VideoData::StorageFailed; 0314 delete d; 0315 d = 0; 0316 return; 0317 } 0318 QCryptographicHash md5(QCryptographicHash::Md5); 0319 char buf[8192]; 0320 while (true) { 0321 device.waitForReadyRead(-1); 0322 qint64 bytes = device.read(buf, sizeof(buf)); 0323 if (bytes <= 0) 0324 break; // done! 0325 md5.addData(buf, bytes); 0326 do { 0327 bytes -= d->temporaryFile->write(buf, bytes); 0328 } while (bytes > 0); 0329 } 0330 d->key = VideoData::generateKey(md5.result()); 0331 d->temporaryFile->close(); 0332 0333 QFileInfo fi(*(d->temporaryFile)); 0334 d->dataStoreState = StateSpooled; 0335 } 0336 0337 qint64 VideoData::generateKey(const QByteArray &bytes) 0338 { 0339 qint64 answer = 1; 0340 const int max = qMin(8, bytes.count()); 0341 for (int x = 0; x < max; ++x) 0342 answer += bytes[x] << (8 * x); 0343 return answer; 0344 } 0345 0346 QString VideoData::saveName() const 0347 { 0348 return d->saveName; 0349 } 0350 0351 void VideoData::setSaveName(const QString &saveName) 0352 { 0353 d->saveName = saveName; 0354 } 0355 0356 VideoCollection *VideoData::collection() 0357 { 0358 return d->collection; 0359 } 0360 0361 void VideoData::setCollection(VideoCollection *collection) 0362 { 0363 d->collection = collection; 0364 } 0365 0366 qint64 VideoData::key() 0367 { 0368 return d->key; 0369 }