PageRenderTime 12ms CodeModel.GetById 4ms app.highlight 137ms RepoModel.GetById 1ms app.codeStats 1ms

/mediaCenterPlugins/XbmcXml.cpp

https://github.com/zabinko/MediaElch
C++ | 1951 lines | 1524 code | 190 blank | 237 comment | 362 complexity | 5039a23fe7903bb37b94871978fd39c0 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1#include "XbmcXml.h"
   2
   3#include <QApplication>
   4#include <QDebug>
   5#include <QDir>
   6#include <QFileInfo>
   7#include <QXmlStreamWriter>
   8
   9#include "globals/Globals.h"
  10#include "globals/Helper.h"
  11#include "globals/Manager.h"
  12#include "settings/Settings.h"
  13
  14/**
  15 * @brief XbmcXml::XbmcXml
  16 * @param parent
  17 */
  18XbmcXml::XbmcXml(QObject *parent)
  19{
  20    setParent(parent);
  21}
  22
  23/**
  24 * @brief XbmcXml::~XbmcXml
  25 */
  26XbmcXml::~XbmcXml()
  27{
  28}
  29
  30/**
  31 * @brief Checks if our MediaCenterPlugin supports a feature
  32 * @param feature Feature to check
  33 * @return Feature is supported or not
  34 */
  35bool XbmcXml::hasFeature(int feature)
  36{
  37    if (feature == MediaCenterFeatures::HandleMovieSetImages)
  38        return false;
  39
  40    return true;
  41}
  42
  43/**
  44 * @brief Writes movie elements to an xml stream
  45 * @param xml XML stream
  46 * @param movie Movie to save
  47 */
  48void XbmcXml::writeMovieXml(QXmlStreamWriter &xml, Movie *movie)
  49{
  50    qDebug() << "Entered, movie=" << movie->name();
  51    xml.writeStartElement("movie");
  52    xml.writeTextElement("title", movie->name());
  53    xml.writeTextElement("originaltitle", movie->originalName());
  54    xml.writeTextElement("rating", QString("%1").arg(movie->rating()));
  55    xml.writeTextElement("votes", QString::number(movie->votes()));
  56    xml.writeTextElement("top250", QString::number(movie->top250()));
  57    xml.writeTextElement("year", movie->released().toString("yyyy"));
  58    xml.writeTextElement("plot", movie->overview());
  59    xml.writeTextElement("outline", movie->outline());
  60    xml.writeTextElement("tagline", movie->tagline());
  61    if (movie->runtime() > 0)
  62        xml.writeTextElement("runtime", QString("%1").arg(movie->runtime()));
  63    xml.writeTextElement("mpaa", movie->certification());
  64    xml.writeTextElement("credits", movie->writer());
  65    xml.writeTextElement("director", movie->director());
  66    xml.writeTextElement("playcount", QString("%1").arg(movie->playcount()));
  67    xml.writeTextElement("lastplayed", movie->lastPlayed().toString("yyyy-MM-dd HH:mm:ss"));
  68    xml.writeTextElement("id", movie->id());
  69    xml.writeTextElement("tmdbid", movie->tmdbId());
  70    xml.writeTextElement("set", movie->set());
  71    xml.writeTextElement("sorttitle", movie->sortTitle());
  72    xml.writeTextElement("trailer", Helper::formatTrailerUrl(movie->trailer().toString()));
  73    xml.writeTextElement("watched", (movie->watched()) ? "true" : "false");
  74    foreach (const QString &studio, movie->studios())
  75        xml.writeTextElement("studio", studio);
  76    foreach (const QString &genre, movie->genres())
  77        xml.writeTextElement("genre", genre);
  78    foreach (const QString &country, movie->countries())
  79        xml.writeTextElement("country", country);
  80    foreach (const QString &tag, movie->tags())
  81        xml.writeTextElement("tag", tag);
  82    foreach (const Actor &actor, movie->actors()) {
  83        xml.writeStartElement("actor");
  84        xml.writeTextElement("name", actor.name);
  85        xml.writeTextElement("role", actor.role);
  86        xml.writeTextElement("thumb", actor.thumb);
  87        xml.writeEndElement();
  88    }
  89    foreach (const Poster &poster, movie->posters()) {
  90        xml.writeStartElement("thumb");
  91        xml.writeAttribute("preview", poster.thumbUrl.toString());
  92        xml.writeCharacters(poster.originalUrl.toString());
  93        xml.writeEndElement();
  94    }
  95    xml.writeStartElement("fanart");
  96    foreach (const Poster &poster, movie->backdrops()) {
  97        xml.writeStartElement("thumb");
  98        xml.writeAttribute("preview", poster.thumbUrl.toString());
  99        xml.writeCharacters(poster.originalUrl.toString());
 100        xml.writeEndElement();
 101    }
 102    xml.writeEndElement();
 103
 104    xml.writeStartElement("fileinfo");
 105    writeStreamDetails(xml, movie->streamDetails());
 106    xml.writeEndElement();
 107
 108    xml.writeEndElement();
 109}
 110
 111/**
 112 * @brief Saves a movie (including images)
 113 * @param movie Movie to save
 114 * @return Saving success
 115 * @see XbmcXml::writeMovieXml
 116 */
 117bool XbmcXml::saveMovie(Movie *movie)
 118{
 119    qDebug() << "Entered, movie=" << movie->name();
 120    QByteArray xmlContent;
 121    QXmlStreamWriter xml(&xmlContent);
 122    xml.setAutoFormatting(true);
 123    xml.writeStartDocument("1.0", true);
 124    writeMovieXml(xml, movie);
 125    xml.writeEndDocument();
 126
 127    if (movie->files().size() == 0) {
 128        qWarning() << "Movie has no files";
 129        return false;
 130    }
 131
 132    movie->setNfoContent(xmlContent);
 133    Manager::instance()->database()->update(movie);
 134
 135    bool saved = false;
 136    QFileInfo fi(movie->files().at(0));
 137    foreach (DataFile dataFile, Settings::instance()->dataFiles(DataFileType::MovieNfo)) {
 138        QString saveFileName = dataFile.saveFileName(fi.fileName());
 139        QFile file(fi.absolutePath() + QDir::separator() + saveFileName);
 140        qDebug() << "Saving to" << file.fileName();
 141        if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
 142            qWarning() << "File could not be openend";
 143        } else {
 144            file.write(xmlContent);
 145            file.close();
 146            saved = true;
 147        }
 148    }
 149    if (!saved)
 150        return false;
 151
 152    if (movie->posterImageChanged() && !movie->posterImage()->isNull()) {
 153        foreach (DataFile dataFile, Settings::instance()->dataFiles(DataFileType::MoviePoster)) {
 154            QString saveFileName = dataFile.saveFileName(fi.fileName(), -1, movie->files().count() > 1);
 155            qDebug() << "Saving poster to" << fi.absolutePath() + QDir::separator() + saveFileName;
 156            movie->posterImage()->save(fi.absolutePath() + QDir::separator() + saveFileName, "jpg", 100);
 157        }
 158    }
 159    if (movie->backdropImageChanged() && !movie->backdropImage()->isNull()) {
 160        foreach (DataFile dataFile, Settings::instance()->dataFiles(DataFileType::MovieBackdrop)) {
 161            QString saveFileName = dataFile.saveFileName(fi.fileName(), -1, movie->files().count() > 1);
 162            qDebug() << "Saving fanart to" << fi.absolutePath() + QDir::separator() + saveFileName;
 163            movie->backdropImage()->save(fi.absolutePath() + QDir::separator() + saveFileName, "jpg", 100);
 164        }
 165    }
 166    saveAdditionalImages(movie);
 167
 168    foreach (const Actor &actor, movie->actors()) {
 169        if (!actor.image.isNull()) {
 170            QDir dir;
 171            dir.mkdir(fi.absolutePath() + QDir::separator() + ".actors");
 172            QString actorName = actor.name;
 173            actorName = actorName.replace(" ", "_");
 174            actor.image.save(fi.absolutePath() + QDir::separator() + ".actors" + QDir::separator() + actorName + ".tbn", "jpg", 100);
 175        }
 176    }
 177
 178    return true;
 179}
 180
 181/**
 182 * @brief Saves additional movie images (logo, clear art, cd art)
 183 * @param movie Movie object
 184 */
 185void XbmcXml::saveAdditionalImages(Movie *movie)
 186{
 187    QFileInfo fi(movie->files().at(0));
 188    if (movie->logoImageChanged() && !movie->logoImage()->isNull()) {
 189        foreach (DataFile dataFile, Settings::instance()->dataFiles(DataFileType::MovieLogo)) {
 190            QString saveFileName = dataFile.saveFileName(fi.fileName());
 191            qDebug() << "Saving logo to" << fi.absolutePath() + QDir::separator() + saveFileName;
 192            movie->logoImage()->save(fi.absolutePath() + QDir::separator() + saveFileName, "png", 100);
 193        }
 194    }
 195    if (movie->clearArtImageChanged() && !movie->clearArtImage()->isNull()) {
 196        foreach (DataFile dataFile, Settings::instance()->dataFiles(DataFileType::MovieClearArt)) {
 197            QString saveFileName = dataFile.saveFileName(fi.fileName());
 198            qDebug() << "Saving clear art to" << fi.absolutePath() + QDir::separator() + saveFileName;
 199            movie->clearArtImage()->save(fi.absolutePath() + QDir::separator() + saveFileName, "png", 100);
 200        }
 201    }
 202    if (movie->cdArtImageChanged() && !movie->cdArtImage()->isNull()) {
 203        foreach (DataFile dataFile, Settings::instance()->dataFiles(DataFileType::MovieCdArt)) {
 204            QString saveFileName = dataFile.saveFileName(fi.fileName());
 205            qDebug() << "Saving cd art to" << fi.absolutePath() + QDir::separator() + saveFileName;
 206            movie->cdArtImage()->save(fi.absolutePath() + QDir::separator() + saveFileName, "png", 100);
 207        }
 208    }
 209
 210    if (movie->inSeparateFolder() && !movie->files().isEmpty()) {
 211        foreach (const QString &file, movie->extraFanartsToRemove())
 212            QFile::remove(file);
 213        QDir dir(QFileInfo(movie->files().first()).absolutePath() + "/extrafanart");
 214        if (!dir.exists() && !movie->extraFanartImagesToAdd().isEmpty())
 215            QDir(QFileInfo(movie->files().first()).absolutePath()).mkdir("extrafanart");
 216        foreach (QImage img, movie->extraFanartImagesToAdd()) {
 217            int num = 1;
 218            while (QFileInfo(dir.absolutePath() + "/" + QString("fanart%1.jpg").arg(num)).exists())
 219                ++num;
 220            img.save(dir.absolutePath() + "/" + QString("fanart%1.jpg").arg(num), "jpg", 100);
 221        }
 222    }
 223}
 224
 225/**
 226 * @brief Tries to find an nfo file for the movie
 227 * @param movie Movie
 228 * @return Path to nfo file, if none found returns an empty string
 229 */
 230QString XbmcXml::nfoFilePath(Movie *movie)
 231{
 232    QString nfoFile;
 233    if (movie->files().size() == 0) {
 234        qWarning() << "Movie has no files";
 235        return nfoFile;
 236    }
 237    QFileInfo fi(movie->files().at(0));
 238    if (!fi.isFile() ) {
 239        qWarning() << "First file of the movie is not readable" << movie->files().at(0);
 240        return nfoFile;
 241    }
 242
 243    foreach (DataFile dataFile, Settings::instance()->dataFiles(DataFileType::MovieNfo)) {
 244        QString file = dataFile.saveFileName(fi.fileName());
 245        QFileInfo nfoFi(fi.absolutePath() + QDir::separator() + file);
 246        if (nfoFi.exists()) {
 247            nfoFile = fi.absolutePath() + QDir::separator() + file;
 248            break;
 249        }
 250    }
 251
 252    return nfoFile;
 253}
 254
 255QString XbmcXml::nfoFilePath(TvShowEpisode *episode)
 256{
 257    QString nfoFile;
 258    if (episode->files().size() == 0) {
 259        qWarning() << "Episode has no files";
 260        return nfoFile;
 261    }
 262    QFileInfo fi(episode->files().at(0));
 263    if (!fi.isFile() ) {
 264        qWarning() << "First file of the episode is not readable" << episode->files().at(0);
 265        return nfoFile;
 266    }
 267
 268    foreach (DataFile dataFile, Settings::instance()->dataFiles(DataFileType::TvShowEpisodeNfo)) {
 269        QString file = dataFile.saveFileName(fi.fileName());
 270        QFileInfo nfoFi(fi.absolutePath() + QDir::separator() + file);
 271        if (nfoFi.exists()) {
 272            nfoFile = fi.absolutePath() + QDir::separator() + file;
 273            break;
 274        }
 275    }
 276
 277    return nfoFile;
 278}
 279
 280/**
 281 * @brief Tries to find an nfo file for the concert
 282 * @param concert Concert
 283 * @return Path to nfo file, if none found returns an empty string
 284 */
 285QString XbmcXml::nfoFilePath(Concert *concert)
 286{
 287    QString nfoFile;
 288    if (concert->files().size() == 0) {
 289        qWarning() << "Concert has no files";
 290        return nfoFile;
 291    }
 292    QFileInfo fi(concert->files().at(0));
 293    if (!fi.isFile() ) {
 294        qWarning() << "First file of the concert is not readable" << concert->files().at(0);
 295        return nfoFile;
 296    }
 297
 298    foreach (DataFile dataFile, Settings::instance()->dataFiles(DataFileType::ConcertNfo)) {
 299        QString file = dataFile.saveFileName(fi.fileName());
 300        QFileInfo nfoFi(fi.absolutePath() + QDir::separator() + file);
 301        if (nfoFi.exists()) {
 302            nfoFile = fi.absolutePath() + QDir::separator() + file;
 303            break;
 304        }
 305    }
 306
 307    return nfoFile;
 308}
 309
 310/**
 311 * @brief Loads movie infos (except images)
 312 * @param movie Movie to load
 313 * @return Loading success
 314 */
 315bool XbmcXml::loadMovie(Movie *movie, QString initialNfoContent)
 316{
 317    movie->clear();
 318    movie->setChanged(false);
 319
 320    QString nfoContent;
 321    if (initialNfoContent.isEmpty()) {
 322        QString nfoFile = nfoFilePath(movie);
 323        if (nfoFile.isEmpty())
 324            return false;
 325
 326        QFile file(nfoFile);
 327        if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
 328            qWarning() << "File" << nfoFile << "could not be opened for reading";
 329            return false;
 330        }
 331        nfoContent = QString::fromUtf8(file.readAll());
 332        movie->setNfoContent(nfoContent);
 333        file.close();
 334    } else {
 335        nfoContent = initialNfoContent;
 336    }
 337
 338    QDomDocument domDoc;
 339    domDoc.setContent(nfoContent);
 340    if (!domDoc.elementsByTagName("title").isEmpty() )
 341        movie->setName(domDoc.elementsByTagName("title").at(0).toElement().text());
 342    if (!domDoc.elementsByTagName("originaltitle").isEmpty())
 343        movie->setOriginalName(domDoc.elementsByTagName("originaltitle").at(0).toElement().text());
 344    if (!domDoc.elementsByTagName("rating").isEmpty())
 345        movie->setRating(domDoc.elementsByTagName("rating").at(0).toElement().text().toFloat());
 346    if (!domDoc.elementsByTagName("votes").isEmpty())
 347        movie->setVotes(domDoc.elementsByTagName("votes").at(0).toElement().text().toInt());
 348    if (!domDoc.elementsByTagName("top250").isEmpty())
 349        movie->setTop250(domDoc.elementsByTagName("top250").at(0).toElement().text().toInt());
 350    if (!domDoc.elementsByTagName("year").isEmpty())
 351        movie->setReleased(QDate::fromString(domDoc.elementsByTagName("year").at(0).toElement().text(), "yyyy"));
 352    if (!domDoc.elementsByTagName("plot").isEmpty())
 353        movie->setOverview(domDoc.elementsByTagName("plot").at(0).toElement().text());
 354    if (!domDoc.elementsByTagName("outline").isEmpty())
 355        movie->setOutline(domDoc.elementsByTagName("outline").at(0).toElement().text());
 356    if (!domDoc.elementsByTagName("tagline").isEmpty())
 357        movie->setTagline(domDoc.elementsByTagName("tagline").at(0).toElement().text());
 358    if (!domDoc.elementsByTagName("runtime").isEmpty())
 359        movie->setRuntime(domDoc.elementsByTagName("runtime").at(0).toElement().text().toInt());
 360    if (!domDoc.elementsByTagName("mpaa").isEmpty())
 361        movie->setCertification(domDoc.elementsByTagName("mpaa").at(0).toElement().text());
 362    if (!domDoc.elementsByTagName("credits").isEmpty())
 363        movie->setWriter(domDoc.elementsByTagName("credits").at(0).toElement().text());
 364    if (!domDoc.elementsByTagName("director").isEmpty())
 365        movie->setDirector(domDoc.elementsByTagName("director").at(0).toElement().text());
 366    if (!domDoc.elementsByTagName("playcount").isEmpty())
 367        movie->setPlayCount(domDoc.elementsByTagName("playcount").at(0).toElement().text().toInt());
 368    if (!domDoc.elementsByTagName("lastplayed").isEmpty())
 369        movie->setLastPlayed(QDateTime::fromString(domDoc.elementsByTagName("lastplayed").at(0).toElement().text(), "yyyy-MM-dd HH:mm:ss"));
 370    if (!domDoc.elementsByTagName("id").isEmpty())
 371        movie->setId(domDoc.elementsByTagName("id").at(0).toElement().text());
 372    if (!domDoc.elementsByTagName("tmdbid").isEmpty())
 373        movie->setTmdbId(domDoc.elementsByTagName("tmdbid").at(0).toElement().text());
 374    if (!domDoc.elementsByTagName("set").isEmpty())
 375        movie->setSet(domDoc.elementsByTagName("set").at(0).toElement().text());
 376    if (!domDoc.elementsByTagName("sorttitle").isEmpty())
 377        movie->setSortTitle(domDoc.elementsByTagName("sorttitle").at(0).toElement().text());
 378    if (!domDoc.elementsByTagName("trailer").isEmpty())
 379        movie->setTrailer(QUrl(domDoc.elementsByTagName("trailer").at(0).toElement().text()));
 380    if (!domDoc.elementsByTagName("watched").isEmpty())
 381        movie->setWatched(domDoc.elementsByTagName("watched").at(0).toElement().text() == "true" ? true : false);
 382
 383    for (int i=0, n=domDoc.elementsByTagName("studio").size() ; i<n ; i++)
 384        movie->addStudio(domDoc.elementsByTagName("studio").at(i).toElement().text());
 385    for (int i=0, n=domDoc.elementsByTagName("genre").size() ; i<n ; i++)
 386        movie->addGenre(domDoc.elementsByTagName("genre").at(i).toElement().text());
 387    for (int i=0, n=domDoc.elementsByTagName("country").size() ; i<n ; i++)
 388        movie->addCountry(domDoc.elementsByTagName("country").at(i).toElement().text());
 389    for (int i=0, n=domDoc.elementsByTagName("tag").size() ; i<n ; i++)
 390        movie->addTag(domDoc.elementsByTagName("tag").at(i).toElement().text());
 391    for (int i=0, n=domDoc.elementsByTagName("actor").size() ; i<n ; i++) {
 392        Actor a;
 393        a.imageHasChanged = false;
 394        if (!domDoc.elementsByTagName("actor").at(i).toElement().elementsByTagName("name").isEmpty())
 395            a.name = domDoc.elementsByTagName("actor").at(i).toElement().elementsByTagName("name").at(0).toElement().text();
 396        if (!domDoc.elementsByTagName("actor").at(i).toElement().elementsByTagName("role").isEmpty())
 397            a.role = domDoc.elementsByTagName("actor").at(i).toElement().elementsByTagName("role").at(0).toElement().text();
 398        if (!domDoc.elementsByTagName("actor").at(i).toElement().elementsByTagName("thumb").isEmpty())
 399            a.thumb = domDoc.elementsByTagName("actor").at(i).toElement().elementsByTagName("thumb").at(0).toElement().text();
 400        movie->addActor(a);
 401    }
 402    for (int i=0, n=domDoc.elementsByTagName("thumb").size() ; i<n ; i++) {
 403        QString parentTag = domDoc.elementsByTagName("thumb").at(i).parentNode().toElement().tagName();
 404        if (parentTag == "movie") {
 405            Poster p;
 406            p.originalUrl = QUrl(domDoc.elementsByTagName("thumb").at(i).toElement().text());
 407            p.thumbUrl = QUrl(domDoc.elementsByTagName("thumb").at(i).toElement().attribute("preview"));
 408            movie->addPoster(p);
 409        } else if (parentTag == "fanart") {
 410            Poster p;
 411            p.originalUrl = QUrl(domDoc.elementsByTagName("thumb").at(i).toElement().text());
 412            p.thumbUrl = QUrl(domDoc.elementsByTagName("thumb").at(i).toElement().attribute("preview"));
 413            movie->addBackdrop(p);
 414        }
 415    }
 416
 417    movie->setStreamDetailsLoaded(loadStreamDetails(movie->streamDetails(), domDoc));
 418
 419    // Existence of images
 420    if (initialNfoContent.isEmpty()) {
 421        movie->setHasPoster(!posterImageName(movie).isEmpty());
 422        movie->setHasBackdrop(!backdropImageName(movie).isEmpty());
 423        movie->setHasLogo(!logoImageName(movie).isEmpty());
 424        movie->setHasClearArt(!clearArtImageName(movie).isEmpty());
 425        movie->setHasCdArt(!cdArtImageName(movie).isEmpty());
 426    }
 427
 428    return true;
 429}
 430
 431/**
 432 * @brief Loads the stream details from the dom document
 433 * @param streamDetails StreamDetails object
 434 * @param domDoc Nfo document
 435 * @return Infos loaded
 436 */
 437bool XbmcXml::loadStreamDetails(StreamDetails* streamDetails, QDomDocument domDoc)
 438{
 439    streamDetails->clear();
 440    if (!domDoc.elementsByTagName("streamdetails").isEmpty()) {
 441        QDomElement elem = domDoc.elementsByTagName("streamdetails").at(0).toElement();
 442        if (!elem.elementsByTagName("video").isEmpty()) {
 443            QDomElement videoElem = elem.elementsByTagName("video").at(0).toElement();
 444            QStringList details = (QStringList() << "codec" << "aspect" << "width" << "height" << "durationinseconds" << "scantype");
 445            foreach (const QString &detail, details) {
 446                if (!videoElem.elementsByTagName(detail).isEmpty())
 447                    streamDetails->setVideoDetail(detail, videoElem.elementsByTagName(detail).at(0).toElement().text());
 448            }
 449        }
 450        if (!elem.elementsByTagName("audio").isEmpty()) {
 451            for (int i=0, n=elem.elementsByTagName("audio").count() ; i<n ; ++i) {
 452                QStringList details = QStringList() << "codec" << "language" << "channels";
 453                QDomElement audioElem = elem.elementsByTagName("audio").at(i).toElement();
 454                foreach (const QString &detail, details) {
 455                    if (!audioElem.elementsByTagName(detail).isEmpty())
 456                        streamDetails->setAudioDetail(i, detail, audioElem.elementsByTagName(detail).at(0).toElement().text());
 457                }
 458            }
 459        }
 460        if (!elem.elementsByTagName("subtitle").isEmpty()) {
 461            for (int i=0, n=elem.elementsByTagName("subtitle").count() ; i<n ; ++i) {
 462                QStringList details = QStringList() << "language";
 463                QDomElement subtitleElem = elem.elementsByTagName("subtitle").at(i).toElement();
 464                foreach (const QString &detail, details) {
 465                    if (!subtitleElem.elementsByTagName(detail).isEmpty())
 466                        streamDetails->setSubtitleDetail(i, detail, subtitleElem.elementsByTagName(detail).at(0).toElement().text());
 467                }
 468            }
 469        }
 470        return true;
 471    }
 472    return false;
 473}
 474
 475/**
 476 * @brief Writes streamdetails to xml stream
 477 * @param xml XML Stream
 478 * @param streamDetails Stream Details object
 479 */
 480void XbmcXml::writeStreamDetails(QXmlStreamWriter &xml, StreamDetails *streamDetails)
 481{
 482    xml.writeStartElement("streamdetails");
 483
 484    xml.writeStartElement("video");
 485    QMapIterator<QString, QString> itVideo(streamDetails->videoDetails());
 486    while (itVideo.hasNext()) {
 487        itVideo.next();
 488        if (itVideo.key() == "width" && itVideo.value().toInt() == 0)
 489            continue;
 490        if (itVideo.key() == "height" && itVideo.value().toInt() == 0)
 491            continue;
 492        if (itVideo.key() == "durationinseconds" && itVideo.value().toInt() == 0)
 493            continue;
 494        if (itVideo.value() == "")
 495            continue;
 496        xml.writeTextElement(itVideo.key(), itVideo.value());
 497    }
 498    xml.writeEndElement();
 499
 500    for (int i=0, n=streamDetails->audioDetails().count() ; i<n ; ++i) {
 501        xml.writeStartElement("audio");
 502        QMapIterator<QString, QString> itAudio(streamDetails->audioDetails().at(i));
 503        while (itAudio.hasNext()) {
 504            itAudio.next();
 505            if (itAudio.value() == "")
 506                continue;
 507            xml.writeTextElement(itAudio.key(), itAudio.value());
 508        }
 509        xml.writeEndElement();
 510    }
 511
 512    for (int i=0, n=streamDetails->subtitleDetails().count() ; i<n ; ++i) {
 513        xml.writeStartElement("subtitle");
 514        QMapIterator<QString, QString> itSubtitle(streamDetails->subtitleDetails().at(i));
 515        while (itSubtitle.hasNext()) {
 516            itSubtitle.next();
 517            if (itSubtitle.value() == "")
 518                continue;
 519            xml.writeTextElement(itSubtitle.key(), itSubtitle.value());
 520        }
 521        xml.writeEndElement();
 522    }
 523
 524    xml.writeEndElement();
 525}
 526
 527/**
 528 * @brief Get the path to the actor image
 529 * @param movie
 530 * @param actor Actor
 531 * @return Path to actor image
 532 */
 533QString XbmcXml::actorImageName(Movie *movie, Actor actor)
 534{
 535    if (movie->files().isEmpty())
 536        return QString();
 537    QFileInfo fi(movie->files().at(0));
 538    QString actorName = actor.name;
 539    actorName = actorName.replace(" ", "_");
 540    QString path = fi.absolutePath() + QDir::separator() + ".actors" + QDir::separator() + actorName + ".tbn";
 541    fi.setFile(path);
 542    if (fi.isFile())
 543        return path;
 544    return QString();
 545}
 546
 547/**
 548 * @brief Get the path to the movie poster
 549 * @param movie Movie object
 550 * @return Path to poster image
 551 */
 552QString XbmcXml::posterImageName(Movie *movie, QList<DataFile> dataFiles, bool constructName)
 553{
 554    QString posterFileName;
 555    if (movie->files().size() == 0) {
 556        qWarning() << "Movie has no files";
 557        return posterFileName;
 558    }
 559    QFileInfo fi(movie->files().at(0));
 560
 561    if (!constructName)
 562        dataFiles = Settings::instance()->dataFiles(DataFileType::MoviePoster);
 563
 564    foreach (DataFile dataFile, dataFiles) {
 565        QString file = dataFile.saveFileName(fi.fileName(), -1, movie->files().count() > 1);
 566        QFileInfo pFi(fi.absolutePath() + QDir::separator() + file);
 567        if (pFi.isFile() || constructName) {
 568            posterFileName = fi.absolutePath() + QDir::separator() + file;
 569            break;
 570        }
 571    }
 572
 573    return posterFileName;
 574}
 575
 576/**
 577 * @brief Get the path to the movie backdrop
 578 * @param movie Movie object
 579 * @return Path to backdrop image
 580 */
 581QString XbmcXml::backdropImageName(Movie *movie, QList<DataFile> dataFiles, bool constructName)
 582{
 583    QString fanartFileName;
 584    if (movie->files().size() == 0) {
 585        qWarning() << "Movie has no files";
 586        return fanartFileName;
 587    }
 588    QFileInfo fi(movie->files().at(0));
 589
 590    if (!constructName)
 591        dataFiles = Settings::instance()->dataFiles(DataFileType::MovieBackdrop);
 592
 593    foreach (DataFile dataFile, dataFiles) {
 594        QString file = dataFile.saveFileName(fi.fileName(), -1, movie->files().count() > 1);
 595        QFileInfo bFi(fi.absolutePath() + QDir::separator() + file);
 596        if (bFi.isFile() || constructName) {
 597            fanartFileName = fi.absolutePath() + QDir::separator() + file;
 598            break;
 599        }
 600    }
 601
 602    return fanartFileName;
 603}
 604
 605/**
 606 * @brief Get the path to the movie logo
 607 * @param movie Movie object
 608 * @return Path to logo image
 609 */
 610QString XbmcXml::logoImageName(Movie *movie, QList<DataFile> dataFiles, bool constructName)
 611{
 612    QString logoFileName;
 613    if (movie->files().size() == 0) {
 614        qWarning() << "Movie has no files";
 615        return logoFileName;
 616    }
 617    QFileInfo fi(movie->files().at(0));
 618
 619    if (!constructName)
 620        dataFiles = Settings::instance()->dataFiles(DataFileType::MovieLogo);
 621
 622    foreach (DataFile dataFile, dataFiles) {
 623        QString file = dataFile.saveFileName(fi.fileName());
 624        QFileInfo bFi(fi.absolutePath() + QDir::separator() + file);
 625        if (bFi.isFile() || constructName) {
 626            logoFileName = fi.absolutePath() + QDir::separator() + file;
 627            break;
 628        }
 629    }
 630
 631    return logoFileName;
 632}
 633
 634/**
 635 * @brief Get the path to the movie clear art
 636 * @param movie Movie object
 637 * @return Path to clear art image
 638 */
 639QString XbmcXml::clearArtImageName(Movie *movie, QList<DataFile> dataFiles, bool constructName)
 640{
 641    QString clearArtFileName;
 642    if (movie->files().size() == 0) {
 643        qWarning() << "Movie has no files";
 644        return clearArtFileName;
 645    }
 646    QFileInfo fi(movie->files().at(0));
 647
 648    if (!constructName)
 649        dataFiles = Settings::instance()->dataFiles(DataFileType::MovieClearArt);
 650
 651    foreach (DataFile dataFile, dataFiles) {
 652        QString file = dataFile.saveFileName(fi.fileName());
 653        QFileInfo bFi(fi.absolutePath() + QDir::separator() + file);
 654        if (bFi.isFile() || constructName) {
 655            clearArtFileName = fi.absolutePath() + QDir::separator() + file;
 656            break;
 657        }
 658    }
 659
 660    return clearArtFileName;
 661}
 662
 663/**
 664 * @brief Get the path to the movie cd art
 665 * @param movie Movie object
 666 * @return Path to cd art image
 667 */
 668QString XbmcXml::cdArtImageName(Movie *movie, QList<DataFile> dataFiles, bool constructName)
 669{
 670    QString cdArtFileName;
 671    if (movie->files().size() == 0) {
 672        qWarning() << "Movie has no files";
 673        return cdArtFileName;
 674    }
 675    QFileInfo fi(movie->files().at(0));
 676
 677    if (!constructName)
 678        dataFiles = Settings::instance()->dataFiles(DataFileType::MovieCdArt);
 679
 680    foreach (DataFile dataFile, dataFiles) {
 681        QString file = dataFile.saveFileName(fi.fileName());
 682        QFileInfo bFi(fi.absolutePath() + QDir::separator() + file);
 683        if (bFi.isFile() || constructName) {
 684            cdArtFileName = fi.absolutePath() + QDir::separator() + file;
 685            break;
 686        }
 687    }
 688
 689    return cdArtFileName;
 690}
 691
 692/**
 693 * @brief Writes concert elements to an xml stream
 694 * @param xml XML stream
 695 * @param concert Concert to save
 696 */
 697void XbmcXml::writeConcertXml(QXmlStreamWriter &xml, Concert *concert)
 698{
 699    qDebug() << "Entered, concert=" << concert->name();
 700    xml.writeStartElement("musicvideo");
 701    xml.writeTextElement("title", concert->name());
 702    xml.writeTextElement("artist", concert->artist());
 703    xml.writeTextElement("album", concert->album());
 704    xml.writeTextElement("id", concert->id());
 705    xml.writeTextElement("tmdbid", concert->tmdbId());
 706    xml.writeTextElement("rating", QString("%1").arg(concert->rating()));
 707    xml.writeTextElement("year", concert->released().toString("yyyy"));
 708    xml.writeTextElement("plot", concert->overview());
 709    xml.writeTextElement("outline", concert->overview());
 710    xml.writeTextElement("tagline", concert->tagline());
 711    if (concert->runtime() > 0)
 712        xml.writeTextElement("runtime", QString("%1").arg(concert->runtime()));
 713    xml.writeTextElement("mpaa", concert->certification());
 714    xml.writeTextElement("playcount", QString("%1").arg(concert->playcount()));
 715    xml.writeTextElement("lastplayed", concert->lastPlayed().toString("yyyy-MM-dd HH:mm:ss"));
 716    xml.writeTextElement("trailer", Helper::formatTrailerUrl(concert->trailer().toString()));
 717    xml.writeTextElement("watched", (concert->watched()) ? "true" : "false");
 718    foreach (const QString &genre, concert->genres())
 719        xml.writeTextElement("genre", genre);
 720    foreach (const QString &tag, concert->tags())
 721        xml.writeTextElement("tag", tag);
 722    foreach (const Poster &poster, concert->posters()) {
 723        xml.writeStartElement("thumb");
 724        xml.writeAttribute("preview", poster.thumbUrl.toString());
 725        xml.writeCharacters(poster.originalUrl.toString());
 726        xml.writeEndElement();
 727    }
 728    xml.writeStartElement("fanart");
 729    foreach (const Poster &poster, concert->backdrops()) {
 730        xml.writeStartElement("thumb");
 731        xml.writeAttribute("preview", poster.thumbUrl.toString());
 732        xml.writeCharacters(poster.originalUrl.toString());
 733        xml.writeEndElement();
 734    }
 735    xml.writeEndElement();
 736
 737    xml.writeStartElement("fileinfo");
 738    writeStreamDetails(xml, concert->streamDetails());
 739    xml.writeEndElement();
 740
 741    xml.writeEndElement();
 742}
 743
 744/**
 745 * @brief Saves a concert (including images)
 746 * @param concert Concert to save
 747 * @return Saving success
 748 * @see XbmcXml::writeConcertXml
 749 */
 750bool XbmcXml::saveConcert(Concert *concert)
 751{
 752    qDebug() << "Entered, movie=" << concert->name();
 753    QByteArray xmlContent;
 754    QXmlStreamWriter xml(&xmlContent);
 755    xml.setAutoFormatting(true);
 756    xml.writeStartDocument("1.0", true);
 757    writeConcertXml(xml, concert);
 758    xml.writeEndDocument();
 759
 760    if (concert->files().size() == 0) {
 761        qWarning() << "Movie has no files";
 762        return false;
 763    }
 764
 765    concert->setNfoContent(xmlContent);
 766    Manager::instance()->database()->update(concert);
 767
 768    bool saved = false;
 769    QFileInfo fi(concert->files().at(0));
 770    foreach (DataFile dataFile, Settings::instance()->dataFiles(DataFileType::ConcertNfo)) {
 771        QString saveFileName = dataFile.saveFileName(fi.fileName());
 772        QFile file(fi.absolutePath() + QDir::separator() + saveFileName);
 773        qDebug() << "Saving to" << file.fileName();
 774        if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
 775            qWarning() << "File could not be openend";
 776        } else {
 777            file.write(xmlContent);
 778            file.close();
 779            saved = true;
 780        }
 781    }
 782    if (!saved)
 783        return false;
 784
 785    if (concert->posterImageChanged() && !concert->posterImage()->isNull()) {
 786        foreach (DataFile dataFile, Settings::instance()->dataFiles(DataFileType::ConcertPoster)) {
 787            QString saveFileName = dataFile.saveFileName(fi.fileName());
 788            qDebug() << "Saving poster to" << fi.absolutePath() + QDir::separator() + saveFileName;
 789            concert->posterImage()->save(fi.absolutePath() + QDir::separator() + saveFileName, "jpg", 100);
 790        }
 791    }
 792    if (concert->backdropImageChanged() && !concert->backdropImage()->isNull()) {
 793        foreach (DataFile dataFile, Settings::instance()->dataFiles(DataFileType::ConcertBackdrop)) {
 794            QString saveFileName = dataFile.saveFileName(fi.fileName());
 795            qDebug() << "Saving fanart to" << fi.absolutePath() + QDir::separator() + saveFileName;
 796            concert->backdropImage()->save(fi.absolutePath() + QDir::separator() + saveFileName, "jpg", 100);
 797        }
 798    }
 799    saveAdditionalImages(concert);
 800
 801    return true;
 802}
 803
 804/**
 805 * @brief Saves additional concert images (logo, clear art, cd art)
 806 * @param concert Concert object
 807 */
 808void XbmcXml::saveAdditionalImages(Concert *concert)
 809{
 810    QFileInfo fi(concert->files().at(0));
 811    if (concert->logoImageChanged() && !concert->logoImage()->isNull()) {
 812        foreach (DataFile dataFile, Settings::instance()->dataFiles(DataFileType::ConcertLogo)) {
 813            QString saveFileName = dataFile.saveFileName(fi.fileName());
 814            qDebug() << "Saving logo to" << fi.absolutePath() + QDir::separator() + saveFileName;
 815            concert->logoImage()->save(fi.absolutePath() + QDir::separator() + saveFileName, "png", 100);
 816        }
 817    }
 818    if (concert->clearArtImageChanged() && !concert->clearArtImage()->isNull()) {
 819        foreach (DataFile dataFile, Settings::instance()->dataFiles(DataFileType::ConcertClearArt)) {
 820            QString saveFileName = dataFile.saveFileName(fi.fileName());
 821            qDebug() << "Saving clear art to" << fi.absolutePath() + QDir::separator() + saveFileName;
 822            concert->clearArtImage()->save(fi.absolutePath() + QDir::separator() + saveFileName, "png", 100);
 823        }
 824    }
 825    if (concert->cdArtImageChanged() && !concert->cdArtImage()->isNull()) {
 826        foreach (DataFile dataFile, Settings::instance()->dataFiles(DataFileType::ConcertCdArt)) {
 827            QString saveFileName = dataFile.saveFileName(fi.fileName());
 828            qDebug() << "Saving cd art to" << fi.absolutePath() + QDir::separator() + saveFileName;
 829            concert->cdArtImage()->save(fi.absolutePath() + QDir::separator() + saveFileName, "png", 100);
 830        }
 831    }
 832
 833    if (concert->inSeparateFolder() && !concert->files().isEmpty()) {
 834        foreach (const QString &file, concert->extraFanartsToRemove())
 835            QFile::remove(file);
 836        QDir dir(QFileInfo(concert->files().first()).absolutePath() + "/extrafanart");
 837        if (!dir.exists() && !concert->extraFanartImagesToAdd().isEmpty())
 838            QDir(QFileInfo(concert->files().first()).absolutePath()).mkdir("extrafanart");
 839        foreach (QImage img, concert->extraFanartImagesToAdd()) {
 840            int num = 1;
 841            while (QFileInfo(dir.absolutePath() + "/" + QString("fanart%1.jpg").arg(num)).exists())
 842                ++num;
 843            img.save(dir.absolutePath() + "/" + QString("fanart%1.jpg").arg(num), "jpg", 100);
 844        }
 845    }
 846}
 847
 848/**
 849 * @brief Loads concert infos (except images)
 850 * @param concert Concert to load
 851 * @return Loading success
 852 */
 853bool XbmcXml::loadConcert(Concert *concert, QString initialNfoContent)
 854{
 855    concert->clear();
 856    concert->setChanged(false);
 857
 858    QString nfoContent;
 859    if (initialNfoContent.isEmpty()) {
 860        QString nfoFile = nfoFilePath(concert);
 861        if (nfoFile.isEmpty())
 862            return false;
 863
 864        QFile file(nfoFile);
 865        if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
 866            qWarning() << "File" << nfoFile << "could not be opened for reading";
 867            return false;
 868        }
 869        nfoContent = QString::fromUtf8(file.readAll());
 870        concert->setNfoContent(nfoContent);
 871        file.close();
 872    } else {
 873        nfoContent = initialNfoContent;
 874    }
 875
 876    QDomDocument domDoc;
 877    domDoc.setContent(nfoContent);
 878    if (!domDoc.elementsByTagName("id").isEmpty() )
 879        concert->setId(domDoc.elementsByTagName("id").at(0).toElement().text());
 880    if (!domDoc.elementsByTagName("tmdbid").isEmpty() )
 881        concert->setTmdbId(domDoc.elementsByTagName("tmdbid").at(0).toElement().text());
 882    if (!domDoc.elementsByTagName("title").isEmpty() )
 883        concert->setName(domDoc.elementsByTagName("title").at(0).toElement().text());
 884    if (!domDoc.elementsByTagName("artist").isEmpty() )
 885        concert->setArtist(domDoc.elementsByTagName("artist").at(0).toElement().text());
 886    if (!domDoc.elementsByTagName("album").isEmpty() )
 887        concert->setAlbum(domDoc.elementsByTagName("album").at(0).toElement().text());
 888    if (!domDoc.elementsByTagName("rating").isEmpty())
 889        concert->setRating(domDoc.elementsByTagName("rating").at(0).toElement().text().toFloat());
 890    if (!domDoc.elementsByTagName("year").isEmpty())
 891        concert->setReleased(QDate::fromString(domDoc.elementsByTagName("year").at(0).toElement().text(), "yyyy"));
 892    if (!domDoc.elementsByTagName("plot").isEmpty())
 893        concert->setOverview(domDoc.elementsByTagName("plot").at(0).toElement().text());
 894    if (!domDoc.elementsByTagName("tagline").isEmpty())
 895        concert->setTagline(domDoc.elementsByTagName("tagline").at(0).toElement().text());
 896    if (!domDoc.elementsByTagName("runtime").isEmpty())
 897        concert->setRuntime(domDoc.elementsByTagName("runtime").at(0).toElement().text().toInt());
 898    if (!domDoc.elementsByTagName("mpaa").isEmpty())
 899        concert->setCertification(domDoc.elementsByTagName("mpaa").at(0).toElement().text());
 900    if (!domDoc.elementsByTagName("playcount").isEmpty())
 901        concert->setPlayCount(domDoc.elementsByTagName("playcount").at(0).toElement().text().toInt());
 902    if (!domDoc.elementsByTagName("lastplayed").isEmpty())
 903        concert->setLastPlayed(QDateTime::fromString(domDoc.elementsByTagName("lastplayed").at(0).toElement().text(), "yyyy-MM-dd HH:mm:ss"));
 904    if (!domDoc.elementsByTagName("trailer").isEmpty())
 905        concert->setTrailer(QUrl(domDoc.elementsByTagName("trailer").at(0).toElement().text()));
 906    if (!domDoc.elementsByTagName("watched").isEmpty())
 907        concert->setWatched(domDoc.elementsByTagName("watched").at(0).toElement().text() == "true" ? true : false);
 908
 909    for (int i=0, n=domDoc.elementsByTagName("genre").size() ; i<n ; i++)
 910        concert->addGenre(domDoc.elementsByTagName("genre").at(i).toElement().text());
 911    for (int i=0, n=domDoc.elementsByTagName("tag").size() ; i<n ; i++)
 912        concert->addTag(domDoc.elementsByTagName("tag").at(i).toElement().text());
 913    for (int i=0, n=domDoc.elementsByTagName("thumb").size() ; i<n ; i++) {
 914        QString parentTag = domDoc.elementsByTagName("thumb").at(i).parentNode().toElement().tagName();
 915        if (parentTag == "movie") {
 916            Poster p;
 917            p.originalUrl = QUrl(domDoc.elementsByTagName("thumb").at(i).toElement().text());
 918            p.thumbUrl = QUrl(domDoc.elementsByTagName("thumb").at(i).toElement().attribute("preview"));
 919            concert->addPoster(p);
 920        } else if (parentTag == "fanart") {
 921            Poster p;
 922            p.originalUrl = QUrl(domDoc.elementsByTagName("thumb").at(i).toElement().text());
 923            p.thumbUrl = QUrl(domDoc.elementsByTagName("thumb").at(i).toElement().attribute("preview"));
 924            concert->addBackdrop(p);
 925        }
 926    }
 927
 928    concert->setStreamDetailsLoaded(loadStreamDetails(concert->streamDetails(), domDoc));
 929
 930    return true;
 931}
 932
 933/**
 934 * @brief Get the path to the concert poster
 935 * @param concert Concert object
 936 * @return Path to poster image
 937 */
 938QString XbmcXml::posterImageName(Concert *concert, QList<DataFile> dataFiles, bool constructName)
 939{
 940    if (concert->files().count() == 0)
 941        return QString();
 942
 943    if (!constructName)
 944        dataFiles = Settings::instance()->dataFiles(DataFileType::ConcertPoster);
 945
 946    QFileInfo fi(concert->files().at(0));
 947    QString posterFileName;
 948    foreach (DataFile dataFile, dataFiles) {
 949        QString file = dataFile.saveFileName(fi.fileName());
 950        QFileInfo pFi(fi.absolutePath() + QDir::separator() + file);
 951        if (pFi.isFile() || constructName) {
 952            posterFileName = fi.absolutePath() + QDir::separator() + file;
 953            break;
 954        }
 955    }
 956    fi.setFile(posterFileName);
 957    if (fi.isFile())
 958        return posterFileName;
 959    return QString();
 960}
 961
 962/**
 963 * @brief Get the path to the concert backdrop
 964 * @param concert Concert object
 965 * @return Path to backdrop image
 966 */
 967QString XbmcXml::backdropImageName(Concert *concert, QList<DataFile> dataFiles, bool constructName)
 968{
 969    if (concert->files().count() == 0)
 970        return QString();
 971
 972    if (!constructName)
 973        dataFiles = Settings::instance()->dataFiles(DataFileType::ConcertBackdrop);
 974
 975    QFileInfo fi(concert->files().at(0));
 976    QString fanartFileName;
 977    foreach (DataFile dataFile, dataFiles) {
 978        QString file = dataFile.saveFileName(fi.fileName());
 979        QFileInfo bFi(fi.absolutePath() + QDir::separator() + file);
 980        if (bFi.isFile() || constructName) {
 981            fanartFileName = fi.absolutePath() + QDir::separator() + file;
 982            break;
 983        }
 984    }
 985    fi.setFile(fanartFileName);
 986    if (fi.isFile())
 987        return fanartFileName;
 988    return QString();
 989}
 990
 991/**
 992 * @brief Get the path to the concert logo
 993 * @param concert Concert object
 994 * @return Path to logo image
 995 */
 996QString XbmcXml::logoImageName(Concert *concert, QList<DataFile> dataFiles, bool constructName)
 997{
 998    QString logoFileName;
 999    if (concert->files().size() == 0) {
1000        qWarning() << "Concert has no files";
1001        return logoFileName;
1002    }
1003    QFileInfo fi(concert->files().at(0));
1004
1005    if (!constructName)
1006        dataFiles = Settings::instance()->dataFiles(DataFileType::ConcertLogo);
1007
1008    foreach (DataFile dataFile, dataFiles) {
1009        QString file = dataFile.saveFileName(fi.fileName());
1010        QFileInfo bFi(fi.absolutePath() + QDir::separator() + file);
1011        if (bFi.isFile() || constructName) {
1012            logoFileName = fi.absolutePath() + QDir::separator() + file;
1013            break;
1014        }
1015    }
1016
1017    return logoFileName;
1018}
1019
1020/**
1021 * @brief Get the path to the concert clear art
1022 * @param concert Concert object
1023 * @return Path to clear art image
1024 */
1025QString XbmcXml::clearArtImageName(Concert *concert, QList<DataFile> dataFiles, bool constructName)
1026{
1027    QString clearArtFileName;
1028    if (concert->files().size() == 0) {
1029        qWarning() << "Concert has no files";
1030        return clearArtFileName;
1031    }
1032    QFileInfo fi(concert->files().at(0));
1033
1034    if (!constructName)
1035        dataFiles = Settings::instance()->dataFiles(DataFileType::ConcertClearArt);
1036
1037    foreach (DataFile dataFile, dataFiles) {
1038        QString file = dataFile.saveFileName(fi.fileName());
1039        QFileInfo bFi(fi.absolutePath() + QDir::separator() + file);
1040        if (bFi.isFile() || constructName) {
1041            clearArtFileName = fi.absolutePath() + QDir::separator() + file;
1042            break;
1043        }
1044    }
1045
1046    return clearArtFileName;
1047}
1048
1049/**
1050 * @brief Get the path to the concert cd art
1051 * @param concert Concert object
1052 * @return Path to cd art image
1053 */
1054QString XbmcXml::cdArtImageName(Concert *concert, QList<DataFile> dataFiles, bool constructName)
1055{
1056    QString cdArtFileName;
1057    if (concert->files().size() == 0) {
1058        qWarning() << "Concert has no files";
1059        return cdArtFileName;
1060    }
1061    QFileInfo fi(concert->files().at(0));
1062
1063    if (!constructName)
1064        dataFiles = Settings::instance()->dataFiles(DataFileType::ConcertCdArt);
1065
1066    foreach (DataFile dataFile, dataFiles) {
1067        QString file = dataFile.saveFileName(fi.fileName());
1068        QFileInfo bFi(fi.absolutePath() + QDir::separator() + file);
1069        if (bFi.isFile() || constructName) {
1070            cdArtFileName = fi.absolutePath() + QDir::separator() + file;
1071            break;
1072        }
1073    }
1074
1075    return cdArtFileName;
1076}
1077
1078/**
1079 * @brief Get path to poster image
1080 * @param show
1081 * @return
1082 */
1083QString XbmcXml::posterImageName(TvShow *show, QList<DataFile> dataFiles, bool constructName)
1084{
1085    if (show->dir().isEmpty())
1086        return QString();
1087
1088    if (!constructName)
1089        dataFiles = Settings::instance()->dataFiles(DataFileType::TvShowPoster);
1090
1091    QString posterFileName;
1092    foreach (DataFile dataFile, dataFiles) {
1093        QString loadFileName = dataFile.saveFileName("");
1094        QFileInfo posterFi(show->dir() + QDir::separator() + loadFileName);
1095        if (posterFi.isFile() || constructName) {
1096            posterFileName = show->dir() + QDir::separator() + loadFileName;
1097            break;
1098        }
1099    }
1100    QFileInfo fi(posterFileName);
1101    if (fi.isFile())
1102        return posterFileName;
1103    return QString();
1104}
1105
1106/**
1107 * @brief Gets path to backdrop image
1108 * @param show
1109 * @return
1110 */
1111QString XbmcXml::backdropImageName(TvShow *show, QList<DataFile> dataFiles, bool constructName)
1112{
1113    if (show->dir().isEmpty())
1114        return QString();
1115
1116    if (!constructName)
1117        dataFiles = Settings::instance()->dataFiles(DataFileType::TvShowBackdrop);
1118
1119    QString posterFileName;
1120    foreach (DataFile dataFile, dataFiles) {
1121        QString loadFileName = dataFile.saveFileName("");
1122        QFileInfo posterFi(show->dir() + QDir::separator() + loadFileName);
1123        if (posterFi.isFile() || constructName) {
1124            posterFileName = show->dir() + QDir::separator() + loadFileName;
1125            break;
1126        }
1127    }
1128    QFileInfo fi(posterFileName);
1129    if (fi.isFile())
1130        return posterFileName;
1131    return QString();
1132}
1133
1134/**
1135 * @brief Get path to banner image
1136 * @param show
1137 * @return
1138 */
1139QString XbmcXml::bannerImageName(TvShow *show, QList<DataFile> dataFiles, bool constructName)
1140{
1141    if (show->dir().isEmpty())
1142        return QString();
1143
1144    if (!constructName)
1145        dataFiles = Settings::instance()->dataFiles(DataFileType::TvShowBanner);
1146
1147    QString bannerFileName;
1148    foreach (DataFile dataFile, dataFiles) {
1149        QString loadFileName = dataFile.saveFileName("");
1150        QFileInfo bannerFi(show->dir() + QDir::separator() + loadFileName);
1151        if (bannerFi.isFile() || constructName) {
1152            bannerFileName = show->dir() + QDir::separator() + loadFileName;
1153            break;
1154        }
1155    }
1156    QFileInfo fi(bannerFileName);
1157    if (fi.isFile())
1158        return bannerFileName;
1159    return QString();
1160}
1161
1162/**
1163 * @brief Get the path to the tv show logo
1164 * @param show TV Show object
1165 * @return Path to logo image
1166 */
1167QString XbmcXml::logoImageName(TvShow *show, QList<DataFile> dataFiles, bool constructName)
1168{
1169    QString logoFileName;
1170    if (show->dir().isEmpty())
1171        return logoFileName;
1172
1173    if (!constructName)
1174        dataFiles = Settings::instance()->dataFiles(DataFileType::TvShowLogo);
1175
1176    foreach (DataFile dataFile, dataFiles) {
1177        QString file = dataFile.saveFileName("");
1178        QFileInfo fi(show->dir() + QDir::separator() + file);
1179        if (fi.isFile() || constructName) {
1180            logoFileName = show->dir() + QDir::separator() + file;
1181            break;
1182        }
1183    }
1184
1185    return logoFileName;
1186}
1187
1188/**
1189 * @brief Get the path to the tv show clear art
1190 * @param show TV Show object
1191 * @return Path to clear art image
1192 */
1193QString XbmcXml::clearArtImageName(TvShow *show, QList<DataFile> dataFiles, bool constructName)
1194{
1195    QString clearArtFileName;
1196    if (show->dir().isEmpty())
1197        return clearArtFileName;
1198
1199    if (!constructName)
1200        dataFiles = Settings::instance()->dataFiles(DataFileType::TvShowClearArt);
1201
1202    foreach (DataFile dataFile, dataFiles) {
1203        QString file = dataFile.saveFileName("");
1204        QFileInfo fi(show->dir() + QDir::separator() + file);
1205        if (fi.isFile() || constructName) {
1206            clearArtFileName = show->dir() + QDir::separator() + file;
1207            break;
1208        }
1209    }
1210
1211    return clearArtFileName;
1212}
1213
1214/**
1215 * @brief Get the path to the tv show character art
1216 * @param show TV Show object
1217 * @return Path to character art image
1218 */
1219QString XbmcXml::characterArtImageName(TvShow *show, QList<DataFile> dataFiles, bool constructName)
1220{
1221    QString characterArtFileName;
1222    if (show->dir().isEmpty())
1223        return characterArtFileName;
1224
1225    if (!constructName)
1226        dataFiles = Settings::instance()->dataFiles(DataFileType::TvShowCharacterArt);
1227
1228    foreach (DataFile dataFile, dataFiles) {
1229        QString file = dataFile.saveFileName("");
1230        QFileInfo fi(show->dir() + QDir::separator() + file);
1231        if (fi.isFile() || constructName) {
1232            characterArtFileName = show->dir() + QDir::separator() + file;
1233            break;
1234        }
1235    }
1236
1237    return characterArtFileName;
1238}
1239
1240/**
1241 * @brief Get path to season poster
1242 * @param show
1243 * @param season
1244 * @return
1245 */
1246QString XbmcXml::seasonPosterImageName(TvShow *show, int season, QList<DataFile> dataFiles, bool constructName)
1247{
1248    QString fileName;
1249    if (show->dir().isEmpty())
1250        return fileName;
1251
1252    if (!constructName)
1253        dataFiles = Settings::instance()->dataFiles(DataFileType::TvShowSeasonPoster);
1254
1255    foreach (DataFile dataFile, dataFiles) {
1256        QString file = dataFile.saveFileName("", season);
1257        QFileInfo fi(show->dir() + QDir::separator() + file);
1258        if (fi.isFile() || constructName) {
1259            fileName = show->dir() + QDir::separator() + file;
1260            break;
1261        }
1262    }
1263
1264    return fileName;
1265}
1266
1267QString XbmcXml::seasonBackdropImageName(TvShow *show, int season, QList<DataFile> dataFiles, bool constructName)
1268{
1269    QString fileName;
1270    if (show->dir().isEmpty())
1271        return fileName;
1272
1273    if (!constructName)
1274        dataFiles = Settings::instance()->dataFiles(DataFileType::TvShowSeasonBackdrop);
1275
1276    foreach (DataFile dataFile, dataFiles) {
1277        QString file = dataFile.saveFileName("", season);
1278        QFileInfo fi(show->dir() + QDir::separator() + file);
1279        if (fi.isFile() || constructName) {
1280            fileName = show->dir() + QDir::separator() + file;
1281            break;
1282        }
1283    }
1284
1285    return fileName;
1286}
1287
1288QString XbmcXml::seasonBannerImageName(TvShow *show, int season, QList<DataFile> dataFiles, bool constructName)
1289{
1290    QString fileName;
1291    if (show->dir().isEmpty())
1292        return fileName;
1293
1294    if (!constructName)
1295        dataFiles = Settings::instance()->dataFiles(DataFileType::TvShowSeasonBanner);
1296
1297    foreach (DataFile dataFile, dataFiles) {
1298        QString file = dataFile.saveFileName("", season);
1299        QFileInfo fi(show->dir() + QDir::separator() + file);
1300        if (fi.isFile() || constructName) {
1301            fileName = show->dir() + QDir::separator() + file;
1302            break;
1303        }
1304    }
1305
1306    return fileName;
1307}
1308
1309/**
1310 * @brief Get path to actor image
1311 * @param show
1312 * @param actor
1313 * @return Path to actor image
1314 */
1315QString XbmcXml::actorImageName(TvShow *show, Actor actor)
1316{
1317    if (show->dir().isEmpty(

Large files files are truncated, but you can click here to view the full file