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 }